Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implicit conversion #2079

Draft
wants to merge 2 commits into
base: dev
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 27 additions & 1 deletion slither/solc_parsing/expressions/expression_parsing.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,28 @@

# pylint: disable=anomalous-backslash-in-string,import-outside-toplevel,too-many-branches,too-many-locals


def _fit_smallest_integer(val: str) -> str:
"""
Return the smallest unsigned integer that can fit val

:param val:
:return:
"""
if val.startswith("0x"):
val = int(val, base=16)
elif val.find("e") != -1:
val = int(float(val))
else:
val = int(val)

n = 8
while n <= 256:
if val <= 2**n - 1:
return f"uint{n}"
n = n + 8


# region Filtering
###################################################################################
###################################################################################
Expand Down Expand Up @@ -470,7 +492,11 @@ def parse_expression(expression: Dict, caller_context: CallerContextExpression)
elif type_candidate.startswith("rational_const "):
type_candidate = ElementaryType("uint256")
elif type_candidate.startswith("int_const "):
type_candidate = ElementaryType("uint256")
if subdenomination:
type_candidate = ElementaryType("uint256")
else:
smallest_type = _fit_smallest_integer(value)
type_candidate = ElementaryType(smallest_type)
elif type_candidate.startswith("bool"):
type_candidate = ElementaryType("bool")
elif type_candidate.startswith("address"):
Expand Down
84 changes: 78 additions & 6 deletions slither/visitors/slithir/expression_to_slithir.py
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,24 @@ def __init__(self, expression: Expression, node: "Node") -> None:
def result(self) -> List[Operation]:
return self._result

def _convert_right_assignment(self, left: Any, right: Any, expression: AssignmentOperation) -> Any:
if (
isinstance(left.type, ElementaryType)
and isinstance(right.type, ElementaryType)
and left.type != right.type
# Avoid implicit conversion for bytes/string
and not left.type.is_dynamic
and not right.type.is_dynamic
):
new_right = TemporaryVariable(self._node)
conv_right = TypeConversion(new_right, right, ElementaryType(left.type.type))
new_right.set_type(ElementaryType(left.type.type))
conv_right.set_expression(expression)
self._result.append(conv_right)
right = new_right
return right

# pylint: disable=too-many-branches
def _post_assignement_operation(self, expression: AssignmentOperation) -> None:
left = get(expression.expression_left)
right = get(expression.expression_right)
Expand All @@ -183,9 +201,10 @@ def _post_assignement_operation(self, expression: AssignmentOperation) -> None:
and expression.type
and expression.expression_return_type
):
right = self._convert_right_assignment(left[idx], right[idx], expression)
operation = convert_assignment(
left[idx],
right[idx],
right,
expression.type,
expression.expression_return_type,
)
Expand All @@ -203,6 +222,7 @@ def _post_assignement_operation(self, expression: AssignmentOperation) -> None:
and left[idx].tuple_index is not None
):
index = left[idx].tuple_index
right = self._convert_right_assignment(left[idx], right, expression)
operation = Unpack(left[idx], right, index)
operation.set_expression(expression)
self._result.append(operation)
Expand All @@ -215,6 +235,7 @@ def _post_assignement_operation(self, expression: AssignmentOperation) -> None:
and left.tuple_index is not None
and isinstance(right, TupleVariable)
):
right = self._convert_right_assignment(left, right, expression)
operation = Unpack(left, right, left.tuple_index)
operation.set_expression(expression)
self._result.append(operation)
Expand All @@ -223,17 +244,20 @@ def _post_assignement_operation(self, expression: AssignmentOperation) -> None:
# Init of array, like
# uint8[2] var = [1,2];
if isinstance(right, list):
right = self._convert_right_assignment(left, right, expression)
operation = InitArray(right, left)
operation.set_expression(expression)
self._result.append(operation)
set_val(expression, left)
elif isinstance(left.type, ArrayType):
right = self._convert_right_assignment(left, [right], expression)
# Special case for init of array, when the right has only one element
operation = InitArray([right], left)
operation = InitArray(right, left)
operation.set_expression(expression)
self._result.append(operation)
set_val(expression, left)
else:
right = self._convert_right_assignment(left, right, expression)
operation = convert_assignment(
left, right, expression.type, expression.expression_return_type
)
Expand All @@ -243,12 +267,13 @@ def _post_assignement_operation(self, expression: AssignmentOperation) -> None:
# a = b = 1;
set_val(expression, left)

# pylint: disable=too-many-statements
def _post_binary_operation(self, expression: BinaryOperation) -> None:
left = get(expression.expression_left)
right = get(expression.expression_right)
val = TemporaryVariable(self._node)

if expression.type in _signed_to_unsigned:
val = TemporaryVariable(self._node)
new_left = TemporaryVariable(self._node)
conv_left = TypeConversion(new_left, left, ElementaryType("int256"))
new_left.set_type(ElementaryType("int256"))
Expand All @@ -274,9 +299,56 @@ def _post_binary_operation(self, expression: BinaryOperation) -> None:
conv_final.set_expression(expression)
self._result.append(conv_final)
else:
operation = Binary(val, left, right, _binary_to_binary[expression.type])
operation.set_expression(expression)
self._result.append(operation)
# From solidity docs
# The operators ** (exponentiation), << and >> use the type of the left operand for the operation and the result.
if ( # pylint: disable=too-many-boolean-expressions
isinstance(left.type, ElementaryType)
and isinstance(right.type, ElementaryType)
and left.type != right.type
and expression.type
not in (
BinaryOperationType.LEFT_SHIFT,
BinaryOperationType.RIGHT_SHIFT,
BinaryOperationType.POWER,
)
# Avoid implicit conversion for bytes/string
and not left.type.is_dynamic
and not right.type.is_dynamic
):
# If one is int and the other is uint -> int size
if (
left.type.type.startswith("uint")
and right.type.type.startswith("int")
or left.type.size < right.type.size
):
new_left = TemporaryVariable(self._node)
conv_left = TypeConversion(new_left, left, ElementaryType(right.type.type))
new_left.set_type(ElementaryType(right.type.type))
conv_left.set_expression(expression)
self._result.append(conv_left)
val = TemporaryVariable(self._node)
operation = Binary(val, new_left, right, _binary_to_binary[expression.type])
operation.set_expression(expression)
self._result.append(operation)
elif (
left.type.type.startswith("int")
and right.type.type.startswith("uint")
or left.type.size > right.type.size
):
new_right = TemporaryVariable(self._node)
conv_right = TypeConversion(new_right, right, ElementaryType(left.type.type))
new_right.set_type(ElementaryType(left.type.type))
conv_right.set_expression(expression)
self._result.append(conv_right)
val = TemporaryVariable(self._node)
operation = Binary(val, left, new_right, _binary_to_binary[expression.type])
operation.set_expression(expression)
self._result.append(operation)
else:
val = TemporaryVariable(self._node)
operation = Binary(val, left, right, _binary_to_binary[expression.type])
operation.set_expression(expression)
self._result.append(operation)

set_val(expression, val)

Expand Down
Loading