Commit df0e875856

Andrew Kelley <superjoe30@gmail.com>
2017-11-26 02:34:05
translate-c: introduce the concept of scopes
in preparation to implement switch and solve variable name collisions
1 parent a2afcae
Changed files (2)
src/translate_c.cpp
@@ -23,6 +23,8 @@
 
 using namespace clang;
 
+struct TransScope;
+
 struct MacroSymbol {
     Buf *name;
     Buf *value;
@@ -52,12 +54,59 @@ struct Context {
     ASTContext *ctx;
 
     HashMap<Buf *, bool, buf_hash, buf_eql_buf> ptr_params;
+    TransScope *child_scope; // TODO refactor out
+};
+
+enum ResultUsed {
+    ResultUsedNo,
+    ResultUsedYes,
+};
+
+enum TransLRValue {
+    TransLValue,
+    TransRValue,
+};
+
+enum TransScopeId {
+    TransScopeIdSwitch,
+    TransScopeIdVar,
+    TransScopeIdBlock,
 };
 
+struct TransScope {
+    TransScopeId id;
+    TransScope *parent;
+};
+
+struct TransScopeSwitch {
+    TransScope base;
+    AstNode *switch_node;
+};
+
+struct TransScopeVar {
+    TransScope base;
+    Buf *c_name;
+    Buf *zig_name;
+};
+
+struct TransScopeBlock {
+    TransScope base;
+    AstNode *node;
+};
+
+static AstNode *const skip_add_to_block_node = (AstNode *) 0x2;
+
+static TransScopeBlock *trans_scope_block_create(Context *c, TransScope *parent_scope);
+static TransScopeVar *trans_scope_var_create(Context *c, TransScope *parent_scope, Buf *wanted_name);
+//static TransScopeSwitch *trans_scope_switch_create(Context *c, TransScope *parent_scope);
+
 static AstNode *resolve_record_decl(Context *c, const RecordDecl *record_decl);
 static AstNode *resolve_enum_decl(Context *c, const EnumDecl *enum_decl);
 static AstNode *resolve_typedef_decl(Context *c, const TypedefNameDecl *typedef_decl);
 
+static AstNode *trans_stmt(Context *c, ResultUsed result_used, TransScope *scope, const Stmt *stmt, TransLRValue lrval);
+static AstNode *trans_qual_type(Context *c, QualType qt, const SourceLocation &source_loc);
+
 
 ATTRIBUTE_PRINTF(3, 4)
 static void emit_warning(Context *c, const SourceLocation &sl, const char *format, ...) {
@@ -165,8 +214,8 @@ static AstNode *trans_create_node_bin_op(Context *c, AstNode *lhs_node, BinOpTyp
     return node;
 }
 
-static AstNode *maybe_suppress_result(Context *c, bool result_used, AstNode *node) {
-    if (result_used) return node;
+static AstNode *maybe_suppress_result(Context *c, ResultUsed result_used, AstNode *node) {
+    if (result_used == ResultUsedYes) return node;
     return trans_create_node_bin_op(c,
         trans_create_node_symbol_str(c, "_"),
         BinOpTypeAssign,
@@ -341,8 +390,6 @@ static AstNode *trans_create_node_apint(Context *c, const llvm::APSInt &aps_int)
 
 }
 
-static AstNode *trans_qual_type(Context *c, QualType qt, const SourceLocation &source_loc);
-
 static QualType get_expr_qual_type(Context *c, const Expr *expr) {
     // String literals in C are `char *` but they should really be `const char *`.
     if (expr->getStmtClass() == Stmt::ImplicitCastExprClass) {
@@ -573,16 +620,8 @@ static bool qual_type_has_wrapping_overflow(Context *c, QualType qt) {
     }
 }
 
-enum TransLRValue {
-    TransLValue,
-    TransRValue,
-};
-
-static AstNode *trans_stmt(Context *c, bool result_used, AstNode *block, Stmt *stmt, TransLRValue lrval);
-static AstNode *const skip_add_to_block_node = (AstNode *) 0x2;
-
-static AstNode *trans_expr(Context *c, bool result_used, AstNode *block, Expr *expr, TransLRValue lrval) {
-    return trans_stmt(c, result_used, block, expr, lrval);
+static AstNode *trans_expr(Context *c, ResultUsed result_used, TransScope *scope, const Expr *expr, TransLRValue lrval) {
+    return trans_stmt(c, result_used, scope, expr, lrval);
 }
 
 static AstNode *trans_type(Context *c, const Type *ty, const SourceLocation &source_loc) {
@@ -922,32 +961,33 @@ static AstNode *trans_qual_type(Context *c, QualType qt, const SourceLocation &s
     return trans_type(c, qt.getTypePtr(), source_loc);
 }
 
-static AstNode *trans_compound_stmt(Context *c, AstNode *parent, CompoundStmt *stmt) {
-    AstNode *child_block = trans_create_node(c, NodeTypeBlock);
-    for (CompoundStmt::body_iterator it = stmt->body_begin(), end_it = stmt->body_end(); it != end_it; ++it) {
-        AstNode *child_node = trans_stmt(c, false, child_block, *it, TransRValue);
+static AstNode *trans_compound_stmt(Context *c, TransScope *parent_scope, const CompoundStmt *stmt) {
+    TransScopeBlock *child_scope_block = trans_scope_block_create(c, parent_scope);
+    for (CompoundStmt::const_body_iterator it = stmt->body_begin(), end_it = stmt->body_end(); it != end_it; ++it) {
+        AstNode *child_node = trans_stmt(c, ResultUsedNo, &child_scope_block->base, *it, TransRValue);
         if (child_node == nullptr)
             return nullptr;
         if (child_node != skip_add_to_block_node)
-            child_block->data.block.statements.append(child_node);
+            child_scope_block->node->data.block.statements.append(child_node);
     }
-    return child_block;
+    c->child_scope = &child_scope_block->base;
+    return child_scope_block->node;
 }
 
-static AstNode *trans_return_stmt(Context *c, AstNode *block, ReturnStmt *stmt) {
-    Expr *value_expr = stmt->getRetValue();
+static AstNode *trans_return_stmt(Context *c, TransScope *scope, const ReturnStmt *stmt) {
+    const Expr *value_expr = stmt->getRetValue();
     if (value_expr == nullptr) {
         return trans_create_node(c, NodeTypeReturnExpr);
     } else {
         AstNode *return_node = trans_create_node(c, NodeTypeReturnExpr);
-        return_node->data.return_expr.expr = trans_expr(c, true, block, value_expr, TransRValue);
+        return_node->data.return_expr.expr = trans_expr(c, ResultUsedYes, scope, value_expr, TransRValue);
         if (return_node->data.return_expr.expr == nullptr)
             return nullptr;
         return return_node;
     }
 }
 
-static AstNode *trans_integer_literal(Context *c, IntegerLiteral *stmt) {
+static AstNode *trans_integer_literal(Context *c, const IntegerLiteral *stmt) {
     llvm::APSInt result;
     if (!stmt->EvaluateAsInt(result, *c->ctx)) {
         emit_warning(c, stmt->getLocStart(), "invalid integer literal");
@@ -956,54 +996,56 @@ static AstNode *trans_integer_literal(Context *c, IntegerLiteral *stmt) {
     return trans_create_node_apint(c, result);
 }
 
-static AstNode *trans_conditional_operator(Context *c, bool result_used, AstNode *block, ConditionalOperator *stmt) {
+static AstNode *trans_conditional_operator(Context *c, ResultUsed result_used, TransScope *scope,
+        const ConditionalOperator *stmt)
+{
     AstNode *node = trans_create_node(c, NodeTypeIfBoolExpr);
 
     Expr *cond_expr = stmt->getCond();
     Expr *true_expr = stmt->getTrueExpr();
     Expr *false_expr = stmt->getFalseExpr();
 
-    node->data.if_bool_expr.condition = trans_expr(c, true, block, cond_expr, TransRValue);
+    node->data.if_bool_expr.condition = trans_expr(c, ResultUsedYes, scope, cond_expr, TransRValue);
     if (node->data.if_bool_expr.condition == nullptr)
         return nullptr;
 
-    node->data.if_bool_expr.then_block = trans_expr(c, result_used, block, true_expr, TransRValue);
+    node->data.if_bool_expr.then_block = trans_expr(c, result_used, scope, true_expr, TransRValue);
     if (node->data.if_bool_expr.then_block == nullptr)
         return nullptr;
 
-    node->data.if_bool_expr.else_node = trans_expr(c, result_used, block, false_expr, TransRValue);
+    node->data.if_bool_expr.else_node = trans_expr(c, result_used, scope, false_expr, TransRValue);
     if (node->data.if_bool_expr.else_node == nullptr)
         return nullptr;
 
     return maybe_suppress_result(c, result_used, node);
 }
 
-static AstNode *trans_create_bin_op(Context *c, AstNode *block, Expr *lhs, BinOpType bin_op, Expr *rhs) {
+static AstNode *trans_create_bin_op(Context *c, TransScope *scope, Expr *lhs, BinOpType bin_op, Expr *rhs) {
     AstNode *node = trans_create_node(c, NodeTypeBinOpExpr);
     node->data.bin_op_expr.bin_op = bin_op;
 
-    node->data.bin_op_expr.op1 = trans_expr(c, true, block, lhs, TransRValue);
+    node->data.bin_op_expr.op1 = trans_expr(c, ResultUsedYes, scope, lhs, TransRValue);
     if (node->data.bin_op_expr.op1 == nullptr)
         return nullptr;
 
-    node->data.bin_op_expr.op2 = trans_expr(c, true, block, rhs, TransRValue);
+    node->data.bin_op_expr.op2 = trans_expr(c, ResultUsedYes, scope, rhs, TransRValue);
     if (node->data.bin_op_expr.op2 == nullptr)
         return nullptr;
 
     return node;
 }
 
-static AstNode *trans_create_assign(Context *c, bool result_used, AstNode *block, Expr *lhs, Expr *rhs) {
-    if (!result_used) {
+static AstNode *trans_create_assign(Context *c, ResultUsed result_used, TransScope *scope, Expr *lhs, Expr *rhs) {
+    if (result_used == ResultUsedNo) {
         // common case
         AstNode *node = trans_create_node(c, NodeTypeBinOpExpr);
         node->data.bin_op_expr.bin_op = BinOpTypeAssign;
 
-        node->data.bin_op_expr.op1 = trans_expr(c, true, block, lhs, TransLValue);
+        node->data.bin_op_expr.op1 = trans_expr(c, ResultUsedYes, scope, lhs, TransLValue);
         if (node->data.bin_op_expr.op1 == nullptr)
             return nullptr;
 
-        node->data.bin_op_expr.op2 = trans_expr(c, true, block, rhs, TransRValue);
+        node->data.bin_op_expr.op2 = trans_expr(c, ResultUsedYes, scope, rhs, TransRValue);
         if (node->data.bin_op_expr.op2 == nullptr)
             return nullptr;
 
@@ -1017,47 +1059,49 @@ static AstNode *trans_create_assign(Context *c, bool result_used, AstNode *block
         // zig:     _tmp
         // zig: }
 
-        AstNode *child_block = trans_create_node(c, NodeTypeBlock);
+        TransScopeBlock *child_scope = trans_scope_block_create(c, scope);
 
         // const _tmp = rhs;
-        AstNode *rhs_node = trans_expr(c, true, child_block, rhs, TransRValue);
+        AstNode *rhs_node = trans_expr(c, ResultUsedYes, &child_scope->base, rhs, TransRValue);
         if (rhs_node == nullptr) return nullptr;
         // TODO: avoid name collisions with generated variable names
         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, rhs_node);
-        child_block->data.block.statements.append(tmp_var_decl);
+        child_scope->node->data.block.statements.append(tmp_var_decl);
 
         // lhs = _tmp;
-        AstNode *lhs_node = trans_expr(c, true, child_block, lhs, TransLValue);
+        AstNode *lhs_node = trans_expr(c, ResultUsedYes, &child_scope->base, lhs, TransLValue);
         if (lhs_node == nullptr) return nullptr;
-        child_block->data.block.statements.append(
+        child_scope->node->data.block.statements.append(
             trans_create_node_bin_op(c, lhs_node, BinOpTypeAssign,
                 trans_create_node_symbol(c, tmp_var_name)));
 
         // _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;
+        child_scope->node->data.block.statements.append(trans_create_node_symbol(c, tmp_var_name));
+        child_scope->node->data.block.last_statement_is_result_expression = true;
 
-        return child_block;
+        return child_scope->node;
     }
 }
 
-static AstNode *trans_create_shift_op(Context *c, AstNode *block, QualType result_type, Expr *lhs_expr, BinOpType bin_op, Expr *rhs_expr) {
+static AstNode *trans_create_shift_op(Context *c, TransScope *scope, 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);
+    AstNode *lhs = trans_expr(c, ResultUsedYes, scope, lhs_expr, TransLValue);
     if (lhs == nullptr) return nullptr;
 
-    AstNode *rhs = trans_expr(c, true, block, rhs_expr, TransRValue);
+    AstNode *rhs = trans_expr(c, ResultUsedYes, scope, 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) {
+static AstNode *trans_binary_operator(Context *c, ResultUsed result_used, TransScope *scope, const BinaryOperator *stmt) {
     switch (stmt->getOpcode()) {
         case BO_PtrMemD:
             emit_warning(c, stmt->getLocStart(), "TODO handle more C binary operators: BO_PtrMemD");
@@ -1066,20 +1110,20 @@ static AstNode *trans_binary_operator(Context *c, bool result_used, AstNode *blo
             emit_warning(c, stmt->getLocStart(), "TODO handle more C binary operators: BO_PtrMemI");
             return nullptr;
         case BO_Mul:
-            return trans_create_bin_op(c, block, stmt->getLHS(),
+            return trans_create_bin_op(c, scope, stmt->getLHS(),
                 qual_type_has_wrapping_overflow(c, stmt->getType()) ? BinOpTypeMultWrap : BinOpTypeMult,
                 stmt->getRHS());
         case BO_Div:
             if (qual_type_has_wrapping_overflow(c, stmt->getType())) {
                 // unsigned/float division uses the operator
-                return trans_create_bin_op(c, block, stmt->getLHS(), BinOpTypeDiv, stmt->getRHS());
+                return trans_create_bin_op(c, scope, stmt->getLHS(), BinOpTypeDiv, stmt->getRHS());
             } else {
                 // signed integer division uses @divTrunc
                 AstNode *fn_call = trans_create_node_builtin_fn_call_str(c, "divTrunc");
-                AstNode *lhs = trans_expr(c, true, block, stmt->getLHS(), TransLValue);
+                AstNode *lhs = trans_expr(c, ResultUsedYes, scope, stmt->getLHS(), TransLValue);
                 if (lhs == nullptr) return nullptr;
                 fn_call->data.fn_call_expr.params.append(lhs);
-                AstNode *rhs = trans_expr(c, true, block, stmt->getRHS(), TransLValue);
+                AstNode *rhs = trans_expr(c, ResultUsedYes, scope, stmt->getRHS(), TransLValue);
                 if (rhs == nullptr) return nullptr;
                 fn_call->data.fn_call_expr.params.append(rhs);
                 return fn_call;
@@ -1087,66 +1131,70 @@ static AstNode *trans_binary_operator(Context *c, bool result_used, AstNode *blo
         case BO_Rem:
             if (qual_type_has_wrapping_overflow(c, stmt->getType())) {
                 // unsigned/float division uses the operator
-                return trans_create_bin_op(c, block, stmt->getLHS(), BinOpTypeMod, stmt->getRHS());
+                return trans_create_bin_op(c, scope, stmt->getLHS(), BinOpTypeMod, stmt->getRHS());
             } else {
                 // 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);
+                AstNode *lhs = trans_expr(c, ResultUsedYes, scope, stmt->getLHS(), TransLValue);
                 if (lhs == nullptr) return nullptr;
                 fn_call->data.fn_call_expr.params.append(lhs);
-                AstNode *rhs = trans_expr(c, true, block, stmt->getRHS(), TransLValue);
+                AstNode *rhs = trans_expr(c, ResultUsedYes, scope, stmt->getRHS(), TransLValue);
                 if (rhs == nullptr) return nullptr;
                 fn_call->data.fn_call_expr.params.append(rhs);
                 return fn_call;
             }
         case BO_Add:
-            return trans_create_bin_op(c, block, stmt->getLHS(),
+            return trans_create_bin_op(c, scope, stmt->getLHS(),
                 qual_type_has_wrapping_overflow(c, stmt->getType()) ? BinOpTypeAddWrap : BinOpTypeAdd,
                 stmt->getRHS());
         case BO_Sub:
-            return trans_create_bin_op(c, block, stmt->getLHS(),
+            return trans_create_bin_op(c, scope, stmt->getLHS(),
                 qual_type_has_wrapping_overflow(c, stmt->getType()) ? BinOpTypeSubWrap : BinOpTypeSub,
                 stmt->getRHS());
         case BO_Shl:
-            return trans_create_shift_op(c, block, stmt->getType(), stmt->getLHS(), BinOpTypeBitShiftLeft, stmt->getRHS());
+            return trans_create_shift_op(c, scope, stmt->getType(), stmt->getLHS(), BinOpTypeBitShiftLeft, stmt->getRHS());
         case BO_Shr:
-            return trans_create_shift_op(c, block, stmt->getType(), stmt->getLHS(), BinOpTypeBitShiftRight, stmt->getRHS());
+            return trans_create_shift_op(c, scope, stmt->getType(), stmt->getLHS(), BinOpTypeBitShiftRight, stmt->getRHS());
         case BO_LT:
-            return trans_create_bin_op(c, block, stmt->getLHS(), BinOpTypeCmpLessThan, stmt->getRHS());
+            return trans_create_bin_op(c, scope, stmt->getLHS(), BinOpTypeCmpLessThan, stmt->getRHS());
         case BO_GT:
-            return trans_create_bin_op(c, block, stmt->getLHS(), BinOpTypeCmpGreaterThan, stmt->getRHS());
+            return trans_create_bin_op(c, scope, stmt->getLHS(), BinOpTypeCmpGreaterThan, stmt->getRHS());
         case BO_LE:
-            return trans_create_bin_op(c, block, stmt->getLHS(), BinOpTypeCmpLessOrEq, stmt->getRHS());
+            return trans_create_bin_op(c, scope, stmt->getLHS(), BinOpTypeCmpLessOrEq, stmt->getRHS());
         case BO_GE:
-            return trans_create_bin_op(c, block, stmt->getLHS(), BinOpTypeCmpGreaterOrEq, stmt->getRHS());
+            return trans_create_bin_op(c, scope, stmt->getLHS(), BinOpTypeCmpGreaterOrEq, stmt->getRHS());
         case BO_EQ:
-            return trans_create_bin_op(c, block, stmt->getLHS(), BinOpTypeCmpEq, stmt->getRHS());
+            return trans_create_bin_op(c, scope, stmt->getLHS(), BinOpTypeCmpEq, stmt->getRHS());
         case BO_NE:
-            return trans_create_bin_op(c, block, stmt->getLHS(), BinOpTypeCmpNotEq, stmt->getRHS());
+            return trans_create_bin_op(c, scope, stmt->getLHS(), BinOpTypeCmpNotEq, stmt->getRHS());
         case BO_And:
-            return trans_create_bin_op(c, block, stmt->getLHS(), BinOpTypeBinAnd, stmt->getRHS());
+            return trans_create_bin_op(c, scope, stmt->getLHS(), BinOpTypeBinAnd, stmt->getRHS());
         case BO_Xor:
-            return trans_create_bin_op(c, block, stmt->getLHS(), BinOpTypeBinXor, stmt->getRHS());
+            return trans_create_bin_op(c, scope, stmt->getLHS(), BinOpTypeBinXor, stmt->getRHS());
         case BO_Or:
-            return trans_create_bin_op(c, block, stmt->getLHS(), BinOpTypeBinOr, stmt->getRHS());
+            return trans_create_bin_op(c, scope, stmt->getLHS(), BinOpTypeBinOr, stmt->getRHS());
         case BO_LAnd:
-            return trans_create_bin_op(c, block, stmt->getLHS(), BinOpTypeBoolAnd, stmt->getRHS());
+            return trans_create_bin_op(c, scope, stmt->getLHS(), BinOpTypeBoolAnd, stmt->getRHS());
         case BO_LOr:
             // TODO: int vs bool
-            return trans_create_bin_op(c, block, stmt->getLHS(), BinOpTypeBoolOr, stmt->getRHS());
+            return trans_create_bin_op(c, scope, stmt->getLHS(), BinOpTypeBoolOr, stmt->getRHS());
         case BO_Assign:
-            return trans_create_assign(c, result_used, block, stmt->getLHS(), stmt->getRHS());
+            return trans_create_assign(c, result_used, scope, stmt->getLHS(), stmt->getRHS());
         case BO_Comma:
             {
-                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;
+                TransScopeBlock *scope_block = trans_scope_block_create(c, scope);
+                AstNode *lhs = trans_expr(c, ResultUsedNo, &scope_block->base, stmt->getLHS(), TransRValue);
+                if (lhs == nullptr)
+                    return nullptr;
+                scope_block->node->data.block.statements.append(maybe_suppress_result(c, ResultUsedNo, lhs));
+
+                AstNode *rhs = trans_expr(c, result_used, &scope_block->base, stmt->getRHS(), TransRValue);
+                if (rhs == nullptr)
+                    return nullptr;
+                scope_block->node->data.block.statements.append(maybe_suppress_result(c, result_used, rhs));
+
+                scope_block->node->data.block.last_statement_is_result_expression = true;
+                return scope_block->node;
             }
         case BO_MulAssign:
         case BO_DivAssign:
@@ -1164,18 +1212,20 @@ static AstNode *trans_binary_operator(Context *c, bool result_used, AstNode *blo
     zig_unreachable();
 }
 
-static AstNode *trans_create_compound_assign_shift(Context *c, bool result_used, AstNode *block, CompoundAssignOperator *stmt, BinOpType assign_op, BinOpType bin_op) {
+static AstNode *trans_create_compound_assign_shift(Context *c, ResultUsed result_used, TransScope *scope,
+        const 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) {
+    if (!use_intermediate_casts && result_used == ResultUsedNo) {
         // simple common case, where the C and Zig are identical:
         // lhs >>= rhs
-        AstNode *lhs = trans_expr(c, true, block, stmt->getLHS(), TransLValue);
+        AstNode *lhs = trans_expr(c, ResultUsedYes, scope, stmt->getLHS(), TransLValue);
         if (lhs == nullptr) return nullptr;
 
-        AstNode *rhs = trans_expr(c, true, block, stmt->getRHS(), TransRValue);
+        AstNode *rhs = trans_expr(c, ResultUsedYes, scope, stmt->getRHS(), TransRValue);
         if (rhs == nullptr) return nullptr;
         AstNode *coerced_rhs = trans_create_node_fn_call_1(c, rhs_type, rhs);
 
@@ -1190,20 +1240,20 @@ static AstNode *trans_create_compound_assign_shift(Context *c, bool result_used,
         // zig: }
         // where u5 is the appropriate type
 
-        AstNode *child_block = trans_create_node(c, NodeTypeBlock);
+        TransScopeBlock *child_scope = trans_scope_block_create(c, scope);
 
         // const _ref = &lhs;
-        AstNode *lhs = trans_expr(c, true, child_block, stmt->getLHS(), TransLValue);
+        AstNode *lhs = trans_expr(c, ResultUsedYes, &child_scope->base, 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);
+        child_scope->node->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);
+        AstNode *rhs = trans_expr(c, ResultUsedYes, &child_scope->base, stmt->getRHS(), TransRValue);
         if (rhs == nullptr) return nullptr;
         AstNode *coerced_rhs = trans_create_node_fn_call_1(c, rhs_type, rhs);
 
@@ -1220,27 +1270,29 @@ static AstNode *trans_create_compound_assign_shift(Context *c, bool result_used,
                             trans_create_node_symbol(c, tmp_var_name))),
                     bin_op,
                     coerced_rhs)));
-        child_block->data.block.statements.append(assign_statement);
+        child_scope->node->data.block.statements.append(assign_statement);
 
-        if (result_used) {
+        if (result_used == ResultUsedYes) {
             // *_ref
-            child_block->data.block.statements.append(
+            child_scope->node->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;
+            child_scope->node->data.block.last_statement_is_result_expression = true;
         }
 
-        return child_block;
+        return child_scope->node;
     }
 }
 
-static AstNode *trans_create_compound_assign(Context *c, bool result_used, AstNode *block, CompoundAssignOperator *stmt, BinOpType assign_op, BinOpType bin_op) {
-    if (!result_used) {
+static AstNode *trans_create_compound_assign(Context *c, ResultUsed result_used, TransScope *scope,
+        const CompoundAssignOperator *stmt, BinOpType assign_op, BinOpType bin_op)
+{
+    if (result_used == ResultUsedNo) {
         // simple common case, where the C and Zig are identical:
         // lhs += rhs
-        AstNode *lhs = trans_expr(c, true, block, stmt->getLHS(), TransLValue);
+        AstNode *lhs = trans_expr(c, ResultUsedYes, scope, stmt->getLHS(), TransLValue);
         if (lhs == nullptr) return nullptr;
-        AstNode *rhs = trans_expr(c, true, block, stmt->getRHS(), TransRValue);
+        AstNode *rhs = trans_expr(c, ResultUsedYes, scope, stmt->getRHS(), TransRValue);
         if (rhs == nullptr) return nullptr;
         return trans_create_node_bin_op(c, lhs, assign_op, rhs);
     } else {
@@ -1252,20 +1304,20 @@ static AstNode *trans_create_compound_assign(Context *c, bool result_used, AstNo
         // zig:     *_ref
         // zig: }
 
-        AstNode *child_block = trans_create_node(c, NodeTypeBlock);
+        TransScopeBlock *child_scope = trans_scope_block_create(c, scope);
 
         // const _ref = &lhs;
-        AstNode *lhs = trans_expr(c, true, child_block, stmt->getLHS(), TransLValue);
+        AstNode *lhs = trans_expr(c, ResultUsedYes, &child_scope->base, 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);
+        child_scope->node->data.block.statements.append(tmp_var_decl);
 
         // *_ref = *_ref + rhs;
 
-        AstNode *rhs = trans_expr(c, true, child_block, stmt->getRHS(), TransRValue);
+        AstNode *rhs = trans_expr(c, ResultUsedYes, &child_scope->base, stmt->getRHS(), TransRValue);
         if (rhs == nullptr) return nullptr;
 
         AstNode *assign_statement = trans_create_node_bin_op(c,
@@ -1277,26 +1329,28 @@ static AstNode *trans_create_compound_assign(Context *c, bool result_used, AstNo
                     trans_create_node_symbol(c, tmp_var_name)),
                 bin_op,
                 rhs));
-        child_block->data.block.statements.append(assign_statement);
+        child_scope->node->data.block.statements.append(assign_statement);
 
         // *_ref
-        child_block->data.block.statements.append(
+        child_scope->node->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;
+        child_scope->node->data.block.last_statement_is_result_expression = true;
 
-        return child_block;
+        return child_scope->node;
     }
 }
 
 
-static AstNode *trans_compound_assign_operator(Context *c, bool result_used, AstNode *block, CompoundAssignOperator *stmt) {
+static AstNode *trans_compound_assign_operator(Context *c, ResultUsed result_used, TransScope *scope,
+        const 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);
+                return trans_create_compound_assign(c, result_used, scope, stmt, BinOpTypeAssignTimesWrap, BinOpTypeMultWrap);
             else
-                return trans_create_compound_assign(c, result_used, block, stmt, BinOpTypeAssignTimes, BinOpTypeMult);
+                return trans_create_compound_assign(c, result_used, scope, stmt, BinOpTypeAssignTimes, BinOpTypeMult);
         case BO_DivAssign:
             emit_warning(c, stmt->getLocStart(), "TODO handle more C compound assign operators: BO_DivAssign");
             return nullptr;
@@ -1305,24 +1359,24 @@ static AstNode *trans_compound_assign_operator(Context *c, bool result_used, Ast
             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);
+                return trans_create_compound_assign(c, result_used, scope, stmt, BinOpTypeAssignPlusWrap, BinOpTypeAddWrap);
             else
-                return trans_create_compound_assign(c, result_used, block, stmt, BinOpTypeAssignPlus, BinOpTypeAdd);
+                return trans_create_compound_assign(c, result_used, scope, 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);
+                return trans_create_compound_assign(c, result_used, scope, stmt, BinOpTypeAssignMinusWrap, BinOpTypeSubWrap);
             else
-                return trans_create_compound_assign(c, result_used, block, stmt, BinOpTypeAssignMinus, BinOpTypeSub);
+                return trans_create_compound_assign(c, result_used, scope, stmt, BinOpTypeAssignMinus, BinOpTypeSub);
         case BO_ShlAssign:
-            return trans_create_compound_assign_shift(c, result_used, block, stmt, BinOpTypeAssignBitShiftLeft, BinOpTypeBitShiftLeft);
+            return trans_create_compound_assign_shift(c, result_used, scope, stmt, BinOpTypeAssignBitShiftLeft, BinOpTypeBitShiftLeft);
         case BO_ShrAssign:
-            return trans_create_compound_assign_shift(c, result_used, block, stmt, BinOpTypeAssignBitShiftRight, BinOpTypeBitShiftRight);
+            return trans_create_compound_assign_shift(c, result_used, scope, stmt, BinOpTypeAssignBitShiftRight, BinOpTypeBitShiftRight);
         case BO_AndAssign:
-            return trans_create_compound_assign(c, result_used, block, stmt, BinOpTypeAssignBitAnd, BinOpTypeBinAnd);
+            return trans_create_compound_assign(c, result_used, scope, stmt, BinOpTypeAssignBitAnd, BinOpTypeBinAnd);
         case BO_XorAssign:
-            return trans_create_compound_assign(c, result_used, block, stmt, BinOpTypeAssignBitXor, BinOpTypeBinXor);
+            return trans_create_compound_assign(c, result_used, scope, stmt, BinOpTypeAssignBitXor, BinOpTypeBinXor);
         case BO_OrAssign:
-            return trans_create_compound_assign(c, result_used, block, stmt, BinOpTypeAssignBitOr, BinOpTypeBinOr);
+            return trans_create_compound_assign(c, result_used, scope, stmt, BinOpTypeAssignBitOr, BinOpTypeBinOr);
         case BO_PtrMemD:
         case BO_PtrMemI:
         case BO_Assign:
@@ -1351,13 +1405,13 @@ static AstNode *trans_compound_assign_operator(Context *c, bool result_used, Ast
     zig_unreachable();
 }
 
-static AstNode *trans_implicit_cast_expr(Context *c, AstNode *block, ImplicitCastExpr *stmt) {
+static AstNode *trans_implicit_cast_expr(Context *c, TransScope *scope, const ImplicitCastExpr *stmt) {
     switch (stmt->getCastKind()) {
         case CK_LValueToRValue:
-            return trans_expr(c, true, block, stmt->getSubExpr(), TransRValue);
+            return trans_expr(c, ResultUsedYes, scope, stmt->getSubExpr(), TransRValue);
         case CK_IntegralCast:
             {
-                AstNode *target_node = trans_expr(c, true, block, stmt->getSubExpr(), TransRValue);
+                AstNode *target_node = trans_expr(c, ResultUsedYes, scope, stmt->getSubExpr(), TransRValue);
                 if (target_node == nullptr)
                     return nullptr;
                 return trans_c_cast(c, stmt->getExprLoc(), stmt->getType(), target_node);
@@ -1365,14 +1419,14 @@ static AstNode *trans_implicit_cast_expr(Context *c, AstNode *block, ImplicitCas
         case CK_FunctionToPointerDecay:
         case CK_ArrayToPointerDecay:
             {
-                AstNode *target_node = trans_expr(c, true, block, stmt->getSubExpr(), TransRValue);
+                AstNode *target_node = trans_expr(c, ResultUsedYes, scope, stmt->getSubExpr(), TransRValue);
                 if (target_node == nullptr)
                     return nullptr;
                 return target_node;
             }
         case CK_BitCast:
             {
-                AstNode *target_node = trans_expr(c, true, block, stmt->getSubExpr(), TransRValue);
+                AstNode *target_node = trans_expr(c, ResultUsedYes, scope, stmt->getSubExpr(), TransRValue);
                 if (target_node == nullptr)
                     return nullptr;
 
@@ -1549,8 +1603,8 @@ static AstNode *trans_implicit_cast_expr(Context *c, AstNode *block, ImplicitCas
     zig_unreachable();
 }
 
-static AstNode *trans_decl_ref_expr(Context *c, DeclRefExpr *stmt, TransLRValue lrval) {
-    ValueDecl *value_decl = stmt->getDecl();
+static AstNode *trans_decl_ref_expr(Context *c, const DeclRefExpr *stmt, TransLRValue lrval) {
+    const ValueDecl *value_decl = stmt->getDecl();
     Buf *symbol_name = buf_create_from_str(decl_name(value_decl));
     if (lrval == TransLValue) {
         c->ptr_params.put(symbol_name, true);
@@ -1558,15 +1612,17 @@ 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) {
+static AstNode *trans_create_post_crement(Context *c, ResultUsed result_used, TransScope *scope,
+        const UnaryOperator *stmt, BinOpType assign_op)
+{
     Expr *op_expr = stmt->getSubExpr();
 
-    if (!result_used) {
+    if (result_used == ResultUsedNo) {
         // common case
         // c: expr++
         // zig: expr += 1
         return trans_create_node_bin_op(c,
-            trans_expr(c, true, block, op_expr, TransLValue),
+            trans_expr(c, ResultUsedYes, scope, op_expr, TransLValue),
             assign_op,
             trans_create_node_unsigned(c, 1));
     }
@@ -1578,23 +1634,23 @@ static AstNode *trans_create_post_crement(Context *c, bool result_used, AstNode
     // zig:     *_ref += 1;
     // zig:     _tmp
     // zig: }
-    AstNode *child_block = trans_create_node(c, NodeTypeBlock);
+    TransScopeBlock *child_scope = trans_scope_block_create(c, scope);
 
     // const _ref = &expr;
-    AstNode *expr = trans_expr(c, true, child_block, op_expr, TransLValue);
+    AstNode *expr = trans_expr(c, ResultUsedYes, &child_scope->base, 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);
+    child_scope->node->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);
+    child_scope->node->data.block.statements.append(tmp_var_decl);
 
     // *_ref += 1;
     AstNode *assign_statement = trans_create_node_bin_op(c,
@@ -1602,24 +1658,26 @@ static AstNode *trans_create_post_crement(Context *c, bool result_used, AstNode
             trans_create_node_symbol(c, ref_var_name)),
         assign_op,
         trans_create_node_unsigned(c, 1));
-    child_block->data.block.statements.append(assign_statement);
+    child_scope->node->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;
+    child_scope->node->data.block.statements.append(trans_create_node_symbol(c, tmp_var_name));
+    child_scope->node->data.block.last_statement_is_result_expression = true;
 
-    return child_block;
+    return child_scope->node;
 }
 
-static AstNode *trans_create_pre_crement(Context *c, bool result_used, AstNode *block, UnaryOperator *stmt, BinOpType assign_op) {
+static AstNode *trans_create_pre_crement(Context *c, ResultUsed result_used, TransScope *scope,
+        const UnaryOperator *stmt, BinOpType assign_op)
+{
     Expr *op_expr = stmt->getSubExpr();
 
-    if (!result_used) {
+    if (result_used == ResultUsedNo) {
         // common case
         // c: ++expr
         // zig: expr += 1
         return trans_create_node_bin_op(c,
-            trans_expr(c, true, block, op_expr, TransLValue),
+            trans_expr(c, ResultUsedYes, scope, op_expr, TransLValue),
             assign_op,
             trans_create_node_unsigned(c, 1));
     }
@@ -1630,16 +1688,16 @@ static AstNode *trans_create_pre_crement(Context *c, bool result_used, AstNode *
     // zig:     *_ref += 1;
     // zig:     *_ref
     // zig: }
-    AstNode *child_block = trans_create_node(c, NodeTypeBlock);
+    TransScopeBlock *child_scope = trans_scope_block_create(c, scope);
 
     // const _ref = &expr;
-    AstNode *expr = trans_expr(c, true, child_block, op_expr, TransLValue);
+    AstNode *expr = trans_expr(c, ResultUsedYes, &child_scope->base, 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);
+    child_scope->node->data.block.statements.append(ref_var_decl);
 
     // *_ref += 1;
     AstNode *assign_statement = trans_create_node_bin_op(c,
@@ -1647,49 +1705,49 @@ static AstNode *trans_create_pre_crement(Context *c, bool result_used, AstNode *
             trans_create_node_symbol(c, ref_var_name)),
         assign_op,
         trans_create_node_unsigned(c, 1));
-    child_block->data.block.statements.append(assign_statement);
+    child_scope->node->data.block.statements.append(assign_statement);
 
     // *_ref
     AstNode *deref_expr = trans_create_node_prefix_op(c, PrefixOpDereference,
             trans_create_node_symbol(c, ref_var_name));
-    child_block->data.block.statements.append(deref_expr);
-    child_block->data.block.last_statement_is_result_expression = true;
+    child_scope->node->data.block.statements.append(deref_expr);
+    child_scope->node->data.block.last_statement_is_result_expression = true;
 
-    return child_block;
+    return child_scope->node;
 }
 
-static AstNode *trans_unary_operator(Context *c, bool result_used, AstNode *block, UnaryOperator *stmt) {
+static AstNode *trans_unary_operator(Context *c, ResultUsed result_used, TransScope *scope, const UnaryOperator *stmt) {
     switch (stmt->getOpcode()) {
         case UO_PostInc:
             if (qual_type_has_wrapping_overflow(c, stmt->getType()))
-                return trans_create_post_crement(c, result_used, block, stmt, BinOpTypeAssignPlusWrap);
+                return trans_create_post_crement(c, result_used, scope, stmt, BinOpTypeAssignPlusWrap);
             else
-                return trans_create_post_crement(c, result_used, block, stmt, BinOpTypeAssignPlus);
+                return trans_create_post_crement(c, result_used, scope, stmt, BinOpTypeAssignPlus);
         case UO_PostDec:
             if (qual_type_has_wrapping_overflow(c, stmt->getType()))
-                return trans_create_post_crement(c, result_used, block, stmt, BinOpTypeAssignMinusWrap);
+                return trans_create_post_crement(c, result_used, scope, stmt, BinOpTypeAssignMinusWrap);
             else
-                return trans_create_post_crement(c, result_used, block, stmt, BinOpTypeAssignMinus);
+                return trans_create_post_crement(c, result_used, scope, stmt, BinOpTypeAssignMinus);
         case UO_PreInc:
             if (qual_type_has_wrapping_overflow(c, stmt->getType()))
-                return trans_create_pre_crement(c, result_used, block, stmt, BinOpTypeAssignPlusWrap);
+                return trans_create_pre_crement(c, result_used, scope, stmt, BinOpTypeAssignPlusWrap);
             else
-                return trans_create_pre_crement(c, result_used, block, stmt, BinOpTypeAssignPlus);
+                return trans_create_pre_crement(c, result_used, scope, stmt, BinOpTypeAssignPlus);
         case UO_PreDec:
             if (qual_type_has_wrapping_overflow(c, stmt->getType()))
-                return trans_create_pre_crement(c, result_used, block, stmt, BinOpTypeAssignMinusWrap);
+                return trans_create_pre_crement(c, result_used, scope, stmt, BinOpTypeAssignMinusWrap);
             else
-                return trans_create_pre_crement(c, result_used, block, stmt, BinOpTypeAssignMinus);
+                return trans_create_pre_crement(c, result_used, scope, stmt, BinOpTypeAssignMinus);
         case UO_AddrOf:
             {
-                AstNode *value_node = trans_expr(c, result_used, block, stmt->getSubExpr(), TransLValue);
+                AstNode *value_node = trans_expr(c, result_used, scope, stmt->getSubExpr(), TransLValue);
                 if (value_node == nullptr)
                     return value_node;
                 return trans_create_node_addr_of(c, false, false, value_node);
             }
         case UO_Deref:
             {
-                AstNode *value_node = trans_expr(c, result_used, block, stmt->getSubExpr(), TransRValue);
+                AstNode *value_node = trans_expr(c, result_used, scope, stmt->getSubExpr(), TransRValue);
                 if (value_node == nullptr)
                     return nullptr;
                 bool is_fn_ptr = qual_type_is_fn_ptr(c, stmt->getSubExpr()->getType());
@@ -1708,7 +1766,7 @@ static AstNode *trans_unary_operator(Context *c, bool result_used, AstNode *bloc
                     AstNode *node = trans_create_node(c, NodeTypePrefixOpExpr);
                     node->data.prefix_op_expr.prefix_op = PrefixOpNegation;
 
-                    node->data.prefix_op_expr.primary_expr = trans_expr(c, true, block, op_expr, TransRValue);
+                    node->data.prefix_op_expr.primary_expr = trans_expr(c, ResultUsedYes, scope, op_expr, TransRValue);
                     if (node->data.prefix_op_expr.primary_expr == nullptr)
                         return nullptr;
 
@@ -1718,7 +1776,7 @@ static AstNode *trans_unary_operator(Context *c, bool result_used, AstNode *bloc
                     AstNode *node = trans_create_node(c, NodeTypeBinOpExpr);
                     node->data.bin_op_expr.op1 = trans_create_node_unsigned(c, 0);
 
-                    node->data.bin_op_expr.op2 = trans_expr(c, true, block, op_expr, TransRValue);
+                    node->data.bin_op_expr.op2 = trans_expr(c, ResultUsedYes, scope, op_expr, TransRValue);
                     if (node->data.bin_op_expr.op2 == nullptr)
                         return nullptr;
 
@@ -1751,7 +1809,7 @@ static AstNode *trans_unary_operator(Context *c, bool result_used, AstNode *bloc
     zig_unreachable();
 }
 
-static AstNode *trans_local_declaration(Context *c, AstNode *block, DeclStmt *stmt) {
+static AstNode *trans_local_declaration(Context *c, TransScope *scope, const DeclStmt *stmt) {
     for (auto iter = stmt->decl_begin(); iter != stmt->decl_end(); iter++) {
         Decl *decl = *iter;
         switch (decl->getKind()) {
@@ -1760,7 +1818,7 @@ static AstNode *trans_local_declaration(Context *c, AstNode *block, DeclStmt *st
                 QualType qual_type = var_decl->getTypeSourceInfo()->getType();
                 AstNode *init_node = nullptr;
                 if (var_decl->hasInit()) {
-                    init_node = trans_expr(c, true, block, var_decl->getInit(), TransRValue);
+                    init_node = trans_expr(c, ResultUsedYes, scope, var_decl->getInit(), TransRValue);
                     if (init_node == nullptr)
                         return nullptr;
 
@@ -1773,7 +1831,10 @@ static AstNode *trans_local_declaration(Context *c, AstNode *block, DeclStmt *st
 
                 AstNode *node = trans_create_node_var_decl_local(c, qual_type.isConstQualified(),
                         symbol_name, type_node, init_node);
-                block->data.block.statements.append(node);
+
+                assert(scope->id == TransScopeIdBlock);
+                TransScopeBlock *scope_block = (TransScopeBlock *)scope;
+                scope_block->node->data.block.statements.append(node);
                 continue;
             }
             case Decl::AccessSpec:
@@ -2000,37 +2061,37 @@ static AstNode *trans_local_declaration(Context *c, AstNode *block, DeclStmt *st
     return skip_add_to_block_node;
 }
 
-static AstNode *trans_while_loop(Context *c, AstNode *block, WhileStmt *stmt) {
+static AstNode *trans_while_loop(Context *c, TransScope *scope, const WhileStmt *stmt) {
     AstNode *while_node = trans_create_node(c, NodeTypeWhileExpr);
 
-    while_node->data.while_expr.condition = trans_expr(c, true, block, stmt->getCond(), TransRValue);
+    while_node->data.while_expr.condition = trans_expr(c, ResultUsedYes, scope, stmt->getCond(), TransRValue);
     if (while_node->data.while_expr.condition == nullptr)
         return nullptr;
 
-    while_node->data.while_expr.body = trans_stmt(c, false, block, stmt->getBody(), TransRValue);
+    while_node->data.while_expr.body = trans_stmt(c, ResultUsedNo, scope, stmt->getBody(), TransRValue);
     if (while_node->data.while_expr.body == nullptr)
         return nullptr;
 
     return while_node;
 }
 
-static AstNode *trans_if_statement(Context *c, AstNode *block, IfStmt *stmt) {
+static AstNode *trans_if_statement(Context *c, TransScope *scope, const IfStmt *stmt) {
     // if (c) t
     // if (c) t else e
     AstNode *if_node = trans_create_node(c, NodeTypeIfBoolExpr);
 
     // TODO: condition != 0
-    AstNode *condition_node = trans_expr(c, true, block, stmt->getCond(), TransRValue);
+    AstNode *condition_node = trans_expr(c, ResultUsedYes, scope, stmt->getCond(), TransRValue);
     if (condition_node == nullptr)
         return nullptr;
     if_node->data.if_bool_expr.condition = condition_node;
 
-    if_node->data.if_bool_expr.then_block = trans_stmt(c, false, block, stmt->getThen(), TransRValue);
+    if_node->data.if_bool_expr.then_block = trans_stmt(c, ResultUsedNo, scope, stmt->getThen(), TransRValue);
     if (if_node->data.if_bool_expr.then_block == nullptr)
         return nullptr;
 
     if (stmt->getElse() != nullptr) {
-        if_node->data.if_bool_expr.else_node = trans_stmt(c, false, block, stmt->getElse(), TransRValue);
+        if_node->data.if_bool_expr.else_node = trans_stmt(c, ResultUsedNo, scope, stmt->getElse(), TransRValue);
         if (if_node->data.if_bool_expr.else_node == nullptr)
             return nullptr;
     }
@@ -2038,10 +2099,10 @@ static AstNode *trans_if_statement(Context *c, AstNode *block, IfStmt *stmt) {
     return if_node;
 }
 
-static AstNode *trans_call_expr(Context *c, bool result_used, AstNode *block, CallExpr *stmt) {
+static AstNode *trans_call_expr(Context *c, ResultUsed result_used, TransScope *scope, const CallExpr *stmt) {
     AstNode *node = trans_create_node(c, NodeTypeFnCallExpr);
 
-    AstNode *callee_raw_node = trans_expr(c, true, block, stmt->getCallee(), TransRValue);
+    AstNode *callee_raw_node = trans_expr(c, ResultUsedYes, scope, stmt->getCallee(), TransRValue);
     if (callee_raw_node == nullptr)
         return nullptr;
 
@@ -2055,9 +2116,9 @@ static AstNode *trans_call_expr(Context *c, bool result_used, AstNode *block, Ca
     node->data.fn_call_expr.fn_ref_expr = callee_node;
 
     unsigned num_args = stmt->getNumArgs();
-    Expr **args = stmt->getArgs();
+    const Expr * const* args = stmt->getArgs();
     for (unsigned i = 0; i < num_args; i += 1) {
-        AstNode *arg_node = trans_expr(c, true, block, args[i], TransRValue);
+        AstNode *arg_node = trans_expr(c, ResultUsedYes, scope, args[i], TransRValue);
         if (arg_node == nullptr)
             return nullptr;
 
@@ -2067,8 +2128,8 @@ static AstNode *trans_call_expr(Context *c, bool result_used, AstNode *block, Ca
     return node;
 }
 
-static AstNode *trans_member_expr(Context *c, AstNode *block, MemberExpr *stmt) {
-    AstNode *container_node = trans_expr(c, true, block, stmt->getBase(), TransRValue);
+static AstNode *trans_member_expr(Context *c, TransScope *scope, const MemberExpr *stmt) {
+    AstNode *container_node = trans_expr(c, ResultUsedYes, scope, stmt->getBase(), TransRValue);
     if (container_node == nullptr)
         return nullptr;
 
@@ -2082,12 +2143,12 @@ static AstNode *trans_member_expr(Context *c, AstNode *block, MemberExpr *stmt)
     return node;
 }
 
-static AstNode *trans_array_subscript_expr(Context *c, AstNode *block, ArraySubscriptExpr *stmt) {
-    AstNode *container_node = trans_expr(c, true, block, stmt->getBase(), TransRValue);
+static AstNode *trans_array_subscript_expr(Context *c, TransScope *scope, const ArraySubscriptExpr *stmt) {
+    AstNode *container_node = trans_expr(c, ResultUsedYes, scope, stmt->getBase(), TransRValue);
     if (container_node == nullptr)
         return nullptr;
 
-    AstNode *idx_node = trans_expr(c, true, block, stmt->getIdx(), TransRValue);
+    AstNode *idx_node = trans_expr(c, ResultUsedYes, scope, stmt->getIdx(), TransRValue);
     if (idx_node == nullptr)
         return nullptr;
 
@@ -2098,17 +2159,19 @@ static AstNode *trans_array_subscript_expr(Context *c, AstNode *block, ArraySubs
     return node;
 }
 
-static AstNode *trans_c_style_cast_expr(Context *c, bool result_used, AstNode *block,
-        CStyleCastExpr *stmt, TransLRValue lrvalue)
+static AstNode *trans_c_style_cast_expr(Context *c, ResultUsed result_used, TransScope *scope,
+        const CStyleCastExpr *stmt, TransLRValue lrvalue)
 {
-    AstNode *sub_expr_node = trans_expr(c, result_used, block, stmt->getSubExpr(), lrvalue);
+    AstNode *sub_expr_node = trans_expr(c, result_used, scope, stmt->getSubExpr(), lrvalue);
     if (sub_expr_node == nullptr)
         return nullptr;
 
     return trans_c_cast(c, stmt->getLocStart(), stmt->getType(), sub_expr_node);
 }
 
-static AstNode *trans_unary_expr_or_type_trait_expr(Context *c, AstNode *block, UnaryExprOrTypeTraitExpr *stmt) {
+static AstNode *trans_unary_expr_or_type_trait_expr(Context *c, TransScope *scope,
+        const UnaryExprOrTypeTraitExpr *stmt)
+{
     AstNode *type_node = trans_qual_type(c, stmt->getTypeOfArgument(), stmt->getLocStart());
     if (type_node == nullptr)
         return nullptr;
@@ -2118,7 +2181,7 @@ 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) {
+static AstNode *trans_do_loop(Context *c, TransScope *parent_scope, const DoStmt *stmt) {
     AstNode *while_node = trans_create_node(c, NodeTypeWhileExpr);
 
     AstNode *true_node = trans_create_node(c, NodeTypeBoolLiteral);
@@ -2126,6 +2189,7 @@ static AstNode *trans_do_loop(Context *c, AstNode *block, DoStmt *stmt) {
     while_node->data.while_expr.condition = true_node;
 
     AstNode *body_node;
+    TransScope *child_scope;
     if (stmt->getBody()->getStmtClass() == Stmt::CompoundStmtClass) {
         // there's already a block in C, so we'll append our condition to it.
         // c: do {
@@ -2137,9 +2201,10 @@ static AstNode *trans_do_loop(Context *c, AstNode *block, DoStmt *stmt) {
         // zig:   b;
         // zig:   if (!cond) break;
         // zig: }
-        body_node = trans_stmt(c, false, block, stmt->getBody(), TransRValue);
+        body_node = trans_stmt(c, ResultUsedNo, parent_scope, stmt->getBody(), TransRValue);
         if (body_node == nullptr) return nullptr;
         assert(body_node->type == NodeTypeBlock);
+        child_scope = c->child_scope;
     } else {
         // the C statement is without a block, so we need to create a block to contain it.
         // c: do
@@ -2149,63 +2214,103 @@ static AstNode *trans_do_loop(Context *c, AstNode *block, DoStmt *stmt) {
         // 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);
+        TransScopeBlock *child_block_scope = trans_scope_block_create(c, parent_scope);
+        body_node = child_block_scope->node;
+        child_scope = &child_block_scope->base;
+        AstNode *child_statement = trans_stmt(c, ResultUsedNo, child_scope, stmt->getBody(), TransRValue);
         if (child_statement == nullptr) return nullptr;
-        body_node->data.block.statements.append(child_statement);
+        child_block_scope->node->data.block.statements.append(child_statement);
     }
 
     // if (!cond) break;
-    AstNode *condition_node = trans_expr(c, true, body_node, stmt->getCond(), TransRValue);
+    AstNode *condition_node = trans_expr(c, ResultUsedYes, child_scope, 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);
+
+    assert(child_scope->id == TransScopeIdBlock);
+    TransScopeBlock *child_block_scope = (TransScopeBlock *)child_scope;
+
+    child_block_scope->node->data.block.statements.append(terminator_node);
 
     while_node->data.while_expr.body = body_node;
 
     return while_node;
 }
 
-static AstNode *trans_for_loop(Context *c, AstNode *block, ForStmt *stmt) {
+//static AstNode *trans_switch_stmt(Context *c, TransScope *scope, const SwitchStmt *stmt) {
+//    AstNode *switch_block_node = trans_create_node(c, NodeTypeBlock);
+//    AstNode *switch_node = trans_create_node(c, NodeTypeSwitchExpr);
+//    const DeclStmt *var_decl_stmt = stmt->getConditionVariableDeclStmt();
+//    if (var_decl_stmt != nullptr) {
+//        AstNode *vars_node = trans_stmt(c, ResultUsedNo, switch_block_node, var_decl_stmt, TransRValue);
+//        if (vars_node == nullptr)
+//            return nullptr;
+//        if (vars_node != skip_add_to_block_node)
+//            switch_block_node->data.block.statements.append(vars_node);
+//    }
+//    switch_block_node->data.block.statements.append(switch_node);
+//
+//    const Expr *cond_expr = stmt->getCond();
+//    assert(cond_expr != nullptr);
+//
+//    AstNode *expr_node = trans_expr(c, ResultUsedYes, switch_block_node, cond_expr, TransRValue);
+//    if (expr_node == nullptr)
+//        return nullptr;
+//    switch_node->data.switch_expr.expr = expr_node;
+//
+//    AstNode *body_node = trans_stmt(c, ResultUsedNo, switch_block_node, stmt->getBody(), TransRValue);
+//    if (body_node == nullptr)
+//        return nullptr;
+//    if (body_node != skip_add_to_block_node)
+//        switch_block_node->data.block.statements.append(body_node);
+//
+//    return switch_block_node;
+//}
+
+static AstNode *trans_for_loop(Context *c, TransScope *parent_scope, const ForStmt *stmt) {
     AstNode *loop_block_node;
+    TransScope *condition_scope;
     AstNode *while_node = trans_create_node(c, NodeTypeWhileExpr);
-    Stmt *init_stmt = stmt->getInit();
+    const Stmt *init_stmt = stmt->getInit();
     if (init_stmt == nullptr) {
         loop_block_node = while_node;
+        condition_scope = parent_scope;
     } else {
-        loop_block_node = trans_create_node(c, NodeTypeBlock);
+        TransScopeBlock *child_scope = trans_scope_block_create(c, parent_scope);
+        loop_block_node = child_scope->node;
+        condition_scope = &child_scope->base;
 
-        AstNode *vars_node = trans_stmt(c, false, loop_block_node, init_stmt, TransRValue);
+        AstNode *vars_node = trans_stmt(c, ResultUsedNo, &child_scope->base, init_stmt, TransRValue);
         if (vars_node == nullptr)
             return nullptr;
         if (vars_node != skip_add_to_block_node)
-            loop_block_node->data.block.statements.append(vars_node);
+            child_scope->node->data.block.statements.append(vars_node);
 
-        loop_block_node->data.block.statements.append(while_node);
+        child_scope->node->data.block.statements.append(while_node);
     }
 
-    Stmt *cond_stmt = stmt->getCond();
+    const Stmt *cond_stmt = stmt->getCond();
     if (cond_stmt == nullptr) {
         AstNode *true_node = trans_create_node(c, NodeTypeBoolLiteral);
         true_node->data.bool_literal.value = true;
         while_node->data.while_expr.condition = true_node;
     } else {
-        while_node->data.while_expr.condition = trans_stmt(c, false, loop_block_node, cond_stmt, TransRValue);
+        while_node->data.while_expr.condition = trans_stmt(c, ResultUsedNo, condition_scope, cond_stmt, TransRValue);
         if (while_node->data.while_expr.condition == nullptr)
             return nullptr;
     }
 
-    Stmt *inc_stmt = stmt->getInc();
+    const Stmt *inc_stmt = stmt->getInc();
     if (inc_stmt != nullptr) {
-        AstNode *inc_node = trans_stmt(c, false, loop_block_node, inc_stmt, TransRValue);
+        AstNode *inc_node = trans_stmt(c, ResultUsedNo, condition_scope, inc_stmt, TransRValue);
         if (inc_node == nullptr)
             return nullptr;
         while_node->data.while_expr.continue_expr = inc_node;
     }
 
-    AstNode *child_statement = trans_stmt(c, false, loop_block_node, stmt->getBody(), TransRValue);
+    AstNode *child_statement = trans_stmt(c, ResultUsedNo, condition_scope, stmt->getBody(), TransRValue);
     if (child_statement == nullptr)
         return nullptr;
     while_node->data.while_expr.body = child_statement;
@@ -2213,7 +2318,7 @@ static AstNode *trans_for_loop(Context *c, AstNode *block, ForStmt *stmt) {
     return loop_block_node;
 }
 
-static AstNode *trans_string_literal(Context *c, AstNode *block, StringLiteral *stmt) {
+static AstNode *trans_string_literal(Context *c, TransScope *scope, const StringLiteral *stmt) {
     switch (stmt->getKind()) {
         case StringLiteral::Ascii:
         case StringLiteral::UTF8:
@@ -2231,72 +2336,75 @@ static AstNode *trans_string_literal(Context *c, AstNode *block, StringLiteral *
     zig_unreachable();
 }
 
-static AstNode *trans_break_stmt(Context *c, AstNode *block, BreakStmt *stmt) {
+static AstNode *trans_break_stmt(Context *c, TransScope *scope, const BreakStmt *stmt) {
     return trans_create_node(c, NodeTypeBreak);
 }
 
-static AstNode *trans_continue_stmt(Context *c, AstNode *block, ContinueStmt *stmt) {
+static AstNode *trans_continue_stmt(Context *c, TransScope *scope, const ContinueStmt *stmt) {
     return trans_create_node(c, NodeTypeContinue);
 }
 
-static AstNode *trans_stmt(Context *c, bool result_used, AstNode *block, Stmt *stmt, TransLRValue lrvalue) {
+static AstNode *trans_stmt(Context *c, ResultUsed result_used, TransScope *scope, const Stmt *stmt, TransLRValue lrvalue) {
+    c->child_scope = scope; // TODO refactor out
     Stmt::StmtClass sc = stmt->getStmtClass();
     switch (sc) {
         case Stmt::ReturnStmtClass:
-            return trans_return_stmt(c, block, (ReturnStmt *)stmt);
+            return trans_return_stmt(c, scope, (const ReturnStmt *)stmt);
         case Stmt::CompoundStmtClass:
-            return trans_compound_stmt(c, block, (CompoundStmt *)stmt);
+            return trans_compound_stmt(c, scope, (const CompoundStmt *)stmt);
         case Stmt::IntegerLiteralClass:
-            return trans_integer_literal(c, (IntegerLiteral *)stmt);
+            return trans_integer_literal(c, (const IntegerLiteral *)stmt);
         case Stmt::ConditionalOperatorClass:
-            return trans_conditional_operator(c, result_used, block, (ConditionalOperator *)stmt);
+            return trans_conditional_operator(c, result_used, scope, (const ConditionalOperator *)stmt);
         case Stmt::BinaryOperatorClass:
-            return trans_binary_operator(c, result_used, block, (BinaryOperator *)stmt);
+            return trans_binary_operator(c, result_used, scope, (const BinaryOperator *)stmt);
         case Stmt::CompoundAssignOperatorClass:
-            return trans_compound_assign_operator(c, result_used, block, (CompoundAssignOperator *)stmt);
+            return trans_compound_assign_operator(c, result_used, scope, (const CompoundAssignOperator *)stmt);
         case Stmt::ImplicitCastExprClass:
-            return trans_implicit_cast_expr(c, block, (ImplicitCastExpr *)stmt);
+            return trans_implicit_cast_expr(c, scope, (const ImplicitCastExpr *)stmt);
         case Stmt::DeclRefExprClass:
-            return trans_decl_ref_expr(c, (DeclRefExpr *)stmt, lrvalue);
+            return trans_decl_ref_expr(c, (const DeclRefExpr *)stmt, lrvalue);
         case Stmt::UnaryOperatorClass:
-            return trans_unary_operator(c, result_used, block, (UnaryOperator *)stmt);
+            return trans_unary_operator(c, result_used, scope, (const UnaryOperator *)stmt);
         case Stmt::DeclStmtClass:
-            return trans_local_declaration(c, block, (DeclStmt *)stmt);
+            return trans_local_declaration(c, scope, (const DeclStmt *)stmt);
         case Stmt::WhileStmtClass:
-            return trans_while_loop(c, block, (WhileStmt *)stmt);
+            return trans_while_loop(c, scope, (const WhileStmt *)stmt);
         case Stmt::IfStmtClass:
-            return trans_if_statement(c, block, (IfStmt *)stmt);
+            return trans_if_statement(c, scope, (const IfStmt *)stmt);
         case Stmt::CallExprClass:
-            return trans_call_expr(c, result_used, block, (CallExpr *)stmt);
+            return trans_call_expr(c, result_used, scope, (const CallExpr *)stmt);
         case Stmt::NullStmtClass:
             return skip_add_to_block_node;
         case Stmt::MemberExprClass:
-            return trans_member_expr(c, block, (MemberExpr *)stmt);
+            return trans_member_expr(c, scope, (const MemberExpr *)stmt);
         case Stmt::ArraySubscriptExprClass:
-            return trans_array_subscript_expr(c, block, (ArraySubscriptExpr *)stmt);
+            return trans_array_subscript_expr(c, scope, (const ArraySubscriptExpr *)stmt);
         case Stmt::CStyleCastExprClass:
-            return trans_c_style_cast_expr(c, result_used, block, (CStyleCastExpr *)stmt, lrvalue);
+            return trans_c_style_cast_expr(c, result_used, scope, (const CStyleCastExpr *)stmt, lrvalue);
         case Stmt::UnaryExprOrTypeTraitExprClass:
-            return trans_unary_expr_or_type_trait_expr(c, block, (UnaryExprOrTypeTraitExpr *)stmt);
+            return trans_unary_expr_or_type_trait_expr(c, scope, (const UnaryExprOrTypeTraitExpr *)stmt);
         case Stmt::DoStmtClass:
-            return trans_do_loop(c, block, (DoStmt *)stmt);
+            return trans_do_loop(c, scope, (const DoStmt *)stmt);
         case Stmt::ForStmtClass:
-            return trans_for_loop(c, block, (ForStmt *)stmt);
+            return trans_for_loop(c, scope, (const ForStmt *)stmt);
         case Stmt::StringLiteralClass:
-            return trans_string_literal(c, block, (StringLiteral *)stmt);
+            return trans_string_literal(c, scope, (const StringLiteral *)stmt);
         case Stmt::BreakStmtClass:
-            return trans_break_stmt(c, block, (BreakStmt *)stmt);
+            return trans_break_stmt(c, scope, (const BreakStmt *)stmt);
         case Stmt::ContinueStmtClass:
-            return trans_continue_stmt(c, block, (ContinueStmt *)stmt);
+            return trans_continue_stmt(c, scope, (const ContinueStmt *)stmt);
+//        case Stmt::SwitchStmtClass:
+//            return trans_switch_stmt(c, scope, (const SwitchStmt *)stmt);
+        case Stmt::SwitchStmtClass:
+            emit_warning(c, stmt->getLocStart(), "TODO handle C SwitchStmtClass");
+            return nullptr;
         case Stmt::CaseStmtClass:
             emit_warning(c, stmt->getLocStart(), "TODO handle C CaseStmtClass");
             return nullptr;
         case Stmt::DefaultStmtClass:
             emit_warning(c, stmt->getLocStart(), "TODO handle C DefaultStmtClass");
             return nullptr;
-        case Stmt::SwitchStmtClass:
-            emit_warning(c, stmt->getLocStart(), "TODO handle C SwitchStmtClass");
-            return nullptr;
         case Stmt::NoStmtClass:
             emit_warning(c, stmt->getLocStart(), "TODO handle C NoStmtClass");
             return nullptr;
@@ -2584,7 +2692,7 @@ static AstNode *trans_stmt(Context *c, bool result_used, AstNode *block, Stmt *s
             emit_warning(c, stmt->getLocStart(), "TODO handle C PackExpansionExprClass");
             return nullptr;
         case Stmt::ParenExprClass:
-            return trans_expr(c, result_used, block, ((ParenExpr*)stmt)->getSubExpr(), lrvalue);
+            return trans_expr(c, result_used, scope, ((const ParenExpr*)stmt)->getSubExpr(), lrvalue);
         case Stmt::ParenListExprClass:
             emit_warning(c, stmt->getLocStart(), "TODO handle C ParenListExprClass");
             return nullptr;
@@ -2838,10 +2946,13 @@ static void visit_fn_decl(Context *c, const FunctionDecl *fn_decl) {
         return;
     }
 
+    TransScope *scope = nullptr;
+
     for (size_t i = 0; i < proto_node->data.fn_proto.params.length; i += 1) {
         AstNode *param_node = proto_node->data.fn_proto.params.at(i);
         const ParmVarDecl *param = fn_decl->getParamDecl(i);
         const char *name = decl_name(param);
+
         Buf *proto_param_name;
         if (strlen(name) != 0) {
             proto_param_name = buf_create_from_str(name);
@@ -2851,7 +2962,11 @@ static void visit_fn_decl(Context *c, const FunctionDecl *fn_decl) {
                 proto_param_name = buf_sprintf("arg%" ZIG_PRI_usize "", i);
             }
         }
-        param_node->data.param_decl.name = proto_param_name;
+
+        TransScopeVar *scope_var = trans_scope_var_create(c, scope, proto_param_name);
+        scope = &scope_var->base;
+
+        param_node->data.param_decl.name = scope_var->zig_name;
     }
 
     if (!fn_decl->hasBody()) {
@@ -2863,7 +2978,7 @@ static void visit_fn_decl(Context *c, const FunctionDecl *fn_decl) {
     // actual function definition with body
     c->ptr_params.clear();
     Stmt *body = fn_decl->getBody();
-    AstNode *actual_body_node = trans_stmt(c, false, nullptr, body, TransRValue);
+    AstNode *actual_body_node = trans_stmt(c, ResultUsedNo, scope, body, TransRValue);
     assert(actual_body_node != skip_add_to_block_node);
     if (actual_body_node == nullptr) {
         emit_warning(c, fn_decl->getLocation(), "unable to translate function");
@@ -3301,14 +3416,66 @@ static bool decl_visitor(void *context, const Decl *decl) {
     return true;
 }
 
-static bool name_exists(Context *c, Buf *name) {
+static bool name_exists_global(Context *c, Buf *name) {
     return get_global(c, name) != nullptr;
 }
 
+static bool name_exists_scope(Context *c, Buf *name, TransScope *scope) {
+    while (scope != nullptr) {
+        if (scope->id == TransScopeIdVar) {
+            TransScopeVar *var_scope = (TransScopeVar *)scope;
+            if (buf_eql_buf(name, var_scope->zig_name)) {
+                return true;
+            }
+        }
+        scope = scope->parent;
+    }
+    return name_exists_global(c, name);
+}
+
+static Buf *get_unique_name(Context *c, Buf *name, TransScope *scope) {
+    Buf *proposed_name = name;
+    int count = 0;
+    while (name_exists_scope(c, proposed_name, scope)) {
+        if (proposed_name == name) {
+            proposed_name = buf_alloc();
+        }
+        buf_resize(proposed_name, 0);
+        buf_appendf(proposed_name, "%s_%d", buf_ptr(name), count);
+        count += 1;
+    }
+    return proposed_name;
+}
+
+static TransScopeBlock *trans_scope_block_create(Context *c, TransScope *parent_scope) {
+    TransScopeBlock *result = allocate<TransScopeBlock>(1);
+    result->base.id = TransScopeIdBlock;
+    result->base.parent = parent_scope;
+    result->node = trans_create_node(c, NodeTypeBlock);
+    return result;
+}
+
+static TransScopeVar *trans_scope_var_create(Context *c, TransScope *parent_scope, Buf *wanted_name) {
+    TransScopeVar *result = allocate<TransScopeVar>(1);
+    result->base.id = TransScopeIdVar;
+    result->base.parent = parent_scope;
+    result->c_name = wanted_name;
+    result->zig_name = get_unique_name(c, wanted_name, parent_scope);
+    return result;
+}
+
+//static TransScopeSwitch *trans_scope_switch_create(Context *c, TransScope *parent_scope) {
+//    TransScopeSwitch *result = allocate<TransScopeSwitch>(1);
+//    result->base.id = TransScopeIdSwitch;
+//    result->base.parent = parent_scope;
+//    result->switch_node = trans_create_node(c, NodeTypeSwitchExpr);
+//    return result;
+//}
+
 static void render_aliases(Context *c) {
     for (size_t i = 0; i < c->aliases.length; i += 1) {
         Alias *alias = &c->aliases.at(i);
-        if (name_exists(c, alias->new_name))
+        if (name_exists_global(c, alias->new_name))
             continue;
 
         add_global_var(c, alias->new_name, trans_create_node_symbol(c, alias->canon_name));
@@ -3438,7 +3605,7 @@ static void process_symbol_macros(Context *c) {
 
         // Check if this macro aliases another top level declaration
         AstNode *existing_node = get_global(c, ms.value);
-        if (!existing_node || name_exists(c, ms.name))
+        if (!existing_node || name_exists_global(c, ms.name))
             continue;
 
         // If a macro aliases a global variable which is a function pointer, we conclude that
@@ -3487,7 +3654,7 @@ static void process_preprocessor_entities(Context *c, ASTUnit &unit) {
                         continue;
                     }
                     Buf *name = buf_create_from_str(raw_name);
-                    if (name_exists(c, name)) {
+                    if (name_exists_global(c, name)) {
                         continue;
                     }
 
test/translate_c.zig
@@ -963,6 +963,16 @@ pub fn addCases(cases: &tests.TranslateCContext) {
         \\}
     );
 
+    cases.add("empty for loop",
+        \\void foo(void) {
+        \\    for (;;) { }
+        \\}
+    ,
+        \\pub fn foo() {
+        \\    while (true) {};
+        \\}
+    );
+
     cases.add("break statement",
         \\void foo(void) {
         \\    for (;;) {
@@ -990,6 +1000,47 @@ pub fn addCases(cases: &tests.TranslateCContext) {
         \\    };
         \\}
     );
+
+    //cases.add("switch statement",
+    //    \\int foo(int x) {
+    //    \\    switch (x) {
+    //    \\        case 1:
+    //    \\            x += 1;
+    //    \\        case 2:
+    //    \\            break;
+    //    \\        case 3:
+    //    \\        case 4:
+    //    \\            return x + 1;
+    //    \\        default:
+    //    \\            return 10;
+    //    \\    }
+    //    \\    return x + 13;
+    //    \\}
+    //,
+    //    \\fn foo(_x: i32) -> i32 {
+    //    \\    var x = _x;
+    //    \\    switch (x) {
+    //    \\        1 => goto switch_case_1;
+    //    \\        2 => goto switch_case_2;
+    //    \\        3 => goto switch_case_3;
+    //    \\        4 => goto switch_case_4;
+    //    \\        else => goto switch_default;
+    //    \\    }
+    //    \\switch_case_1:
+    //    \\    x += 1;
+    //    \\    goto switch_case_2;
+    //    \\switch_case_2:
+    //    \\    goto switch_end;
+    //    \\switch_case_3:
+    //    \\    goto switch_case_4;
+    //    \\switch_case_4:
+    //    \\    return x += 1;
+    //    \\switch_default:
+    //    \\    return 10;
+    //    \\switch_end:
+    //    \\    return x + 13;
+    //    \\}
+    //);
 }