| ... | ... | @@ -2770,6 +2770,7 @@ static TypeTableEntry *resolve_expr_const_val_as_c_string_lit(CodeGen *g, AstNod |
| 2770 | 2770 | int len_with_null = buf_len(str) + 1; |
| 2771 | 2771 | expr->const_val.data.x_ptr.ptr = allocate<ConstExprValue*>(len_with_null); |
| 2772 | 2772 | expr->const_val.data.x_ptr.len = len_with_null; |
| 2773 | expr->const_val.data.x_ptr.is_c_str = true; |
| 2773 | 2774 | |
| 2774 | 2775 | ConstExprValue *all_chars = allocate<ConstExprValue>(len_with_null); |
| 2775 | 2776 | for (int i = 0; i < buf_len(str); i += 1) { |
| ... | ... | @@ -2974,7 +2975,7 @@ static bool is_op_allowed(TypeTableEntry *type, BinOpType op) { |
| 2974 | 2975 | case BinOpTypeDiv: |
| 2975 | 2976 | case BinOpTypeMod: |
| 2976 | 2977 | case BinOpTypeUnwrapMaybe: |
| 2977 | | case BinOpTypeStrCat: |
| 2978 | case BinOpTypeArrayCat: |
| 2978 | 2979 | case BinOpTypeArrayMult: |
| 2979 | 2980 | zig_unreachable(); |
| 2980 | 2981 | } |
| ... | ... | @@ -3379,19 +3380,42 @@ static TypeTableEntry *analyze_bin_op_expr(CodeGen *g, ImportTableEntry *import, |
| 3379 | 3380 | return g->builtin_types.entry_invalid; |
| 3380 | 3381 | } |
| 3381 | 3382 | } |
| 3382 | | case BinOpTypeStrCat: |
| 3383 | case BinOpTypeArrayCat: |
| 3383 | 3384 | { |
| 3384 | 3385 | AstNode **op1 = node->data.bin_op_expr.op1->parent_field; |
| 3385 | 3386 | AstNode **op2 = node->data.bin_op_expr.op2->parent_field; |
| 3386 | 3387 | |
| 3387 | | TypeTableEntry *str_type = get_slice_type(g, g->builtin_types.entry_u8, true); |
| 3388 | TypeTableEntry *op1_type = analyze_expression(g, import, context, nullptr, *op1); |
| 3389 | TypeTableEntry *child_type; |
| 3390 | if (op1_type->id == TypeTableEntryIdInvalid) { |
| 3391 | return g->builtin_types.entry_invalid; |
| 3392 | } else if (op1_type->id == TypeTableEntryIdArray) { |
| 3393 | child_type = op1_type->data.array.child_type; |
| 3394 | } else if (op1_type->id == TypeTableEntryIdPointer && |
| 3395 | op1_type->data.pointer.child_type == g->builtin_types.entry_u8) { |
| 3396 | child_type = op1_type->data.pointer.child_type; |
| 3397 | } else { |
| 3398 | add_node_error(g, *op1, buf_sprintf("expected array or C string literal, got '%s'", |
| 3399 | buf_ptr(&op1_type->name))); |
| 3400 | return g->builtin_types.entry_invalid; |
| 3401 | } |
| 3388 | 3402 | |
| 3389 | | TypeTableEntry *op1_type = analyze_expression(g, import, context, str_type, *op1); |
| 3390 | | TypeTableEntry *op2_type = analyze_expression(g, import, context, str_type, *op2); |
| 3403 | TypeTableEntry *op2_type = analyze_expression(g, import, context, nullptr, *op2); |
| 3391 | 3404 | |
| 3392 | | if (op1_type->id == TypeTableEntryIdInvalid || |
| 3393 | | op2_type->id == TypeTableEntryIdInvalid) |
| 3394 | | { |
| 3405 | if (op2_type->id == TypeTableEntryIdInvalid) { |
| 3406 | return g->builtin_types.entry_invalid; |
| 3407 | } else if (op2_type->id == TypeTableEntryIdArray) { |
| 3408 | if (op2_type->data.array.child_type != child_type) { |
| 3409 | add_node_error(g, *op2, buf_sprintf("expected array of type '%s', got '%s'", |
| 3410 | buf_ptr(&child_type->name), |
| 3411 | buf_ptr(&op2_type->name))); |
| 3412 | return g->builtin_types.entry_invalid; |
| 3413 | } |
| 3414 | } else if (op2_type->id == TypeTableEntryIdPointer && |
| 3415 | op2_type->data.pointer.child_type == g->builtin_types.entry_u8) { |
| 3416 | } else { |
| 3417 | add_node_error(g, *op2, buf_sprintf("expected array or C string literal, got '%s'", |
| 3418 | buf_ptr(&op2_type->name))); |
| 3395 | 3419 | return g->builtin_types.entry_invalid; |
| 3396 | 3420 | } |
| 3397 | 3421 | |
| ... | ... | @@ -3407,41 +3431,52 @@ static TypeTableEntry *analyze_bin_op_expr(CodeGen *g, ImportTableEntry *import, |
| 3407 | 3431 | bad_node = nullptr; |
| 3408 | 3432 | } |
| 3409 | 3433 | if (bad_node) { |
| 3410 | | add_node_error(g, bad_node, buf_sprintf("string concatenation requires constant expression")); |
| 3434 | add_node_error(g, bad_node, buf_sprintf("array concatenation requires constant expression")); |
| 3411 | 3435 | return g->builtin_types.entry_invalid; |
| 3412 | 3436 | } |
| 3437 | |
| 3413 | 3438 | ConstExprValue *const_val = &get_resolved_expr(node)->const_val; |
| 3414 | 3439 | const_val->ok = true; |
| 3415 | 3440 | const_val->depends_on_compile_var = op1_val->depends_on_compile_var || |
| 3416 | 3441 | op2_val->depends_on_compile_var; |
| 3417 | 3442 | |
| 3418 | | ConstExprValue *all_fields = allocate<ConstExprValue>(2); |
| 3419 | | ConstExprValue *ptr_field = &all_fields[0]; |
| 3420 | | ConstExprValue *len_field = &all_fields[1]; |
| 3421 | | |
| 3422 | | const_val->data.x_struct.fields = allocate<ConstExprValue*>(2); |
| 3423 | | const_val->data.x_struct.fields[0] = ptr_field; |
| 3424 | | const_val->data.x_struct.fields[1] = len_field; |
| 3425 | | |
| 3426 | | len_field->ok = true; |
| 3427 | | uint64_t op1_len = op1_val->data.x_struct.fields[1]->data.x_bignum.data.x_uint; |
| 3428 | | uint64_t op2_len = op2_val->data.x_struct.fields[1]->data.x_bignum.data.x_uint; |
| 3429 | | uint64_t len = op1_len + op2_len; |
| 3430 | | bignum_init_unsigned(&len_field->data.x_bignum, len); |
| 3431 | | |
| 3432 | | ptr_field->ok = true; |
| 3433 | | ptr_field->data.x_ptr.ptr = allocate<ConstExprValue*>(len); |
| 3434 | | ptr_field->data.x_ptr.len = len; |
| 3435 | | |
| 3436 | | uint64_t i = 0; |
| 3437 | | for (uint64_t op1_i = 0; op1_i < op1_len; op1_i += 1, i += 1) { |
| 3438 | | ptr_field->data.x_ptr.ptr[i] = op1_val->data.x_struct.fields[0]->data.x_ptr.ptr[op1_i]; |
| 3439 | | } |
| 3440 | | for (uint64_t op2_i = 0; op2_i < op2_len; op2_i += 1, i += 1) { |
| 3441 | | ptr_field->data.x_ptr.ptr[i] = op2_val->data.x_struct.fields[0]->data.x_ptr.ptr[op2_i]; |
| 3443 | if (op1_type->id == TypeTableEntryIdArray) { |
| 3444 | uint64_t new_len = op1_type->data.array.len + op2_type->data.array.len; |
| 3445 | const_val->data.x_array.fields = allocate<ConstExprValue*>(new_len); |
| 3446 | uint64_t next_index = 0; |
| 3447 | for (uint64_t i = 0; i < op1_type->data.array.len; i += 1, next_index += 1) { |
| 3448 | const_val->data.x_array.fields[next_index] = op1_val->data.x_array.fields[i]; |
| 3449 | } |
| 3450 | for (uint64_t i = 0; i < op2_type->data.array.len; i += 1, next_index += 1) { |
| 3451 | const_val->data.x_array.fields[next_index] = op2_val->data.x_array.fields[i]; |
| 3452 | } |
| 3453 | return get_array_type(g, child_type, new_len); |
| 3454 | } else if (op1_type->id == TypeTableEntryIdPointer) { |
| 3455 | if (!op1_val->data.x_ptr.is_c_str) { |
| 3456 | add_node_error(g, *op1, |
| 3457 | buf_sprintf("expected array or C string literal, got '%s'", |
| 3458 | buf_ptr(&op1_type->name))); |
| 3459 | return g->builtin_types.entry_invalid; |
| 3460 | } else if (!op2_val->data.x_ptr.is_c_str) { |
| 3461 | add_node_error(g, *op2, |
| 3462 | buf_sprintf("expected array or C string literal, got '%s'", |
| 3463 | buf_ptr(&op2_type->name))); |
| 3464 | return g->builtin_types.entry_invalid; |
| 3465 | } |
| 3466 | const_val->data.x_ptr.is_c_str = true; |
| 3467 | const_val->data.x_ptr.len = op1_val->data.x_ptr.len + op2_val->data.x_ptr.len - 1; |
| 3468 | const_val->data.x_ptr.ptr = allocate<ConstExprValue*>(const_val->data.x_ptr.len); |
| 3469 | uint64_t next_index = 0; |
| 3470 | for (uint64_t i = 0; i < op1_val->data.x_ptr.len - 1; i += 1, next_index += 1) { |
| 3471 | const_val->data.x_ptr.ptr[next_index] = op1_val->data.x_ptr.ptr[i]; |
| 3472 | } |
| 3473 | for (uint64_t i = 0; i < op2_val->data.x_ptr.len; i += 1, next_index += 1) { |
| 3474 | const_val->data.x_ptr.ptr[next_index] = op2_val->data.x_ptr.ptr[i]; |
| 3475 | } |
| 3476 | return op1_type; |
| 3477 | } else { |
| 3478 | zig_unreachable(); |
| 3442 | 3479 | } |
| 3443 | | |
| 3444 | | return str_type; |
| 3445 | 3480 | } |
| 3446 | 3481 | case BinOpTypeArrayMult: |
| 3447 | 3482 | return analyze_array_mult(g, import, context, expected_type, node); |