diff options
20 files changed, 264 insertions, 7 deletions
diff --git a/docs/050-breaking-changes.rst b/docs/050-breaking-changes.rst index 7b227297..48112cd9 100644 --- a/docs/050-breaking-changes.rst +++ b/docs/050-breaking-changes.rst @@ -12,7 +12,8 @@ For the full list check Contracts compiled with Solidity v0.5.0 can still interface with contracts and even libraries compiled with older versions without recompiling or redeploying them. Changing the interfaces to include data locations and - visibility and mutability specifiers suffices. + visibility and mutability specifiers suffices. See the + :ref:`Interoperability With Older Contracts <interoperability>` section below. Semantic Only Changes ===================== @@ -101,6 +102,14 @@ For most of the topics the compiler will provide suggestions. ``c.transfer(...)`` to ``address(c).transfer(...)``, and ``c.balance`` to ``address(c).balance``. +* Explicit conversions between unrelated contract types are now disallowed. You can only + convert from a contract type to one of its base or ancestor types. If you are sure that + a contract is compatible with the contract type you want to convert to, although it does not + inherit from it, you can work around this by converting to ``address`` first. + Example: if ``A`` and ``B`` are contract types, ``B`` does not inherit from ``A`` and + ``b`` is a contract of type ``B``, you can still convert ``b`` to type ``A`` using ``A(address(b))``. + Note that you still need to watch out for matching payable fallback functions, as explained below. + * The ``address`` type was split into ``address`` and ``address payable``, where only ``address payable`` provides the ``transfer`` function. An ``address payable`` can be directly converted to an ``address``, but the @@ -272,6 +281,83 @@ Syntax ``override``, ``partial``, ``promise``, ``reference``, ``sealed``, ``sizeof``, ``supports``, ``typedef`` and ``unchecked``. +.. _interoperability: + +Interoperability With Older Contracts +===================================== + +It is still possible to interface with contracts written for Solidity versions prior to +v0.5.0 (or the other way around) by defining interfaces for them. +Consider you have the following pre-0.5.0 contract already deployed: + +:: + + // This will not compile with the current version of the compiler + pragma solidity ^0.4.25; + contract OldContract { + function someOldFunction(uint8 a) { + //... + } + function anotherOldFunction() constant returns (bool) { + //... + } + // ... + } + +This will no longer compile with Solidity v0.5.0. However, you can define a compatible interface for it: + +:: + + pragma solidity >0.4.99 <0.6.0; + interface OldContract { + function someOldFunction(uint8 a) external; + function anotherOldFunction() external returns (bool); + } + +Note that we did not declare ``anotherOldFunction`` to be ``view``, despite it being declared ``constant`` in the original +contract. This is due to the fact that starting with Solidity v0.5.0 ``staticcall`` is used to call ``view`` functions. +Prior to v0.5.0 the ``constant`` keyword was not enforced, so calling a function declared ``constant`` with ``staticcall`` +may still revert, since the ``constant`` function may still attempt to modify storage. Consequently, when defining an +interface for older contracts, you should only use ``view`` in place of ``constant`` in case you are absolutely sure that +the function will work with ``staticcall``. + +Given the interface defined above, you can now easily use the already deployed pre-0.5.0 contract: + +:: + + pragma solidity >0.4.99 <0.6.0; + + interface OldContract { + function someOldFunction(uint8 a) external; + function anotherOldFunction() external returns (bool); + } + + contract NewContract { + function doSomething(OldContract a) public returns (bool) { + a.someOldFunction(0x42); + return a.anotherOldFunction(); + } + } + +Similarly, pre-0.5.0 libraries can be used by defining the functions of the library without implementation and +supplying the address of the pre-0.5.0 library during linking (see :ref:`commandline-compiler` for how to use the +commandline compiler for linking): + +:: + + pragma solidity >0.4.99 <0.6.0; + + library OldLibrary { + function someFunction(uint8 a) public returns(bool); + } + + contract NewContract { + function f(uint8 a) public returns (bool) { + return OldLibrary.someFunction(a); + } + } + + Example ======= diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp index d5d11478..6c3863e6 100644 --- a/libsolidity/ast/Types.cpp +++ b/libsolidity/ast/Types.cpp @@ -1144,10 +1144,10 @@ TypePointer RationalNumberType::binaryOperatorResult(Token _operator, TypePointe bigint denominator = optimizedPow(m_value.denominator(), absExp); if (exp >= 0) - value = rational(numerator, denominator); + value = makeRational(numerator, denominator); else // invert - value = rational(denominator, numerator); + value = makeRational(denominator, numerator); } break; } diff --git a/libsolidity/ast/Types.h b/libsolidity/ast/Types.h index b764717f..24ace447 100644 --- a/libsolidity/ast/Types.h +++ b/libsolidity/ast/Types.h @@ -51,6 +51,15 @@ using FunctionTypePointer = std::shared_ptr<FunctionType const>; using TypePointers = std::vector<TypePointer>; using rational = boost::rational<dev::bigint>; +inline rational makeRational(bigint const& _numerator, bigint const& _denominator) +{ + solAssert(_denominator != 0, "division by zero"); + // due to a bug in certain versions of boost the denominator has to be positive + if (_denominator < 0) + return rational(-_numerator, -_denominator); + else + return rational(_numerator, _denominator); +} enum class DataLocation { Storage, CallData, Memory }; diff --git a/libsolidity/codegen/CompilerUtils.cpp b/libsolidity/codegen/CompilerUtils.cpp index d89d023e..90eb74fe 100644 --- a/libsolidity/codegen/CompilerUtils.cpp +++ b/libsolidity/codegen/CompilerUtils.cpp @@ -1016,8 +1016,22 @@ void CompilerUtils::convertType( } else { - // All other types should not be convertible to non-equal types. - solAssert(_typeOnStack == _targetType, "Invalid type conversion requested."); + if (stackTypeCategory == Type::Category::Function && targetTypeCategory == Type::Category::Function) + { + FunctionType const& typeOnStack = dynamic_cast<FunctionType const&>(_typeOnStack); + FunctionType const& targetType = dynamic_cast<FunctionType const&>(_targetType); + solAssert( + typeOnStack.isImplicitlyConvertibleTo(targetType) && + typeOnStack.sizeOnStack() == targetType.sizeOnStack() && + (typeOnStack.kind() == FunctionType::Kind::Internal || typeOnStack.kind() == FunctionType::Kind::External) && + typeOnStack.kind() == targetType.kind(), + "Invalid function type conversion requested." + ); + } + else + // All other types should not be convertible to non-equal types. + solAssert(_typeOnStack == _targetType, "Invalid type conversion requested."); + if (_cleanupNeeded && _targetType.canBeStored() && _targetType.storageBytes() < 32) m_context << ((u256(1) << (8 * _targetType.storageBytes())) - 1) diff --git a/libyul/optimiser/Suite.cpp b/libyul/optimiser/Suite.cpp index c7339d2e..7d52a5a8 100644 --- a/libyul/optimiser/Suite.cpp +++ b/libyul/optimiser/Suite.cpp @@ -33,6 +33,7 @@ #include <libyul/optimiser/CommonSubexpressionEliminator.h> #include <libyul/optimiser/SSATransform.h> #include <libyul/optimiser/RedundantAssignEliminator.h> +#include <libyul/optimiser/VarDeclPropagator.h> #include <libsolidity/inlineasm/AsmAnalysisInfo.h> #include <libsolidity/inlineasm/AsmData.h> @@ -65,6 +66,7 @@ void OptimiserSuite::run( ExpressionSplitter{dispenser}(ast); SSATransform::run(ast, dispenser); RedundantAssignEliminator::run(ast); + VarDeclPropagator{}(ast); RedundantAssignEliminator::run(ast); CommonSubexpressionEliminator{}(ast); @@ -90,21 +92,26 @@ void OptimiserSuite::run( RedundantAssignEliminator::run(ast); CommonSubexpressionEliminator{}(ast); FullInliner{ast, dispenser}.run(); + VarDeclPropagator{}(ast); SSATransform::run(ast, dispenser); RedundantAssignEliminator::run(ast); + VarDeclPropagator{}(ast); RedundantAssignEliminator::run(ast); ExpressionSimplifier::run(ast); CommonSubexpressionEliminator{}(ast); SSATransform::run(ast, dispenser); RedundantAssignEliminator::run(ast); + VarDeclPropagator{}(ast); RedundantAssignEliminator::run(ast); UnusedPruner::runUntilStabilised(ast, reservedIdentifiers); } ExpressionJoiner::run(ast); + VarDeclPropagator{}(ast); UnusedPruner::runUntilStabilised(ast); ExpressionJoiner::run(ast); UnusedPruner::runUntilStabilised(ast); ExpressionJoiner::run(ast); + VarDeclPropagator{}(ast); UnusedPruner::runUntilStabilised(ast); ExpressionJoiner::run(ast); UnusedPruner::runUntilStabilised(ast); diff --git a/test/libsolidity/syntaxTests/conversion/function_type_nonpayable_payable.sol b/test/libsolidity/syntaxTests/conversion/function_type_nonpayable_payable.sol new file mode 100644 index 00000000..75f7a953 --- /dev/null +++ b/test/libsolidity/syntaxTests/conversion/function_type_nonpayable_payable.sol @@ -0,0 +1,10 @@ +contract C { + function h() external { + } + function f() view external returns (bytes4) { + function () payable external g = this.h; + return g.selector; + } +} +// ---- +// TypeError: (105-144): Type function () external is not implicitly convertible to expected type function () payable external. diff --git a/test/libsolidity/syntaxTests/conversion/function_type_nonpayable_pure.sol b/test/libsolidity/syntaxTests/conversion/function_type_nonpayable_pure.sol new file mode 100644 index 00000000..8d1b08aa --- /dev/null +++ b/test/libsolidity/syntaxTests/conversion/function_type_nonpayable_pure.sol @@ -0,0 +1,10 @@ +contract C { + function h() external { + } + function f() view external returns (bytes4) { + function () pure external g = this.h; + return g.selector; + } +} +// ---- +// TypeError: (105-141): Type function () external is not implicitly convertible to expected type function () pure external. diff --git a/test/libsolidity/syntaxTests/conversion/function_type_nonpayable_view.sol b/test/libsolidity/syntaxTests/conversion/function_type_nonpayable_view.sol new file mode 100644 index 00000000..535d6c77 --- /dev/null +++ b/test/libsolidity/syntaxTests/conversion/function_type_nonpayable_view.sol @@ -0,0 +1,10 @@ +contract C { + function h() external { + } + function f() view external returns (bytes4) { + function () view external g = this.h; + return g.selector; + } +} +// ---- +// TypeError: (105-141): Type function () external is not implicitly convertible to expected type function () view external. diff --git a/test/libsolidity/syntaxTests/conversion/function_type_payable_nonpayable.sol b/test/libsolidity/syntaxTests/conversion/function_type_payable_nonpayable.sol new file mode 100644 index 00000000..299d7e30 --- /dev/null +++ b/test/libsolidity/syntaxTests/conversion/function_type_payable_nonpayable.sol @@ -0,0 +1,8 @@ +contract C { + function h() payable external { + } + function f() view external returns (bytes4) { + function () external g = this.h; + return g.selector; + } +} diff --git a/test/libsolidity/syntaxTests/conversion/function_type_payable_pure.sol b/test/libsolidity/syntaxTests/conversion/function_type_payable_pure.sol new file mode 100644 index 00000000..78bada51 --- /dev/null +++ b/test/libsolidity/syntaxTests/conversion/function_type_payable_pure.sol @@ -0,0 +1,10 @@ +contract C { + function h() payable external { + } + function f() view external returns (bytes4) { + function () pure external g = this.h; + return g.selector; + } +} +// ---- +// TypeError: (113-149): Type function () payable external is not implicitly convertible to expected type function () pure external. diff --git a/test/libsolidity/syntaxTests/conversion/function_type_payable_view.sol b/test/libsolidity/syntaxTests/conversion/function_type_payable_view.sol new file mode 100644 index 00000000..f12cb301 --- /dev/null +++ b/test/libsolidity/syntaxTests/conversion/function_type_payable_view.sol @@ -0,0 +1,10 @@ +contract C { + function h() payable external { + } + function f() view external returns (bytes4) { + function () view external g = this.h; + return g.selector; + } +} +// ---- +// TypeError: (113-149): Type function () payable external is not implicitly convertible to expected type function () view external. diff --git a/test/libsolidity/syntaxTests/conversion/function_type_pure_nonpayable.sol b/test/libsolidity/syntaxTests/conversion/function_type_pure_nonpayable.sol new file mode 100644 index 00000000..7742e0c1 --- /dev/null +++ b/test/libsolidity/syntaxTests/conversion/function_type_pure_nonpayable.sol @@ -0,0 +1,8 @@ +contract C { + function h() pure external { + } + function f() view external returns (bytes4) { + function () external g = this.h; + return g.selector; + } +} diff --git a/test/libsolidity/syntaxTests/conversion/function_type_pure_payable.sol b/test/libsolidity/syntaxTests/conversion/function_type_pure_payable.sol new file mode 100644 index 00000000..cd4e9b4e --- /dev/null +++ b/test/libsolidity/syntaxTests/conversion/function_type_pure_payable.sol @@ -0,0 +1,10 @@ +contract C { + function h() pure external { + } + function f() view external returns (bytes4) { + function () payable external g = this.h; + return g.selector; + } +} +// ---- +// TypeError: (110-149): Type function () pure external is not implicitly convertible to expected type function () payable external. diff --git a/test/libsolidity/syntaxTests/conversion/function_type_pure_view.sol b/test/libsolidity/syntaxTests/conversion/function_type_pure_view.sol new file mode 100644 index 00000000..578ecdbd --- /dev/null +++ b/test/libsolidity/syntaxTests/conversion/function_type_pure_view.sol @@ -0,0 +1,8 @@ +contract C { + function h() pure external { + } + function f() view external returns (bytes4) { + function () view external g = this.h; + return g.selector; + } +} diff --git a/test/libsolidity/syntaxTests/conversion/function_type_same.sol b/test/libsolidity/syntaxTests/conversion/function_type_same.sol new file mode 100644 index 00000000..c5ebe1ca --- /dev/null +++ b/test/libsolidity/syntaxTests/conversion/function_type_same.sol @@ -0,0 +1,14 @@ +contract C { + int dummy; + function h_nonpayable() external { dummy = 1; } + function h_payable() payable external {} + function h_view() view external { dummy; } + function h_pure() pure external {} + function f() view external { + function () external g_nonpayable = this.h_nonpayable; g_nonpayable; + function () payable external g_payable = this.h_payable; g_payable; + function () view external g_view = this.h_view; g_view; + function () pure external g_pure = this.h_pure; g_pure; + } +} +// ---- diff --git a/test/libsolidity/syntaxTests/conversion/function_type_view_nonpayable.sol b/test/libsolidity/syntaxTests/conversion/function_type_view_nonpayable.sol new file mode 100644 index 00000000..f52aece0 --- /dev/null +++ b/test/libsolidity/syntaxTests/conversion/function_type_view_nonpayable.sol @@ -0,0 +1,10 @@ +contract C { + int dummy; + function h() view external { + dummy; + } + function f() view external returns (bytes4) { + function () external g = this.h; + return g.selector; + } +} diff --git a/test/libsolidity/syntaxTests/conversion/function_type_view_payable.sol b/test/libsolidity/syntaxTests/conversion/function_type_view_payable.sol new file mode 100644 index 00000000..3bf4bac2 --- /dev/null +++ b/test/libsolidity/syntaxTests/conversion/function_type_view_payable.sol @@ -0,0 +1,10 @@ +contract C { + function h() view external { + } + function f() view external returns (bytes4) { + function () payable external g = this.h; + return g.selector; + } +} +// ---- +// TypeError: (110-149): Type function () view external is not implicitly convertible to expected type function () payable external. diff --git a/test/libsolidity/syntaxTests/conversion/function_type_view_pure.sol b/test/libsolidity/syntaxTests/conversion/function_type_view_pure.sol new file mode 100644 index 00000000..c567a2c8 --- /dev/null +++ b/test/libsolidity/syntaxTests/conversion/function_type_view_pure.sol @@ -0,0 +1,10 @@ +contract C { + function h() view external { + } + function f() view external returns (bytes4) { + function () pure external g = this.h; + return g.selector; + } +} +// ---- +// TypeError: (110-146): Type function () view external is not implicitly convertible to expected type function () pure external. diff --git a/test/libsolidity/syntaxTests/types/rational_negative_numerator_negative_exp.sol b/test/libsolidity/syntaxTests/types/rational_negative_numerator_negative_exp.sol new file mode 100644 index 00000000..b694992c --- /dev/null +++ b/test/libsolidity/syntaxTests/types/rational_negative_numerator_negative_exp.sol @@ -0,0 +1,5 @@ +contract C { + function f() public pure returns (int) { + return (-1 / 2) ** -1; + } +} diff --git a/test/tools/yulopti.cpp b/test/tools/yulopti.cpp index 83c54959..348c5f4a 100644 --- a/test/tools/yulopti.cpp +++ b/test/tools/yulopti.cpp @@ -45,6 +45,7 @@ #include <libyul/optimiser/ExpressionJoiner.h> #include <libyul/optimiser/RedundantAssignEliminator.h> #include <libyul/optimiser/SSATransform.h> +#include <libyul/optimiser/VarDeclPropagator.h> #include <libdevcore/JSON.h> @@ -120,8 +121,9 @@ public: m_nameDispenser = make_shared<NameDispenser>(*m_ast); disambiguated = true; } - cout << "(q)quit/(f)flatten/(c)se/(x)plit/(j)oin/(g)rouper/(h)oister/" << endl; - cout << " (e)xpr inline/(i)nline/(s)implify/(u)nusedprune/ss(a) transform/(r)edundant assign elim.? "; + cout << "(q)quit/(f)flatten/(c)se/propagate var(d)ecls/(x)plit/(j)oin/(g)rouper/(h)oister/" << endl; + cout << " (e)xpr inline/(i)nline/(s)implify/(u)nusedprune/ss(a) transform/" << endl; + cout << " (r)edundant assign elim./re(m)aterializer? "; cout.flush(); int option = readStandardInputChar(); cout << ' ' << char(option) << endl; @@ -135,6 +137,9 @@ public: case 'c': (CommonSubexpressionEliminator{})(*m_ast); break; + case 'd': + (VarDeclPropagator{})(*m_ast); + break; case 'x': ExpressionSplitter{*m_nameDispenser}(*m_ast); break; @@ -165,6 +170,9 @@ public: case 'r': RedundantAssignEliminator::run(*m_ast); break; + case 'm': + Rematerialiser{}(*m_ast); + break; default: cout << "Unknown option." << endl; } |