From 6eb5589f3572cb8cbe7bb730911ee10dcdbc5e1c Mon Sep 17 00:00:00 2001 From: iamdefinitelyahuman Date: Sat, 28 Dec 2019 21:44:23 +0200 Subject: [PATCH 1/3] bugfix - unary operation on literals --- vyper/parser/expr.py | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/vyper/parser/expr.py b/vyper/parser/expr.py index 41186195b3..89df59c461 100644 --- a/vyper/parser/expr.py +++ b/vyper/parser/expr.py @@ -935,21 +935,18 @@ def unary_operations(self): self.expr, ) elif isinstance(self.expr.op, ast.USub): - # Must be a signed integer - if not is_numeric_type(operand.typ) or operand.typ.typ.lower().startswith('u'): + if not is_numeric_type(operand.typ): raise TypeMismatchException( f"Unsupported type for negation: {operand.typ}", - operand, ) - if operand.typ.is_literal and 'int' in operand.typ.typ: - num = ast.Num(n=0 - operand.value) - num.source_code = self.expr.source_code - num.lineno = self.expr.lineno - num.col_offset = self.expr.col_offset - num.end_lineno = self.expr.end_lineno - num.end_col_offset = self.expr.end_col_offset - return Expr.parse_value_expr(num, self.context) + if operand.typ.is_literal: + typ = "decimal" if operand.typ.typ == "decimal" else "int128" + return LLLnode.from_list( + 0-operand.value, + typ=BaseType(typ, unit=operand.typ.unit, is_literal=True), + pos=getpos(self.expr), + ) # Clamp on minimum integer value as we cannot negate that value # (all other integer values are fine) From f3d1817cf2b17b47cbe5688ba82da503583ac1ec Mon Sep 17 00:00:00 2001 From: iamdefinitelyahuman Date: Sat, 28 Dec 2019 21:46:38 +0200 Subject: [PATCH 2/3] add test cases for unary negation of literals --- tests/parser/functions/test_unary.py | 41 ++++++++++++++++++++++++++-- 1 file changed, 38 insertions(+), 3 deletions(-) diff --git a/tests/parser/functions/test_unary.py b/tests/parser/functions/test_unary.py index e95b0500f2..fde4219d35 100644 --- a/tests/parser/functions/test_unary.py +++ b/tests/parser/functions/test_unary.py @@ -37,9 +37,8 @@ def negate(a: int128) -> int128: assert c.negate(val) == -val -decimal_divisor = Decimal('1e10') -min_decimal = (-2**127 + 1) / decimal_divisor -max_decimal = (2**127 - 1) / decimal_divisor +min_decimal = -2**127 + 1 +max_decimal = 2**127 - 1 @pytest.mark.parametrize("val", [min_decimal, 0, max_decimal]) def test_unary_sub_decimal_pass(get_contract, val): code = """@public @@ -48,3 +47,39 @@ def negate(a: decimal) -> decimal: """ c = get_contract(code) assert c.negate(val) == -val + + +def test_negation_decimal(get_contract): + code = """ +a: constant(decimal) = 170141183460469231731687303715884105726.9999999999 +b: constant(decimal) = -170141183460469231731687303715884105726.9999999999 + +@public +def foo() -> decimal: + return -a + +@public +def bar() -> decimal: + return -b + """ + + c = get_contract(code) + assert c.foo() == Decimal("-170141183460469231731687303715884105726.9999999999") + assert c.bar() == Decimal("170141183460469231731687303715884105726.9999999999") + + +def test_negation_int128(get_contract): + code = """ +a: constant(int128) = -2**127 + +@public +def foo() -> int128: + return -2**127 + +@public +def bar() -> int128: + return -(a+1) + """ + c = get_contract(code) + assert c.foo() == -2**127 + assert c.bar() == 2**127-1 From 0c57a056fa94cb49d3935476ef6b207ffcb8ccb8 Mon Sep 17 00:00:00 2001 From: iamdefinitelyahuman Date: Sat, 28 Dec 2019 21:47:24 +0200 Subject: [PATCH 3/3] minor fixes on error messages / locations --- vyper/parser/expr.py | 2 ++ vyper/parser/parser_utils.py | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/vyper/parser/expr.py b/vyper/parser/expr.py index 89df59c461..bc6b896507 100644 --- a/vyper/parser/expr.py +++ b/vyper/parser/expr.py @@ -674,6 +674,7 @@ def build_in_comparator(self): if left.typ != right.typ.subtype: raise TypeMismatchException( f"{left.typ} cannot be in a list of {right.typ.subtype}", + self.expr, ) result_placeholder = self.context.new_placeholder(BaseType('bool')) @@ -938,6 +939,7 @@ def unary_operations(self): if not is_numeric_type(operand.typ): raise TypeMismatchException( f"Unsupported type for negation: {operand.typ}", + self.expr, ) if operand.typ.is_literal: diff --git a/vyper/parser/parser_utils.py b/vyper/parser/parser_utils.py index de2201467a..36fed4893c 100644 --- a/vyper/parser/parser_utils.py +++ b/vyper/parser/parser_utils.py @@ -64,7 +64,7 @@ def get_number_as_fraction(expr, context): if exponent < -10: raise InvalidLiteralException( - "`decimal` literal cannot have more than 10 decimal places: {literal}", + f"`decimal` literal cannot have more than 10 decimal places: {literal}", expr )