Skip to content

Latest commit

 

History

History
53 lines (30 loc) · 2.27 KB

NUANCES.md

File metadata and controls

53 lines (30 loc) · 2.27 KB

Semantical Nuances

Count Loops

In the original Vyper language, the loop doing something n times looks like this:

	for i in range(start, start + n):
		do_stuff()

Here range can be thought of as expanding to a list:

	range(start, start + n) = [start, start + 1, ..., start + n - 1]

Instead, our dialect introduces count function with the correct semantics for doing an iteration n times:

	count(start, n) = [start, start + 1, ..., start + (n - 1)]

The only difference here is the extra parentheses which would not matter if addition was associative. But Vyper arithmetic is not associative because of overflow checks. So this loop fails due to the overflow check:

	max_uint: uint256 = ~0
	for i in range(max_uint, max_uint + 1):
		do_stuff()

whereas this one works:

	for i in count(~0, 1):
		do_stuff()

Of course, the inability to finish loops at exactly 2256 – 1 is not an everyday problem. Yet for a verified compiler no problem is too small. Together with the weird and irregular grammar of the range(var, var + ...) construct, this is the reason for introducing the count loop.

Augmented Assignment

There are two possible ways to interpret a += f() and our dialect follows the Python way:

old_a = a
increment = f()
a = old_a + increment

For comparison, here's the C way:

increment = f()
a = a + increment

The difference is observable if f() changes a.

Multiple Representations of Integers

The semantics of uint256 is defined without requiring only one representation for the same value modulo 2256. The default implementation uses big integers for uint256, so for example the integers 2256 and 0 both represent the uint256 value of 0.

This does not affect the semantics of the language because there's no way to obtain and no way to detect the integers outside of the 0..2256 range by using the basic operators. However, that could be possible with custom built-in functions.

There's one (fortunately, only one) resulting quirk in the compiler: it is unable to to replace x ** 1 with x. The reason is, x ** 1 is the canonical representation of x and therefore is not identical to x, but merely indistinguishable from x by normal means. But the compiled code must produce an identical result.