diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp index aef782177..914ca19bd 100644 --- a/libsolidity/ast/Types.cpp +++ b/libsolidity/ast/Types.cpp @@ -3017,6 +3017,11 @@ TypePointers FunctionType::parameterTypes() const return TypePointers(m_parameterTypes.cbegin() + 1, m_parameterTypes.cend()); } +TypePointers const& FunctionType::parameterTypesIncludingSelf() const +{ + return m_parameterTypes; +} + string FunctionType::richIdentifier() const { string id = "t_function_"; @@ -3267,8 +3272,7 @@ vector> FunctionType::makeStackItems() const if (m_saltSet) slots.emplace_back("salt", TypeProvider::fixedBytes(32)); if (bound()) - for (auto const& [boundName, boundType]: m_parameterTypes.front()->stackItems()) - slots.emplace_back("self_" + boundName, boundType); + slots.emplace_back("self", m_parameterTypes.front()); return slots; } diff --git a/libsolidity/ast/Types.h b/libsolidity/ast/Types.h index 66df9b249..8d7165989 100644 --- a/libsolidity/ast/Types.h +++ b/libsolidity/ast/Types.h @@ -1244,6 +1244,7 @@ public: static FunctionTypePointer newExpressionType(ContractDefinition const& _contract); TypePointers parameterTypes() const; + TypePointers const& parameterTypesIncludingSelf() const; std::vector parameterNames() const; TypePointers const& returnParameterTypes() const { return m_returnParameterTypes; } /// @returns the list of return parameter types. All dynamically-sized types (this excludes diff --git a/libsolidity/codegen/ABIFunctions.cpp b/libsolidity/codegen/ABIFunctions.cpp index 88e3c39fd..1f3d7a4ff 100644 --- a/libsolidity/codegen/ABIFunctions.cpp +++ b/libsolidity/codegen/ABIFunctions.cpp @@ -42,6 +42,7 @@ string ABIFunctions::tupleEncoder( bool _reversed ) { + solAssert(_givenTypes.size() == _targetTypes.size(), ""); EncodingOptions options; options.encodeAsLibraryTypes = _encodeAsLibraryTypes; options.encodeFunctionFromStack = true; diff --git a/libsolidity/codegen/ir/Common.cpp b/libsolidity/codegen/ir/Common.cpp index 42a4650db..41c711c99 100644 --- a/libsolidity/codegen/ir/Common.cpp +++ b/libsolidity/codegen/ir/Common.cpp @@ -28,7 +28,7 @@ using namespace solidity::frontend; YulArity YulArity::fromType(FunctionType const& _functionType) { return YulArity{ - TupleType(_functionType.parameterTypes()).sizeOnStack(), + TupleType(_functionType.parameterTypesIncludingSelf()).sizeOnStack(), TupleType(_functionType.returnParameterTypes()).sizeOnStack() }; } diff --git a/libsolidity/codegen/ir/IRGeneratorForStatements.cpp b/libsolidity/codegen/ir/IRGeneratorForStatements.cpp index add458d50..5e75f1d17 100644 --- a/libsolidity/codegen/ir/IRGeneratorForStatements.cpp +++ b/libsolidity/codegen/ir/IRGeneratorForStatements.cpp @@ -811,7 +811,6 @@ bool IRGeneratorForStatements::visit(FunctionCall const& _functionCall) if ( functionType && functionType->kind() == FunctionType::Kind::Internal && - !functionType->bound() && IRHelpers::referencedFunctionDeclaration(_functionCall.expression()) ) m_context.internalFunctionCalledDirectly(_functionCall.expression()); @@ -888,8 +887,6 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall) solAssert(functionType->kind() == FunctionType::Kind::Internal || functionType->kind() == FunctionType::Kind::DelegateCall, ""); } } - else - solAssert(!functionType->bound(), ""); switch (functionType->kind()) { @@ -924,19 +921,17 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall) } solAssert(functionDef && functionDef->isImplemented(), ""); + solAssert( + functionDef->parameters().size() == arguments.size() + (functionType->bound() ? 1 : 0), + "" + ); } solAssert(!functionType->takesArbitraryParameters(), ""); vector 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(), ""); + args += IRVariable(_functionCall.expression()).part("self").stackSlots(); for (size_t i = 0; i < arguments.size(); ++i) args += convert(*arguments[i], *parameterTypes[i]).stackSlots(); @@ -1580,6 +1575,23 @@ void IRGeneratorForStatements::endVisit(MemberAccess const& _memberAccess) Type::Category::Array, Type::Category::FixedBytes, }).count(objectCategory) > 0, ""); + + define(IRVariable(_memberAccess).part("self"), _memberAccess.expression()); + auto const& functionDefinition = dynamic_cast(memberFunctionType->declaration()); + solAssert(*_memberAccess.annotation().requiredLookup == VirtualLookup::Static, ""); + if (memberFunctionType->kind() == FunctionType::Kind::Internal) + { + define(IRVariable(_memberAccess).part("functionIdentifier")) << to_string(functionDefinition.id()) << "\n"; + m_context.internalFunctionAccessed(_memberAccess, functionDefinition); + } + else + { + solAssert(memberFunctionType->kind() == FunctionType::Kind::DelegateCall, ""); + auto contract = dynamic_cast(functionDefinition.scope()); + solAssert(contract && contract->isLibrary(), ""); + define(IRVariable(_memberAccess).part("address")) << linkerSymbol(*contract) << "\n"; + define(IRVariable(_memberAccess).part("functionSelector")) << memberFunctionType->externalIdentifier(); + } return; } @@ -2283,15 +2295,21 @@ void IRGeneratorForStatements::appendExternalFunctionCall( "Can only be used for regular external calls." ); - solUnimplementedAssert(!funType.bound(), ""); - bool const isDelegateCall = funKind == FunctionType::Kind::DelegateCall; bool const useStaticCall = funType.stateMutability() <= StateMutability::View && m_context.evmVersion().hasStaticCall(); ReturnInfo const returnInfo{m_context.evmVersion(), funType}; + TypePointers parameterTypes = funType.parameterTypes(); TypePointers argumentTypes; vector argumentStrings; + if (funType.bound()) + { + parameterTypes.insert(parameterTypes.begin(), funType.selfType()); + argumentTypes.emplace_back(funType.selfType()); + argumentStrings += IRVariable(_functionCall.expression()).part("self").stackSlots(); + } + for (auto const& arg: _arguments) { argumentTypes.emplace_back(&type(*arg)); @@ -2370,7 +2388,7 @@ void IRGeneratorForStatements::appendExternalFunctionCall( bool encodeForLibraryCall = funKind == FunctionType::Kind::DelegateCall; solAssert(funType.padArguments(), ""); - templ("encodeArgs", m_context.abiFunctions().tupleEncoder(argumentTypes, funType.parameterTypes(), encodeForLibraryCall)); + templ("encodeArgs", m_context.abiFunctions().tupleEncoder(argumentTypes, parameterTypes, encodeForLibraryCall)); templ("argumentString", joinHumanReadablePrefixed(argumentStrings)); solAssert(!isDelegateCall || !funType.valueSet(), "Value set for delegatecall"); diff --git a/test/cmdlineTests/yul_unimplemented/err b/test/cmdlineTests/yul_unimplemented/err index dd3572497..1ce8608a6 100644 --- a/test/cmdlineTests/yul_unimplemented/err +++ b/test/cmdlineTests/yul_unimplemented/err @@ -1,5 +1,5 @@ Error (1834): Unimplemented feature error in - --> yul_unimplemented/input.sol:8:9: + --> yul_unimplemented/input.sol:5:16: | -8 | x.f(); - | ^^^^^ +5 | return type(test).name; + | ^^^^^^^^^^^^^^^ diff --git a/test/cmdlineTests/yul_unimplemented/input.sol b/test/cmdlineTests/yul_unimplemented/input.sol index 811f3e4a0..eab2109d8 100644 --- a/test/cmdlineTests/yul_unimplemented/input.sol +++ b/test/cmdlineTests/yul_unimplemented/input.sol @@ -1,10 +1,7 @@ // SPDX-License-Identifier: GPL-3.0 pragma solidity >=0.0; -library L { function f(uint) public {} } contract test { - using L for uint; - function f() public { - uint x; - x.f(); + function f() public pure returns (string memory) { + return type(test).name; } } \ No newline at end of file diff --git a/test/libsolidity/semanticTests/libraries/bound_returning_calldata_external.sol b/test/libsolidity/semanticTests/libraries/bound_returning_calldata_external.sol index 3114f8510..f9808e100 100644 --- a/test/libsolidity/semanticTests/libraries/bound_returning_calldata_external.sol +++ b/test/libsolidity/semanticTests/libraries/bound_returning_calldata_external.sol @@ -14,6 +14,7 @@ contract C { } } // ==== +// compileViaYul: also // EVMVersion: >homestead // ---- // library: D diff --git a/test/libsolidity/semanticTests/libraries/bound_to_calldata_external.sol b/test/libsolidity/semanticTests/libraries/bound_to_calldata_external.sol index 2f8c7c7f3..db5eaeea3 100644 --- a/test/libsolidity/semanticTests/libraries/bound_to_calldata_external.sol +++ b/test/libsolidity/semanticTests/libraries/bound_to_calldata_external.sol @@ -14,6 +14,7 @@ contract C { } } // ==== +// compileViaYul: also // EVMVersion: >homestead // ---- // library: D diff --git a/test/libsolidity/semanticTests/libraries/internal_call_bound_with_parentheses.sol b/test/libsolidity/semanticTests/libraries/internal_call_bound_with_parentheses.sol new file mode 100644 index 000000000..6d073c627 --- /dev/null +++ b/test/libsolidity/semanticTests/libraries/internal_call_bound_with_parentheses.sol @@ -0,0 +1,27 @@ +library L { + struct S { + uint256[] data; + } + + function f(S memory _s) internal { + _s.data[3] += 2; + } +} + + +contract C { + using L for L.S; + + function f() public returns (uint256) { + L.S memory x; + x.data = new uint256[](7); + x.data[3] = 8; + (x.f)(); + return x.data[3]; + } +} + +// ==== +// compileViaYul: also +// ---- +// f() -> 0x0a diff --git a/test/libsolidity/semanticTests/libraries/internal_library_function_bound_to_storage_string.sol b/test/libsolidity/semanticTests/libraries/internal_library_function_bound_to_storage_string.sol new file mode 100644 index 000000000..6c15fa735 --- /dev/null +++ b/test/libsolidity/semanticTests/libraries/internal_library_function_bound_to_storage_string.sol @@ -0,0 +1,22 @@ +library L { + function f(string memory a) internal pure returns (string memory) { + return a; + } + function g(string storage a) internal pure returns (string memory) { + return a; + } +} + +contract C { + using L for string; + string s; + + function test(string calldata x) public returns (string memory, string memory) { + s = x; + return (s.f(), s.g()); + } +} +// ==== +// compileViaYul: also +// ---- +// test(string): 0x20, 3, "def" -> 0x40, 0x80, 3, "def", 3, "def"