Repository: vyperlang/vyper

Issue:
Fix literal int for min/max.

### What's your issue about?

a: uint256 = 1
b: uint256 = max(2, a)

Should be allowed to compiled, as 2 is a valid uint256.


### How can it be fixed?

functions.py

#### Cute Animal Picture
^.^



Discussion:
Note: This is the first time I'm checking out the Vyper repo and I hope to start contributing.

The note in the docs [here](https://vyper.readthedocs.io/en/latest/types.html#id4) reads as follows: 
Integer literals are always interpreted as `int128`. In order to assign a literal to a `uint256` use `as_uint256(_literal)`.

After going through the commits logs I realized that this note no longer accurate as `as_*` are no longer available globally and that one should use `convert(2, 'uint256')` to do what's mentioned in the note. I'll submit a PR to change this in the docs. (Update: #876 )

---

From the PR description I gather that this issue can be resolved by making changes in this [file](https://github.com/ethereum/vyper/blob/master/vyper/functions/functions.py). I'll try to figure out how to resolve this issue if you can confirm that this issue should be worked on. I'm not sure as the docs say that the default assumption for integer literals is that they're int128's.
Hi @rohithpr !

Yes, a lot of the documentation is inaccurate (currently working on it). Also, as for now, Vyper will implicitly convert the num literal to ``uint256`` in cases like ``a: uint256 = 10``.

Resolving patch:
diff --git a/vyper/functions/functions.py b/vyper/functions/functions.py
--- a/vyper/functions/functions.py
+++ b/vyper/functions/functions.py
@@ -38,6 +38,7 @@
 )
 from vyper.utils import (
     MemoryPositions,
+    SizeLimits,
     DECIMAL_DIVISOR,
     RLP_DECODER_ADDRESS
 )
@@ -625,6 +626,13 @@ def _max(expr, args, kwargs, context):
 
 
 def minmax(expr, args, kwargs, context, is_min):
+    def _can_compare_with_uint256(operand):
+        if operand.typ.typ == 'uint256':
+            return True
+        elif operand.typ.typ == 'int128' and operand.typ.is_literal and SizeLimits.in_bounds('uint256', operand.value):
+            return True
+        return False
+
     left, right = args[0], args[1]
     if not are_units_compatible(left.typ, right.typ) and not are_units_compatible(right.typ, left.typ):
         raise TypeMismatchException("Units must be compatible", expr)
@@ -636,12 +644,13 @@ def minmax(expr, args, kwargs, context, is_min):
         o = ['if', [comparator, '_l', '_r'], '_r', '_l']
         otyp = left.typ
         otyp.is_literal = False
-    # elif left.typ.typ == 'int128' and right.typ.typ == 'decimal':
-    #     o = ['if', [comparator, ['mul', '_l', DECIMAL_DIVISOR], '_r'], '_r', ['mul', '_l', DECIMAL_DIVISOR]]
-    #     otyp = 'decimal'
-    # elif left.typ.typ == 'decimal' and right.typ.typ == 'int128':
-    #     o = ['if', [comparator, '_l', ['mul', '_r', DECIMAL_DIVISOR]], ['mul', '_r', DECIMAL_DIVISOR], '_l']
-    #     otyp = 'decimal'
+    elif _can_compare_with_uint256(left) and _can_compare_with_uint256(right):
+        o = ['if', [comparator, '_l', '_r'], '_r', '_l']
+        if right.typ.typ == 'uint256':
+            otyp = right.typ
+        else:
+            otyp = left.typ
+        otyp.is_literal = False
     else:
         raise TypeMismatchException("Minmax types incompatible: %s %s" % (left.typ.typ, right.typ.typ))
     return LLLnode.from_list(['with', '_l', left, ['with', '_r', right, o]], typ=otyp, pos=getpos(expr))


Test patch:
diff --git a/tests/parser/functions/test_minmax.py b/tests/parser/functions/test_minmax.py
--- a/tests/parser/functions/test_minmax.py
+++ b/tests/parser/functions/test_minmax.py
@@ -17,3 +17,158 @@ def goo() -> uint256:
     assert c.goo() == 83
 
     print("Passed min/max test")
+
+
+def test_max_var_uint256_literal_int128(get_contract_with_gas_estimation):
+    """Tests to verify that max works as expected when a variable/literal uint256 and a literal int128 are passed."""
+    code = """
+@public
+def foo() -> uint256:
+    a: uint256 = 2 ** 200
+    b: uint256 = 5
+    return max(a, 5) + max(b, 5)
+
+@public
+def goo() -> uint256:
+    a: uint256 = 2 ** 200
+    b: uint256 = 5
+    return max(5, a) + max(5, b)
+
+@public
+def bar() -> uint256:
+    a: uint256 = 2
+    b: uint256 = 5
+    return max(a, 5) + max(b, 5)
+
+@public
+def baz() -> uint256:
+    a: uint256 = 2
+    b: uint256 = 5
+    return max(5, a) + max(5, b)
+
+@public
+def both_literals() -> uint256:
+    return max(2 ** 200, 2)
+"""
+    c = get_contract_with_gas_estimation(code)
+    assert c.foo() == 2 ** 200 + 5
+    assert c.goo() == 2 ** 200 + 5
+    assert c.bar() == 5 + 5
+    assert c.baz() == 5 + 5
+    assert c.both_literals() == 2 ** 200
+
+
+def test_min_var_uint256_literal_int128(get_contract_with_gas_estimation):
+    """Tests to verify that max works as expected when a variable/literal uint256 and a literal int128 are passed."""
+    code = """
+@public
+def foo() -> uint256:
+    a: uint256 = 2 ** 200
+    b: uint256 = 5
+    return min(a, 5) + min(b, 5)
+
+@public
+def goo() -> uint256:
+    a: uint256 = 2 ** 200
+    b: uint256 = 5
+    return min(5, a) + min(5, b)
+
+@public
+def bar() -> uint256:
+    a: uint256 = 2
+    b: uint256 = 5
+    return min(a, 5) + min(b, 5)
+
+@public
+def baz() -> uint256:
+    a: uint256 = 2
+    b: uint256 = 5
+    return min(5, a) + min(5, b)
+
+@public
+def both_literals() -> uint256:
+    return min(2 ** 200, 2)
+"""
+    c = get_contract_with_gas_estimation(code)
+    assert c.foo() == 5 + 5
+    assert c.goo() == 5 + 5
+    assert c.bar() == 2 + 5
+    assert c.baz() == 2 + 5
+    assert c.both_literals() == 2
+
+
+def test_minmax_var_uint256_var_int128(get_contract_with_gas_estimation, assert_compile_failed):
+    """Tests to verify that max throws an error if a variable uint256 and a variable int128 are passed."""
+    from vyper.exceptions import TypeMismatchException
+    code_1 = """
+@public
+def foo() -> uint256:
+    a: uint256 = 2
+    b: int128 = 3
+    return max(a, b)
+"""
+    assert_compile_failed(
+        lambda: get_contract_with_gas_estimation(code_1),
+        TypeMismatchException
+    )
+
+    code_2 = """
+@public
+def foo() -> uint256:
+    a: uint256 = 2
+    b: int128 = 3
+    return max(b, a)
+"""
+    assert_compile_failed(
+        lambda: get_contract_with_gas_estimation(code_2),
+        TypeMismatchException
+    )
+
+    code_3 = """
+@public
+def foo() -> uint256:
+    a: uint256 = 2
+    b: int128 = 3
+    return min(a, b)
+"""
+    assert_compile_failed(
+        lambda: get_contract_with_gas_estimation(code_3),
+        TypeMismatchException
+    )
+
+    code_4 = """
+@public
+def foo() -> uint256:
+    a: uint256 = 2
+    b: int128 = 3
+    return min(b, a)
+"""
+    assert_compile_failed(
+        lambda: get_contract_with_gas_estimation(code_4),
+        TypeMismatchException
+    )
+
+
+def test_minmax_var_uint256_negative_int128(get_contract_with_gas_estimation, assert_tx_failed, assert_compile_failed):
+    from vyper.exceptions import TypeMismatchException
+    code_1 = """
+@public
+def foo() -> uint256:
+    a: uint256 = 2 ** 200
+    return max(a, -1)
+"""
+    assert_compile_failed(
+        lambda: get_contract_with_gas_estimation(code_1),
+        TypeMismatchException
+    )
+
+    code_2 = """
+@public
+def foo() -> uint256:
+    a: uint256 = 2 ** 200
+    return min(a, -1)
+"""
+    assert_compile_failed(
+        lambda: get_contract_with_gas_estimation(code_2),
+        TypeMismatchException
+    )
diff --git a/tests/parser/syntax/test_minmax.py b/tests/parser/syntax/test_minmax.py
--- a/tests/parser/syntax/test_minmax.py
+++ b/tests/parser/syntax/test_minmax.py
@@ -10,11 +10,6 @@
 @public
 def foo():
     y = min(7, 0x1234567890123456789012345678901234567890)
-    """,
-    """
-@public
-def foo():
-    y = min(7, convert(3, 'uint256'))
     """
 ]