def test_struct_collector(): modules = {'module': """ struct S: member x : S* member y : S* end """, '__main__': """ from module import S func foo{z}(a : S, b) -> (c : S): struct T: member x : S* end const X = 5 return (c=a + X) end const Y = 1 + 1 """} scope = ScopedName.from_string struct_defs = _collect_struct_definitions(modules) expected_def = { 'module.S': StructDefinition( full_name=scope('module.S'), members={ 'x': MemberDefinition(offset=0, cairo_type=TypePointer(pointee=TypeStruct( scope=scope('module.S'), is_fully_resolved=True))), 'y': MemberDefinition(offset=1, cairo_type=TypePointer(pointee=TypeStruct( scope=scope('module.S'), is_fully_resolved=True))), }, size=2), '__main__.S': AliasDefinition(destination=scope('module.S')), '__main__.foo.Args': StructDefinition( full_name=scope('__main__.foo.Args'), members={ 'a': MemberDefinition(offset=0, cairo_type=TypeStruct( scope=scope('module.S'), is_fully_resolved=True)), 'b': MemberDefinition(offset=2, cairo_type=TypeFelt()), }, size=3), '__main__.foo.ImplicitArgs': StructDefinition( full_name=scope('__main__.foo.ImplicitArgs'), members={'z': MemberDefinition(offset=0, cairo_type=TypeFelt())}, size=1), '__main__.foo.Return': StructDefinition( full_name=scope('__main__.foo.Return'), members={ 'c': MemberDefinition(offset=0, cairo_type=TypeStruct( scope=scope('module.S'), is_fully_resolved=True)) }, size=2), '__main__.foo.T': StructDefinition( full_name=scope('__main__.foo.T'), members={ 'x': MemberDefinition(offset=0, cairo_type=TypePointer(pointee=TypeStruct( scope=scope('module.S'), is_fully_resolved=True))), }, size=1) } assert struct_defs == expected_def
def test_offset_reference_definition_typed_members(): t = TypeStruct(scope=scope('T'), is_fully_resolved=True) s_star = TypePointer( pointee=TypeStruct(scope=scope('S'), is_fully_resolved=True)) reference_manager = ReferenceManager() identifiers = { scope('T'): ScopeDefinition(), scope('T.x'): MemberDefinition(offset=3, cairo_type=s_star), scope('T.flt'): MemberDefinition(offset=4, cairo_type=TypeFelt()), scope('S'): ScopeDefinition(), scope('S.x'): MemberDefinition(offset=10, cairo_type=t), } main_reference = ReferenceDefinition(full_name=scope('a'), references=[]) references = { scope('a'): reference_manager.get_id( Reference( pc=0, value=mark_types_in_expr_resolved(parse_expr('cast(ap, T*)')), ap_tracking_data=RegTrackingData(group=0, offset=0), )), } flow_tracking_data = FlowTrackingDataActual( ap_tracking=RegTrackingData(group=0, offset=1), reference_ids=references, ) # Create OffsetReferenceDefinition instances for expressions of the form "a.<member_path>", # such as a.x and a.x.x, and check the result of evaluation those expressions. for member_path, expected_result in [ ('x', 'cast([ap - 1 + 3], S*)'), ('x.x', 'cast([[ap - 1 + 3] + 10], T)'), ('x.x.x', 'cast([&[[ap - 1 + 3] + 10] + 3], S*)'), ('x.x.flt', 'cast([&[[ap - 1 + 3] + 10] + 4], felt)') ]: definition = OffsetReferenceDefinition(parent=main_reference, identifier_values=identifiers, member_path=scope(member_path)) assert definition.eval( reference_manager=reference_manager, flow_tracking_data=flow_tracking_data).format() == expected_result definition = OffsetReferenceDefinition(parent=main_reference, identifier_values=identifiers, member_path=scope('x.x.flt.x')) with pytest.raises( DefinitionError, match='Member access requires a type of the form Struct*.'): assert definition.eval( reference_manager=reference_manager, flow_tracking_data=flow_tracking_data).format() == expected_result
def test_reference_to_structs(): t = TypeStruct(scope=scope('T'), is_fully_resolved=True) t_star = TypePointer(pointee=t) identifier_values = { scope('ref'): ReferenceDefinition(full_name=scope('ref'), references=[]), scope('T.x'): MemberDefinition(offset=3, cairo_type=t_star), } reference_manager = ReferenceManager() flow_tracking_data = FlowTrackingDataActual(ap_tracking=RegTrackingData()) flow_tracking_data = flow_tracking_data.add_reference( reference_manager=reference_manager, name=scope('ref'), ref=Reference( pc=0, value=mark_types_in_expr_resolved(parse_expr('cast([100], T)')), ap_tracking_data=RegTrackingData(group=0, offset=2), )) consts = get_vm_consts(identifier_values, reference_manager, flow_tracking_data, memory={103: 200}) assert consts.ref.address_ == 100 assert consts.ref.x == 200
def test_offset_reference_definition_typed_members(): t = TypeStruct(scope=scope('T'), is_fully_resolved=True) t_star = TypePointer(pointee=t) reference_manager = ReferenceManager() main_reference = ReferenceDefinition(full_name=scope('a'), cairo_type=t_star, references=[]) references = { scope('a'): reference_manager.alloc_id( Reference( pc=0, value=mark_types_in_expr_resolved(parse_expr('cast(ap, T*)')), ap_tracking_data=RegTrackingData(group=0, offset=0), )), } flow_tracking_data = FlowTrackingDataActual( ap_tracking=RegTrackingData(group=0, offset=1), reference_ids=references, ) # Create OffsetReferenceDefinition instance for an expression of the form "a.<member_path>", # in this case a.x.y.z, and check the result of evaluation of this expression. definition = OffsetReferenceDefinition(parent=main_reference, member_path=scope('x.y.z')) assert definition.eval(reference_manager=reference_manager, flow_tracking_data=flow_tracking_data).format( ) == 'cast(ap - 1, T*).x.y.z'
def test_type_visitor(): t = TypeStruct(scope=scope('T'), is_fully_resolved=False) t_star = TypePointer(pointee=t) t_star2 = TypePointer(pointee=t_star) assert simplify_type_system( parse_expr('fp + 3 + [ap]')) == (parse_expr('fp + 3 + [ap]'), TypeFelt()) assert simplify_type_system( parse_expr('cast(fp + 3 + [ap], T*)')) == (parse_expr('fp + 3 + [ap]'), t_star) # Two casts. assert simplify_type_system( parse_expr('cast(cast(fp, T*), felt)')) == (parse_expr('fp'), TypeFelt()) # Cast from T to T. assert simplify_type_system( parse_expr('cast([cast(fp, T*)], T)')) == (parse_expr('[fp]'), t) # Dereference. assert simplify_type_system( parse_expr('[cast(fp, T**)]')) == (parse_expr('[fp]'), t_star) assert simplify_type_system( parse_expr('[[cast(fp, T**)]]')) == (parse_expr('[[fp]]'), t) # Address of. assert simplify_type_system( parse_expr('&([[cast(fp, T**)]])')) == (parse_expr('[fp]'), t_star) assert simplify_type_system( parse_expr('&&[[cast(fp, T**)]]')) == (parse_expr('fp'), t_star2)
def test_type_visitor_pointer_arithmetic(): t = TypeStruct(scope=scope('T'), is_fully_resolved=True) t_star = TypePointer(pointee=t) simplify_type_system_test('cast(fp, T*) + 3', 'fp + 3', t_star) simplify_type_system_test('cast(fp, T*) - 3', 'fp - 3', t_star) simplify_type_system_test('cast(fp, T*) - cast(3, T*)', 'fp - 3', TypeFelt())
def test_type_visitor_pointer_arithmetic(): t = TypeStruct(scope=scope('T'), is_fully_resolved=False) t_star = TypePointer(pointee=t) assert simplify_type_system(parse_expr('cast(fp, T*) + 3')) == ( parse_expr('fp + 3'), t_star) assert simplify_type_system(parse_expr('cast(fp, T*) - 3')) == ( parse_expr('fp - 3'), t_star) assert simplify_type_system(parse_expr('cast(fp, T*) - cast(3, T*)')) == ( parse_expr('fp - 3'), TypeFelt())
def visit_ExprFuncCall(self, expr: ExprFuncCall): # Convert ExprFuncCall to ExprCast. rvalue = expr.rvalue if rvalue.implicit_arguments is not None: raise CairoTypeError( 'Implicit arguments cannot be used with struct constructors.', location=rvalue.implicit_arguments.location) struct_type = self.resolve_type_callback( TypeStruct(scope=ScopedName.from_string(rvalue.func_ident.name), is_fully_resolved=False, location=expr.location)) return self.visit( ExprCast(expr=ExprTuple(rvalue.arguments, location=expr.location), dest_type=struct_type, location=expr.location))
def test_type_tuples(): t = TypeStruct(scope=scope('T'), is_fully_resolved=True) t_star = TypePointer(pointee=t) # Simple tuple. simplify_type_system_test('(fp, [cast(fp, T*)], cast(fp,T*))', '(fp, [fp], fp)', TypeTuple(members=[TypeFelt(), t, t_star], )) # Nested. simplify_type_system_test( '(fp, (), ([cast(fp, T*)],))', '(fp, (), ([fp],))', TypeTuple(members=[ TypeFelt(), TypeTuple(members=[]), TypeTuple(members=[t]) ], ))
def test_type_tuples(): t = TypeStruct(scope=scope('T'), is_fully_resolved=False) t_star = TypePointer(pointee=t) # Simple tuple. assert simplify_type_system(parse_expr('(fp, [cast(fp, T*)], cast(fp,T*))')) == ( parse_expr('(fp, [fp], fp)'), TypeTuple(members=[TypeFelt(), t, t_star],) ) # Nested. assert simplify_type_system(parse_expr('(fp, (), ([cast(fp, T*)],))')) == ( parse_expr('(fp, (), ([fp],))'), TypeTuple( members=[ TypeFelt(), TypeTuple(members=[]), TypeTuple(members=[t])], ) )
def test_type_visitor(): t = TypeStruct(scope=scope('T'), is_fully_resolved=True) t_star = TypePointer(pointee=t) t_star2 = TypePointer(pointee=t_star) simplify_type_system_test('fp + 3 + [ap]', 'fp + 3 + [ap]', TypeFelt()) simplify_type_system_test('cast(fp + 3 + [ap], T*)', 'fp + 3 + [ap]', t_star) # Two casts. simplify_type_system_test('cast(cast(fp, T*), felt)', 'fp', TypeFelt()) # Cast from T to T. simplify_type_system_test('cast([cast(fp, T*)], T)', '[fp]', t) # Dereference. simplify_type_system_test('[cast(fp, T**)]', '[fp]', t_star) simplify_type_system_test('[[cast(fp, T**)]]', '[[fp]]', t) # Address of. simplify_type_system_test('&([[cast(fp, T**)]])', '[fp]', t_star) simplify_type_system_test('&&[[cast(fp, T**)]]', 'fp', t_star2)
def test_reference_to_structs(): t = TypeStruct(scope=scope('T'), is_fully_resolved=True) t_star = TypePointer(pointee=t) identifier_values = { scope('ref'): ReferenceDefinition( full_name=scope('ref'), cairo_type=t, references=[] ), scope('T'): StructDefinition( full_name=scope('T'), members={ 'x': MemberDefinition(offset=3, cairo_type=t_star), }, size=4, ), } reference_manager = ReferenceManager() flow_tracking_data = FlowTrackingDataActual(ap_tracking=RegTrackingData()) flow_tracking_data = flow_tracking_data.add_reference( reference_manager=reference_manager, name=scope('ref'), ref=Reference( pc=0, value=mark_types_in_expr_resolved(parse_expr('[cast(100, T*)]')), ap_tracking_data=RegTrackingData(group=0, offset=2), ), ) memory = {103: 200} consts = get_vm_consts( identifier_values, reference_manager, flow_tracking_data, memory=memory) assert consts.ref.address_ == 100 assert consts.ref.x.address_ == 200 # Set the pointer ref.x.x to 300. consts.ref.x.x = 300 assert memory[203] == 300 assert consts.ref.x.x.address_ == 300 assert consts.ref.type_ == consts.T
def test_references(): reference_manager = ReferenceManager() references = { scope('x.ref'): reference_manager.alloc_id( Reference( pc=0, value=parse_expr('[ap + 1]'), ap_tracking_data=RegTrackingData(group=0, offset=2), )), scope('x.ref2'): reference_manager.alloc_id( Reference( pc=0, value=parse_expr('[ap + 1] + 0'), ap_tracking_data=RegTrackingData(group=0, offset=2), )), scope('x.ref3'): reference_manager.alloc_id( Reference( pc=0, value=parse_expr('ap + 1'), ap_tracking_data=RegTrackingData(group=0, offset=2), )), scope('x.typeref'): reference_manager.alloc_id( Reference( pc=0, value=mark_types_in_expr_resolved( parse_expr('cast(ap + 1, MyStruct*)')), ap_tracking_data=RegTrackingData(group=0, offset=3), )), scope('x.typeref2'): reference_manager.alloc_id( Reference( pc=0, value=mark_types_in_expr_resolved( parse_expr('cast([ap + 1], MyStruct*)')), ap_tracking_data=RegTrackingData(group=0, offset=3), )), } my_struct_star = TypePointer( pointee=TypeStruct(scope=scope('MyStruct'), is_fully_resolved=True)) identifier_values = { scope('x.ref'): ReferenceDefinition(full_name=scope('x.ref'), cairo_type=TypeFelt(), references=[]), scope('x.ref2'): ReferenceDefinition(full_name=scope('x.ref2'), cairo_type=TypeFelt(), references=[]), scope('x.ref3'): ReferenceDefinition(full_name=scope('x.ref3'), cairo_type=TypeFelt(), references=[]), scope('x.typeref'): ReferenceDefinition(full_name=scope('x.typeref'), cairo_type=my_struct_star, references=[]), scope('x.typeref2'): ReferenceDefinition(full_name=scope('x.typeref2'), cairo_type=my_struct_star, references=[]), scope('MyStruct'): StructDefinition( full_name=scope('MyStruct'), members={ 'member': MemberDefinition(offset=10, cairo_type=TypeFelt()), }, size=11, ), } prime = 2**64 + 13 ap = 100 fp = 200 memory = { (ap - 2) + 1: 1234, (ap - 1) + 1: 1000, (ap - 1) + 1 + 2: 13, (ap - 1) + 1 + 10: 17, } flow_tracking_data = FlowTrackingDataActual( ap_tracking=RegTrackingData(group=0, offset=4), reference_ids=references, ) context = VmConstsContext( identifiers=IdentifierManager.from_dict(identifier_values), evaluator=ExpressionEvaluator(prime, ap, fp, memory).eval, reference_manager=reference_manager, flow_tracking_data=flow_tracking_data, memory=memory, pc=0) consts = VmConsts(context=context, accessible_scopes=[ScopedName()]) assert consts.x.ref == memory[(ap - 2) + 1] assert consts.x.typeref.address_ == (ap - 1) + 1 assert consts.x.typeref.member == memory[(ap - 1) + 1 + 10] with pytest.raises(IdentifierError, match="'abc' is not a member of 'MyStruct'."): consts.x.typeref.abc with pytest.raises(IdentifierError, match="'SIZE' is not a member of 'MyStruct'."): consts.x.typeref.SIZE with pytest.raises( AssertionError, match='Cannot change the value of a struct definition.'): consts.MyStruct = 13 assert consts.MyStruct.member == 10 with pytest.raises(AssertionError, match='Cannot change the value of a constant.'): consts.MyStruct.member = 13 assert consts.MyStruct.SIZE == 11 with pytest.raises(AssertionError, match='Cannot change the value of a constant.'): consts.MyStruct.SIZE = 13 with pytest.raises(IdentifierError, match="'abc' is not a member of 'MyStruct'."): consts.MyStruct.abc # Test that VmConsts can be used to assign values to references of the form '[...]'. memory.clear() consts.x.ref = 1234 assert memory == {(ap - 2) + 1: 1234} memory.clear() consts.x.typeref.member = 1001 assert memory == {(ap - 1) + 1 + 10: 1001} memory.clear() consts.x.typeref2 = 4321 assert memory == {(ap - 1) + 1: 4321} consts.x.typeref2.member = 1 assert memory == { (ap - 1) + 1: 4321, 4321 + 10: 1, } with pytest.raises(AssertionError, match='Cannot change the value of a scope definition'): consts.x = 1000 with pytest.raises( AssertionError, match= r'x.ref2 \(= \[ap \+ 1\] \+ 0\) does not reference memory and cannot be assigned.', ): consts.x.ref2 = 1000 with pytest.raises( AssertionError, match= r'x.typeref \(= ap \+ 1\) does not reference memory and cannot be assigned.', ): consts.x.typeref = 1000
def test_type_subscript_op(): felt_star_star = TypePointer(pointee=TypePointer(pointee=TypeFelt())) t = TypeStruct(scope=scope('T'), is_fully_resolved=True) t_star = TypePointer(pointee=t) identifier_dict = { scope('T'): StructDefinition(full_name=scope('T'), members={}, size=7) } identifiers = IdentifierManager.from_dict(identifier_dict) simplify_type_system_test('cast(fp, felt*)[3]', '[fp + 3 * 1]', TypeFelt()) simplify_type_system_test('cast(fp, felt***)[0]', '[fp + 0 * 1]', felt_star_star) simplify_type_system_test('[cast(fp, T****)][ap][ap]', '[[[fp] + ap * 1] + ap * 1]', t_star) simplify_type_system_test('cast(fp, T**)[1][2]', '[[fp + 1 * 1] + 2 * 7]', t, identifiers=identifiers) # Test that 'cast(fp, T*)[2 * ap + 3]' simplifies into '[fp + (2 * ap + 3) * 7]', but without # the parentheses. assert simplify_type_system( mark_types_in_expr_resolved(parse_expr('cast(fp, T*)[2 * ap + 3]')), identifiers=identifiers) == (remove_parentheses( parse_expr('[fp + (2 * ap + 3) * 7]')), t) # Test subscript operator for tuples. simplify_type_system_test('(cast(fp, felt**), fp, cast(fp, T*))[2]', 'fp', t_star) simplify_type_system_test('(cast(fp, felt**), fp, cast(fp, T*))[0]', 'fp', felt_star_star) simplify_type_system_test('(cast(fp, felt**), ap, cast(fp, T*))[3*4 - 11]', 'ap', TypeFelt()) simplify_type_system_test('[cast(ap, (felt, felt)*)][0]', '[ap + 0]', TypeFelt()) simplify_type_system_test('[cast(ap, (T*, T, felt, T*, felt*)*)][3]', '[ap + 9]', t_star, identifiers=identifiers) # Test failures. verify_exception( '(fp, fp, fp)[cast(ap, felt*)]', """ file:?:?: Cannot apply subscript-operator with offset of non-felt type 'felt*'. (fp, fp, fp)[cast(ap, felt*)] ^*************^ """) verify_exception( '(fp, fp, fp)[[ap]]', """ file:?:?: Subscript-operator for tuples supports only constant offsets, found 'ExprDeref'. (fp, fp, fp)[[ap]] ^**^ """) # The simplifier in TypeSystemVisitor cannot access PRIME, so PyConsts are unsimplified. verify_exception( '(fp, fp, fp)[%[1%]]', """ file:?:?: Subscript-operator for tuples supports only constant offsets, found 'ExprPyConst'. (fp, fp, fp)[%[1%]] ^***^ """) verify_exception( '(fp, fp, fp)[3]', """ file:?:?: Tuple index 3 is out of range [0, 3). (fp, fp, fp)[3] ^*************^ """) verify_exception( '[cast(fp, (T*, T, felt)*)][-1]', """ file:?:?: Tuple index -1 is out of range [0, 3). [cast(fp, (T*, T, felt)*)][-1] ^****************************^ """) verify_exception( 'cast(fp, felt)[0]', """ file:?:?: Cannot apply subscript-operator to non-pointer, non-tuple type 'felt'. cast(fp, felt)[0] ^***************^ """) verify_exception( '[cast(fp, T*)][0]', """ file:?:?: Cannot apply subscript-operator to non-pointer, non-tuple type 'T'. [cast(fp, T*)][0] ^***************^ """) verify_exception( 'cast(fp, felt*)[[cast(ap, T*)]]', """ file:?:?: Cannot apply subscript-operator with offset of non-felt type 'T'. cast(fp, felt*)[[cast(ap, T*)]] ^************^ """) verify_exception('cast(fp, Z*)[0]', """ file:?:?: Unknown identifier 'Z'. cast(fp, Z*)[0] ^*************^ """, identifiers=identifiers) verify_exception('cast(fp, T*)[0]', """ file:?:?: Unknown identifier 'T'. cast(fp, T*)[0] ^*************^ """, identifiers=None)
def create_func_wrapper(self, elm: CodeElementFunction, func_alias_name: str): """ Generates a wrapper that converts between the StarkNet contract ABI and the Cairo calling convention. Arguments: elm - the CodeElementFunction of the wrapped function. func_alias_name - an alias for the FunctionDefention in the current scope. """ os_context = self.get_os_context() func_location = elm.identifier.location assert func_location is not None # We expect the call stack to look as follows: # pointer to os_context struct. # calldata size. # pointer to the call data array. # ret_fp. # ret_pc. os_context_ptr = ExprDeref( addr=ExprOperator( ExprReg(reg=Register.FP, location=func_location), '+', ExprConst(-5, location=func_location), location=func_location), location=func_location) calldata_size = ExprDeref( addr=ExprOperator( ExprReg(reg=Register.FP, location=func_location), '+', ExprConst(-4, location=func_location), location=func_location), location=func_location) calldata_ptr = ExprDeref( addr=ExprOperator( ExprReg(reg=Register.FP, location=func_location), '+', ExprConst(-3, location=func_location), location=func_location), location=func_location) implicit_arguments = None implicit_arguments_identifiers: Dict[str, TypedIdentifier] = {} if elm.implicit_arguments is not None: args = [] for typed_identifier in elm.implicit_arguments.identifiers: ptr_name = typed_identifier.name if ptr_name not in os_context: raise PreprocessorError( f"Unexpected implicit argument '{ptr_name}' in an external function.", location=typed_identifier.identifier.location) implicit_arguments_identifiers[ptr_name] = typed_identifier # Add the assignment expression 'ptr_name = ptr_name' to the implicit arg list. args.append(ExprAssignment( identifier=typed_identifier.identifier, expr=typed_identifier.identifier, location=typed_identifier.location, )) implicit_arguments = ArgList( args=args, notes=[], has_trailing_comma=True, location=elm.implicit_arguments.location) return_args_exprs: List[Expression] = [] # Create references. for ptr_name, index in os_context.items(): ref_name = self.current_scope + ptr_name arg_identifier = implicit_arguments_identifiers.get(ptr_name) if arg_identifier is None: location: Optional[Location] = func_location cairo_type: CairoType = TypeFelt(location=location) else: location = arg_identifier.location cairo_type = self.resolve_type(arg_identifier.get_type()) # Add a reference of the form # 'let ref_name = [cast(os_context_ptr + index, cairo_type*)]'. self.add_reference( name=ref_name, value=ExprDeref( addr=ExprCast( ExprOperator( os_context_ptr, '+', ExprConst(index, location=location), location=location), dest_type=TypePointer(pointee=cairo_type, location=cairo_type.location), location=cairo_type.location), location=location), cairo_type=cairo_type, location=location, require_future_definition=False) assert index == len(return_args_exprs), 'Unexpected index.' return_args_exprs.append(ExprIdentifier(name=ptr_name, location=func_location)) arg_struct_def = self.get_struct_definition( name=ScopedName.from_string(func_alias_name) + CodeElementFunction.ARGUMENT_SCOPE, location=func_location) code_elements, call_args = process_calldata( calldata_ptr=calldata_ptr, calldata_size=calldata_size, identifiers=self.identifiers, struct_def=arg_struct_def, has_range_check_builtin='range_check_ptr' in os_context, location=func_location, ) for code_element in code_elements: self.visit(code_element) self.visit(CodeElementFuncCall( func_call=RvalueFuncCall( func_ident=ExprIdentifier(name=func_alias_name, location=func_location), arguments=call_args, implicit_arguments=implicit_arguments, location=func_location))) ret_struct_name = ScopedName.from_string(func_alias_name) + CodeElementFunction.RETURN_SCOPE ret_struct_type = self.resolve_type(TypeStruct(ret_struct_name, False)) ret_struct_def = self.get_struct_definition( name=ret_struct_name, location=func_location) ret_struct_expr = create_simple_ref_expr( reg=Register.AP, offset=-ret_struct_def.size, cairo_type=ret_struct_type, location=func_location) self.add_reference( name=self.current_scope + 'ret_struct', value=ret_struct_expr, cairo_type=ret_struct_type, require_future_definition=False, location=func_location) # Add function return values. retdata_size, retdata_ptr = self.process_retdata( ret_struct_ptr=ExprIdentifier(name='ret_struct'), ret_struct_type=ret_struct_type, struct_def=ret_struct_def, location=func_location, ) return_args_exprs += [retdata_size, retdata_ptr] # Push the return values. self.push_compound_expressions( compound_expressions=[self.simplify_expr_as_felt(expr) for expr in return_args_exprs], location=func_location, ) # Add a ret instruction. self.visit(CodeElementInstruction( instruction=InstructionAst( body=RetInstruction(), inc_ap=False, location=func_location))) # Add an entry to the ABI. external_decorator = self.get_external_decorator(elm) assert external_decorator is not None is_view = external_decorator.name == 'view' if external_decorator.name == L1_HANDLER_DECORATOR: entry_type = 'l1_handler' elif external_decorator.name in [EXTERNAL_DECORATOR, VIEW_DECORATOR]: entry_type = 'function' else: raise NotImplementedError(f'Unsupported decorator {external_decorator.name}') entry_type = ( 'function' if external_decorator.name != L1_HANDLER_DECORATOR else L1_HANDLER_DECORATOR) self.add_abi_entry( name=elm.name, arg_struct_def=arg_struct_def, ret_struct_def=ret_struct_def, is_view=is_view, entry_type=entry_type)
def type_struct(self, value): assert len(value) == 1 and isinstance(value[0], ExprIdentifier) return TypeStruct(scope=ScopedName.from_string(value[0].name), is_fully_resolved=False, location=value[0].location)
def test_type_dot_op(): """ Tests type_system_visitor for ExprDot-s, in the following struct architecture: struct S: member x : felt member y : felt end struct T: member t : felt member s : S member sp : S* end struct R: member r : R* end """ t = TypeStruct(scope=scope('T'), is_fully_resolved=True) s = TypeStruct(scope=scope('S'), is_fully_resolved=True) s_star = TypePointer(pointee=s) r = TypeStruct(scope=scope('R'), is_fully_resolved=True) r_star = TypePointer(pointee=r) identifier_dict = { scope('T'): StructDefinition( full_name=scope('T'), members={ 't': MemberDefinition(offset=0, cairo_type=TypeFelt()), 's': MemberDefinition(offset=1, cairo_type=s), 'sp': MemberDefinition(offset=3, cairo_type=s_star), }, size=4, ), scope('S'): StructDefinition( full_name=scope('S'), members={ 'x': MemberDefinition(offset=0, cairo_type=TypeFelt()), 'y': MemberDefinition(offset=1, cairo_type=TypeFelt()), }, size=2, ), scope('R'): StructDefinition( full_name=scope('R'), members={ 'r': MemberDefinition(offset=0, cairo_type=r_star), }, size=1, ), } identifiers = IdentifierManager.from_dict(identifier_dict) for (orig_expr, simplified_expr, simplified_type) in [ ('[cast(fp, T*)].t', '[fp]', TypeFelt()), ('[cast(fp, T*)].s', '[fp + 1]', s), ('[cast(fp, T*)].sp', '[fp + 3]', s_star), ('[cast(fp, T*)].s.x', '[fp + 1]', TypeFelt()), ('[cast(fp, T*)].s.y', '[fp + 1 + 1]', TypeFelt()), ('[[cast(fp, T*)].sp].x', '[[fp + 3]]', TypeFelt()), ('[cast(fp, R*)]', '[fp]', r), ('[cast(fp, R*)].r', '[fp]', r_star), ('[[[cast(fp, R*)].r].r].r', '[[[fp]]]', r_star), # Test . as -> ('cast(fp, T*).t', '[fp]', TypeFelt()), ('cast(fp, T*).sp.y', '[[fp + 3] + 1]', TypeFelt()), ('cast(fp, R*).r.r.r', '[[[fp]]]', r_star), # More tests. ('(cast(fp, T*).s)', '[fp + 1]', s), ('(cast(fp, T*).s).x', '[fp + 1]', TypeFelt()), ('(&(cast(fp, T*).s)).x', '[fp + 1]', TypeFelt()) ]: simplify_type_system_test(orig_expr, simplified_expr, simplified_type, identifiers=identifiers) # Test failures. verify_exception('cast(fp, felt).x', """ file:?:?: Cannot apply dot-operator to non-struct type 'felt'. cast(fp, felt).x ^**************^ """, identifiers=identifiers) verify_exception('cast(fp, felt*).x', """ file:?:?: Cannot apply dot-operator to pointer-to-non-struct type 'felt*'. cast(fp, felt*).x ^***************^ """, identifiers=identifiers) verify_exception('cast(fp, T*).x', """ file:?:?: Member 'x' does not appear in definition of struct 'T'. cast(fp, T*).x ^************^ """, identifiers=identifiers) verify_exception('cast(fp, Z*).x', """ file:?:?: Unknown identifier 'Z'. cast(fp, Z*).x ^************^ """, identifiers=identifiers) verify_exception('cast(fp, T*).x', """ file:?:?: Identifiers must be initialized for type-simplification of dot-operator expressions. cast(fp, T*).x ^************^ """, identifiers=None) verify_exception('cast(fp, Z*).x', """ file:?:?: Type is expected to be fully resolved at this point. cast(fp, Z*).x ^************^ """, identifiers=identifiers, resolve_types=False)