def visit_ExprDot(self, expr: ExprDot) -> Tuple[ExprDeref, CairoType]: self.verify_identifier_manager_initialized(location=expr.location) inner_expr, inner_type = self.visit(expr.expr) if isinstance(inner_type, TypePointer): if not isinstance(inner_type.pointee, TypeStruct): raise CairoTypeError( f'Cannot apply dot-operator to pointer-to-non-struct type ' f"'{inner_type.format()}'.", location=expr.location) # Allow for . as ->, once. inner_type = inner_type.pointee elif isinstance(inner_type, TypeStruct): if isinstance(inner_expr, ExprTuple): raise CairoTypeError( 'Accessing struct members for r-value structs is not supported yet.', location=expr.location) # Get the address, to evaluate . as ->. inner_expr = get_expr_addr(inner_expr) else: raise CairoTypeError( f"Cannot apply dot-operator to non-struct type '{inner_type.format()}'.", location=expr.location) try: struct_def = get_struct_definition( struct_name=inner_type.resolved_scope, identifier_manager=self.identifiers) except Exception as exc: raise CairoTypeError(str(exc), location=expr.location) if expr.member.name not in struct_def.members: raise CairoTypeError( f"Member '{expr.member.name}' does not appear in definition of struct " f"'{inner_type.format()}'.", location=expr.location) member_definition = struct_def.members[expr.member.name] member_type = member_definition.cairo_type member_offset = member_definition.offset if member_offset == 0: simplified_expr = ExprDeref(addr=inner_expr, location=expr.location) else: mem_offset_expr = ExprConst(val=member_offset, location=expr.location) simplified_expr = ExprDeref(addr=ExprOperator( a=inner_expr, op='+', b=mem_offset_expr, location=expr.location), location=expr.location) return simplified_expr, member_type
def verify_offset_is_felt(offset_type: CairoType, offset_location: Location): if not isinstance(offset_type, TypeFelt): raise CairoTypeError( 'Cannot apply subscript-operator with offset of non-felt type ' f"'{offset_type.format()}'.", location=offset_location)
def verify_identifier_manager_initialized(self, location: Optional[Location]): if self.identifiers_initalized: return raise CairoTypeError( 'Identifiers must be initialized for type-simplification of dot-operator ' 'expressions.', location=location)
def visit_ExprNeg(self, expr: ExprNeg) -> Tuple[ExprNeg, TypeFelt]: inner_expr, inner_type = self.visit(expr.val) if not isinstance(inner_type, TypeFelt): raise CairoTypeError( f"Unary '-' is not supported for type '{inner_type.format()}'.", location=expr.location) return dataclasses.replace( expr, val=inner_expr), TypeFelt(location=expr.location)
def visit_ExprDeref(self, expr: ExprDeref) -> Tuple[ExprDeref, CairoType]: addr_expr, addr_type = self.visit(expr.addr) if isinstance(addr_type, TypeFelt): return dataclasses.replace( expr, addr=addr_expr), TypeFelt(location=expr.location) elif isinstance(addr_type, TypePointer): return dataclasses.replace(expr, addr=addr_expr), addr_type.pointee else: raise CairoTypeError( f"Cannot dereference type '{addr_type.format()}'.", location=expr.location)
def visit_ExprPow(self, expr: ExprOperator) -> Tuple[ExprOperator, CairoType]: a_expr, a_type = self.visit(expr.a) b_expr, b_type = self.visit(expr.b) if not isinstance(a_type, TypeFelt) and isinstance(b_type, TypeFelt): raise CairoTypeError( f"Operator '**' is not implemented for types " f"'{a_type.format()}' and '{b_type.format()}'.", location=expr.location) return dataclasses.replace(expr, a=a_expr, b=b_expr), TypeFelt(location=expr.location)
def visit_ExprCast(self, expr: ExprCast) -> Tuple[Expression, CairoType]: inner_expr, inner_type = self.visit(expr.expr) dest_type = expr.dest_type if not check_cast(src_type=inner_type, dest_type=dest_type, expr=inner_expr, cast_type=expr.cast_type): raise CairoTypeError( f"Cannot cast '{inner_type.format()}' to '{dest_type.format()}'.", location=expr.location) # Remove the cast() from the expression. return inner_expr, dest_type
def visit_ExprCast(self, expr: ExprCast) -> Tuple[Expression, CairoType]: inner_expr, src_type = self.visit(expr.expr) dest_type = expr.dest_type if not check_cast(src_type=src_type, dest_type=dest_type, identifier_manager=self.identifiers, expr=inner_expr, cast_type=expr.cast_type): raise CairoTypeError( f"Cannot cast '{src_type.format()}' to '{dest_type.format()}'.", location=expr.location) # Remove the cast() from the expression, but keep its original location. return dataclasses.replace(inner_expr, location=expr.location), dest_type
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 visit_ExprOperator( self, expr: ExprOperator) -> Tuple[ExprOperator, CairoType]: a_expr, a_type = self.visit(expr.a) b_expr, b_type = self.visit(expr.b) op = expr.op result_type: CairoType if isinstance(a_type, TypeFelt) and isinstance(b_type, TypeFelt): result_type = TypeFelt(location=expr.location) elif isinstance(a_type, TypePointer) and isinstance( b_type, TypeFelt) and op in ['+', '-']: result_type = a_type elif isinstance(a_type, TypeFelt) and isinstance( b_type, TypePointer) and op == '+': result_type = b_type elif isinstance(a_type, TypePointer) and a_type == b_type and op == '-': result_type = TypeFelt(location=expr.location) else: raise CairoTypeError( f"Operator '{op}' is not implemented for types " f"'{a_type.format()}' and '{b_type.format()}'.", location=expr.location) return dataclasses.replace(expr, a=a_expr, b=b_expr), result_type
def visit_ExprIdentifier( self, expr: ExprIdentifier) -> Tuple[Expression, CairoType]: raise CairoTypeError( f"Identifier '{expr.format()}' is not allowed in this context.", location=expr.location)
def get_expr_addr(expr: Expression): if not isinstance(expr, ExprDeref): raise CairoTypeError('Expression has no address.', location=expr.location) return expr.addr
def visit_ExprSubscript( self, expr: ExprSubscript) -> Tuple[Expression, CairoType]: inner_expr, inner_type = self.visit(expr.expr) offset_expr, offset_type = self.visit(expr.offset) if isinstance(inner_type, TypeTuple): self.verify_offset_is_felt(offset_type, offset_expr.location) offset_expr = ExpressionSimplifier().visit(offset_expr) if not isinstance(offset_expr, ExprConst): raise CairoTypeError( 'Subscript-operator for tuples supports only constant offsets, found ' f"'{type(offset_expr).__name__}'.", location=offset_expr.location) offset_value = offset_expr.val tuple_len = len(inner_type.members) if not 0 <= offset_value < tuple_len: raise CairoTypeError( f'Tuple index {offset_value} is out of range [0, {tuple_len}).', location=expr.location) item_type = inner_type.members[offset_value] if isinstance(inner_expr, ExprTuple): assert len(inner_expr.members.args) == tuple_len return ( # Take the inner item, but keep the original expression's location. dataclasses.replace( inner_expr.members.args[offset_value].expr, location=expr.location), item_type) elif isinstance(inner_expr, ExprDeref): # Handles pointers cast as tuples*, e.g. `[cast(ap, (felt, felt)*][0]`. addr = inner_expr.addr offset_in_felts = ExprConst(val=sum( map(self.get_size, inner_type.members[:offset_value])), location=offset_expr.location) addr_with_offset = ExprOperator(a=addr, op='+', b=offset_in_felts, location=expr.location) return ExprDeref(addr=addr_with_offset, location=expr.location), item_type else: raise CairoTypeError( 'Unexpected expression typed as TypeTuple. Expected ExprTuple or ExprDeref, ' f"found '{type(inner_expr).__name__}'.", location=expr.location) elif isinstance(inner_type, TypePointer): self.verify_offset_is_felt(offset_type, offset_expr.location) try: # If pointed type is struct, get_size could throw IdentifierErrors. We catch and # convert them to CairoTypeErrors. element_size = self.get_size(inner_type.pointee) except Exception as exc: raise CairoTypeError(str(exc), location=expr.location) element_size_expr = ExprConst(val=element_size, location=expr.location) modified_offset_expr = ExprOperator(a=offset_expr, op='*', b=element_size_expr, location=expr.location) simplified_expr = ExprDeref(addr=ExprOperator( a=inner_expr, op='+', b=modified_offset_expr, location=expr.location), location=expr.location) return simplified_expr, inner_type.pointee else: raise CairoTypeError( 'Cannot apply subscript-operator to non-pointer, non-tuple type ' f"'{inner_type.format()}'.", location=expr.location)
def visit_ExprIdentifier( self, expr: ExprIdentifier) -> Tuple[Expression, CairoType]: raise CairoTypeError( f'Unexpected unresolved identifier {expr.format()}.', location=expr.location)