diff --git a/slither/slithir/convert.py b/slither/slithir/convert.py index fcb6c3afa7..3130335465 100644 --- a/slither/slithir/convert.py +++ b/slither/slithir/convert.py @@ -1926,35 +1926,21 @@ def convert_constant_types(irs: List[Operation]) -> None: while was_changed: was_changed = False for ir in irs: - if isinstance(ir, Assignment): - if isinstance(ir.lvalue.type, ElementaryType): - if ir.lvalue.type.type in ElementaryTypeInt: - if isinstance(ir.rvalue, Function): - continue - if isinstance(ir.rvalue, TupleVariable): - # TODO: fix missing Unpack conversion - continue - if isinstance(ir.rvalue.type, TypeAlias): - ir.rvalue.set_type(ElementaryType(ir.lvalue.type.name)) - was_changed = True - elif ir.rvalue.type.type not in ElementaryTypeInt: - ir.rvalue.set_type(ElementaryType(ir.lvalue.type.type)) + if isinstance(ir, (Assignment, Binary)): + if ( + isinstance(ir.lvalue.type, ElementaryType) + and ir.lvalue.type.type in ElementaryTypeInt + ): + for r in ir.read: + if isinstance(r, Constant) and r.type.type not in ElementaryTypeInt: + r.set_type(ElementaryType(ir.lvalue.type.type)) was_changed = True - if isinstance(ir, Binary): - if isinstance(ir.lvalue.type, ElementaryType): - if ir.lvalue.type.type in ElementaryTypeInt: - for r in ir.read: - if r.type.type not in ElementaryTypeInt: - r.set_type(ElementaryType(ir.lvalue.type.type)) - was_changed = True + if isinstance(ir, (HighLevelCall, InternalCall)): func = ir.function if isinstance(func, StateVariable): types = export_nested_types_from_variable(func) else: - if func is None: - # TODO: add POP instruction - break types = [p.type for p in func.parameters] assert len(types) == len(ir.arguments) for idx, arg in enumerate(ir.arguments): @@ -1964,6 +1950,7 @@ def convert_constant_types(irs: List[Operation]) -> None: if arg.type.type not in ElementaryTypeInt: arg.set_type(ElementaryType(t.type)) was_changed = True + if isinstance(ir, NewStructure): st = ir.structure for idx, arg in enumerate(ir.arguments): @@ -1973,11 +1960,15 @@ def convert_constant_types(irs: List[Operation]) -> None: if arg.type.type not in ElementaryTypeInt: arg.set_type(ElementaryType(e.type.type)) was_changed = True + + def is_elementary_array(t): + return isinstance(t, ArrayType) and isinstance(t.type, ElementaryType) + if isinstance(ir, InitArray): - if isinstance(ir.lvalue.type, ArrayType): - if isinstance(ir.lvalue.type.type, ElementaryType): - if ir.lvalue.type.type.type in ElementaryTypeInt: - for r in ir.read: + if is_elementary_array(ir.lvalue.type): + if ir.lvalue.type.type.type in ElementaryTypeInt: + for r in ir.read: + if isinstance(r, Constant) and is_elementary_array(r.type): if r.type.type.type not in ElementaryTypeInt: r.set_type(ElementaryType(ir.lvalue.type.type.type)) was_changed = True diff --git a/slither/slithir/operations/init_array.py b/slither/slithir/operations/init_array.py index ec2a63ef00..99065d48ad 100644 --- a/slither/slithir/operations/init_array.py +++ b/slither/slithir/operations/init_array.py @@ -1,14 +1,13 @@ from typing import List, Union from slither.slithir.operations.lvalue import OperationWithLValue -from slither.slithir.utils.utils import is_valid_rvalue -from slither.slithir.variables.constant import Constant +from slither.slithir.utils.utils import is_valid_rvalue, RVALUE from slither.slithir.variables.temporary import TemporaryVariable from slither.slithir.variables.temporary_ssa import TemporaryVariableSSA class InitArray(OperationWithLValue): def __init__( - self, init_values: List[Constant], lvalue: Union[TemporaryVariableSSA, TemporaryVariable] + self, init_values: List[RVALUE], lvalue: Union[TemporaryVariableSSA, TemporaryVariable] ) -> None: # init_values can be an array of n dimension # reduce was removed in py3 @@ -30,11 +29,11 @@ def check(elem): self._lvalue = lvalue @property - def read(self) -> List[Constant]: + def read(self) -> List[RVALUE]: return self._unroll(self.init_values) @property - def init_values(self) -> List[Constant]: + def init_values(self) -> List[RVALUE]: return list(self._init_values) def __str__(self): diff --git a/slither/slithir/operations/new_array.py b/slither/slithir/operations/new_array.py index 917bb11ee9..e4f508efcc 100644 --- a/slither/slithir/operations/new_array.py +++ b/slither/slithir/operations/new_array.py @@ -3,9 +3,9 @@ from slither.core.solidity_types.array_type import ArrayType from slither.slithir.operations.call import Call from slither.slithir.operations.lvalue import OperationWithLValue +from slither.slithir.utils.utils import RVALUE if TYPE_CHECKING: - from slither.slithir.variables.constant import Constant from slither.slithir.variables.temporary import TemporaryVariable from slither.slithir.variables.temporary_ssa import TemporaryVariableSSA @@ -27,7 +27,7 @@ def array_type(self) -> "ArrayType": return self._array_type @property - def read(self) -> List["Constant"]: + def read(self) -> List[RVALUE]: return self._unroll(self.arguments) def __str__(self): diff --git a/slither/visitors/slithir/expression_to_slithir.py b/slither/visitors/slithir/expression_to_slithir.py index 9c80fc46a2..cd164a3941 100644 --- a/slither/visitors/slithir/expression_to_slithir.py +++ b/slither/visitors/slithir/expression_to_slithir.py @@ -227,15 +227,16 @@ def _post_assignement_operation(self, expression: AssignmentOperation) -> None: self._result.append(operation) set_val(expression, None) else: - # Init of array, like - # uint8[2] var = [1,2]; + # For `InitArray`, the rhs is a list or singleton of `TupleExpression` elements. + # Init of array e.g. uint8[2] var = [1,2]; if isinstance(right, list): operation = InitArray(right, left) operation.set_expression(expression) self._result.append(operation) set_val(expression, left) - elif isinstance(left.type, ArrayType): - # Special case for init of array, when the right has only one element + + # Special case for init of array, when the right has only one element e.g. arr = [1]; + elif isinstance(left.type, ArrayType) and not isinstance(right.type, ArrayType): operation = InitArray([right], left) operation.set_expression(expression) self._result.append(operation) @@ -270,6 +271,7 @@ def _post_assignement_operation(self, expression: AssignmentOperation) -> None: self._result.append(operation) else: + operation = convert_assignment( left, right, expression.type, expression.expression_return_type ) @@ -430,7 +432,7 @@ def _post_call_expression(self, expression: CallExpression) -> None: set_val(expression, val) def _post_conditional_expression(self, expression: ConditionalExpression) -> None: - raise Exception(f"Ternary operator are not convertible to SlithIR {expression}") + raise SlithIRError(f"Ternary operator are not convertible to SlithIR {expression}") def _post_elementary_type_name_expression( self, diff --git a/tests/e2e/vyper_parsing/snapshots/ast_parsing__vyper_cfgir_for2_for_loop__0.txt b/tests/e2e/vyper_parsing/snapshots/ast_parsing__vyper_cfgir_for2_for_loop__0.txt index 9e35f147e3..cf57a06c0b 100644 --- a/tests/e2e/vyper_parsing/snapshots/ast_parsing__vyper_cfgir_for2_for_loop__0.txt +++ b/tests/e2e/vyper_parsing/snapshots/ast_parsing__vyper_cfgir_for2_for_loop__0.txt @@ -8,7 +8,7 @@ EXPRESSION: _strategies = strategies IRs: -_strategies(address[3]) = ['strategies(address[3])']"]; +_strategies(address[3]) := strategies(address[3])"]; 1->2; 2[label="Node Type: BEGIN_LOOP 2 "]; diff --git a/tests/unit/slithir/test_ssa_generation.py b/tests/unit/slithir/test_ssa_generation.py index 1ecf82a2dd..1a709c2f78 100644 --- a/tests/unit/slithir/test_ssa_generation.py +++ b/tests/unit/slithir/test_ssa_generation.py @@ -26,6 +26,7 @@ InternalCall, Index, InitArray, + NewArray, ) from slither.slithir.utils.ssa import is_used_later from slither.slithir.variables import ( @@ -1131,8 +1132,62 @@ def test_issue_2016(slither_from_solidity_source): f = c.functions[0] operations = f.slithir_operations new_op = operations[0] + assert isinstance(new_op, NewArray) lvalue = new_op.lvalue lvalue_type = lvalue.type assert isinstance(lvalue_type, ArrayType) assert lvalue_type.type == ElementaryType("int256") assert lvalue_type.is_dynamic + + +def test_issue_2210(slither_from_solidity_source): + source = """ + contract C { + function f (int x) public returns(int) { + int h = 1; + int k = 5; + int[5] memory arr = [x, C.x, C.y, h - k, h + k]; + } + int x= 4; + int y = 5; + } + """ + with slither_from_solidity_source(source) as slither: + c = slither.get_contract_from_name("C")[0] + f = c.functions[0] + operations = f.slithir_operations + new_op = operations[6] + assert isinstance(new_op, InitArray) + lvalue = new_op.lvalue + lvalue_type = lvalue.type + assert isinstance(lvalue_type, ArrayType) + assert lvalue_type.type == ElementaryType("int256") + assert not lvalue_type.is_dynamic + + source2 = """ + contract X { + function _toInts(uint256[] memory a) private pure returns (int256[] memory casted) { + /// @solidity memory-safe-assembly + assembly { + casted := a + } + } + } + """ + with slither_from_solidity_source(source2) as slither: + x = slither.get_contract_from_name("X")[0] + f2 = x.functions[0] + operations = f2.slithir_operations + new_op2 = operations[0] + assert isinstance(new_op2, Assignment) + + lvalue = new_op2.lvalue + lvalue_type = lvalue.type + assert isinstance(lvalue_type, ArrayType) + assert lvalue_type.type == ElementaryType("int256") + assert lvalue_type.is_dynamic + + rvalue_type = new_op2.rvalue.type + assert isinstance(rvalue_type, ArrayType) + assert rvalue_type.type == ElementaryType("uint256") + assert rvalue_type.is_dynamic