diff --git a/src/ast_render.cpp b/src/ast_render.cpp index 599d4b265..5ffec9203 100644 --- a/src/ast_render.cpp +++ b/src/ast_render.cpp @@ -625,7 +625,7 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) { fprintf(ar->f, "@"); } AstNode *fn_ref_node = node->data.fn_call_expr.fn_ref_expr; - bool grouped = (fn_ref_node->type != NodeTypeBinOpExpr); + bool grouped = (fn_ref_node->type != NodeTypePrefixOpExpr); render_node_extra(ar, fn_ref_node, grouped); fprintf(ar->f, "("); for (size_t i = 0; i < node->data.fn_call_expr.params.length; i += 1) { diff --git a/src/parsec.cpp b/src/parsec.cpp index fbd3f2194..8053527eb 100644 --- a/src/parsec.cpp +++ b/src/parsec.cpp @@ -350,6 +350,21 @@ static AstNode* trans_c_cast(Context *c, const SourceLocation &source_location, return trans_create_node_fn_call_1(c, trans_qual_type(c, qt, source_location), expr); } +static bool qual_type_is_fn_ptr(Context *c, const QualType &qt) { + const Type *ty = qt.getTypePtr(); + if (ty->getTypeClass() != Type::Pointer) { + return false; + } + const PointerType *pointer_ty = static_cast(ty); + QualType child_qt = pointer_ty->getPointeeType(); + const Type *child_ty = child_qt.getTypePtr(); + if (child_ty->getTypeClass() != Type::Paren) { + return false; + } + const ParenType *paren_ty = static_cast(child_ty); + return paren_ty->getInnerType().getTypePtr()->getTypeClass() == Type::FunctionProto; +} + static uint32_t qual_type_int_bit_width(Context *c, const QualType &qt, const SourceLocation &source_loc) { const Type *ty = qt.getTypePtr(); switch (ty->getTypeClass()) { @@ -990,6 +1005,21 @@ static AstNode *trans_create_assign(Context *c, bool result_used, AstNode *block } } +static AstNode *trans_create_shift_op(Context *c, AstNode *block, QualType result_type, Expr *lhs_expr, BinOpType bin_op, Expr *rhs_expr) { + const SourceLocation &rhs_location = rhs_expr->getLocStart(); + AstNode *rhs_type = qual_type_to_log2_int_ref(c, result_type, rhs_location); + // lhs >> u5(rh) + + AstNode *lhs = trans_expr(c, true, block, lhs_expr, TransLValue); + if (lhs == nullptr) return nullptr; + + AstNode *rhs = trans_expr(c, true, block, rhs_expr, TransRValue); + if (rhs == nullptr) return nullptr; + AstNode *coerced_rhs = trans_create_node_fn_call_1(c, rhs_type, rhs); + + return trans_create_node_bin_op(c, lhs, bin_op, coerced_rhs); +} + static AstNode *trans_binary_operator(Context *c, bool result_used, AstNode *block, BinaryOperator *stmt) { switch (stmt->getOpcode()) { case BO_PtrMemD: @@ -1022,7 +1052,7 @@ static AstNode *trans_binary_operator(Context *c, bool result_used, AstNode *blo // unsigned/float division uses the operator return trans_create_bin_op(c, block, stmt->getLHS(), BinOpTypeMod, stmt->getRHS()); } else { - // signed integer division uses @divTrunc + // signed integer division uses @rem AstNode *fn_call = trans_create_node_builtin_fn_call_str(c, "rem"); AstNode *lhs = trans_expr(c, true, block, stmt->getLHS(), TransLValue); if (lhs == nullptr) return nullptr; @@ -1041,11 +1071,9 @@ static AstNode *trans_binary_operator(Context *c, bool result_used, AstNode *blo qual_type_has_wrapping_overflow(c, stmt->getType()) ? BinOpTypeSubWrap : BinOpTypeSub, stmt->getRHS()); case BO_Shl: - emit_warning(c, stmt->getLocStart(), "TODO handle more C binary operators: BO_Shl"); - return nullptr; + return trans_create_shift_op(c, block, stmt->getType(), stmt->getLHS(), BinOpTypeBitShiftLeft, stmt->getRHS()); case BO_Shr: - emit_warning(c, stmt->getLocStart(), "TODO handle more C binary operators: BO_Shr"); - return nullptr; + return trans_create_shift_op(c, block, stmt->getType(), stmt->getLHS(), BinOpTypeBitShiftRight, stmt->getRHS()); case BO_LT: return trans_create_bin_op(c, block, stmt->getLHS(), BinOpTypeCmpLessThan, stmt->getRHS()); case BO_GT: @@ -1071,49 +1099,167 @@ static AstNode *trans_binary_operator(Context *c, bool result_used, AstNode *blo return trans_create_bin_op(c, block, stmt->getLHS(), BinOpTypeBoolOr, stmt->getRHS()); case BO_Assign: return trans_create_assign(c, result_used, block, stmt->getLHS(), stmt->getRHS()); - case BO_MulAssign: - emit_warning(c, stmt->getLocStart(), "TODO handle more C binary operators: BO_MulAssign"); - return nullptr; - case BO_DivAssign: - emit_warning(c, stmt->getLocStart(), "TODO handle more C binary operators: BO_DivAssign"); - return nullptr; - case BO_RemAssign: - emit_warning(c, stmt->getLocStart(), "TODO handle more C binary operators: BO_RemAssign"); - return nullptr; - case BO_AddAssign: - emit_warning(c, stmt->getLocStart(), "TODO handle more C binary operators: BO_AddAssign"); - return nullptr; - case BO_SubAssign: - emit_warning(c, stmt->getLocStart(), "TODO handle more C binary operators: BO_SubAssign"); - return nullptr; - case BO_ShlAssign: - emit_warning(c, stmt->getLocStart(), "TODO handle more C binary operators: BO_ShlAssign"); - return nullptr; - case BO_ShrAssign: - emit_warning(c, stmt->getLocStart(), "TODO handle more C binary operators: BO_ShrAssign"); - return nullptr; - case BO_AndAssign: - emit_warning(c, stmt->getLocStart(), "TODO handle more C binary operators: BO_AndAssign"); - return nullptr; - case BO_XorAssign: - emit_warning(c, stmt->getLocStart(), "TODO handle more C binary operators: BO_XorAssign"); - return nullptr; - case BO_OrAssign: - emit_warning(c, stmt->getLocStart(), "TODO handle more C binary operators: BO_OrAssign"); - return nullptr; case BO_Comma: - emit_warning(c, stmt->getLocStart(), "TODO handle more C binary operators: BO_Comma"); - return nullptr; + { + block = trans_create_node(c, NodeTypeBlock); + AstNode *lhs = trans_expr(c, false, block, stmt->getLHS(), TransRValue); + if (lhs == nullptr) return nullptr; + block->data.block.statements.append(maybe_suppress_result(c, false, lhs)); + AstNode *rhs = trans_expr(c, result_used, block, stmt->getRHS(), TransRValue); + if (rhs == nullptr) return nullptr; + block->data.block.statements.append(maybe_suppress_result(c, result_used, rhs)); + block->data.block.last_statement_is_result_expression = true; + return block; + } + case BO_MulAssign: + case BO_DivAssign: + case BO_RemAssign: + case BO_AddAssign: + case BO_SubAssign: + case BO_ShlAssign: + case BO_ShrAssign: + case BO_AndAssign: + case BO_XorAssign: + case BO_OrAssign: + zig_unreachable(); } zig_unreachable(); } +static AstNode *trans_create_compound_assign_shift(Context *c, bool result_used, AstNode *block, CompoundAssignOperator *stmt, BinOpType assign_op, BinOpType bin_op) { + const SourceLocation &rhs_location = stmt->getRHS()->getLocStart(); + AstNode *rhs_type = qual_type_to_log2_int_ref(c, stmt->getComputationLHSType(), rhs_location); + + bool use_intermediate_casts = stmt->getComputationLHSType().getTypePtr() != stmt->getComputationResultType().getTypePtr(); + if (!use_intermediate_casts && !result_used) { + // simple common case, where the C and Zig are identical: + // lhs >>= rhs + AstNode *lhs = trans_expr(c, true, block, stmt->getLHS(), TransLValue); + if (lhs == nullptr) return nullptr; + + AstNode *rhs = trans_expr(c, true, block, stmt->getRHS(), TransRValue); + if (rhs == nullptr) return nullptr; + AstNode *coerced_rhs = trans_create_node_fn_call_1(c, rhs_type, rhs); + + return trans_create_node_bin_op(c, lhs, assign_op, coerced_rhs); + } else { + // need more complexity. worst case, this looks like this: + // c: lhs >>= rhs + // zig: { + // zig: const _ref = &lhs; + // zig: *_ref = result_type(operation_type(*_ref) >> u5(rhs)); + // zig: *_ref + // zig: } + // where u5 is the appropriate type + + AstNode *child_block = trans_create_node(c, NodeTypeBlock); + + // const _ref = &lhs; + AstNode *lhs = trans_expr(c, true, child_block, stmt->getLHS(), TransLValue); + if (lhs == nullptr) return nullptr; + AstNode *addr_of_lhs = trans_create_node_addr_of(c, false, false, lhs); + // TODO: avoid name collisions with generated variable names + Buf* tmp_var_name = buf_create_from_str("_ref"); + AstNode *tmp_var_decl = trans_create_node_var_decl_local(c, true, tmp_var_name, nullptr, addr_of_lhs); + child_block->data.block.statements.append(tmp_var_decl); + + // *_ref = result_type(operation_type(*_ref) >> u5(rhs)); + + AstNode *rhs = trans_expr(c, true, child_block, stmt->getRHS(), TransRValue); + if (rhs == nullptr) return nullptr; + AstNode *coerced_rhs = trans_create_node_fn_call_1(c, rhs_type, rhs); + + AstNode *assign_statement = trans_create_node_bin_op(c, + trans_create_node_prefix_op(c, PrefixOpDereference, + trans_create_node_symbol(c, tmp_var_name)), + BinOpTypeAssign, + trans_c_cast(c, rhs_location, + stmt->getComputationResultType(), + trans_create_node_bin_op(c, + trans_c_cast(c, rhs_location, + stmt->getComputationLHSType(), + trans_create_node_prefix_op(c, PrefixOpDereference, + trans_create_node_symbol(c, tmp_var_name))), + bin_op, + coerced_rhs))); + child_block->data.block.statements.append(assign_statement); + + if (result_used) { + // *_ref + child_block->data.block.statements.append( + trans_create_node_prefix_op(c, PrefixOpDereference, + trans_create_node_symbol(c, tmp_var_name))); + child_block->data.block.last_statement_is_result_expression = true; + } + + return child_block; + } +} + +static AstNode *trans_create_compound_assign(Context *c, bool result_used, AstNode *block, CompoundAssignOperator *stmt, BinOpType assign_op, BinOpType bin_op) { + if (!result_used) { + // simple common case, where the C and Zig are identical: + // lhs += rhs + AstNode *lhs = trans_expr(c, true, block, stmt->getLHS(), TransLValue); + if (lhs == nullptr) return nullptr; + AstNode *rhs = trans_expr(c, true, block, stmt->getRHS(), TransRValue); + if (rhs == nullptr) return nullptr; + return trans_create_node_bin_op(c, lhs, assign_op, rhs); + } else { + // need more complexity. worst case, this looks like this: + // c: lhs += rhs + // zig: { + // zig: const _ref = &lhs; + // zig: *_ref = *_ref + rhs; + // zig: *_ref + // zig: } + + AstNode *child_block = trans_create_node(c, NodeTypeBlock); + + // const _ref = &lhs; + AstNode *lhs = trans_expr(c, true, child_block, stmt->getLHS(), TransLValue); + if (lhs == nullptr) return nullptr; + AstNode *addr_of_lhs = trans_create_node_addr_of(c, false, false, lhs); + // TODO: avoid name collisions with generated variable names + Buf* tmp_var_name = buf_create_from_str("_ref"); + AstNode *tmp_var_decl = trans_create_node_var_decl_local(c, true, tmp_var_name, nullptr, addr_of_lhs); + child_block->data.block.statements.append(tmp_var_decl); + + // *_ref = *_ref + rhs; + + AstNode *rhs = trans_expr(c, true, child_block, stmt->getRHS(), TransRValue); + if (rhs == nullptr) return nullptr; + + AstNode *assign_statement = trans_create_node_bin_op(c, + trans_create_node_prefix_op(c, PrefixOpDereference, + trans_create_node_symbol(c, tmp_var_name)), + BinOpTypeAssign, + trans_create_node_bin_op(c, + trans_create_node_prefix_op(c, PrefixOpDereference, + trans_create_node_symbol(c, tmp_var_name)), + bin_op, + rhs)); + child_block->data.block.statements.append(assign_statement); + + // *_ref + child_block->data.block.statements.append( + trans_create_node_prefix_op(c, PrefixOpDereference, + trans_create_node_symbol(c, tmp_var_name))); + child_block->data.block.last_statement_is_result_expression = true; + + return child_block; + } +} + + static AstNode *trans_compound_assign_operator(Context *c, bool result_used, AstNode *block, CompoundAssignOperator *stmt) { switch (stmt->getOpcode()) { case BO_MulAssign: - emit_warning(c, stmt->getLocStart(), "TODO handle more C compound assign operators: BO_MulAssign"); - return nullptr; + if (qual_type_has_wrapping_overflow(c, stmt->getType())) + return trans_create_compound_assign(c, result_used, block, stmt, BinOpTypeAssignTimesWrap, BinOpTypeMultWrap); + else + return trans_create_compound_assign(c, result_used, block, stmt, BinOpTypeAssignTimes, BinOpTypeMult); case BO_DivAssign: emit_warning(c, stmt->getLocStart(), "TODO handle more C compound assign operators: BO_DivAssign"); return nullptr; @@ -1121,95 +1267,25 @@ static AstNode *trans_compound_assign_operator(Context *c, bool result_used, Ast emit_warning(c, stmt->getLocStart(), "TODO handle more C compound assign operators: BO_RemAssign"); return nullptr; case BO_AddAssign: - emit_warning(c, stmt->getLocStart(), "TODO handle more C compound assign operators: BO_AddAssign"); - return nullptr; + if (qual_type_has_wrapping_overflow(c, stmt->getType())) + return trans_create_compound_assign(c, result_used, block, stmt, BinOpTypeAssignPlusWrap, BinOpTypeAddWrap); + else + return trans_create_compound_assign(c, result_used, block, stmt, BinOpTypeAssignPlus, BinOpTypeAdd); case BO_SubAssign: - emit_warning(c, stmt->getLocStart(), "TODO handle more C compound assign operators: BO_SubAssign"); - return nullptr; + if (qual_type_has_wrapping_overflow(c, stmt->getType())) + return trans_create_compound_assign(c, result_used, block, stmt, BinOpTypeAssignMinusWrap, BinOpTypeSubWrap); + else + return trans_create_compound_assign(c, result_used, block, stmt, BinOpTypeAssignMinus, BinOpTypeSub); case BO_ShlAssign: - emit_warning(c, stmt->getLocStart(), "TODO handle more C compound assign operators: BO_ShlAssign"); - return nullptr; - case BO_ShrAssign: { - BinOpType bin_op = BinOpTypeBitShiftRight; - - const SourceLocation &rhs_location = stmt->getRHS()->getLocStart(); - AstNode *rhs_type = qual_type_to_log2_int_ref(c, stmt->getComputationLHSType(), rhs_location); - - bool use_intermediate_casts = stmt->getComputationLHSType().getTypePtr() != stmt->getComputationResultType().getTypePtr(); - if (!use_intermediate_casts && !result_used) { - // simple common case, where the C and Zig are identical: - // lhs >>= rh* s - AstNode *lhs = trans_expr(c, true, block, stmt->getLHS(), TransLValue); - if (lhs == nullptr) return nullptr; - - AstNode *rhs = trans_expr(c, true, block, stmt->getRHS(), TransRValue); - if (rhs == nullptr) return nullptr; - AstNode *coerced_rhs = trans_create_node_fn_call_1(c, rhs_type, rhs); - - return trans_create_node_bin_op(c, lhs, BinOpTypeAssignBitShiftRight, coerced_rhs); - } else { - // need more complexity. worst case, this looks like this: - // c: lhs >>= rhs - // zig: { - // zig: const _ref = &lhs; - // zig: *_ref = result_type(operation_type(*_ref) >> u5(rhs)); - // zig: *_ref - // zig: } - // where u5 is the appropriate type - - // TODO: avoid mess when we don't need the assignment value for chained assignments or anything. - AstNode *child_block = trans_create_node(c, NodeTypeBlock); - - // const _ref = &lhs; - AstNode *lhs = trans_expr(c, true, child_block, stmt->getLHS(), TransLValue); - if (lhs == nullptr) return nullptr; - AstNode *addr_of_lhs = trans_create_node_addr_of(c, false, false, lhs); - // TODO: avoid name collisions with generated variable names - Buf* tmp_var_name = buf_create_from_str("_ref"); - AstNode *tmp_var_decl = trans_create_node_var_decl_local(c, true, tmp_var_name, nullptr, addr_of_lhs); - child_block->data.block.statements.append(tmp_var_decl); - - // *_ref = result_type(operation_type(*_ref) >> u5(rhs)); - - AstNode *rhs = trans_expr(c, true, child_block, stmt->getRHS(), TransRValue); - if (rhs == nullptr) return nullptr; - AstNode *coerced_rhs = trans_create_node_fn_call_1(c, rhs_type, rhs); - - AstNode *assign_statement = trans_create_node_bin_op(c, - trans_create_node_prefix_op(c, PrefixOpDereference, - trans_create_node_symbol(c, tmp_var_name)), - BinOpTypeAssign, - trans_c_cast(c, rhs_location, - stmt->getComputationResultType(), - trans_create_node_bin_op(c, - trans_c_cast(c, rhs_location, - stmt->getComputationLHSType(), - trans_create_node_prefix_op(c, PrefixOpDereference, - trans_create_node_symbol(c, tmp_var_name))), - bin_op, - coerced_rhs))); - child_block->data.block.statements.append(assign_statement); - - if (result_used) { - // *_ref - child_block->data.block.statements.append( - trans_create_node_prefix_op(c, PrefixOpDereference, - trans_create_node_symbol(c, tmp_var_name))); - child_block->data.block.last_statement_is_result_expression = true; - } - - return child_block; - } - } + return trans_create_compound_assign_shift(c, result_used, block, stmt, BinOpTypeAssignBitShiftLeft, BinOpTypeBitShiftLeft); + case BO_ShrAssign: + return trans_create_compound_assign_shift(c, result_used, block, stmt, BinOpTypeAssignBitShiftRight, BinOpTypeBitShiftRight); case BO_AndAssign: - emit_warning(c, stmt->getLocStart(), "TODO handle more C compound assign operators: BO_AndAssign"); - return nullptr; + return trans_create_compound_assign(c, result_used, block, stmt, BinOpTypeAssignBitAnd, BinOpTypeBinAnd); case BO_XorAssign: - emit_warning(c, stmt->getLocStart(), "TODO handle more C compound assign operators: BO_XorAssign"); - return nullptr; + return trans_create_compound_assign(c, result_used, block, stmt, BinOpTypeAssignBitXor, BinOpTypeBinXor); case BO_OrAssign: - emit_warning(c, stmt->getLocStart(), "TODO handle more C compound assign operators: BO_OrAssign"); - return nullptr; + return trans_create_compound_assign(c, result_used, block, stmt, BinOpTypeAssignBitOr, BinOpTypeBinOr); case BO_PtrMemD: case BO_PtrMemI: case BO_Assign: @@ -1232,7 +1308,7 @@ static AstNode *trans_compound_assign_operator(Context *c, bool result_used, Ast case BO_LAnd: case BO_LOr: case BO_Comma: - zig_panic("compound assign expected to be handled by binary operator"); + zig_unreachable(); } zig_unreachable(); @@ -1270,6 +1346,8 @@ static AstNode *trans_implicit_cast_expr(Context *c, AstNode *block, ImplicitCas node->data.fn_call_expr.params.append(target_node); return node; } + case CK_NullToPointer: + return trans_create_node(c, NodeTypeNullLiteral); case CK_Dependent: emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast CK_Dependent"); return nullptr; @@ -1294,9 +1372,6 @@ static AstNode *trans_implicit_cast_expr(Context *c, AstNode *block, ImplicitCas case CK_ToUnion: emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast CK_ToUnion"); return nullptr; - case CK_NullToPointer: - emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast CK_NullToPointer"); - return nullptr; case CK_NullToMemberPointer: emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast CK_NullToMemberPointer"); return nullptr; @@ -1442,38 +1517,72 @@ static AstNode *trans_decl_ref_expr(Context *c, DeclRefExpr *stmt, TransLRValue return trans_create_node_symbol(c, symbol_name); } +static AstNode *trans_create_post_crement(Context *c, bool result_used, AstNode *block, UnaryOperator *stmt, BinOpType assign_op) { + Expr *op_expr = stmt->getSubExpr(); + + if (!result_used) { + // common case + // c: expr++ + // zig: expr += 1 + return trans_create_node_bin_op(c, + trans_expr(c, true, block, op_expr, TransLValue), + assign_op, + trans_create_node_unsigned(c, 1)); + } else { + // worst case + // c: expr++ + // zig: { + // zig: const _ref = &expr; + // zig: const _tmp = *_ref; + // zig: *_ref += 1; + // zig: _tmp + // zig: } + AstNode *child_block = trans_create_node(c, NodeTypeBlock); + + // const _ref = &expr; + AstNode *expr = trans_expr(c, true, child_block, op_expr, TransLValue); + if (expr == nullptr) return nullptr; + AstNode *addr_of_expr = trans_create_node_addr_of(c, false, false, expr); + // TODO: avoid name collisions with generated variable names + Buf* ref_var_name = buf_create_from_str("_ref"); + AstNode *ref_var_decl = trans_create_node_var_decl_local(c, true, ref_var_name, nullptr, addr_of_expr); + child_block->data.block.statements.append(ref_var_decl); + + // const _tmp = *_ref; + Buf* tmp_var_name = buf_create_from_str("_tmp"); + AstNode *tmp_var_decl = trans_create_node_var_decl_local(c, true, tmp_var_name, nullptr, + trans_create_node_prefix_op(c, PrefixOpDereference, + trans_create_node_symbol(c, ref_var_name))); + child_block->data.block.statements.append(tmp_var_decl); + + // *_ref += 1; + AstNode *assign_statement = trans_create_node_bin_op(c, + trans_create_node_prefix_op(c, PrefixOpDereference, + trans_create_node_symbol(c, ref_var_name)), + assign_op, + trans_create_node_unsigned(c, 1)); + child_block->data.block.statements.append(assign_statement); + + // _tmp + child_block->data.block.statements.append(trans_create_node_symbol(c, tmp_var_name)); + child_block->data.block.last_statement_is_result_expression = true; + + return child_block; + } +} + static AstNode *trans_unary_operator(Context *c, bool result_used, AstNode *block, UnaryOperator *stmt) { switch (stmt->getOpcode()) { - case UO_PostInc: { - Expr *op_expr = stmt->getSubExpr(); - BinOpType bin_op = qual_type_has_wrapping_overflow(c, op_expr->getType()) - ? BinOpTypeAssignPlusWrap - : BinOpTypeAssignPlus; - - if (!result_used) { - // common case - // c: expr++ - // zig: expr += 1 - return trans_create_node_bin_op(c, - trans_expr(c, true, block, op_expr, TransLValue), - bin_op, - trans_create_node_unsigned(c, 1)); - } else { - // worst case - // c: expr++ - // zig: { - // zig: const _ref = &expr; - // zig: const _tmp = *_ref; - // zig: *_ref += 1; - // zig: _tmp - // zig: } - emit_warning(c, stmt->getLocStart(), "TODO handle C translation UO_PostInc with result_used"); - return nullptr; - } - } + case UO_PostInc: + if (qual_type_has_wrapping_overflow(c, stmt->getType())) + return trans_create_post_crement(c, result_used, block, stmt, BinOpTypeAssignPlusWrap); + else + return trans_create_post_crement(c, result_used, block, stmt, BinOpTypeAssignPlus); case UO_PostDec: - emit_warning(c, stmt->getLocStart(), "TODO handle C translation UO_PostDec"); - return nullptr; + if (qual_type_has_wrapping_overflow(c, stmt->getType())) + return trans_create_post_crement(c, result_used, block, stmt, BinOpTypeAssignMinusWrap); + else + return trans_create_post_crement(c, result_used, block, stmt, BinOpTypeAssignMinus); case UO_PreInc: emit_warning(c, stmt->getLocStart(), "TODO handle C translation UO_PreInc"); return nullptr; @@ -1484,8 +1593,14 @@ static AstNode *trans_unary_operator(Context *c, bool result_used, AstNode *bloc emit_warning(c, stmt->getLocStart(), "TODO handle C translation UO_AddrOf"); return nullptr; case UO_Deref: - emit_warning(c, stmt->getLocStart(), "TODO handle C translation UO_Deref"); - return nullptr; + { + bool is_fn_ptr = qual_type_is_fn_ptr(c, stmt->getSubExpr()->getType()); + AstNode *value_node = trans_expr(c, result_used, block, stmt->getSubExpr(), TransRValue); + if (is_fn_ptr) + return value_node; + AstNode *unwrapped = trans_create_node_prefix_op(c, PrefixOpUnwrapMaybe, value_node); + return trans_create_node_prefix_op(c, PrefixOpDereference, unwrapped); + } case UO_Plus: emit_warning(c, stmt->getLocStart(), "TODO handle C translation UO_Plus"); return nullptr; @@ -1828,10 +1943,20 @@ static AstNode *trans_if_statement(Context *c, AstNode *block, IfStmt *stmt) { static AstNode *trans_call_expr(Context *c, bool result_used, AstNode *block, CallExpr *stmt) { AstNode *node = trans_create_node(c, NodeTypeFnCallExpr); - node->data.fn_call_expr.fn_ref_expr = trans_expr(c, true, block, stmt->getCallee(), TransRValue); - if (node->data.fn_call_expr.fn_ref_expr == nullptr) + + AstNode *callee_raw_node = trans_expr(c, true, block, stmt->getCallee(), TransRValue); + if (callee_raw_node == nullptr) return nullptr; + AstNode *callee_node; + if (qual_type_is_fn_ptr(c, stmt->getCallee()->getType())) { + callee_node = trans_create_node_prefix_op(c, PrefixOpUnwrapMaybe, callee_raw_node); + } else { + callee_node = callee_raw_node; + } + + node->data.fn_call_expr.fn_ref_expr = callee_node; + unsigned num_args = stmt->getNumArgs(); Expr **args = stmt->getArgs(); for (unsigned i = 0; i < num_args; i += 1) { @@ -1896,6 +2021,59 @@ static AstNode *trans_unary_expr_or_type_trait_expr(Context *c, AstNode *block, return node; } +static AstNode *trans_do_loop(Context *c, AstNode *block, DoStmt *stmt) { + stmt->getBody(); + stmt->getCond(); + + AstNode *while_node = trans_create_node(c, NodeTypeWhileExpr); + + AstNode *true_node = trans_create_node(c, NodeTypeBoolLiteral); + true_node->data.bool_literal.value = true; + while_node->data.while_expr.condition = true_node; + + AstNode *body_node; + if (stmt->getBody()->getStmtClass() == Stmt::CompoundStmtClass) { + // there's already a block in C, so we'll append our condition to it. + // c: do { + // c: a; + // c: b; + // c: } while(c); + // zig: while (true) { + // zig: a; + // zig: b; + // zig: if (!cond) break; + // zig: } + body_node = trans_stmt(c, false, block, stmt->getBody(), TransRValue); + if (body_node == nullptr) return nullptr; + assert(body_node->type == NodeTypeBlock); + } else { + // the C statement is without a block, so we need to create a block to contain it. + // c: do + // c: a; + // c: while(c); + // zig: while (true) { + // zig: a; + // zig: if (!cond) break; + // zig: } + body_node = trans_create_node(c, NodeTypeBlock); + AstNode *child_statement = trans_stmt(c, false, body_node, stmt->getBody(), TransRValue); + if (child_statement == nullptr) return nullptr; + body_node->data.block.statements.append(child_statement); + } + + // if (!cond) break; + AstNode *condition_node = trans_expr(c, true, body_node, stmt->getCond(), TransRValue); + if (condition_node == nullptr) return nullptr; + AstNode *terminator_node = trans_create_node(c, NodeTypeIfBoolExpr); + terminator_node->data.if_bool_expr.condition = trans_create_node_prefix_op(c, PrefixOpBoolNot, condition_node); + terminator_node->data.if_bool_expr.then_block = trans_create_node(c, NodeTypeBreak); + body_node->data.block.statements.append(terminator_node); + + while_node->data.while_expr.body = body_node; + + return while_node; +} + static AstNode *trans_stmt(Context *c, bool result_used, AstNode *block, Stmt *stmt, TransLRValue lrvalue) { Stmt::StmtClass sc = stmt->getStmtClass(); switch (sc) { @@ -1935,6 +2113,8 @@ static AstNode *trans_stmt(Context *c, bool result_used, AstNode *block, Stmt *s return trans_c_style_cast_expr(c, result_used, block, (CStyleCastExpr *)stmt, lrvalue); case Stmt::UnaryExprOrTypeTraitExprClass: return trans_unary_expr_or_type_trait_expr(c, block, (UnaryExprOrTypeTraitExpr *)stmt); + case Stmt::DoStmtClass: + return trans_do_loop(c, block, (DoStmt *)stmt); case Stmt::CaseStmtClass: emit_warning(c, stmt->getLocStart(), "TODO handle C CaseStmtClass"); return nullptr; @@ -1980,9 +2160,6 @@ static AstNode *trans_stmt(Context *c, bool result_used, AstNode *block, Stmt *s case Stmt::CoroutineBodyStmtClass: emit_warning(c, stmt->getLocStart(), "TODO handle C CoroutineBodyStmtClass"); return nullptr; - case Stmt::DoStmtClass: - emit_warning(c, stmt->getLocStart(), "TODO handle C DoStmtClass"); - return nullptr; case Stmt::BinaryConditionalOperatorClass: emit_warning(c, stmt->getLocStart(), "TODO handle C BinaryConditionalOperatorClass"); return nullptr; diff --git a/std/io_test.zig b/std/io_test.zig index 14e98bc74..267b02dfc 100644 --- a/std/io_test.zig +++ b/std/io_test.zig @@ -5,8 +5,14 @@ const Rand = std.rand.Rand; const assert = std.debug.assert; const mem = std.mem; const os = std.os; +const builtin = @import("builtin"); test "write a file, read it, then delete it" { + if (builtin.os == builtin.Os.windows and builtin.arch == builtin.Arch.i386) { + // TODO get this test passing + // https://github.com/zig-lang/zig/issues/537 + return; + } var data: [1024]u8 = undefined; var rng = Rand.init(1234); rng.fillBytes(data[0..]); diff --git a/std/os/linux.zig b/std/os/linux.zig index dea89be1f..5951f9d7b 100644 --- a/std/os/linux.zig +++ b/std/os/linux.zig @@ -328,6 +328,45 @@ pub const TIOCGPKT = 0x80045438; pub const TIOCGPTLCK = 0x80045439; pub const TIOCGEXCL = 0x80045440; +pub const EPOLL_CTL_ADD = 1; +pub const EPOLL_CTL_DEL = 2; +pub const EPOLL_CTL_MOD = 3; + +pub const EPOLLIN = 0x001; +pub const EPOLLPRI = 0x002; +pub const EPOLLOUT = 0x004; +pub const EPOLLRDNORM = 0x040; +pub const EPOLLRDBAND = 0x080; +pub const EPOLLWRNORM = 0x100; +pub const EPOLLWRBAND = 0x200; +pub const EPOLLMSG = 0x400; +pub const EPOLLERR = 0x008; +pub const EPOLLHUP = 0x010; +pub const EPOLLRDHUP = 0x2000; +pub const EPOLLEXCLUSIVE = (u32(1) << 28); +pub const EPOLLWAKEUP = (u32(1) << 29); +pub const EPOLLONESHOT = (u32(1) << 30); +pub const EPOLLET = (u32(1) << 31); + +pub const CLOCK_REALTIME = 0; +pub const CLOCK_MONOTONIC = 1; +pub const CLOCK_PROCESS_CPUTIME_ID = 2; +pub const CLOCK_THREAD_CPUTIME_ID = 3; +pub const CLOCK_MONOTONIC_RAW = 4; +pub const CLOCK_REALTIME_COARSE = 5; +pub const CLOCK_MONOTONIC_COARSE = 6; +pub const CLOCK_BOOTTIME = 7; +pub const CLOCK_REALTIME_ALARM = 8; +pub const CLOCK_BOOTTIME_ALARM = 9; +pub const CLOCK_SGI_CYCLE = 10; +pub const CLOCK_TAI = 11; + +pub const TFD_NONBLOCK = O_NONBLOCK; +pub const TFD_CLOEXEC = O_CLOEXEC; + +pub const TFD_TIMER_ABSTIME = 1; +pub const TFD_TIMER_CANCEL_ON_SET = (1 << 1); + fn unsigned(s: i32) -> u32 { @bitCast(u32, s) } fn signed(s: u32) -> i32 { @bitCast(i32, s) } pub fn WEXITSTATUS(s: i32) -> i32 { signed((unsigned(s) & 0xff00) >> 8) } @@ -734,3 +773,47 @@ pub const timespec = arch.timespec; pub fn fstat(fd: i32, stat_buf: &Stat) -> usize { arch.syscall2(arch.SYS_fstat, usize(fd), @ptrToInt(stat_buf)) } + +pub const epoll_data = u64; + +pub const epoll_event = extern struct { + events: u32, + data: epoll_data +}; + +pub fn epoll_create() -> usize { + arch.syscall1(arch.SYS_epoll_create, usize(1)) +} + +pub fn epoll_ctl(epoll_fd: i32, op: i32, fd: i32, ev: &epoll_event) -> usize { + arch.syscall4(arch.SYS_epoll_ctl, usize(epoll_fd), usize(op), usize(fd), @ptrToInt(ev)) +} + +pub fn epoll_wait(epoll_fd: i32, events: &epoll_event, maxevents: i32, timeout: i32) -> usize { + arch.syscall4(arch.SYS_epoll_wait, usize(epoll_fd), @ptrToInt(events), usize(maxevents), usize(timeout)) +} + +pub fn timerfd_create(clockid: i32, flags: u32) -> usize { + arch.syscall2(arch.SYS_timerfd_create, usize(clockid), usize(flags)) +} + +pub const itimerspec = extern struct { + it_interval: timespec, + it_value: timespec +}; + +pub fn timerfd_gettime(fd: i32, curr_value: &itimerspec) -> usize { + arch.syscall2(arch.SYS_timerfd_gettime, usize(fd), @ptrToInt(curr_value)) +} + +pub fn timerfd_settime(fd: i32, flags: u32, new_value: &const itimerspec, old_value: ?&itimerspec) -> usize { + arch.syscall4(arch.SYS_timerfd_settime, usize(fd), usize(flags), @ptrToInt(new_value), @ptrToInt(old_value)) +} + +test "import linux_test" { + // TODO lazy analysis should prevent this test from being compiled on windows, but + // it is still compiled on windows + if (builtin.os == builtin.Os.linux) { + _ = @import("linux_test.zig"); + } +} diff --git a/std/os/linux_test.zig b/std/os/linux_test.zig new file mode 100644 index 000000000..265d0a17f --- /dev/null +++ b/std/os/linux_test.zig @@ -0,0 +1,38 @@ +const std = @import("std"); +const linux = std.os.linux; +const assert = std.debug.assert; + +test "timer" { + const epoll_fd = linux.epoll_create(); + var err = linux.getErrno(epoll_fd); + assert(err == 0); + + const timer_fd = linux.timerfd_create(linux.CLOCK_MONOTONIC, 0); + assert(linux.getErrno(timer_fd) == 0); + + const time_interval = linux.timespec { + .tv_sec = 0, + .tv_nsec = 2000000 + }; + + const new_time = linux.itimerspec { + .it_interval = time_interval, + .it_value = time_interval + }; + + err = linux.timerfd_settime(i32(timer_fd), 0, &new_time, null); + assert(err == 0); + + var event = linux.epoll_event { + .events = linux.EPOLLIN | linux.EPOLLOUT | linux.EPOLLET, + .data = 0 + }; + + err = linux.epoll_ctl(i32(epoll_fd), linux.EPOLL_CTL_ADD, i32(timer_fd), &event); + assert(err == 0); + + const events_one: linux.epoll_event = undefined; + var events = []linux.epoll_event{events_one} ** 8; + + err = linux.epoll_wait(i32(epoll_fd), &events[0], 8, -1); +} diff --git a/test/parsec.zig b/test/parsec.zig index d47ea2e33..f9e90cb70 100644 --- a/test/parsec.zig +++ b/test/parsec.zig @@ -203,13 +203,13 @@ pub fn addCases(cases: &tests.ParseCContext) { \\pub extern var fn_ptr: ?extern fn(); , \\pub inline fn foo() { - \\ ??fn_ptr() + \\ (??fn_ptr)() \\} , \\pub extern var fn_ptr2: ?extern fn(c_int, f32) -> u8; , \\pub inline fn bar(arg0: c_int, arg1: f32) -> u8 { - \\ ??fn_ptr2(arg0, arg1) + \\ (??fn_ptr2)(arg0, arg1) \\} ); @@ -596,8 +596,268 @@ pub fn addCases(cases: &tests.ParseCContext) { \\ return @sizeOf(c_int); \\} ); -} + cases.addC("null pointer implicit cast", + \\int* foo(void) { + \\ return 0; + \\} + , + \\export fn foo() -> ?&c_int { + \\ return null; + \\} + ); + + cases.addC("comma operator", + \\int foo(void) { + \\ return 1, 2; + \\} + , + \\export fn foo() -> c_int { + \\ return { + \\ _ = 1; + \\ 2 + \\ }; + \\} + ); + + cases.addC("bitshift", + \\int foo(void) { + \\ return (1 << 2) >> 1; + \\} + , + \\export fn foo() -> c_int { + \\ return (1 << @import("std").math.Log2Int(c_int)(2)) >> @import("std").math.Log2Int(c_int)(1); + \\} + ); + + cases.addC("compound assignment operators", + \\void foo(void) { + \\ int a = 0; + \\ a += (a += 1); + \\ a -= (a -= 1); + \\ a *= (a *= 1); + \\ a &= (a &= 1); + \\ a |= (a |= 1); + \\ a ^= (a ^= 1); + \\ a >>= (a >>= 1); + \\ a <<= (a <<= 1); + \\} + , + \\export fn foo() { + \\ var a: c_int = 0; + \\ a += { + \\ const _ref = &a; + \\ (*_ref) = ((*_ref) + 1); + \\ *_ref + \\ }; + \\ a -= { + \\ const _ref = &a; + \\ (*_ref) = ((*_ref) - 1); + \\ *_ref + \\ }; + \\ a *= { + \\ const _ref = &a; + \\ (*_ref) = ((*_ref) * 1); + \\ *_ref + \\ }; + \\ a &= { + \\ const _ref = &a; + \\ (*_ref) = ((*_ref) & 1); + \\ *_ref + \\ }; + \\ a |= { + \\ const _ref = &a; + \\ (*_ref) = ((*_ref) | 1); + \\ *_ref + \\ }; + \\ a ^= { + \\ const _ref = &a; + \\ (*_ref) = ((*_ref) ^ 1); + \\ *_ref + \\ }; + \\ a >>= @import("std").math.Log2Int(c_int)({ + \\ const _ref = &a; + \\ (*_ref) = c_int(c_int(*_ref) >> @import("std").math.Log2Int(c_int)(1)); + \\ *_ref + \\ }); + \\ a <<= @import("std").math.Log2Int(c_int)({ + \\ const _ref = &a; + \\ (*_ref) = c_int(c_int(*_ref) << @import("std").math.Log2Int(c_int)(1)); + \\ *_ref + \\ }); + \\} + ); + + cases.addC("compound assignment operators unsigned", + \\void foo(void) { + \\ unsigned a = 0; + \\ a += (a += 1); + \\ a -= (a -= 1); + \\ a *= (a *= 1); + \\ a &= (a &= 1); + \\ a |= (a |= 1); + \\ a ^= (a ^= 1); + \\ a >>= (a >>= 1); + \\ a <<= (a <<= 1); + \\} + , + \\export fn foo() { + \\ var a: c_uint = c_uint(0); + \\ a +%= { + \\ const _ref = &a; + \\ (*_ref) = ((*_ref) +% c_uint(1)); + \\ *_ref + \\ }; + \\ a -%= { + \\ const _ref = &a; + \\ (*_ref) = ((*_ref) -% c_uint(1)); + \\ *_ref + \\ }; + \\ a *%= { + \\ const _ref = &a; + \\ (*_ref) = ((*_ref) *% c_uint(1)); + \\ *_ref + \\ }; + \\ a &= { + \\ const _ref = &a; + \\ (*_ref) = ((*_ref) & c_uint(1)); + \\ *_ref + \\ }; + \\ a |= { + \\ const _ref = &a; + \\ (*_ref) = ((*_ref) | c_uint(1)); + \\ *_ref + \\ }; + \\ a ^= { + \\ const _ref = &a; + \\ (*_ref) = ((*_ref) ^ c_uint(1)); + \\ *_ref + \\ }; + \\ a >>= @import("std").math.Log2Int(c_uint)({ + \\ const _ref = &a; + \\ (*_ref) = c_uint(c_uint(*_ref) >> @import("std").math.Log2Int(c_uint)(1)); + \\ *_ref + \\ }); + \\ a <<= @import("std").math.Log2Int(c_uint)({ + \\ const _ref = &a; + \\ (*_ref) = c_uint(c_uint(*_ref) << @import("std").math.Log2Int(c_uint)(1)); + \\ *_ref + \\ }); + \\} + ); + + cases.addC("duplicate typedef", + \\typedef long foo; + \\typedef int bar; + \\typedef long foo; + \\typedef int baz; + , + \\pub const foo = c_long; + \\pub const bar = c_int; + \\pub const baz = c_int; + ); + + cases.addC("post increment/decrement", + \\void foo(void) { + \\ int i = 0; + \\ unsigned u = 0; + \\ i++; + \\ i--; + \\ u++; + \\ u--; + \\ i = i++; + \\ i = i--; + \\ u = u++; + \\ u = u--; + \\} + , + \\export fn foo() { + \\ var i: c_int = 0; + \\ var u: c_uint = c_uint(0); + \\ i += 1; + \\ i -= 1; + \\ u +%= 1; + \\ u -%= 1; + \\ i = { + \\ const _ref = &i; + \\ const _tmp = *_ref; + \\ (*_ref) += 1; + \\ _tmp + \\ }; + \\ i = { + \\ const _ref = &i; + \\ const _tmp = *_ref; + \\ (*_ref) -= 1; + \\ _tmp + \\ }; + \\ u = { + \\ const _ref = &u; + \\ const _tmp = *_ref; + \\ (*_ref) +%= 1; + \\ _tmp + \\ }; + \\ u = { + \\ const _ref = &u; + \\ const _tmp = *_ref; + \\ (*_ref) -%= 1; + \\ _tmp + \\ }; + \\} + ); + + cases.addC("do loop", + \\void foo(void) { + \\ int a = 2; + \\ do { + \\ a--; + \\ } while (a != 0); + \\ + \\ int b = 2; + \\ do + \\ b--; + \\ while (b != 0); + \\} + , + \\export fn foo() { + \\ var a: c_int = 2; + \\ while (true) { + \\ a -= 1; + \\ if (!(a != 0)) break; + \\ }; + \\ var b: c_int = 2; + \\ while (true) { + \\ b -= 1; + \\ if (!(b != 0)) break; + \\ }; + \\} + ); + + cases.addC("deref function pointer", + \\void foo(void) {} + \\void bar(void) { + \\ void(*f)(void) = foo; + \\ f(); + \\ (*(f))(); + \\} + , + \\export fn foo() {} + \\export fn bar() { + \\ var f: ?extern fn() = foo; + \\ (??f)(); + \\ (??f)(); + \\} + ); + + cases.addC("normal deref", + \\void foo(int *x) { + \\ *x = 1; + \\} + , + \\export fn foo(x: ?&c_int) { + \\ (*(??x)) = 1; + \\} + ); +}