Repository: vyperlang/vyper

Issue:
Convert units should carry
### What's your issue about?

As @djrtwo found.

`convert` should preserve the unit that have been set, currently it doesn't:

```
b: uint256(meter) = 1234
a: int128 = convert(b, "int128") # should error
a: int128(meter) = convert(b, "int128") # should work
```

### How can it be fixed?

Fill this in if you know how to fix it.

#### Cute Animal Picture
![](https://i.ytimg.com/vi/gMRScklkpa4/hqdefault.jpg)



Discussion:

Resolving patch:
diff --git a/vyper/parser/stmt.py b/vyper/parser/stmt.py
--- a/vyper/parser/stmt.py
+++ b/vyper/parser/stmt.py
@@ -78,7 +78,7 @@ def parse_pass(self):
 
     def _check_valid_assign(self, sub):
         if isinstance(self.stmt.annotation, ast.Call):  # unit style: num(wei)
-            if self.stmt.annotation.func.id != sub.typ.typ:
+            if self.stmt.annotation.func.id != sub.typ.typ and not sub.typ.is_literal:
                 raise TypeMismatchException('Invalid type, expected: %s' % self.stmt.annotation.func.id, self.stmt)
         elif isinstance(self.stmt.annotation, ast.Dict):
             if not isinstance(sub.typ, StructType):
diff --git a/vyper/types/convert.py b/vyper/types/convert.py
--- a/vyper/types/convert.py
+++ b/vyper/types/convert.py
@@ -31,7 +31,8 @@ def to_int128(expr, args, kwargs, context):
         if in_node.typ.is_literal and not SizeLimits.in_bounds('int128', in_node.value):
             raise InvalidLiteralException("Number out of range: {}".format(in_node.value), expr)
         return LLLnode.from_list(
-            ['clamp', ['mload', MemoryPositions.MINNUM], in_node, ['mload', MemoryPositions.MAXNUM]], typ=BaseType('int128'), pos=getpos(expr)
+            ['clamp', ['mload', MemoryPositions.MINNUM], in_node,
+                        ['mload', MemoryPositions.MAXNUM]], typ=BaseType('int128', in_node.typ.unit), pos=getpos(expr)
         )
     else:
         return byte_array_to_num(in_node, expr, 'int128')
@@ -39,18 +40,21 @@ def to_int128(expr, args, kwargs, context):
 
 @signature(('num_literal', 'int128', 'bytes32'), 'str_literal')
 def to_uint256(expr, args, kwargs, context):
-    input = args[0]
-    typ, len = get_type(input)
-    if isinstance(input, int):
-        if not(0 <= input <= 2**256 - 1):
-            raise InvalidLiteralException("Number out of range: {}".format(input))
-        return LLLnode.from_list(input, typ=BaseType('uint256'), pos=getpos(expr))
-    elif isinstance(input, LLLnode) and typ in ('int128', 'num_literal'):
-        return LLLnode.from_list(['clampge', input, 0], typ=BaseType('uint256'), pos=getpos(expr))
-    elif isinstance(input, LLLnode) and typ in ('bytes32'):
-        return LLLnode(value=input.value, args=input.args, typ=BaseType('uint256'), pos=getpos(expr))
+    in_node = args[0]
+    typ, len = get_type(in_node)
+    if isinstance(in_node, int):
+
+        if not SizeLimits.in_bounds('uint256', in_node):
+            raise InvalidLiteralException("Number out of range: {}".format(in_node))
+        _unit = in_node.typ.unit if typ == 'int128' else None
+        return LLLnode.from_list(in_node, typ=BaseType('uint256', _unit), pos=getpos(expr))
+    elif isinstance(in_node, LLLnode) and typ in ('int128', 'num_literal'):
+        _unit = in_node.typ.unit if typ == 'int128' else None
+        return LLLnode.from_list(['clampge', in_node, 0], typ=BaseType('uint256', _unit), pos=getpos(expr))
+    elif isinstance(in_node, LLLnode) and typ in ('bytes32'):
+        return LLLnode(value=in_node.value, args=in_node.args, typ=BaseType('uint256'), pos=getpos(expr))
     else:
-        raise InvalidLiteralException("Invalid input for uint256: %r" % input, expr)
+        raise InvalidLiteralException("Invalid input for uint256: %r" % in_node, expr)
 
 
 @signature('int128', 'str_literal')


Test patch:
diff --git a/tests/parser/functions/test_convert.py b/tests/parser/functions/test_convert.py
--- a/tests/parser/functions/test_convert.py
+++ b/tests/parser/functions/test_convert.py
@@ -1,3 +1,6 @@
+from decimal import Decimal
+
+
 def test_convert_to_num(get_contract_with_gas_estimation, assert_tx_failed):
     code = """
 a: int128
@@ -134,3 +137,60 @@ def bytes_to_bytes32(inp: bytes[32]) -> (bytes32, bytes32):
     assert c.uint256_to_bytes32(1) == [bytes_helper('', 31) + b'\x01'] * 3
     assert c.address_to_bytes32(w3.eth.accounts[0]) == [bytes_helper('', 12) + w3.toBytes(hexstr=w3.eth.accounts[0])] * 2
     assert c.bytes_to_bytes32(bytes_helper('', 32)) == [bytes_helper('', 32)] * 2
+
+
+def test_convert_to_uint256_units(get_contract, assert_tx_failed):
+    code = """
+units: {
+    meter: "Meter"
+}
+
+@public
+def test() -> int128(meter):
+    b: uint256(meter) = 1234
+    a: int128(meter) = convert(b, "int128")
+    return a
+    """
+
+    c = get_contract(code)
+    assert c.test() == 1234
+
+
+def test_convert_to_int128_units(get_contract, assert_tx_failed):
+    code = """
+units: {
+    meter: "Meter"
+}
+
+@public
+def test() -> uint256(meter):
+    b: int128(meter) = 4321
+    a: uint256(meter) = convert(b, "uint256")
+    return a
+    """
+
+    c = get_contract(code)
+    assert c.test() == 4321
+
+
+def test_convert_to_int128_decimal_units(get_contract, assert_tx_failed):
+    code = """
+units: {
+    meter: "Meter"
+}
+
+@public
+def test() -> decimal(meter):
+    a: decimal(meter) = convert(5001, "decimal")
+    return a
+
+@public
+def test2() -> decimal(meter):
+    b: int128(meter) = 1234
+    a: decimal(meter) = convert(b, "decimal")
+    return a
+    """
+
+    c = get_contract(code)
+    assert c.test() == Decimal('5001')
+    assert c.test2() == Decimal('1234')
diff --git a/tests/parser/functions/test_convert_int128.py b/tests/parser/functions/test_convert_int128.py
--- a/tests/parser/functions/test_convert_int128.py
+++ b/tests/parser/functions/test_convert_int128.py
@@ -16,7 +16,7 @@ def test_convert_address_to_num(assert_compile_failed, get_contract_with_gas_est
     code = """
 @public
 def test2():
-  x: int128 = convert(msg.sender, 'int128')
+    x: int128 = convert(msg.sender, 'int128')
     """
 
     assert_compile_failed(lambda: get_contract_with_gas_estimation(code), Exception)