diff --git a/docs/frequently-asked-questions.rst b/docs/frequently-asked-questions.rst index b26ff527f..2c5f86614 100644 --- a/docs/frequently-asked-questions.rst +++ b/docs/frequently-asked-questions.rst @@ -446,6 +446,25 @@ does not fit inside this range, it is truncated. These truncations can have above is necessary to avoid certain attacks. +Why are explicit conversions between fixed-size bytes types and integer types failing? +====================================================================================== + +Since version 0.5.0 explicit conversions between fixed-size byte arrays and integers are only allowed, +if both types have the same size. This prevents unexpected behaviour when truncating or padding. +Such conversions are still possible, but intermediate casts are required that make the desired +truncation and padding convention explicit. See :ref:`types-conversion-elementary-types` for a full +explanation and examples. + + +Why can number literals not be converted to fixed-size bytes types? +=================================================================== + +Since version 0.5.0 only hexadecimal number literals can be converted to fixed-size bytes +types and only if the number of hex digits matches the size of the type. See :ref:`types-conversion-literals` +for a full explanation and examples. + + + More Questions? =============== diff --git a/docs/types.rst b/docs/types.rst index 0e0f4b13d..6024cbb74 100644 --- a/docs/types.rst +++ b/docs/types.rst @@ -1004,6 +1004,8 @@ It is important to note that ``delete a`` really behaves like an assignment to ` .. index:: ! type;conversion, ! cast +.. _types-conversion-elementary-types: + Conversions between Elementary Types ==================================== @@ -1017,9 +1019,7 @@ is possible if it makes sense semantically and no information is lost: ``uint8`` is convertible to ``uint16`` and ``int128`` to ``int256``, but ``int8`` is not convertible to ``uint256`` (because ``uint256`` cannot hold e.g. ``-1``). -Furthermore, unsigned integers can be converted to bytes of the same or larger -size, but not vice-versa. Any type that can be converted to ``uint160`` can also -be converted to ``address``. +Any integer type that can be converted to ``uint160`` can also be converted to ``address``. Explicit Conversions -------------------- @@ -1038,17 +1038,90 @@ a negative ``int8`` to a ``uint``: At the end of this code snippet, ``x`` will have the value ``0xfffff..fd`` (64 hex characters), which is -3 in the two's complement representation of 256 bits. -If a type is explicitly converted to a smaller type, higher-order bits are +If an integer is explicitly converted to a smaller type, higher-order bits are cut off:: uint32 a = 0x12345678; uint16 b = uint16(a); // b will be 0x5678 now -Since 0.5.0 explicit conversions between integers and fixed-size byte arrays -are only allowed, if both have the same size. To convert between integers and -fixed-size byte arrays of different size, they first have to be explicitly -converted to a matching size. This makes alignment and padding explicit:: +If an integer is explicitly converted to a larger type, it is padded on the left (i.e. at the higher order end). +The result of the conversion will compare equal to the original integer. - uint16 x = 0xffff; - bytes32(uint256(x)); // pad on the left - bytes32(bytes2(x)); // pad on the right + uint16 a = 0x1234; + uint32 b = uint32(a); // b will be 0x00001234 now + assert(a == b); + +Fixed-size bytes types behave differently during conversions. They can be thought of as +sequences of individual bytes and converting to a smaller type will cut off the +sequence:: + + bytes2 a = 0x1234; + bytes1 b = bytes1(a); // b will be 0x12 + +If a fixed-size bytes type is explicitly converted to a larger type, it is padded on +the right. Accessing the byte at a fixed index will result in the same value before and +after the conversion (if the index is still in range):: + + bytes2 a = 0x1234; + bytes4 b = bytes4(a); // b will be 0x12340000 + assert(a[0] == b[0]); + assert(a[1] == b[1]); + +Since integers and fixed-size byte arrays behave differently when truncating or +padding, explicit conversions between integers and fixed-size byte arrays are only allowed, +if both have the same size. If you want to convert between integers and fixed-size byte arrays of +different size, you have to use intermediate conversions that make the desired truncation and padding +rules explicit:: + + bytes2 a = 0x1234; + uint32 b = uint16(a); // b will be 0x00001234 + uint32 c = uint32(bytes4(a)); // c will be 0x12340000 + uint8 d = uint8(uint16(a)); // d will be 0x34 + uint8 e = uint8(bytes1(a)); // d will be 0x12 + +.. _types-conversion-literals: + +Conversions between Literals and Elementary Types +================================================= + +Integer Types +------------- + +Decimal and hexadecimal number literals can be implicitly converted to any integer type +that is large enough to represent it without truncation:: + + uint8 a = 12; // fine + uint32 b = 1234; // fine + uint16 c = 0x123456; // fails, since it would have to truncate to 0x3456 + +Fixed-Size Byte Arrays +---------------------- + +Decimal number literals cannot be implicitly converted to fixed-size byte arrays. Hexadecimal +number literals can be, but only if the number of hex digits exactly fits the size of the bytes +type. As an exception both decimal and hexadecimal literals which have a value of zero can be +converted to any fixed-size bytes type:: + + bytes2 a = 54321; // not allowed + bytes2 b = 0x12; // not allowed + bytes2 c = 0x123; // not allowed + bytes2 d = 0x1234; // fine + bytes2 e = 0x0012; // fine + bytes4 f = 0; // fine + bytes4 g = 0x0; // fine + +String literals and hex string literals can be implicitly converted to fixed-size byte arrays, +if their number of characters matches the size of the bytes type:: + + bytes2 a = hex"1234"; // fine + bytes2 b = "xy"; // fine + bytes2 c = hex"12"; // not allowed + bytes2 d = hex"123"; // not allowed + bytes2 e = "x"; // not allowed + bytes2 f = "xyz"; // not allowed + +Addresses +--------- + +As described in :ref:`address_literals`, hex literals of the correct size that pass the checksum +test are of ``address`` type. No other literals can be implicitly converted to the ``address`` type.