Commit 4c2cdf6f4d

Josh Wolfe <thejoshwolfe@gmail.com>
2017-11-14 05:37:30
parsec supports more compound assign operators
1 parent c1fde0e
Changed files (2)
src/parsec.cpp
@@ -1034,7 +1034,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;
@@ -1081,36 +1081,6 @@ 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:
             {
                 block = trans_create_node(c, NodeTypeBlock);
@@ -1123,112 +1093,181 @@ static AstNode *trans_binary_operator(Context *c, bool result_used, AstNode *blo
                 block->data.block.last_statement_is_result_expression = true;
                 return block;
             }
-    }
-
-    zig_unreachable();
-}
-
-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;
         case BO_DivAssign:
-            emit_warning(c, stmt->getLocStart(), "TODO handle more C compound assign operators: BO_DivAssign");
-            return nullptr;
         case BO_RemAssign:
-            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;
         case BO_SubAssign:
-            emit_warning(c, stmt->getLocStart(), "TODO handle more C compound assign operators: BO_SubAssign");
-            return nullptr;
         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;
+        case BO_ShrAssign:
+        case BO_AndAssign:
+        case BO_XorAssign:
+        case BO_OrAssign:
+            zig_unreachable();
+    }
 
-            const SourceLocation &rhs_location = stmt->getRHS()->getLocStart();
-            AstNode *rhs_type = qual_type_to_log2_int_ref(c, stmt->getComputationLHSType(), rhs_location);
+    zig_unreachable();
+}
 
-            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;
+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);
 
-                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);
+    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;
 
-                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
+        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);
 
-                // TODO: avoid mess when we don't need the assignment value for chained assignments or anything.
-                AstNode *child_block = trans_create_node(c, NodeTypeBlock);
+        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
 
-                // 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);
+        AstNode *child_block = trans_create_node(c, NodeTypeBlock);
 
-                // *_ref = result_type(operation_type(*_ref) >> u5(rhs));
+        // 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);
 
-                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);
+        // *_ref = result_type(operation_type(*_ref) >> u5(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,
+        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->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(
+                        stmt->getComputationLHSType(),
                         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;
-            }
+                            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;
         }
-        case BO_AndAssign:
-            emit_warning(c, stmt->getLocStart(), "TODO handle more C compound assign operators: BO_AndAssign");
+
+        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:
+            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;
-        case BO_XorAssign:
-            emit_warning(c, stmt->getLocStart(), "TODO handle more C compound assign operators: BO_XorAssign");
+        case BO_RemAssign:
+            emit_warning(c, stmt->getLocStart(), "TODO handle more C compound assign operators: BO_RemAssign");
             return nullptr;
+        case BO_AddAssign:
+            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:
+            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:
+            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:
+            return trans_create_compound_assign(c, result_used, block, stmt, BinOpTypeAssignBitAnd, BinOpTypeBinAnd);
+        case BO_XorAssign:
+            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:
@@ -1251,7 +1290,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();
test/parsec.zig
@@ -630,6 +630,63 @@ pub fn addCases(cases: &tests.ParseCContext) {
         \\}
     );
 
+    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("duplicate typedef",
         \\typedef long foo;
         \\typedef int bar;