Merge pull request #12708 from nishant-sachdeva/warn_when_rationals_implicitly_converting_to_mobile_type

Document behaviour of ternary operator of literals.
This commit is contained in:
chriseth 2022-03-14 10:25:30 +01:00 committed by GitHub
commit 401a0465d7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 79 additions and 1 deletions

View File

@ -26,6 +26,23 @@ except for comparison operators where the result is always ``bool``.
The operators ``**`` (exponentiation), ``<<`` and ``>>`` use the type of the
left operand for the operation and the result.
Ternary Operator
----------------
The ternary operator is used in expressions of the form ``<expression> ? <trueExpression> : <falseExpression>``.
It evaluates one of the latter two given expressions depending upon the result of the evaluation of the main ``<expression>``.
If ``<expression>`` evaluates to ``true``, then ``<trueExpression>`` will be evaluated, otherwise ``<falseExpression>`` is evaluated.
The result of the ternary operator does not have a rational number type, even if all of its operands are rational number literals.
The result type is determined from the types of the two operands in the same way as above, converting to their mobile type first if required.
As a consequence, ``255 + (true ? 1 : 0)`` will revert due to arithmetic overflow.
The reason is that ``(true ? 1 : 0)`` is of ``uint8`` type, which forces the addition to be performed in ``uint8`` as well,
and 256 exceeds the range allowed for this type.
Another consequence is that an expression like ``1.5 + 1.5`` is valid but ``1.5 + (true ? 1.5 : 2.5)`` is not.
This is because the former is a rational expression evaluated in unlimited precision and only its final value matters.
The latter involves a conversion of a fractional rational number to an integer, which is currently disallowed.
.. index:: assignment, lvalue, ! compound operators
Compound and Increment/Decrement Operators

View File

@ -463,7 +463,7 @@ There is no additional semantic meaning added to a number literal containing und
the underscores are ignored.
Number literal expressions retain arbitrary precision until they are converted to a non-literal type (i.e. by
using them together with a non-literal expression or by explicit conversion).
using them together with anything else than a number literal expression (like boolean literals) or by explicit conversion).
This means that computations do not overflow and divisions do not truncate
in number literal expressions.
@ -471,6 +471,15 @@ For example, ``(2**800 + 1) - 2**800`` results in the constant ``1`` (of type ``
although intermediate results would not even fit the machine word size. Furthermore, ``.5 * 8`` results
in the integer ``4`` (although non-integers were used in between).
.. warning::
While most operators produce a literal expression when applied to literals, there are certain operators that do not follow this pattern:
- Ternary operator (``... ? ... : ...``),
- Array subscript (``<array>[<index>]``).
You might expect expressions like ``255 + (true ? 1 : 0)`` or ``255 + [1, 2, 3][0]`` to be equivalent to using the literal 256
directly, but in fact they are computed within the type ``uint8`` and can overflow.
Any operator that can be applied to integers can also be applied to number literal expressions as
long as the operands are integers. If any of the two is fractional, bit operations are disallowed
and exponentiation is disallowed if the exponent is fractional (because that might result in

View File

@ -0,0 +1,19 @@
contract TestTernary
{
function h() pure public returns (uint16 b)
{
b = (true ? 63 : 255) + (false ? 63 : 255);
}
function g() pure public returns (uint16 a)
{
bool t = true;
bool f = false;
a = (t ? 63 : 255) + (f ? 63 : 255);
}
}
// ====
// compileViaYul: also
// ----
// g() -> FAILURE, hex"4e487b71", 0x11
// h() -> FAILURE, hex"4e487b71", 0x11

View File

@ -0,0 +1,33 @@
contract TestTernary
{
function g() pure public
{
bool t = true;
bool f = false;
uint8 v255 = 255;
uint8 v63 = 63;
uint8 a;
// Currently none of these should produce errors or warnings.
// The result of the operator is always a limited-precision integer, even if all arguments are literals.
a = (t ? 63 : 255) + (f ? 63 : 255);
a = (t ? 0x3f : 0xff) + (f ? 0x3f : 0xff);
a = (t ? uint8(63) : 255) + (f ? 63 : uint8(255));
a = (t ? v63 : 255) + (f ? 63 : v255);
a = (true ? 63 : 255) + (false ? 63 : 255);
a = (true ? 0x3f : 0xff) + (false ? 0x3f : 0xff);
a = (true ? uint8(63) : 255) + (false ? 63 : uint8(255));
a = (true ? v63 : 255) + (false ? 63 : v255);
a = (t ? 63 : 255) - (f ? 63 : 255);
a = (t ? 63 : 255) * (f ? 63 : 255);
a = (t ? 63 : 255) / (f ? 63 : 255);
a = (t ? (true ? 63 : 255) : (false ? 63 : 255)) + (f ? (t ? 63 : 255) : (f ? 63 : 255));
a = uint8(t ? 63 : 255) + uint8(f ? 63 : 255);
}
}
// ----