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 {...@@ -54,6 +54,7 @@ struct ConstPtrValue {
54 ConstExprValue **ptr;54 ConstExprValue **ptr;
55 // len should almost always be 1. exceptions include C strings55 // len should almost always be 1. exceptions include C strings
56 uint64_t len;56 uint64_t len;
57 bool is_c_str;
57};58};
5859
59struct ConstErrValue {60struct ConstErrValue {
...@@ -341,7 +342,7 @@ enum BinOpType {...@@ -341,7 +342,7 @@ enum BinOpType {
341 BinOpTypeDiv,342 BinOpTypeDiv,
342 BinOpTypeMod,343 BinOpTypeMod,
343 BinOpTypeUnwrapMaybe,344 BinOpTypeUnwrapMaybe,
344 BinOpTypeStrCat,345 BinOpTypeArrayCat,
345 BinOpTypeArrayMult,346 BinOpTypeArrayMult,
346};347};
347348
src/analyze.cpp+70-35
...@@ -2770,6 +2770,7 @@ static TypeTableEntry *resolve_expr_const_val_as_c_string_lit(CodeGen *g, AstNod...@@ -2770,6 +2770,7 @@ static TypeTableEntry *resolve_expr_const_val_as_c_string_lit(CodeGen *g, AstNod
2770 int len_with_null = buf_len(str) + 1;2770 int len_with_null = buf_len(str) + 1;
2771 expr->const_val.data.x_ptr.ptr = allocate<ConstExprValue*>(len_with_null);2771 expr->const_val.data.x_ptr.ptr = allocate<ConstExprValue*>(len_with_null);
2772 expr->const_val.data.x_ptr.len = len_with_null;2772 expr->const_val.data.x_ptr.len = len_with_null;
2773 expr->const_val.data.x_ptr.is_c_str = true;
27732774
2774 ConstExprValue *all_chars = allocate<ConstExprValue>(len_with_null);2775 ConstExprValue *all_chars = allocate<ConstExprValue>(len_with_null);
2775 for (int i = 0; i < buf_len(str); i += 1) {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,7 +2975,7 @@ static bool is_op_allowed(TypeTableEntry *type, BinOpType op) {
2974 case BinOpTypeDiv:2975 case BinOpTypeDiv:
2975 case BinOpTypeMod:2976 case BinOpTypeMod:
2976 case BinOpTypeUnwrapMaybe:2977 case BinOpTypeUnwrapMaybe:
2977 case BinOpTypeStrCat:2978 case BinOpTypeArrayCat:
2978 case BinOpTypeArrayMult:2979 case BinOpTypeArrayMult:
2979 zig_unreachable();2980 zig_unreachable();
2980 }2981 }
...@@ -3379,19 +3380,42 @@ static TypeTableEntry *analyze_bin_op_expr(CodeGen *g, ImportTableEntry *import,...@@ -3379,19 +3380,42 @@ static TypeTableEntry *analyze_bin_op_expr(CodeGen *g, ImportTableEntry *import,
3379 return g->builtin_types.entry_invalid;3380 return g->builtin_types.entry_invalid;
3380 }3381 }
3381 }3382 }
3382 case BinOpTypeStrCat:3383 case BinOpTypeArrayCat:
3383 {3384 {
3384 AstNode **op1 = node->data.bin_op_expr.op1->parent_field;3385 AstNode **op1 = node->data.bin_op_expr.op1->parent_field;
3385 AstNode **op2 = node->data.bin_op_expr.op2->parent_field;3386 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);3403 TypeTableEntry *op2_type = analyze_expression(g, import, context, nullptr, *op2);
3390 TypeTableEntry *op2_type = analyze_expression(g, import, context, str_type, *op2);
33913404
3392 if (op1_type->id == TypeTableEntryIdInvalid ||3405 if (op2_type->id == TypeTableEntryIdInvalid) {
3393 op2_type->id == TypeTableEntryIdInvalid)3406 return g->builtin_types.entry_invalid;
3394 {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 return g->builtin_types.entry_invalid;3419 return g->builtin_types.entry_invalid;
3396 }3420 }
33973421
...@@ -3407,41 +3431,52 @@ static TypeTableEntry *analyze_bin_op_expr(CodeGen *g, ImportTableEntry *import,...@@ -3407,41 +3431,52 @@ static TypeTableEntry *analyze_bin_op_expr(CodeGen *g, ImportTableEntry *import,
3407 bad_node = nullptr;3431 bad_node = nullptr;
3408 }3432 }
3409 if (bad_node) {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 return g->builtin_types.entry_invalid;3435 return g->builtin_types.entry_invalid;
3412 }3436 }
3437
3413 ConstExprValue *const_val = &get_resolved_expr(node)->const_val;3438 ConstExprValue *const_val = &get_resolved_expr(node)->const_val;
3414 const_val->ok = true;3439 const_val->ok = true;
3415 const_val->depends_on_compile_var = op1_val->depends_on_compile_var ||3440 const_val->depends_on_compile_var = op1_val->depends_on_compile_var ||
3416 op2_val->depends_on_compile_var;3441 op2_val->depends_on_compile_var;
34173442
3418 ConstExprValue *all_fields = allocate<ConstExprValue>(2);3443 if (op1_type->id == TypeTableEntryIdArray) {
3419 ConstExprValue *ptr_field = &all_fields[0];3444 uint64_t new_len = op1_type->data.array.len + op2_type->data.array.len;
3420 ConstExprValue *len_field = &all_fields[1];3445 const_val->data.x_array.fields = allocate<ConstExprValue*>(new_len);
34213446 uint64_t next_index = 0;
3422 const_val->data.x_struct.fields = allocate<ConstExprValue*>(2);3447 for (uint64_t i = 0; i < op1_type->data.array.len; i += 1, next_index += 1) {
3423 const_val->data.x_struct.fields[0] = ptr_field;3448 const_val->data.x_array.fields[next_index] = op1_val->data.x_array.fields[i];
3424 const_val->data.x_struct.fields[1] = len_field;3449 }
34253450 for (uint64_t i = 0; i < op2_type->data.array.len; i += 1, next_index += 1) {
3426 len_field->ok = true;3451 const_val->data.x_array.fields[next_index] = op2_val->data.x_array.fields[i];
3427 uint64_t op1_len = op1_val->data.x_struct.fields[1]->data.x_bignum.data.x_uint;3452 }
3428 uint64_t op2_len = op2_val->data.x_struct.fields[1]->data.x_bignum.data.x_uint;3453 return get_array_type(g, child_type, new_len);
3429 uint64_t len = op1_len + op2_len;3454 } else if (op1_type->id == TypeTableEntryIdPointer) {
3430 bignum_init_unsigned(&len_field->data.x_bignum, len);3455 if (!op1_val->data.x_ptr.is_c_str) {
34313456 add_node_error(g, *op1,
3432 ptr_field->ok = true;3457 buf_sprintf("expected array or C string literal, got '%s'",
3433 ptr_field->data.x_ptr.ptr = allocate<ConstExprValue*>(len);3458 buf_ptr(&op1_type->name)));
3434 ptr_field->data.x_ptr.len = len;3459 return g->builtin_types.entry_invalid;
34353460 } else if (!op2_val->data.x_ptr.is_c_str) {
3436 uint64_t i = 0;3461 add_node_error(g, *op2,
3437 for (uint64_t op1_i = 0; op1_i < op1_len; op1_i += 1, i += 1) {3462 buf_sprintf("expected array or C string literal, got '%s'",
3438 ptr_field->data.x_ptr.ptr[i] = op1_val->data.x_struct.fields[0]->data.x_ptr.ptr[op1_i];3463 buf_ptr(&op2_type->name)));
3439 }3464 return g->builtin_types.entry_invalid;
3440 for (uint64_t op2_i = 0; op2_i < op2_len; op2_i += 1, i += 1) {3465 }
3441 ptr_field->data.x_ptr.ptr[i] = op2_val->data.x_struct.fields[0]->data.x_ptr.ptr[op2_i];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 case BinOpTypeArrayMult:3481 case BinOpTypeArrayMult:
3447 return analyze_array_mult(g, import, context, expected_type, node);3482 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) {...@@ -37,7 +37,7 @@ static const char *bin_op_str(BinOpType bin_op) {
37 case BinOpTypeAssignBoolAnd: return "&&=";37 case BinOpTypeAssignBoolAnd: return "&&=";
38 case BinOpTypeAssignBoolOr: return "||=";38 case BinOpTypeAssignBoolOr: return "||=";
39 case BinOpTypeUnwrapMaybe: return "??";39 case BinOpTypeUnwrapMaybe: return "??";
40 case BinOpTypeStrCat: return "++";40 case BinOpTypeArrayCat: return "++";
41 case BinOpTypeArrayMult: return "**";41 case BinOpTypeArrayMult: return "**";
42 }42 }
43 zig_unreachable();43 zig_unreachable();
src/codegen.cpp+2-2
...@@ -1665,7 +1665,7 @@ static LLVMValueRef gen_arithmetic_bin_op(CodeGen *g, AstNode *source_node,...@@ -1665,7 +1665,7 @@ static LLVMValueRef gen_arithmetic_bin_op(CodeGen *g, AstNode *source_node,
1665 case BinOpTypeAssignBoolAnd:1665 case BinOpTypeAssignBoolAnd:
1666 case BinOpTypeAssignBoolOr:1666 case BinOpTypeAssignBoolOr:
1667 case BinOpTypeUnwrapMaybe:1667 case BinOpTypeUnwrapMaybe:
1668 case BinOpTypeStrCat:1668 case BinOpTypeArrayCat:
1669 case BinOpTypeArrayMult:1669 case BinOpTypeArrayMult:
1670 zig_unreachable();1670 zig_unreachable();
1671 }1671 }
...@@ -1972,7 +1972,7 @@ static LLVMValueRef gen_unwrap_maybe_expr(CodeGen *g, AstNode *node) {...@@ -1972,7 +1972,7 @@ static LLVMValueRef gen_unwrap_maybe_expr(CodeGen *g, AstNode *node) {
1972static LLVMValueRef gen_bin_op_expr(CodeGen *g, AstNode *node) {1972static LLVMValueRef gen_bin_op_expr(CodeGen *g, AstNode *node) {
1973 switch (node->data.bin_op_expr.bin_op) {1973 switch (node->data.bin_op_expr.bin_op) {
1974 case BinOpTypeInvalid:1974 case BinOpTypeInvalid:
1975 case BinOpTypeStrCat:1975 case BinOpTypeArrayCat:
1976 case BinOpTypeArrayMult:1976 case BinOpTypeArrayMult:
1977 zig_unreachable();1977 zig_unreachable();
1978 case BinOpTypeAssign:1978 case BinOpTypeAssign:
src/eval.cpp+2-2
...@@ -296,7 +296,7 @@ int eval_const_expr_bin_op(ConstExprValue *op1_val, TypeTableEntry *op1_type,...@@ -296,7 +296,7 @@ int eval_const_expr_bin_op(ConstExprValue *op1_val, TypeTableEntry *op1_type,
296 return eval_const_expr_bin_op_bignum(op1_val, op2_val, out_val, bignum_mod, op1_type);296 return eval_const_expr_bin_op_bignum(op1_val, op2_val, out_val, bignum_mod, op1_type);
297 case BinOpTypeUnwrapMaybe:297 case BinOpTypeUnwrapMaybe:
298 zig_panic("TODO");298 zig_panic("TODO");
299 case BinOpTypeStrCat:299 case BinOpTypeArrayCat:
300 case BinOpTypeArrayMult:300 case BinOpTypeArrayMult:
301 case BinOpTypeInvalid:301 case BinOpTypeInvalid:
302 zig_unreachable();302 zig_unreachable();
...@@ -345,7 +345,7 @@ static bool eval_bin_op_expr(EvalFn *ef, AstNode *node, ConstExprValue *out_val)...@@ -345,7 +345,7 @@ static bool eval_bin_op_expr(EvalFn *ef, AstNode *node, ConstExprValue *out_val)
345 case BinOpTypeDiv:345 case BinOpTypeDiv:
346 case BinOpTypeMod:346 case BinOpTypeMod:
347 case BinOpTypeUnwrapMaybe:347 case BinOpTypeUnwrapMaybe:
348 case BinOpTypeStrCat:348 case BinOpTypeArrayCat:
349 case BinOpTypeArrayMult:349 case BinOpTypeArrayMult:
350 break;350 break;
351 case BinOpTypeInvalid:351 case BinOpTypeInvalid:
src/parser.cpp+1-1
...@@ -1480,7 +1480,7 @@ static BinOpType tok_to_add_op(Token *token) {...@@ -1480,7 +1480,7 @@ static BinOpType tok_to_add_op(Token *token) {
1480 switch (token->id) {1480 switch (token->id) {
1481 case TokenIdPlus: return BinOpTypeAdd;1481 case TokenIdPlus: return BinOpTypeAdd;
1482 case TokenIdDash: return BinOpTypeSub;1482 case TokenIdDash: return BinOpTypeSub;
1483 case TokenIdPlusPlus: return BinOpTypeStrCat;1483 case TokenIdPlusPlus: return BinOpTypeArrayCat;
1484 default: return BinOpTypeInvalid;1484 default: return BinOpTypeInvalid;
1485 }1485 }
1486}1486}
std/str.zig+3
...@@ -1,5 +1,8 @@...@@ -1,5 +1,8 @@
1const assert = @import("index.zig").assert;1const 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)
3pub fn len(ptr: &const u8) -> isize {6pub fn len(ptr: &const u8) -> isize {
4 var count: isize = 0;7 var count: isize = 0;
5 while (ptr[count] != 0; count += 1) {}8 while (ptr[count] != 0; count += 1) {}
test/run_tests.cpp+8-2
...@@ -999,11 +999,17 @@ extern fn foo() -> i32;...@@ -999,11 +999,17 @@ extern fn foo() -> i32;
999const x = foo();999const x = foo();
1000 )SOURCE", 1, ".tmp_source.zig:3:11: error: global variable initializer requires constant expression");1000 )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(
1003fn f(s: []u8) -> []u8 {1003fn f(s: []u8) -> []u8 {
1004 s ++ "foo"1004 s ++ "foo"
1005}1005}
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
1008 add_compile_fail_case("c_import with bogus include", R"SOURCE(1014 add_compile_fail_case("c_import with bogus include", R"SOURCE(
1009const c = @c_import(@c_include("bogus.h"));1015const c = @c_import(@c_include("bogus.h"));
test/self_hosted.zig+14
...@@ -1551,3 +1551,17 @@ fn combine_non_wrap_with_wrap() {...@@ -1551,3 +1551,17 @@ fn combine_non_wrap_with_wrap() {
1551 assert(@typeof(c) == i32w);1551 assert(@typeof(c) == i32w);
1552 assert(@typeof(d) == i32w);1552 assert(@typeof(d) == i32w);
1553}1553}
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}