diff --git a/Changelog.md b/Changelog.md index 70cde92e9..23445afc3 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,5 +1,7 @@ ### 0.8.0 (unreleased) +Breaking Changes: + * Type System: Unary negation can only be used on signed integers, not on unsigned integers. ### 0.7.2 (unreleased) diff --git a/docs/types/value-types.rst b/docs/types/value-types.rst index 77e74746c..e05685c9c 100644 --- a/docs/types/value-types.rst +++ b/docs/types/value-types.rst @@ -38,7 +38,7 @@ Operators: * Comparisons: ``<=``, ``<``, ``==``, ``!=``, ``>=``, ``>`` (evaluate to ``bool``) * Bit operators: ``&``, ``|``, ``^`` (bitwise exclusive or), ``~`` (bitwise negation) * Shift operators: ``<<`` (left shift), ``>>`` (right shift) -* Arithmetic operators: ``+``, ``-``, unary ``-``, ``*``, ``/``, ``%`` (modulo), ``**`` (exponentiation) +* Arithmetic operators: ``+``, ``-``, unary ``-`` (only for signed integers), ``*``, ``/``, ``%`` (modulo), ``**`` (exponentiation) For an integer type ``X``, you can use ``type(X).min`` and ``type(X).max`` to access the minimum and maximum value representable by the type. @@ -83,8 +83,8 @@ for example ``uint256(0) - uint256(1) == 2**256 - 1``. You have to take these ov into account when designing safe smart contracts. The expression ``-x`` is equivalent to ``(T(0) - x)`` where -``T`` is the type of ``x``. This means that ``-x`` will not be negative -if the type of ``x`` is an unsigned integer type. Also, ``-x`` can be +``T`` is the type of ``x``. It can only be applied to signed types. +The value of ``-x`` can be positive if ``x`` is negative. There is another caveat also resulting from two's complement representation:: diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp index b0418dee5..46f4afb72 100644 --- a/libsolidity/ast/Types.cpp +++ b/libsolidity/ast/Types.cpp @@ -627,9 +627,10 @@ TypeResult IntegerType::unaryOperatorResult(Token _operator) const // "delete" is ok for all integer types if (_operator == Token::Delete) return TypeResult{TypeProvider::emptyTuple()}; - // we allow -, ++ and -- - else if (_operator == Token::Sub || _operator == Token::Inc || - _operator == Token::Dec || _operator == Token::BitNot) + // unary negation only on signed types + else if (_operator == Token::Sub) + return isSigned() ? TypeResult{this} : TypeResult::err("Unary negation is only allowed for signed integers."); + else if (_operator == Token::Inc || _operator == Token::Dec || _operator == Token::BitNot) return TypeResult{this}; else return TypeResult::err(""); diff --git a/test/libsolidity/SolidityEndToEndTest.cpp b/test/libsolidity/SolidityEndToEndTest.cpp index 5dc022137..06ddc0271 100644 --- a/test/libsolidity/SolidityEndToEndTest.cpp +++ b/test/libsolidity/SolidityEndToEndTest.cpp @@ -778,7 +778,7 @@ BOOST_AUTO_TEST_CASE(sign_extension) function run() public returns(uint256 y) { int64 x = -int32(0xff); if (x >= 0xff) return 0; - return -uint256(x); + return 0 - uint256(x); } } )"; diff --git a/test/libsolidity/semanticTests/storage/packed_storage_signed.sol b/test/libsolidity/semanticTests/storage/packed_storage_signed.sol index 8db0c757e..99501f73e 100644 --- a/test/libsolidity/semanticTests/storage/packed_storage_signed.sol +++ b/test/libsolidity/semanticTests/storage/packed_storage_signed.sol @@ -9,7 +9,7 @@ contract C { returns (uint256 x1, uint256 x2, uint256 x3, uint256 x4) { a = -2; - b = -uint8(a) * 2; + b = (0 - uint8(a)) * 2; c = a * int8(120) * int8(121); x1 = uint256(a); x2 = b; diff --git a/test/libsolidity/syntaxTests/negation.sol b/test/libsolidity/syntaxTests/negation.sol new file mode 100644 index 000000000..f79920954 --- /dev/null +++ b/test/libsolidity/syntaxTests/negation.sol @@ -0,0 +1,9 @@ +contract test { + function f() public pure { + int x; + uint y = uint(-x); + -y; + } +} +// ---- +// TypeError 4907: (97-99): Unary operator - cannot be applied to type uint256