Skip to content

Commit 401a046

Browse files
authored
Merge pull request #12708 from nishant-sachdeva/warn_when_rationals_implicitly_converting_to_mobile_type
Document behaviour of ternary operator of literals.
2 parents 2696377 + b7a9daa commit 401a046

File tree

4 files changed

+79
-1
lines changed

4 files changed

+79
-1
lines changed

docs/types/operators.rst

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,23 @@ except for comparison operators where the result is always ``bool``.
2626
The operators ``**`` (exponentiation), ``<<`` and ``>>`` use the type of the
2727
left operand for the operation and the result.
2828

29+
Ternary Operator
30+
----------------
31+
The ternary operator is used in expressions of the form ``<expression> ? <trueExpression> : <falseExpression>``.
32+
It evaluates one of the latter two given expressions depending upon the result of the evaluation of the main ``<expression>``.
33+
If ``<expression>`` evaluates to ``true``, then ``<trueExpression>`` will be evaluated, otherwise ``<falseExpression>`` is evaluated.
34+
35+
The result of the ternary operator does not have a rational number type, even if all of its operands are rational number literals.
36+
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.
37+
38+
As a consequence, ``255 + (true ? 1 : 0)`` will revert due to arithmetic overflow.
39+
The reason is that ``(true ? 1 : 0)`` is of ``uint8`` type, which forces the addition to be performed in ``uint8`` as well,
40+
and 256 exceeds the range allowed for this type.
41+
42+
Another consequence is that an expression like ``1.5 + 1.5`` is valid but ``1.5 + (true ? 1.5 : 2.5)`` is not.
43+
This is because the former is a rational expression evaluated in unlimited precision and only its final value matters.
44+
The latter involves a conversion of a fractional rational number to an integer, which is currently disallowed.
45+
2946
.. index:: assignment, lvalue, ! compound operators
3047

3148
Compound and Increment/Decrement Operators

docs/types/value-types.rst

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -463,14 +463,23 @@ There is no additional semantic meaning added to a number literal containing und
463463
the underscores are ignored.
464464

465465
Number literal expressions retain arbitrary precision until they are converted to a non-literal type (i.e. by
466-
using them together with a non-literal expression or by explicit conversion).
466+
using them together with anything else than a number literal expression (like boolean literals) or by explicit conversion).
467467
This means that computations do not overflow and divisions do not truncate
468468
in number literal expressions.
469469

470470
For example, ``(2**800 + 1) - 2**800`` results in the constant ``1`` (of type ``uint8``)
471471
although intermediate results would not even fit the machine word size. Furthermore, ``.5 * 8`` results
472472
in the integer ``4`` (although non-integers were used in between).
473473

474+
.. warning::
475+
While most operators produce a literal expression when applied to literals, there are certain operators that do not follow this pattern:
476+
477+
- Ternary operator (``... ? ... : ...``),
478+
- Array subscript (``<array>[<index>]``).
479+
480+
You might expect expressions like ``255 + (true ? 1 : 0)`` or ``255 + [1, 2, 3][0]`` to be equivalent to using the literal 256
481+
directly, but in fact they are computed within the type ``uint8`` and can overflow.
482+
474483
Any operator that can be applied to integers can also be applied to number literal expressions as
475484
long as the operands are integers. If any of the two is fractional, bit operations are disallowed
476485
and exponentiation is disallowed if the exponent is fractional (because that might result in
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
contract TestTernary
2+
{
3+
function h() pure public returns (uint16 b)
4+
{
5+
b = (true ? 63 : 255) + (false ? 63 : 255);
6+
}
7+
8+
function g() pure public returns (uint16 a)
9+
{
10+
bool t = true;
11+
bool f = false;
12+
a = (t ? 63 : 255) + (f ? 63 : 255);
13+
}
14+
}
15+
// ====
16+
// compileViaYul: also
17+
// ----
18+
// g() -> FAILURE, hex"4e487b71", 0x11
19+
// h() -> FAILURE, hex"4e487b71", 0x11
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
contract TestTernary
2+
{
3+
function g() pure public
4+
{
5+
bool t = true;
6+
bool f = false;
7+
uint8 v255 = 255;
8+
uint8 v63 = 63;
9+
uint8 a;
10+
11+
// Currently none of these should produce errors or warnings.
12+
// The result of the operator is always a limited-precision integer, even if all arguments are literals.
13+
14+
a = (t ? 63 : 255) + (f ? 63 : 255);
15+
a = (t ? 0x3f : 0xff) + (f ? 0x3f : 0xff);
16+
a = (t ? uint8(63) : 255) + (f ? 63 : uint8(255));
17+
a = (t ? v63 : 255) + (f ? 63 : v255);
18+
19+
a = (true ? 63 : 255) + (false ? 63 : 255);
20+
a = (true ? 0x3f : 0xff) + (false ? 0x3f : 0xff);
21+
a = (true ? uint8(63) : 255) + (false ? 63 : uint8(255));
22+
a = (true ? v63 : 255) + (false ? 63 : v255);
23+
24+
a = (t ? 63 : 255) - (f ? 63 : 255);
25+
a = (t ? 63 : 255) * (f ? 63 : 255);
26+
a = (t ? 63 : 255) / (f ? 63 : 255);
27+
28+
a = (t ? (true ? 63 : 255) : (false ? 63 : 255)) + (f ? (t ? 63 : 255) : (f ? 63 : 255));
29+
a = uint8(t ? 63 : 255) + uint8(f ? 63 : 255);
30+
31+
}
32+
}
33+
// ----

0 commit comments

Comments
 (0)