authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2016-05-07 10:05:59-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2016-05-07 10:05:59-07:00
log8c79438f6b76f1ad4b4941cdb46ae1e7aa12ce14
tree266e5cc959092bfc6e0567916f37cd34fbd0f712
parentd5d5fd928c79df6e4060c7ad84068fcee28c2391

better array concatenation semantics

closes #87

9 files changed, 103 insertions(+), 44 deletions(-)

src/all_types.hpp+2-1
......@@ -54,6 +54,7 @@ struct ConstPtrValue {
5454 ConstExprValue **ptr;
5555 // len should almost always be 1. exceptions include C strings
5656 uint64_t len;
57 bool is_c_str;
5758};
5859
5960struct ConstErrValue {
......@@ -341,7 +342,7 @@ enum BinOpType {
341342 BinOpTypeDiv,
342343 BinOpTypeMod,
343344 BinOpTypeUnwrapMaybe,
344 BinOpTypeStrCat,
345 BinOpTypeArrayCat,
345346 BinOpTypeArrayMult,
346347};
347348
src/analyze.cpp+70-35
......@@ -2770,6 +2770,7 @@ static TypeTableEntry *resolve_expr_const_val_as_c_string_lit(CodeGen *g, AstNod
27702770 int len_with_null = buf_len(str) + 1;
27712771 expr->const_val.data.x_ptr.ptr = allocate<ConstExprValue*>(len_with_null);
27722772 expr->const_val.data.x_ptr.len = len_with_null;
2773 expr->const_val.data.x_ptr.is_c_str = true;
27732774
27742775 ConstExprValue *all_chars = allocate<ConstExprValue>(len_with_null);
27752776 for (int i = 0; i < buf_len(str); i += 1) {
......@@ -2974,7 +2975,7 @@ static bool is_op_allowed(TypeTableEntry *type, BinOpType op) {
29742975 case BinOpTypeDiv:
29752976 case BinOpTypeMod:
29762977 case BinOpTypeUnwrapMaybe:
2977 case BinOpTypeStrCat:
2978 case BinOpTypeArrayCat:
29782979 case BinOpTypeArrayMult:
29792980 zig_unreachable();
29802981 }
......@@ -3379,19 +3380,42 @@ static TypeTableEntry *analyze_bin_op_expr(CodeGen *g, ImportTableEntry *import,
33793380 return g->builtin_types.entry_invalid;
33803381 }
33813382 }
3382 case BinOpTypeStrCat:
3383 case BinOpTypeArrayCat:
33833384 {
33843385 AstNode **op1 = node->data.bin_op_expr.op1->parent_field;
33853386 AstNode **op2 = node->data.bin_op_expr.op2->parent_field;
33863387
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 }
33883402
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);
33913404
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)));
33953419 return g->builtin_types.entry_invalid;
33963420 }
33973421
......@@ -3407,41 +3431,52 @@ static TypeTableEntry *analyze_bin_op_expr(CodeGen *g, ImportTableEntry *import,
34073431 bad_node = nullptr;
34083432 }
34093433 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"));
34113435 return g->builtin_types.entry_invalid;
34123436 }
3437
34133438 ConstExprValue *const_val = &get_resolved_expr(node)->const_val;
34143439 const_val->ok = true;
34153440 const_val->depends_on_compile_var = op1_val->depends_on_compile_var ||
34163441 op2_val->depends_on_compile_var;
34173442
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();
34423479 }
3443
3444 return str_type;
34453480 }
34463481 case BinOpTypeArrayMult:
34473482 return analyze_array_mult(g, import, context, expected_type, node);
src/ast_render.cpp+1-1
......@@ -37,7 +37,7 @@ static const char *bin_op_str(BinOpType bin_op) {
3737 case BinOpTypeAssignBoolAnd: return "&&=";
3838 case BinOpTypeAssignBoolOr: return "||=";
3939 case BinOpTypeUnwrapMaybe: return "??";
40 case BinOpTypeStrCat: return "++";
40 case BinOpTypeArrayCat: return "++";
4141 case BinOpTypeArrayMult: return "**";
4242 }
4343 zig_unreachable();
src/codegen.cpp+2-2
......@@ -1665,7 +1665,7 @@ static LLVMValueRef gen_arithmetic_bin_op(CodeGen *g, AstNode *source_node,
16651665 case BinOpTypeAssignBoolAnd:
16661666 case BinOpTypeAssignBoolOr:
16671667 case BinOpTypeUnwrapMaybe:
1668 case BinOpTypeStrCat:
1668 case BinOpTypeArrayCat:
16691669 case BinOpTypeArrayMult:
16701670 zig_unreachable();
16711671 }
......@@ -1972,7 +1972,7 @@ static LLVMValueRef gen_unwrap_maybe_expr(CodeGen *g, AstNode *node) {
19721972static LLVMValueRef gen_bin_op_expr(CodeGen *g, AstNode *node) {
19731973 switch (node->data.bin_op_expr.bin_op) {
19741974 case BinOpTypeInvalid:
1975 case BinOpTypeStrCat:
1975 case BinOpTypeArrayCat:
19761976 case BinOpTypeArrayMult:
19771977 zig_unreachable();
19781978 case BinOpTypeAssign:
src/eval.cpp+2-2
......@@ -296,7 +296,7 @@ int eval_const_expr_bin_op(ConstExprValue *op1_val, TypeTableEntry *op1_type,
296296 return eval_const_expr_bin_op_bignum(op1_val, op2_val, out_val, bignum_mod, op1_type);
297297 case BinOpTypeUnwrapMaybe:
298298 zig_panic("TODO");
299 case BinOpTypeStrCat:
299 case BinOpTypeArrayCat:
300300 case BinOpTypeArrayMult:
301301 case BinOpTypeInvalid:
302302 zig_unreachable();
......@@ -345,7 +345,7 @@ static bool eval_bin_op_expr(EvalFn *ef, AstNode *node, ConstExprValue *out_val)
345345 case BinOpTypeDiv:
346346 case BinOpTypeMod:
347347 case BinOpTypeUnwrapMaybe:
348 case BinOpTypeStrCat:
348 case BinOpTypeArrayCat:
349349 case BinOpTypeArrayMult:
350350 break;
351351 case BinOpTypeInvalid:
src/parser.cpp+1-1
......@@ -1480,7 +1480,7 @@ static BinOpType tok_to_add_op(Token *token) {
14801480 switch (token->id) {
14811481 case TokenIdPlus: return BinOpTypeAdd;
14821482 case TokenIdDash: return BinOpTypeSub;
1483 case TokenIdPlusPlus: return BinOpTypeStrCat;
1483 case TokenIdPlusPlus: return BinOpTypeArrayCat;
14841484 default: return BinOpTypeInvalid;
14851485 }
14861486}
std/str.zig+3
......@@ -1,5 +1,8 @@
11const assert = @import("index.zig").assert;
22
3// fix https://github.com/andrewrk/zig/issues/140
4// and then make this able to run at compile time
5#static_eval_enable(false)
36pub fn len(ptr: &const u8) -> isize {
47 var count: isize = 0;
58 while (ptr[count] != 0; count += 1) {}
test/run_tests.cpp+8-2
......@@ -999,11 +999,17 @@ extern fn foo() -> i32;
999999const x = foo();
10001000 )SOURCE", 1, ".tmp_source.zig:3:11: error: global variable initializer requires constant expression");
10011001
1002 add_compile_fail_case("non compile time string concatenation", R"SOURCE(
1002 add_compile_fail_case("array concatenation with wrong type", R"SOURCE(
10031003fn f(s: []u8) -> []u8 {
10041004 s ++ "foo"
10051005}
1006 )SOURCE", 1, ".tmp_source.zig:3:5: error: string concatenation requires constant expression");
1006 )SOURCE", 1, ".tmp_source.zig:3:5: error: expected array or C string literal, got '[]u8'");
1007
1008 add_compile_fail_case("non compile time array concatenation", R"SOURCE(
1009fn f(s: [10]u8) -> []u8 {
1010 s ++ "foo"
1011}
1012 )SOURCE", 1, ".tmp_source.zig:3:5: error: array concatenation requires constant expression");
10071013
10081014 add_compile_fail_case("c_import with bogus include", R"SOURCE(
10091015const c = @c_import(@c_include("bogus.h"));
test/self_hosted.zig+14
......@@ -1551,3 +1551,17 @@ fn combine_non_wrap_with_wrap() {
15511551 assert(@typeof(c) == i32w);
15521552 assert(@typeof(d) == i32w);
15531553}
1554
1555#attribute("test")
1556fn c_string_concatenation() {
1557 const a = c"OK" ++ c" IT " ++ c"WORKED";
1558 const b = c"OK IT WORKED";
1559
1560 const len = str.len(b);
1561 const len_with_null = len + 1;
1562 {var i: i32 = 0; while (i < len_with_null; i += 1) {
1563 assert(a[i] == b[i]);
1564 }}
1565 assert(a[len] == 0);
1566 assert(b[len] == 0);
1567}