| ... | @@ -350,6 +350,21 @@ static AstNode* trans_c_cast(Context *c, const SourceLocation &source_location, | ... | @@ -350,6 +350,21 @@ static AstNode* trans_c_cast(Context *c, const SourceLocation &source_location, |
| 350 | return trans_create_node_fn_call_1(c, trans_qual_type(c, qt, source_location), expr); | 350 | return trans_create_node_fn_call_1(c, trans_qual_type(c, qt, source_location), expr); |
| 351 | } | 351 | } |
| 352 | | 352 | |
| | 353 | static bool qual_type_is_fn_ptr(Context *c, const QualType &qt) { |
| | 354 | const Type *ty = qt.getTypePtr(); |
| | 355 | if (ty->getTypeClass() != Type::Pointer) { |
| | 356 | return false; |
| | 357 | } |
| | 358 | const PointerType *pointer_ty = static_cast<const PointerType*>(ty); |
| | 359 | QualType child_qt = pointer_ty->getPointeeType(); |
| | 360 | const Type *child_ty = child_qt.getTypePtr(); |
| | 361 | if (child_ty->getTypeClass() != Type::Paren) { |
| | 362 | return false; |
| | 363 | } |
| | 364 | const ParenType *paren_ty = static_cast<const ParenType *>(child_ty); |
| | 365 | return paren_ty->getInnerType().getTypePtr()->getTypeClass() == Type::FunctionProto; |
| | 366 | } |
| | 367 | |
| 353 | static uint32_t qual_type_int_bit_width(Context *c, const QualType &qt, const SourceLocation &source_loc) { | 368 | static uint32_t qual_type_int_bit_width(Context *c, const QualType &qt, const SourceLocation &source_loc) { |
| 354 | const Type *ty = qt.getTypePtr(); | 369 | const Type *ty = qt.getTypePtr(); |
| 355 | switch (ty->getTypeClass()) { | 370 | switch (ty->getTypeClass()) { |
| ... | @@ -990,6 +1005,21 @@ static AstNode *trans_create_assign(Context *c, bool result_used, AstNode *block | ... | @@ -990,6 +1005,21 @@ static AstNode *trans_create_assign(Context *c, bool result_used, AstNode *block |
| 990 | } | 1005 | } |
| 991 | } | 1006 | } |
| 992 | | 1007 | |
| | 1008 | static AstNode *trans_create_shift_op(Context *c, AstNode *block, QualType result_type, Expr *lhs_expr, BinOpType bin_op, Expr *rhs_expr) { |
| | 1009 | const SourceLocation &rhs_location = rhs_expr->getLocStart(); |
| | 1010 | AstNode *rhs_type = qual_type_to_log2_int_ref(c, result_type, rhs_location); |
| | 1011 | // lhs >> u5(rh) |
| | 1012 | |
| | 1013 | AstNode *lhs = trans_expr(c, true, block, lhs_expr, TransLValue); |
| | 1014 | if (lhs == nullptr) return nullptr; |
| | 1015 | |
| | 1016 | AstNode *rhs = trans_expr(c, true, block, rhs_expr, TransRValue); |
| | 1017 | if (rhs == nullptr) return nullptr; |
| | 1018 | AstNode *coerced_rhs = trans_create_node_fn_call_1(c, rhs_type, rhs); |
| | 1019 | |
| | 1020 | return trans_create_node_bin_op(c, lhs, bin_op, coerced_rhs); |
| | 1021 | } |
| | 1022 | |
| 993 | static AstNode *trans_binary_operator(Context *c, bool result_used, AstNode *block, BinaryOperator *stmt) { | 1023 | static AstNode *trans_binary_operator(Context *c, bool result_used, AstNode *block, BinaryOperator *stmt) { |
| 994 | switch (stmt->getOpcode()) { | 1024 | switch (stmt->getOpcode()) { |
| 995 | case BO_PtrMemD: | 1025 | case BO_PtrMemD: |
| ... | @@ -1022,7 +1052,7 @@ static AstNode *trans_binary_operator(Context *c, bool result_used, AstNode *blo | ... | @@ -1022,7 +1052,7 @@ static AstNode *trans_binary_operator(Context *c, bool result_used, AstNode *blo |
| 1022 | // unsigned/float division uses the operator | 1052 | // unsigned/float division uses the operator |
| 1023 | return trans_create_bin_op(c, block, stmt->getLHS(), BinOpTypeMod, stmt->getRHS()); | 1053 | return trans_create_bin_op(c, block, stmt->getLHS(), BinOpTypeMod, stmt->getRHS()); |
| 1024 | } else { | 1054 | } else { |
| 1025 | // signed integer division uses @divTrunc | 1055 | // signed integer division uses @rem |
| 1026 | AstNode *fn_call = trans_create_node_builtin_fn_call_str(c, "rem"); | 1056 | AstNode *fn_call = trans_create_node_builtin_fn_call_str(c, "rem"); |
| 1027 | AstNode *lhs = trans_expr(c, true, block, stmt->getLHS(), TransLValue); | 1057 | AstNode *lhs = trans_expr(c, true, block, stmt->getLHS(), TransLValue); |
| 1028 | if (lhs == nullptr) return nullptr; | 1058 | if (lhs == nullptr) return nullptr; |
| ... | @@ -1041,11 +1071,9 @@ static AstNode *trans_binary_operator(Context *c, bool result_used, AstNode *blo | ... | @@ -1041,11 +1071,9 @@ static AstNode *trans_binary_operator(Context *c, bool result_used, AstNode *blo |
| 1041 | qual_type_has_wrapping_overflow(c, stmt->getType()) ? BinOpTypeSubWrap : BinOpTypeSub, | 1071 | qual_type_has_wrapping_overflow(c, stmt->getType()) ? BinOpTypeSubWrap : BinOpTypeSub, |
| 1042 | stmt->getRHS()); | 1072 | stmt->getRHS()); |
| 1043 | case BO_Shl: | 1073 | case BO_Shl: |
| 1044 | emit_warning(c, stmt->getLocStart(), "TODO handle more C binary operators: BO_Shl"); | 1074 | return trans_create_shift_op(c, block, stmt->getType(), stmt->getLHS(), BinOpTypeBitShiftLeft, stmt->getRHS()); |
| 1045 | return nullptr; | | |
| 1046 | case BO_Shr: | 1075 | case BO_Shr: |
| 1047 | emit_warning(c, stmt->getLocStart(), "TODO handle more C binary operators: BO_Shr"); | 1076 | return trans_create_shift_op(c, block, stmt->getType(), stmt->getLHS(), BinOpTypeBitShiftRight, stmt->getRHS()); |
| 1048 | return nullptr; | | |
| 1049 | case BO_LT: | 1077 | case BO_LT: |
| 1050 | return trans_create_bin_op(c, block, stmt->getLHS(), BinOpTypeCmpLessThan, stmt->getRHS()); | 1078 | return trans_create_bin_op(c, block, stmt->getLHS(), BinOpTypeCmpLessThan, stmt->getRHS()); |
| 1051 | case BO_GT: | 1079 | case BO_GT: |
| ... | @@ -1071,49 +1099,167 @@ static AstNode *trans_binary_operator(Context *c, bool result_used, AstNode *blo | ... | @@ -1071,49 +1099,167 @@ static AstNode *trans_binary_operator(Context *c, bool result_used, AstNode *blo |
| 1071 | return trans_create_bin_op(c, block, stmt->getLHS(), BinOpTypeBoolOr, stmt->getRHS()); | 1099 | return trans_create_bin_op(c, block, stmt->getLHS(), BinOpTypeBoolOr, stmt->getRHS()); |
| 1072 | case BO_Assign: | 1100 | case BO_Assign: |
| 1073 | return trans_create_assign(c, result_used, block, stmt->getLHS(), stmt->getRHS()); | 1101 | return trans_create_assign(c, result_used, block, stmt->getLHS(), stmt->getRHS()); |
| | 1102 | case BO_Comma: |
| | 1103 | { |
| | 1104 | block = trans_create_node(c, NodeTypeBlock); |
| | 1105 | AstNode *lhs = trans_expr(c, false, block, stmt->getLHS(), TransRValue); |
| | 1106 | if (lhs == nullptr) return nullptr; |
| | 1107 | block->data.block.statements.append(maybe_suppress_result(c, false, lhs)); |
| | 1108 | AstNode *rhs = trans_expr(c, result_used, block, stmt->getRHS(), TransRValue); |
| | 1109 | if (rhs == nullptr) return nullptr; |
| | 1110 | block->data.block.statements.append(maybe_suppress_result(c, result_used, rhs)); |
| | 1111 | block->data.block.last_statement_is_result_expression = true; |
| | 1112 | return block; |
| | 1113 | } |
| 1074 | case BO_MulAssign: | 1114 | case BO_MulAssign: |
| 1075 | emit_warning(c, stmt->getLocStart(), "TODO handle more C binary operators: BO_MulAssign"); | | |
| 1076 | return nullptr; | | |
| 1077 | case BO_DivAssign: | 1115 | case BO_DivAssign: |
| 1078 | emit_warning(c, stmt->getLocStart(), "TODO handle more C binary operators: BO_DivAssign"); | | |
| 1079 | return nullptr; | | |
| 1080 | case BO_RemAssign: | 1116 | case BO_RemAssign: |
| 1081 | emit_warning(c, stmt->getLocStart(), "TODO handle more C binary operators: BO_RemAssign"); | | |
| 1082 | return nullptr; | | |
| 1083 | case BO_AddAssign: | 1117 | case BO_AddAssign: |
| 1084 | emit_warning(c, stmt->getLocStart(), "TODO handle more C binary operators: BO_AddAssign"); | | |
| 1085 | return nullptr; | | |
| 1086 | case BO_SubAssign: | 1118 | case BO_SubAssign: |
| 1087 | emit_warning(c, stmt->getLocStart(), "TODO handle more C binary operators: BO_SubAssign"); | | |
| 1088 | return nullptr; | | |
| 1089 | case BO_ShlAssign: | 1119 | case BO_ShlAssign: |
| 1090 | emit_warning(c, stmt->getLocStart(), "TODO handle more C binary operators: BO_ShlAssign"); | | |
| 1091 | return nullptr; | | |
| 1092 | case BO_ShrAssign: | 1120 | case BO_ShrAssign: |
| 1093 | emit_warning(c, stmt->getLocStart(), "TODO handle more C binary operators: BO_ShrAssign"); | | |
| 1094 | return nullptr; | | |
| 1095 | case BO_AndAssign: | 1121 | case BO_AndAssign: |
| 1096 | emit_warning(c, stmt->getLocStart(), "TODO handle more C binary operators: BO_AndAssign"); | | |
| 1097 | return nullptr; | | |
| 1098 | case BO_XorAssign: | 1122 | case BO_XorAssign: |
| 1099 | emit_warning(c, stmt->getLocStart(), "TODO handle more C binary operators: BO_XorAssign"); | | |
| 1100 | return nullptr; | | |
| 1101 | case BO_OrAssign: | 1123 | case BO_OrAssign: |
| 1102 | emit_warning(c, stmt->getLocStart(), "TODO handle more C binary operators: BO_OrAssign"); | 1124 | zig_unreachable(); |
| 1103 | return nullptr; | | |
| 1104 | case BO_Comma: | | |
| 1105 | emit_warning(c, stmt->getLocStart(), "TODO handle more C binary operators: BO_Comma"); | | |
| 1106 | return nullptr; | | |
| 1107 | } | 1125 | } |
| 1108 | | 1126 | |
| 1109 | zig_unreachable(); | 1127 | zig_unreachable(); |
| 1110 | } | 1128 | } |
| 1111 | | 1129 | |
| | 1130 | static AstNode *trans_create_compound_assign_shift(Context *c, bool result_used, AstNode *block, CompoundAssignOperator *stmt, BinOpType assign_op, BinOpType bin_op) { |
| | 1131 | const SourceLocation &rhs_location = stmt->getRHS()->getLocStart(); |
| | 1132 | AstNode *rhs_type = qual_type_to_log2_int_ref(c, stmt->getComputationLHSType(), rhs_location); |
| | 1133 | |
| | 1134 | bool use_intermediate_casts = stmt->getComputationLHSType().getTypePtr() != stmt->getComputationResultType().getTypePtr(); |
| | 1135 | if (!use_intermediate_casts && !result_used) { |
| | 1136 | // simple common case, where the C and Zig are identical: |
| | 1137 | // lhs >>= rhs |
| | 1138 | AstNode *lhs = trans_expr(c, true, block, stmt->getLHS(), TransLValue); |
| | 1139 | if (lhs == nullptr) return nullptr; |
| | 1140 | |
| | 1141 | AstNode *rhs = trans_expr(c, true, block, stmt->getRHS(), TransRValue); |
| | 1142 | if (rhs == nullptr) return nullptr; |
| | 1143 | AstNode *coerced_rhs = trans_create_node_fn_call_1(c, rhs_type, rhs); |
| | 1144 | |
| | 1145 | return trans_create_node_bin_op(c, lhs, assign_op, coerced_rhs); |
| | 1146 | } else { |
| | 1147 | // need more complexity. worst case, this looks like this: |
| | 1148 | // c: lhs >>= rhs |
| | 1149 | // zig: { |
| | 1150 | // zig: const _ref = &lhs; |
| | 1151 | // zig: *_ref = result_type(operation_type(*_ref) >> u5(rhs)); |
| | 1152 | // zig: *_ref |
| | 1153 | // zig: } |
| | 1154 | // where u5 is the appropriate type |
| | 1155 | |
| | 1156 | AstNode *child_block = trans_create_node(c, NodeTypeBlock); |
| | 1157 | |
| | 1158 | // const _ref = &lhs; |
| | 1159 | AstNode *lhs = trans_expr(c, true, child_block, stmt->getLHS(), TransLValue); |
| | 1160 | if (lhs == nullptr) return nullptr; |
| | 1161 | AstNode *addr_of_lhs = trans_create_node_addr_of(c, false, false, lhs); |
| | 1162 | // TODO: avoid name collisions with generated variable names |
| | 1163 | Buf* tmp_var_name = buf_create_from_str("_ref"); |
| | 1164 | AstNode *tmp_var_decl = trans_create_node_var_decl_local(c, true, tmp_var_name, nullptr, addr_of_lhs); |
| | 1165 | child_block->data.block.statements.append(tmp_var_decl); |
| | 1166 | |
| | 1167 | // *_ref = result_type(operation_type(*_ref) >> u5(rhs)); |
| | 1168 | |
| | 1169 | AstNode *rhs = trans_expr(c, true, child_block, stmt->getRHS(), TransRValue); |
| | 1170 | if (rhs == nullptr) return nullptr; |
| | 1171 | AstNode *coerced_rhs = trans_create_node_fn_call_1(c, rhs_type, rhs); |
| | 1172 | |
| | 1173 | AstNode *assign_statement = trans_create_node_bin_op(c, |
| | 1174 | trans_create_node_prefix_op(c, PrefixOpDereference, |
| | 1175 | trans_create_node_symbol(c, tmp_var_name)), |
| | 1176 | BinOpTypeAssign, |
| | 1177 | trans_c_cast(c, rhs_location, |
| | 1178 | stmt->getComputationResultType(), |
| | 1179 | trans_create_node_bin_op(c, |
| | 1180 | trans_c_cast(c, rhs_location, |
| | 1181 | stmt->getComputationLHSType(), |
| | 1182 | trans_create_node_prefix_op(c, PrefixOpDereference, |
| | 1183 | trans_create_node_symbol(c, tmp_var_name))), |
| | 1184 | bin_op, |
| | 1185 | coerced_rhs))); |
| | 1186 | child_block->data.block.statements.append(assign_statement); |
| | 1187 | |
| | 1188 | if (result_used) { |
| | 1189 | // *_ref |
| | 1190 | child_block->data.block.statements.append( |
| | 1191 | trans_create_node_prefix_op(c, PrefixOpDereference, |
| | 1192 | trans_create_node_symbol(c, tmp_var_name))); |
| | 1193 | child_block->data.block.last_statement_is_result_expression = true; |
| | 1194 | } |
| | 1195 | |
| | 1196 | return child_block; |
| | 1197 | } |
| | 1198 | } |
| | 1199 | |
| | 1200 | static AstNode *trans_create_compound_assign(Context *c, bool result_used, AstNode *block, CompoundAssignOperator *stmt, BinOpType assign_op, BinOpType bin_op) { |
| | 1201 | if (!result_used) { |
| | 1202 | // simple common case, where the C and Zig are identical: |
| | 1203 | // lhs += rhs |
| | 1204 | AstNode *lhs = trans_expr(c, true, block, stmt->getLHS(), TransLValue); |
| | 1205 | if (lhs == nullptr) return nullptr; |
| | 1206 | AstNode *rhs = trans_expr(c, true, block, stmt->getRHS(), TransRValue); |
| | 1207 | if (rhs == nullptr) return nullptr; |
| | 1208 | return trans_create_node_bin_op(c, lhs, assign_op, rhs); |
| | 1209 | } else { |
| | 1210 | // need more complexity. worst case, this looks like this: |
| | 1211 | // c: lhs += rhs |
| | 1212 | // zig: { |
| | 1213 | // zig: const _ref = &lhs; |
| | 1214 | // zig: *_ref = *_ref + rhs; |
| | 1215 | // zig: *_ref |
| | 1216 | // zig: } |
| | 1217 | |
| | 1218 | AstNode *child_block = trans_create_node(c, NodeTypeBlock); |
| | 1219 | |
| | 1220 | // const _ref = &lhs; |
| | 1221 | AstNode *lhs = trans_expr(c, true, child_block, stmt->getLHS(), TransLValue); |
| | 1222 | if (lhs == nullptr) return nullptr; |
| | 1223 | AstNode *addr_of_lhs = trans_create_node_addr_of(c, false, false, lhs); |
| | 1224 | // TODO: avoid name collisions with generated variable names |
| | 1225 | Buf* tmp_var_name = buf_create_from_str("_ref"); |
| | 1226 | AstNode *tmp_var_decl = trans_create_node_var_decl_local(c, true, tmp_var_name, nullptr, addr_of_lhs); |
| | 1227 | child_block->data.block.statements.append(tmp_var_decl); |
| | 1228 | |
| | 1229 | // *_ref = *_ref + rhs; |
| | 1230 | |
| | 1231 | AstNode *rhs = trans_expr(c, true, child_block, stmt->getRHS(), TransRValue); |
| | 1232 | if (rhs == nullptr) return nullptr; |
| | 1233 | |
| | 1234 | AstNode *assign_statement = trans_create_node_bin_op(c, |
| | 1235 | trans_create_node_prefix_op(c, PrefixOpDereference, |
| | 1236 | trans_create_node_symbol(c, tmp_var_name)), |
| | 1237 | BinOpTypeAssign, |
| | 1238 | trans_create_node_bin_op(c, |
| | 1239 | trans_create_node_prefix_op(c, PrefixOpDereference, |
| | 1240 | trans_create_node_symbol(c, tmp_var_name)), |
| | 1241 | bin_op, |
| | 1242 | rhs)); |
| | 1243 | child_block->data.block.statements.append(assign_statement); |
| | 1244 | |
| | 1245 | // *_ref |
| | 1246 | child_block->data.block.statements.append( |
| | 1247 | trans_create_node_prefix_op(c, PrefixOpDereference, |
| | 1248 | trans_create_node_symbol(c, tmp_var_name))); |
| | 1249 | child_block->data.block.last_statement_is_result_expression = true; |
| | 1250 | |
| | 1251 | return child_block; |
| | 1252 | } |
| | 1253 | } |
| | 1254 | |
| | 1255 | |
| 1112 | static AstNode *trans_compound_assign_operator(Context *c, bool result_used, AstNode *block, CompoundAssignOperator *stmt) { | 1256 | static AstNode *trans_compound_assign_operator(Context *c, bool result_used, AstNode *block, CompoundAssignOperator *stmt) { |
| 1113 | switch (stmt->getOpcode()) { | 1257 | switch (stmt->getOpcode()) { |
| 1114 | case BO_MulAssign: | 1258 | case BO_MulAssign: |
| 1115 | emit_warning(c, stmt->getLocStart(), "TODO handle more C compound assign operators: BO_MulAssign"); | 1259 | if (qual_type_has_wrapping_overflow(c, stmt->getType())) |
| 1116 | return nullptr; | 1260 | return trans_create_compound_assign(c, result_used, block, stmt, BinOpTypeAssignTimesWrap, BinOpTypeMultWrap); |
| | 1261 | else |
| | 1262 | return trans_create_compound_assign(c, result_used, block, stmt, BinOpTypeAssignTimes, BinOpTypeMult); |
| 1117 | case BO_DivAssign: | 1263 | case BO_DivAssign: |
| 1118 | emit_warning(c, stmt->getLocStart(), "TODO handle more C compound assign operators: BO_DivAssign"); | 1264 | emit_warning(c, stmt->getLocStart(), "TODO handle more C compound assign operators: BO_DivAssign"); |
| 1119 | return nullptr; | 1265 | return nullptr; |
| ... | @@ -1121,95 +1267,25 @@ static AstNode *trans_compound_assign_operator(Context *c, bool result_used, Ast | ... | @@ -1121,95 +1267,25 @@ static AstNode *trans_compound_assign_operator(Context *c, bool result_used, Ast |
| 1121 | emit_warning(c, stmt->getLocStart(), "TODO handle more C compound assign operators: BO_RemAssign"); | 1267 | emit_warning(c, stmt->getLocStart(), "TODO handle more C compound assign operators: BO_RemAssign"); |
| 1122 | return nullptr; | 1268 | return nullptr; |
| 1123 | case BO_AddAssign: | 1269 | case BO_AddAssign: |
| 1124 | emit_warning(c, stmt->getLocStart(), "TODO handle more C compound assign operators: BO_AddAssign"); | 1270 | if (qual_type_has_wrapping_overflow(c, stmt->getType())) |
| 1125 | return nullptr; | 1271 | return trans_create_compound_assign(c, result_used, block, stmt, BinOpTypeAssignPlusWrap, BinOpTypeAddWrap); |
| | 1272 | else |
| | 1273 | return trans_create_compound_assign(c, result_used, block, stmt, BinOpTypeAssignPlus, BinOpTypeAdd); |
| 1126 | case BO_SubAssign: | 1274 | case BO_SubAssign: |
| 1127 | emit_warning(c, stmt->getLocStart(), "TODO handle more C compound assign operators: BO_SubAssign"); | 1275 | if (qual_type_has_wrapping_overflow(c, stmt->getType())) |
| 1128 | return nullptr; | 1276 | return trans_create_compound_assign(c, result_used, block, stmt, BinOpTypeAssignMinusWrap, BinOpTypeSubWrap); |
| | 1277 | else |
| | 1278 | return trans_create_compound_assign(c, result_used, block, stmt, BinOpTypeAssignMinus, BinOpTypeSub); |
| 1129 | case BO_ShlAssign: | 1279 | case BO_ShlAssign: |
| 1130 | emit_warning(c, stmt->getLocStart(), "TODO handle more C compound assign operators: BO_ShlAssign"); | 1280 | return trans_create_compound_assign_shift(c, result_used, block, stmt, BinOpTypeAssignBitShiftLeft, BinOpTypeBitShiftLeft); |
| 1131 | return nullptr; | 1281 | case BO_ShrAssign: |
| 1132 | case BO_ShrAssign: { | 1282 | return trans_create_compound_assign_shift(c, result_used, block, stmt, BinOpTypeAssignBitShiftRight, BinOpTypeBitShiftRight); |
| 1133 | BinOpType bin_op = BinOpTypeBitShiftRight; | | |
| 1134 | | | |
| 1135 | const SourceLocation &rhs_location = stmt->getRHS()->getLocStart(); | | |
| 1136 | AstNode *rhs_type = qual_type_to_log2_int_ref(c, stmt->getComputationLHSType(), rhs_location); | | |
| 1137 | | | |
| 1138 | bool use_intermediate_casts = stmt->getComputationLHSType().getTypePtr() != stmt->getComputationResultType().getTypePtr(); | | |
| 1139 | if (!use_intermediate_casts && !result_used) { | | |
| 1140 | // simple common case, where the C and Zig are identical: | | |
| 1141 | // lhs >>= rh* s | | |
| 1142 | AstNode *lhs = trans_expr(c, true, block, stmt->getLHS(), TransLValue); | | |
| 1143 | if (lhs == nullptr) return nullptr; | | |
| 1144 | | | |
| 1145 | AstNode *rhs = trans_expr(c, true, block, stmt->getRHS(), TransRValue); | | |
| 1146 | if (rhs == nullptr) return nullptr; | | |
| 1147 | AstNode *coerced_rhs = trans_create_node_fn_call_1(c, rhs_type, rhs); | | |
| 1148 | | | |
| 1149 | return trans_create_node_bin_op(c, lhs, BinOpTypeAssignBitShiftRight, coerced_rhs); | | |
| 1150 | } else { | | |
| 1151 | // need more complexity. worst case, this looks like this: | | |
| 1152 | // c: lhs >>= rhs | | |
| 1153 | // zig: { | | |
| 1154 | // zig: const _ref = &lhs; | | |
| 1155 | // zig: *_ref = result_type(operation_type(*_ref) >> u5(rhs)); | | |
| 1156 | // zig: *_ref | | |
| 1157 | // zig: } | | |
| 1158 | // where u5 is the appropriate type | | |
| 1159 | | | |
| 1160 | // TODO: avoid mess when we don't need the assignment value for chained assignments or anything. | | |
| 1161 | AstNode *child_block = trans_create_node(c, NodeTypeBlock); | | |
| 1162 | | | |
| 1163 | // const _ref = &lhs; | | |
| 1164 | AstNode *lhs = trans_expr(c, true, child_block, stmt->getLHS(), TransLValue); | | |
| 1165 | if (lhs == nullptr) return nullptr; | | |
| 1166 | AstNode *addr_of_lhs = trans_create_node_addr_of(c, false, false, lhs); | | |
| 1167 | // TODO: avoid name collisions with generated variable names | | |
| 1168 | Buf* tmp_var_name = buf_create_from_str("_ref"); | | |
| 1169 | AstNode *tmp_var_decl = trans_create_node_var_decl_local(c, true, tmp_var_name, nullptr, addr_of_lhs); | | |
| 1170 | child_block->data.block.statements.append(tmp_var_decl); | | |
| 1171 | | | |
| 1172 | // *_ref = result_type(operation_type(*_ref) >> u5(rhs)); | | |
| 1173 | | | |
| 1174 | AstNode *rhs = trans_expr(c, true, child_block, stmt->getRHS(), TransRValue); | | |
| 1175 | if (rhs == nullptr) return nullptr; | | |
| 1176 | AstNode *coerced_rhs = trans_create_node_fn_call_1(c, rhs_type, rhs); | | |
| 1177 | | | |
| 1178 | AstNode *assign_statement = trans_create_node_bin_op(c, | | |
| 1179 | trans_create_node_prefix_op(c, PrefixOpDereference, | | |
| 1180 | trans_create_node_symbol(c, tmp_var_name)), | | |
| 1181 | BinOpTypeAssign, | | |
| 1182 | trans_c_cast(c, rhs_location, | | |
| 1183 | stmt->getComputationResultType(), | | |
| 1184 | trans_create_node_bin_op(c, | | |
| 1185 | trans_c_cast(c, rhs_location, | | |
| 1186 | stmt->getComputationLHSType(), | | |
| 1187 | trans_create_node_prefix_op(c, PrefixOpDereference, | | |
| 1188 | trans_create_node_symbol(c, tmp_var_name))), | | |
| 1189 | bin_op, | | |
| 1190 | coerced_rhs))); | | |
| 1191 | child_block->data.block.statements.append(assign_statement); | | |
| 1192 | | | |
| 1193 | if (result_used) { | | |
| 1194 | // *_ref | | |
| 1195 | child_block->data.block.statements.append( | | |
| 1196 | trans_create_node_prefix_op(c, PrefixOpDereference, | | |
| 1197 | trans_create_node_symbol(c, tmp_var_name))); | | |
| 1198 | child_block->data.block.last_statement_is_result_expression = true; | | |
| 1199 | } | | |
| 1200 | | | |
| 1201 | return child_block; | | |
| 1202 | } | | |
| 1203 | } | | |
| 1204 | case BO_AndAssign: | 1283 | case BO_AndAssign: |
| 1205 | emit_warning(c, stmt->getLocStart(), "TODO handle more C compound assign operators: BO_AndAssign"); | 1284 | return trans_create_compound_assign(c, result_used, block, stmt, BinOpTypeAssignBitAnd, BinOpTypeBinAnd); |
| 1206 | return nullptr; | | |
| 1207 | case BO_XorAssign: | 1285 | case BO_XorAssign: |
| 1208 | emit_warning(c, stmt->getLocStart(), "TODO handle more C compound assign operators: BO_XorAssign"); | 1286 | return trans_create_compound_assign(c, result_used, block, stmt, BinOpTypeAssignBitXor, BinOpTypeBinXor); |
| 1209 | return nullptr; | | |
| 1210 | case BO_OrAssign: | 1287 | case BO_OrAssign: |
| 1211 | emit_warning(c, stmt->getLocStart(), "TODO handle more C compound assign operators: BO_OrAssign"); | 1288 | return trans_create_compound_assign(c, result_used, block, stmt, BinOpTypeAssignBitOr, BinOpTypeBinOr); |
| 1212 | return nullptr; | | |
| 1213 | case BO_PtrMemD: | 1289 | case BO_PtrMemD: |
| 1214 | case BO_PtrMemI: | 1290 | case BO_PtrMemI: |
| 1215 | case BO_Assign: | 1291 | case BO_Assign: |
| ... | @@ -1232,7 +1308,7 @@ static AstNode *trans_compound_assign_operator(Context *c, bool result_used, Ast | ... | @@ -1232,7 +1308,7 @@ static AstNode *trans_compound_assign_operator(Context *c, bool result_used, Ast |
| 1232 | case BO_LAnd: | 1308 | case BO_LAnd: |
| 1233 | case BO_LOr: | 1309 | case BO_LOr: |
| 1234 | case BO_Comma: | 1310 | case BO_Comma: |
| 1235 | zig_panic("compound assign expected to be handled by binary operator"); | 1311 | zig_unreachable(); |
| 1236 | } | 1312 | } |
| 1237 | | 1313 | |
| 1238 | zig_unreachable(); | 1314 | zig_unreachable(); |
| ... | @@ -1270,6 +1346,8 @@ static AstNode *trans_implicit_cast_expr(Context *c, AstNode *block, ImplicitCas | ... | @@ -1270,6 +1346,8 @@ static AstNode *trans_implicit_cast_expr(Context *c, AstNode *block, ImplicitCas |
| 1270 | node->data.fn_call_expr.params.append(target_node); | 1346 | node->data.fn_call_expr.params.append(target_node); |
| 1271 | return node; | 1347 | return node; |
| 1272 | } | 1348 | } |
| | 1349 | case CK_NullToPointer: |
| | 1350 | return trans_create_node(c, NodeTypeNullLiteral); |
| 1273 | case CK_Dependent: | 1351 | case CK_Dependent: |
| 1274 | emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast CK_Dependent"); | 1352 | emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast CK_Dependent"); |
| 1275 | return nullptr; | 1353 | return nullptr; |
| ... | @@ -1294,9 +1372,6 @@ static AstNode *trans_implicit_cast_expr(Context *c, AstNode *block, ImplicitCas | ... | @@ -1294,9 +1372,6 @@ static AstNode *trans_implicit_cast_expr(Context *c, AstNode *block, ImplicitCas |
| 1294 | case CK_ToUnion: | 1372 | case CK_ToUnion: |
| 1295 | emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast CK_ToUnion"); | 1373 | emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast CK_ToUnion"); |
| 1296 | return nullptr; | 1374 | return nullptr; |
| 1297 | case CK_NullToPointer: | | |
| 1298 | emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast CK_NullToPointer"); | | |
| 1299 | return nullptr; | | |
| 1300 | case CK_NullToMemberPointer: | 1375 | case CK_NullToMemberPointer: |
| 1301 | emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast CK_NullToMemberPointer"); | 1376 | emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast CK_NullToMemberPointer"); |
| 1302 | return nullptr; | 1377 | return nullptr; |
| ... | @@ -1442,38 +1517,72 @@ static AstNode *trans_decl_ref_expr(Context *c, DeclRefExpr *stmt, TransLRValue | ... | @@ -1442,38 +1517,72 @@ static AstNode *trans_decl_ref_expr(Context *c, DeclRefExpr *stmt, TransLRValue |
| 1442 | return trans_create_node_symbol(c, symbol_name); | 1517 | return trans_create_node_symbol(c, symbol_name); |
| 1443 | } | 1518 | } |
| 1444 | | 1519 | |
| | 1520 | static AstNode *trans_create_post_crement(Context *c, bool result_used, AstNode *block, UnaryOperator *stmt, BinOpType assign_op) { |
| | 1521 | Expr *op_expr = stmt->getSubExpr(); |
| | 1522 | |
| | 1523 | if (!result_used) { |
| | 1524 | // common case |
| | 1525 | // c: expr++ |
| | 1526 | // zig: expr += 1 |
| | 1527 | return trans_create_node_bin_op(c, |
| | 1528 | trans_expr(c, true, block, op_expr, TransLValue), |
| | 1529 | assign_op, |
| | 1530 | trans_create_node_unsigned(c, 1)); |
| | 1531 | } else { |
| | 1532 | // worst case |
| | 1533 | // c: expr++ |
| | 1534 | // zig: { |
| | 1535 | // zig: const _ref = &expr; |
| | 1536 | // zig: const _tmp = *_ref; |
| | 1537 | // zig: *_ref += 1; |
| | 1538 | // zig: _tmp |
| | 1539 | // zig: } |
| | 1540 | AstNode *child_block = trans_create_node(c, NodeTypeBlock); |
| | 1541 | |
| | 1542 | // const _ref = &expr; |
| | 1543 | AstNode *expr = trans_expr(c, true, child_block, op_expr, TransLValue); |
| | 1544 | if (expr == nullptr) return nullptr; |
| | 1545 | AstNode *addr_of_expr = trans_create_node_addr_of(c, false, false, expr); |
| | 1546 | // TODO: avoid name collisions with generated variable names |
| | 1547 | Buf* ref_var_name = buf_create_from_str("_ref"); |
| | 1548 | AstNode *ref_var_decl = trans_create_node_var_decl_local(c, true, ref_var_name, nullptr, addr_of_expr); |
| | 1549 | child_block->data.block.statements.append(ref_var_decl); |
| | 1550 | |
| | 1551 | // const _tmp = *_ref; |
| | 1552 | Buf* tmp_var_name = buf_create_from_str("_tmp"); |
| | 1553 | AstNode *tmp_var_decl = trans_create_node_var_decl_local(c, true, tmp_var_name, nullptr, |
| | 1554 | trans_create_node_prefix_op(c, PrefixOpDereference, |
| | 1555 | trans_create_node_symbol(c, ref_var_name))); |
| | 1556 | child_block->data.block.statements.append(tmp_var_decl); |
| | 1557 | |
| | 1558 | // *_ref += 1; |
| | 1559 | AstNode *assign_statement = trans_create_node_bin_op(c, |
| | 1560 | trans_create_node_prefix_op(c, PrefixOpDereference, |
| | 1561 | trans_create_node_symbol(c, ref_var_name)), |
| | 1562 | assign_op, |
| | 1563 | trans_create_node_unsigned(c, 1)); |
| | 1564 | child_block->data.block.statements.append(assign_statement); |
| | 1565 | |
| | 1566 | // _tmp |
| | 1567 | child_block->data.block.statements.append(trans_create_node_symbol(c, tmp_var_name)); |
| | 1568 | child_block->data.block.last_statement_is_result_expression = true; |
| | 1569 | |
| | 1570 | return child_block; |
| | 1571 | } |
| | 1572 | } |
| | 1573 | |
| 1445 | static AstNode *trans_unary_operator(Context *c, bool result_used, AstNode *block, UnaryOperator *stmt) { | 1574 | static AstNode *trans_unary_operator(Context *c, bool result_used, AstNode *block, UnaryOperator *stmt) { |
| 1446 | switch (stmt->getOpcode()) { | 1575 | switch (stmt->getOpcode()) { |
| 1447 | case UO_PostInc: { | 1576 | case UO_PostInc: |
| 1448 | Expr *op_expr = stmt->getSubExpr(); | 1577 | if (qual_type_has_wrapping_overflow(c, stmt->getType())) |
| 1449 | BinOpType bin_op = qual_type_has_wrapping_overflow(c, op_expr->getType()) | 1578 | return trans_create_post_crement(c, result_used, block, stmt, BinOpTypeAssignPlusWrap); |
| 1450 | ? BinOpTypeAssignPlusWrap | 1579 | else |
| 1451 | : BinOpTypeAssignPlus; | 1580 | return trans_create_post_crement(c, result_used, block, stmt, BinOpTypeAssignPlus); |
| 1452 | | | |
| 1453 | if (!result_used) { | | |
| 1454 | // common case | | |
| 1455 | // c: expr++ | | |
| 1456 | // zig: expr += 1 | | |
| 1457 | return trans_create_node_bin_op(c, | | |
| 1458 | trans_expr(c, true, block, op_expr, TransLValue), | | |
| 1459 | bin_op, | | |
| 1460 | trans_create_node_unsigned(c, 1)); | | |
| 1461 | } else { | | |
| 1462 | // worst case | | |
| 1463 | // c: expr++ | | |
| 1464 | // zig: { | | |
| 1465 | // zig: const _ref = &expr; | | |
| 1466 | // zig: const _tmp = *_ref; | | |
| 1467 | // zig: *_ref += 1; | | |
| 1468 | // zig: _tmp | | |
| 1469 | // zig: } | | |
| 1470 | emit_warning(c, stmt->getLocStart(), "TODO handle C translation UO_PostInc with result_used"); | | |
| 1471 | return nullptr; | | |
| 1472 | } | | |
| 1473 | } | | |
| 1474 | case UO_PostDec: | 1581 | case UO_PostDec: |
| 1475 | emit_warning(c, stmt->getLocStart(), "TODO handle C translation UO_PostDec"); | 1582 | if (qual_type_has_wrapping_overflow(c, stmt->getType())) |
| 1476 | return nullptr; | 1583 | return trans_create_post_crement(c, result_used, block, stmt, BinOpTypeAssignMinusWrap); |
| | 1584 | else |
| | 1585 | return trans_create_post_crement(c, result_used, block, stmt, BinOpTypeAssignMinus); |
| 1477 | case UO_PreInc: | 1586 | case UO_PreInc: |
| 1478 | emit_warning(c, stmt->getLocStart(), "TODO handle C translation UO_PreInc"); | 1587 | emit_warning(c, stmt->getLocStart(), "TODO handle C translation UO_PreInc"); |
| 1479 | return nullptr; | 1588 | return nullptr; |
| ... | @@ -1484,8 +1593,14 @@ static AstNode *trans_unary_operator(Context *c, bool result_used, AstNode *bloc | ... | @@ -1484,8 +1593,14 @@ static AstNode *trans_unary_operator(Context *c, bool result_used, AstNode *bloc |
| 1484 | emit_warning(c, stmt->getLocStart(), "TODO handle C translation UO_AddrOf"); | 1593 | emit_warning(c, stmt->getLocStart(), "TODO handle C translation UO_AddrOf"); |
| 1485 | return nullptr; | 1594 | return nullptr; |
| 1486 | case UO_Deref: | 1595 | case UO_Deref: |
| 1487 | emit_warning(c, stmt->getLocStart(), "TODO handle C translation UO_Deref"); | 1596 | { |
| 1488 | return nullptr; | 1597 | bool is_fn_ptr = qual_type_is_fn_ptr(c, stmt->getSubExpr()->getType()); |
| | 1598 | AstNode *value_node = trans_expr(c, result_used, block, stmt->getSubExpr(), TransRValue); |
| | 1599 | if (is_fn_ptr) |
| | 1600 | return value_node; |
| | 1601 | AstNode *unwrapped = trans_create_node_prefix_op(c, PrefixOpUnwrapMaybe, value_node); |
| | 1602 | return trans_create_node_prefix_op(c, PrefixOpDereference, unwrapped); |
| | 1603 | } |
| 1489 | case UO_Plus: | 1604 | case UO_Plus: |
| 1490 | emit_warning(c, stmt->getLocStart(), "TODO handle C translation UO_Plus"); | 1605 | emit_warning(c, stmt->getLocStart(), "TODO handle C translation UO_Plus"); |
| 1491 | return nullptr; | 1606 | return nullptr; |
| ... | @@ -1828,10 +1943,20 @@ static AstNode *trans_if_statement(Context *c, AstNode *block, IfStmt *stmt) { | ... | @@ -1828,10 +1943,20 @@ static AstNode *trans_if_statement(Context *c, AstNode *block, IfStmt *stmt) { |
| 1828 | | 1943 | |
| 1829 | static AstNode *trans_call_expr(Context *c, bool result_used, AstNode *block, CallExpr *stmt) { | 1944 | static AstNode *trans_call_expr(Context *c, bool result_used, AstNode *block, CallExpr *stmt) { |
| 1830 | AstNode *node = trans_create_node(c, NodeTypeFnCallExpr); | 1945 | AstNode *node = trans_create_node(c, NodeTypeFnCallExpr); |
| 1831 | node->data.fn_call_expr.fn_ref_expr = trans_expr(c, true, block, stmt->getCallee(), TransRValue); | 1946 | |
| 1832 | if (node->data.fn_call_expr.fn_ref_expr == nullptr) | 1947 | AstNode *callee_raw_node = trans_expr(c, true, block, stmt->getCallee(), TransRValue); |
| | 1948 | if (callee_raw_node == nullptr) |
| 1833 | return nullptr; | 1949 | return nullptr; |
| 1834 | | 1950 | |
| | 1951 | AstNode *callee_node; |
| | 1952 | if (qual_type_is_fn_ptr(c, stmt->getCallee()->getType())) { |
| | 1953 | callee_node = trans_create_node_prefix_op(c, PrefixOpUnwrapMaybe, callee_raw_node); |
| | 1954 | } else { |
| | 1955 | callee_node = callee_raw_node; |
| | 1956 | } |
| | 1957 | |
| | 1958 | node->data.fn_call_expr.fn_ref_expr = callee_node; |
| | 1959 | |
| 1835 | unsigned num_args = stmt->getNumArgs(); | 1960 | unsigned num_args = stmt->getNumArgs(); |
| 1836 | Expr **args = stmt->getArgs(); | 1961 | Expr **args = stmt->getArgs(); |
| 1837 | for (unsigned i = 0; i < num_args; i += 1) { | 1962 | for (unsigned i = 0; i < num_args; i += 1) { |
| ... | @@ -1896,6 +2021,59 @@ static AstNode *trans_unary_expr_or_type_trait_expr(Context *c, AstNode *block, | ... | @@ -1896,6 +2021,59 @@ static AstNode *trans_unary_expr_or_type_trait_expr(Context *c, AstNode *block, |
| 1896 | return node; | 2021 | return node; |
| 1897 | } | 2022 | } |
| 1898 | | 2023 | |
| | 2024 | static AstNode *trans_do_loop(Context *c, AstNode *block, DoStmt *stmt) { |
| | 2025 | stmt->getBody(); |
| | 2026 | stmt->getCond(); |
| | 2027 | |
| | 2028 | AstNode *while_node = trans_create_node(c, NodeTypeWhileExpr); |
| | 2029 | |
| | 2030 | AstNode *true_node = trans_create_node(c, NodeTypeBoolLiteral); |
| | 2031 | true_node->data.bool_literal.value = true; |
| | 2032 | while_node->data.while_expr.condition = true_node; |
| | 2033 | |
| | 2034 | AstNode *body_node; |
| | 2035 | if (stmt->getBody()->getStmtClass() == Stmt::CompoundStmtClass) { |
| | 2036 | // there's already a block in C, so we'll append our condition to it. |
| | 2037 | // c: do { |
| | 2038 | // c: a; |
| | 2039 | // c: b; |
| | 2040 | // c: } while(c); |
| | 2041 | // zig: while (true) { |
| | 2042 | // zig: a; |
| | 2043 | // zig: b; |
| | 2044 | // zig: if (!cond) break; |
| | 2045 | // zig: } |
| | 2046 | body_node = trans_stmt(c, false, block, stmt->getBody(), TransRValue); |
| | 2047 | if (body_node == nullptr) return nullptr; |
| | 2048 | assert(body_node->type == NodeTypeBlock); |
| | 2049 | } else { |
| | 2050 | // the C statement is without a block, so we need to create a block to contain it. |
| | 2051 | // c: do |
| | 2052 | // c: a; |
| | 2053 | // c: while(c); |
| | 2054 | // zig: while (true) { |
| | 2055 | // zig: a; |
| | 2056 | // zig: if (!cond) break; |
| | 2057 | // zig: } |
| | 2058 | body_node = trans_create_node(c, NodeTypeBlock); |
| | 2059 | AstNode *child_statement = trans_stmt(c, false, body_node, stmt->getBody(), TransRValue); |
| | 2060 | if (child_statement == nullptr) return nullptr; |
| | 2061 | body_node->data.block.statements.append(child_statement); |
| | 2062 | } |
| | 2063 | |
| | 2064 | // if (!cond) break; |
| | 2065 | AstNode *condition_node = trans_expr(c, true, body_node, stmt->getCond(), TransRValue); |
| | 2066 | if (condition_node == nullptr) return nullptr; |
| | 2067 | AstNode *terminator_node = trans_create_node(c, NodeTypeIfBoolExpr); |
| | 2068 | terminator_node->data.if_bool_expr.condition = trans_create_node_prefix_op(c, PrefixOpBoolNot, condition_node); |
| | 2069 | terminator_node->data.if_bool_expr.then_block = trans_create_node(c, NodeTypeBreak); |
| | 2070 | body_node->data.block.statements.append(terminator_node); |
| | 2071 | |
| | 2072 | while_node->data.while_expr.body = body_node; |
| | 2073 | |
| | 2074 | return while_node; |
| | 2075 | } |
| | 2076 | |
| 1899 | static AstNode *trans_stmt(Context *c, bool result_used, AstNode *block, Stmt *stmt, TransLRValue lrvalue) { | 2077 | static AstNode *trans_stmt(Context *c, bool result_used, AstNode *block, Stmt *stmt, TransLRValue lrvalue) { |
| 1900 | Stmt::StmtClass sc = stmt->getStmtClass(); | 2078 | Stmt::StmtClass sc = stmt->getStmtClass(); |
| 1901 | switch (sc) { | 2079 | switch (sc) { |
| ... | @@ -1935,6 +2113,8 @@ static AstNode *trans_stmt(Context *c, bool result_used, AstNode *block, Stmt *s | ... | @@ -1935,6 +2113,8 @@ static AstNode *trans_stmt(Context *c, bool result_used, AstNode *block, Stmt *s |
| 1935 | return trans_c_style_cast_expr(c, result_used, block, (CStyleCastExpr *)stmt, lrvalue); | 2113 | return trans_c_style_cast_expr(c, result_used, block, (CStyleCastExpr *)stmt, lrvalue); |
| 1936 | case Stmt::UnaryExprOrTypeTraitExprClass: | 2114 | case Stmt::UnaryExprOrTypeTraitExprClass: |
| 1937 | return trans_unary_expr_or_type_trait_expr(c, block, (UnaryExprOrTypeTraitExpr *)stmt); | 2115 | return trans_unary_expr_or_type_trait_expr(c, block, (UnaryExprOrTypeTraitExpr *)stmt); |
| | 2116 | case Stmt::DoStmtClass: |
| | 2117 | return trans_do_loop(c, block, (DoStmt *)stmt); |
| 1938 | case Stmt::CaseStmtClass: | 2118 | case Stmt::CaseStmtClass: |
| 1939 | emit_warning(c, stmt->getLocStart(), "TODO handle C CaseStmtClass"); | 2119 | emit_warning(c, stmt->getLocStart(), "TODO handle C CaseStmtClass"); |
| 1940 | return nullptr; | 2120 | return nullptr; |
| ... | @@ -1980,9 +2160,6 @@ static AstNode *trans_stmt(Context *c, bool result_used, AstNode *block, Stmt *s | ... | @@ -1980,9 +2160,6 @@ static AstNode *trans_stmt(Context *c, bool result_used, AstNode *block, Stmt *s |
| 1980 | case Stmt::CoroutineBodyStmtClass: | 2160 | case Stmt::CoroutineBodyStmtClass: |
| 1981 | emit_warning(c, stmt->getLocStart(), "TODO handle C CoroutineBodyStmtClass"); | 2161 | emit_warning(c, stmt->getLocStart(), "TODO handle C CoroutineBodyStmtClass"); |
| 1982 | return nullptr; | 2162 | return nullptr; |
| 1983 | case Stmt::DoStmtClass: | | |
| 1984 | emit_warning(c, stmt->getLocStart(), "TODO handle C DoStmtClass"); | | |
| 1985 | return nullptr; | | |
| 1986 | case Stmt::BinaryConditionalOperatorClass: | 2163 | case Stmt::BinaryConditionalOperatorClass: |
| 1987 | emit_warning(c, stmt->getLocStart(), "TODO handle C BinaryConditionalOperatorClass"); | 2164 | emit_warning(c, stmt->getLocStart(), "TODO handle C BinaryConditionalOperatorClass"); |
| 1988 | return nullptr; | 2165 | return nullptr; |