Skip to content

Commit 96164ce

Browse files
committedJun 4, 2018
disallow single-item pointer indexing
add pointer arithmetic for unknown length pointer
·
0.15.10.3.0
1 parent 4c27312 commit 96164ce

35 files changed

+584
-443
lines changed
 

‎doc/langref.html.in‎

Lines changed: 28 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -458,7 +458,7 @@ test "string literals" {
458458

459459
// A C string literal is a null terminated pointer.
460460
const null_terminated_bytes = c"hello";
461-
assert(@typeOf(null_terminated_bytes) == *const u8);
461+
assert(@typeOf(null_terminated_bytes) == [*]const u8);
462462
assert(null_terminated_bytes[5] == 0);
463463
}
464464
{#code_end#}
@@ -547,7 +547,7 @@ const c_string_literal =
547547
;
548548
{#code_end#}
549549
<p>
550-
In this example the variable <code>c_string_literal</code> has type <code>*const char</code> and
550+
In this example the variable <code>c_string_literal</code> has type <code>[*]const char</code> and
551551
has a terminating null byte.
552552
</p>
553553
{#see_also|@embedFile#}
@@ -1288,7 +1288,7 @@ const assert = @import("std").debug.assert;
12881288
const mem = @import("std").mem;
12891289

12901290
// array literal
1291-
const message = []u8{'h', 'e', 'l', 'l', 'o'};
1291+
const message = []u8{ 'h', 'e', 'l', 'l', 'o' };
12921292

12931293
// get the size of an array
12941294
comptime {
@@ -1324,11 +1324,11 @@ test "modify an array" {
13241324

13251325
// array concatenation works if the values are known
13261326
// at compile time
1327-
const part_one = []i32{1, 2, 3, 4};
1328-
const part_two = []i32{5, 6, 7, 8};
1327+
const part_one = []i32{ 1, 2, 3, 4 };
1328+
const part_two = []i32{ 5, 6, 7, 8 };
13291329
const all_of_it = part_one ++ part_two;
13301330
comptime {
1331-
assert(mem.eql(i32, all_of_it, []i32{1,2,3,4,5,6,7,8}));
1331+
assert(mem.eql(i32, all_of_it, []i32{ 1, 2, 3, 4, 5, 6, 7, 8 }));
13321332
}
13331333

13341334
// remember that string literals are arrays
@@ -1357,7 +1357,7 @@ comptime {
13571357
var fancy_array = init: {
13581358
var initial_value: [10]Point = undefined;
13591359
for (initial_value) |*pt, i| {
1360-
pt.* = Point {
1360+
pt.* = Point{
13611361
.x = i32(i),
13621362
.y = i32(i) * 2,
13631363
};
@@ -1377,7 +1377,7 @@ test "compile-time array initalization" {
13771377
// call a function to initialize an array
13781378
var more_points = []Point{makePoint(3)} ** 10;
13791379
fn makePoint(x: i32) Point {
1380-
return Point {
1380+
return Point{
13811381
.x = x,
13821382
.y = x * 2,
13831383
};
@@ -1414,25 +1414,24 @@ test "address of syntax" {
14141414
}
14151415

14161416
test "pointer array access" {
1417-
// Pointers do not support pointer arithmetic. If you
1418-
// need such a thing, use array index syntax:
1417+
// Taking an address of an individual element gives a
1418+
// pointer to a single item. This kind of pointer
1419+
// does not support pointer arithmetic.
14191420

14201421
var array = []u8{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
1421-
const ptr = &array[1];
1422+
const ptr = &array[2];
1423+
assert(@typeOf(ptr) == *u8);
14221424

14231425
assert(array[2] == 3);
1424-
ptr[1] += 1;
1426+
ptr.* += 1;
14251427
assert(array[2] == 4);
14261428
}
14271429

14281430
test "pointer slicing" {
14291431
// In Zig, we prefer using slices over null-terminated pointers.
1430-
// You can turn a pointer into a slice using slice syntax:
1432+
// You can turn an array into a slice using slice syntax:
14311433
var array = []u8{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
1432-
const ptr = &array[1];
1433-
const slice = ptr[1..3];
1434-
1435-
assert(slice.ptr == &ptr[1]);
1434+
const slice = array[2..4];
14361435
assert(slice.len == 2);
14371436

14381437
// Slices have bounds checking and are therefore protected
@@ -1622,18 +1621,27 @@ fn foo(bytes: []u8) u32 {
16221621
const assert = @import("std").debug.assert;
16231622

16241623
test "basic slices" {
1625-
var array = []i32{1, 2, 3, 4};
1624+
var array = []i32{ 1, 2, 3, 4 };
16261625
// A slice is a pointer and a length. The difference between an array and
16271626
// a slice is that the array's length is part of the type and known at
16281627
// compile-time, whereas the slice's length is known at runtime.
16291628
// Both can be accessed with the `len` field.
16301629
const slice = array[0..array.len];
1631-
assert(slice.ptr == &array[0]);
1630+
assert(&slice[0] == &array[0]);
16321631
assert(slice.len == array.len);
16331632

1633+
// Using the address-of operator on a slice gives a pointer to a single
1634+
// item, while using the `ptr` field gives an unknown length pointer.
1635+
assert(@typeOf(slice.ptr) == [*]i32);
1636+
assert(@typeOf(&slice[0]) == *i32);
1637+
assert(@ptrToInt(slice.ptr) == @ptrToInt(&slice[0]));
1638+
16341639
// Slices have array bounds checking. If you try to access something out
16351640
// of bounds, you'll get a safety check failure:
16361641
slice[10] += 1;
1642+
1643+
// Note that `slice.ptr` does not invoke safety checking, while `&slice[0]`
1644+
// asserts that the slice has len >= 1.
16371645
}
16381646
{#code_end#}
16391647
<p>This is one reason we prefer slices to pointers.</p>
@@ -5937,7 +5945,7 @@ pub const __zig_test_fn_slice = {}; // overwritten later
59375945
{#header_open|C String Literals#}
59385946
{#code_begin|exe#}
59395947
{#link_libc#}
5940-
extern fn puts(*const u8) void;
5948+
extern fn puts([*]const u8) void;
59415949

59425950
pub fn main() void {
59435951
puts(c"this has a null terminator");

‎src/all_types.hpp‎

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -974,8 +974,14 @@ struct FnTypeId {
974974
uint32_t fn_type_id_hash(FnTypeId*);
975975
bool fn_type_id_eql(FnTypeId *a, FnTypeId *b);
976976

977+
enum PtrLen {
978+
PtrLenUnknown,
979+
PtrLenSingle,
980+
};
981+
977982
struct TypeTableEntryPointer {
978983
TypeTableEntry *child_type;
984+
PtrLen ptr_len;
979985
bool is_const;
980986
bool is_volatile;
981987
uint32_t alignment;
@@ -1397,6 +1403,7 @@ struct TypeId {
13971403
union {
13981404
struct {
13991405
TypeTableEntry *child_type;
1406+
PtrLen ptr_len;
14001407
bool is_const;
14011408
bool is_volatile;
14021409
uint32_t alignment;
@@ -2268,6 +2275,7 @@ struct IrInstructionElemPtr {
22682275

22692276
IrInstruction *array_ptr;
22702277
IrInstruction *elem_index;
2278+
PtrLen ptr_len;
22712279
bool is_const;
22722280
bool safety_check_on;
22732281
};
@@ -2419,6 +2427,7 @@ struct IrInstructionPtrType {
24192427
IrInstruction *child_type;
24202428
uint32_t bit_offset_start;
24212429
uint32_t bit_offset_end;
2430+
PtrLen ptr_len;
24222431
bool is_const;
24232432
bool is_volatile;
24242433
};

‎src/analyze.cpp‎

Lines changed: 36 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -381,21 +381,22 @@ TypeTableEntry *get_promise_type(CodeGen *g, TypeTableEntry *result_type) {
381381
}
382382

383383
TypeTableEntry *get_pointer_to_type_extra(CodeGen *g, TypeTableEntry *child_type, bool is_const,
384-
bool is_volatile, uint32_t byte_alignment, uint32_t bit_offset, uint32_t unaligned_bit_count)
384+
bool is_volatile, PtrLen ptr_len, uint32_t byte_alignment, uint32_t bit_offset, uint32_t unaligned_bit_count)
385385
{
386386
assert(!type_is_invalid(child_type));
387387

388388
TypeId type_id = {};
389389
TypeTableEntry **parent_pointer = nullptr;
390390
uint32_t abi_alignment = get_abi_alignment(g, child_type);
391-
if (unaligned_bit_count != 0 || is_volatile || byte_alignment != abi_alignment) {
391+
if (unaligned_bit_count != 0 || is_volatile || byte_alignment != abi_alignment || ptr_len != PtrLenSingle) {
392392
type_id.id = TypeTableEntryIdPointer;
393393
type_id.data.pointer.child_type = child_type;
394394
type_id.data.pointer.is_const = is_const;
395395
type_id.data.pointer.is_volatile = is_volatile;
396396
type_id.data.pointer.alignment = byte_alignment;
397397
type_id.data.pointer.bit_offset = bit_offset;
398398
type_id.data.pointer.unaligned_bit_count = unaligned_bit_count;
399+
type_id.data.pointer.ptr_len = ptr_len;
399400

400401
auto existing_entry = g->type_table.maybe_get(type_id);
401402
if (existing_entry)
@@ -414,16 +415,17 @@ TypeTableEntry *get_pointer_to_type_extra(CodeGen *g, TypeTableEntry *child_type
414415
TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdPointer);
415416
entry->is_copyable = true;
416417

418+
const char *star_str = ptr_len == PtrLenSingle ? "*" : "[*]";
417419
const char *const_str = is_const ? "const " : "";
418420
const char *volatile_str = is_volatile ? "volatile " : "";
419421
buf_resize(&entry->name, 0);
420422
if (unaligned_bit_count == 0 && byte_alignment == abi_alignment) {
421-
buf_appendf(&entry->name, "*%s%s%s", const_str, volatile_str, buf_ptr(&child_type->name));
423+
buf_appendf(&entry->name, "%s%s%s%s", star_str, const_str, volatile_str, buf_ptr(&child_type->name));
422424
} else if (unaligned_bit_count == 0) {
423-
buf_appendf(&entry->name, "*align(%" PRIu32 ") %s%s%s", byte_alignment,
425+
buf_appendf(&entry->name, "%salign(%" PRIu32 ") %s%s%s", star_str, byte_alignment,
424426
const_str, volatile_str, buf_ptr(&child_type->name));
425427
} else {
426-
buf_appendf(&entry->name, "*align(%" PRIu32 ":%" PRIu32 ":%" PRIu32 ") %s%s%s", byte_alignment,
428+
buf_appendf(&entry->name, "%salign(%" PRIu32 ":%" PRIu32 ":%" PRIu32 ") %s%s%s", star_str, byte_alignment,
427429
bit_offset, bit_offset + unaligned_bit_count, const_str, volatile_str, buf_ptr(&child_type->name));
428430
}
429431

@@ -433,7 +435,9 @@ TypeTableEntry *get_pointer_to_type_extra(CodeGen *g, TypeTableEntry *child_type
433435

434436
if (!entry->zero_bits) {
435437
assert(byte_alignment > 0);
436-
if (is_const || is_volatile || unaligned_bit_count != 0 || byte_alignment != abi_alignment) {
438+
if (is_const || is_volatile || unaligned_bit_count != 0 || byte_alignment != abi_alignment ||
439+
ptr_len != PtrLenSingle)
440+
{
437441
TypeTableEntry *peer_type = get_pointer_to_type(g, child_type, false);
438442
entry->type_ref = peer_type->type_ref;
439443
entry->di_type = peer_type->di_type;
@@ -451,6 +455,7 @@ TypeTableEntry *get_pointer_to_type_extra(CodeGen *g, TypeTableEntry *child_type
451455
entry->di_type = g->builtin_types.entry_void->di_type;
452456
}
453457

458+
entry->data.pointer.ptr_len = ptr_len;
454459
entry->data.pointer.child_type = child_type;
455460
entry->data.pointer.is_const = is_const;
456461
entry->data.pointer.is_volatile = is_volatile;
@@ -467,7 +472,8 @@ TypeTableEntry *get_pointer_to_type_extra(CodeGen *g, TypeTableEntry *child_type
467472
}
468473

469474
TypeTableEntry *get_pointer_to_type(CodeGen *g, TypeTableEntry *child_type, bool is_const) {
470-
return get_pointer_to_type_extra(g, child_type, is_const, false, get_abi_alignment(g, child_type), 0, 0);
475+
return get_pointer_to_type_extra(g, child_type, is_const, false, PtrLenSingle,
476+
get_abi_alignment(g, child_type), 0, 0);
471477
}
472478

473479
TypeTableEntry *get_promise_frame_type(CodeGen *g, TypeTableEntry *return_type) {
@@ -757,6 +763,7 @@ static void slice_type_common_init(CodeGen *g, TypeTableEntry *pointer_type, Typ
757763

758764
TypeTableEntry *get_slice_type(CodeGen *g, TypeTableEntry *ptr_type) {
759765
assert(ptr_type->id == TypeTableEntryIdPointer);
766+
assert(ptr_type->data.pointer.ptr_len == PtrLenUnknown);
760767

761768
TypeTableEntry **parent_pointer = &ptr_type->data.pointer.slice_parent;
762769
if (*parent_pointer) {
@@ -768,14 +775,16 @@ TypeTableEntry *get_slice_type(CodeGen *g, TypeTableEntry *ptr_type) {
768775

769776
// replace the & with [] to go from a ptr type name to a slice type name
770777
buf_resize(&entry->name, 0);
771-
buf_appendf(&entry->name, "[]%s", buf_ptr(&ptr_type->name) + 1);
778+
size_t name_offset = (ptr_type->data.pointer.ptr_len == PtrLenSingle) ? 1 : 3;
779+
buf_appendf(&entry->name, "[]%s", buf_ptr(&ptr_type->name) + name_offset);
772780

773781
TypeTableEntry *child_type = ptr_type->data.pointer.child_type;
774-
uint32_t abi_alignment;
782+
uint32_t abi_alignment = get_abi_alignment(g, child_type);
775783
if (ptr_type->data.pointer.is_const || ptr_type->data.pointer.is_volatile ||
776-
ptr_type->data.pointer.alignment != (abi_alignment = get_abi_alignment(g, child_type)))
784+
ptr_type->data.pointer.alignment != abi_alignment)
777785
{
778-
TypeTableEntry *peer_ptr_type = get_pointer_to_type(g, child_type, false);
786+
TypeTableEntry *peer_ptr_type = get_pointer_to_type_extra(g, child_type, false, false,
787+
PtrLenUnknown, abi_alignment, 0, 0);
779788
TypeTableEntry *peer_slice_type = get_slice_type(g, peer_ptr_type);
780789

781790
slice_type_common_init(g, ptr_type, entry);
@@ -799,9 +808,11 @@ TypeTableEntry *get_slice_type(CodeGen *g, TypeTableEntry *ptr_type) {
799808
if (child_ptr_type->data.pointer.is_const || child_ptr_type->data.pointer.is_volatile ||
800809
child_ptr_type->data.pointer.alignment != get_abi_alignment(g, grand_child_type))
801810
{
802-
TypeTableEntry *bland_child_ptr_type = get_pointer_to_type(g, grand_child_type, false);
811+
TypeTableEntry *bland_child_ptr_type = get_pointer_to_type_extra(g, grand_child_type, false, false,
812+
PtrLenUnknown, get_abi_alignment(g, grand_child_type), 0, 0);
803813
TypeTableEntry *bland_child_slice = get_slice_type(g, bland_child_ptr_type);
804-
TypeTableEntry *peer_ptr_type = get_pointer_to_type(g, bland_child_slice, false);
814+
TypeTableEntry *peer_ptr_type = get_pointer_to_type_extra(g, bland_child_slice, false, false,
815+
PtrLenUnknown, get_abi_alignment(g, bland_child_slice), 0, 0);
805816
TypeTableEntry *peer_slice_type = get_slice_type(g, peer_ptr_type);
806817

807818
entry->type_ref = peer_slice_type->type_ref;
@@ -1284,7 +1295,8 @@ static bool analyze_const_align(CodeGen *g, Scope *scope, AstNode *node, uint32_
12841295
}
12851296

12861297
static bool analyze_const_string(CodeGen *g, Scope *scope, AstNode *node, Buf **out_buffer) {
1287-
TypeTableEntry *ptr_type = get_pointer_to_type(g, g->builtin_types.entry_u8, true);
1298+
TypeTableEntry *ptr_type = get_pointer_to_type_extra(g, g->builtin_types.entry_u8, true, false,
1299+
PtrLenUnknown, get_abi_alignment(g, g->builtin_types.entry_u8), 0, 0);
12881300
TypeTableEntry *str_type = get_slice_type(g, ptr_type);
12891301
IrInstruction *instr = analyze_const_value(g, scope, node, str_type, nullptr);
12901302
if (type_is_invalid(instr->value.type))
@@ -2954,7 +2966,8 @@ static void typecheck_panic_fn(CodeGen *g, FnTableEntry *panic_fn) {
29542966
if (fn_type_id->param_count != 2) {
29552967
return wrong_panic_prototype(g, proto_node, fn_type);
29562968
}
2957-
TypeTableEntry *const_u8_ptr = get_pointer_to_type(g, g->builtin_types.entry_u8, true);
2969+
TypeTableEntry *const_u8_ptr = get_pointer_to_type_extra(g, g->builtin_types.entry_u8, true, false,
2970+
PtrLenUnknown, get_abi_alignment(g, g->builtin_types.entry_u8), 0, 0);
29582971
TypeTableEntry *const_u8_slice = get_slice_type(g, const_u8_ptr);
29592972
if (fn_type_id->param_info[0].type != const_u8_slice) {
29602973
return wrong_panic_prototype(g, proto_node, fn_type);
@@ -4994,7 +5007,9 @@ void init_const_c_str_lit(CodeGen *g, ConstExprValue *const_val, Buf *str) {
49945007

49955008
// then make the pointer point to it
49965009
const_val->special = ConstValSpecialStatic;
4997-
const_val->type = get_pointer_to_type(g, g->builtin_types.entry_u8, true);
5010+
// TODO make this `[*]null u8` instead of `[*]u8`
5011+
const_val->type = get_pointer_to_type_extra(g, g->builtin_types.entry_u8, true, false,
5012+
PtrLenUnknown, get_abi_alignment(g, g->builtin_types.entry_u8), 0, 0);
49985013
const_val->data.x_ptr.special = ConstPtrSpecialBaseArray;
49995014
const_val->data.x_ptr.data.base_array.array_val = array_val;
50005015
const_val->data.x_ptr.data.base_array.elem_index = 0;
@@ -5135,7 +5150,9 @@ void init_const_slice(CodeGen *g, ConstExprValue *const_val, ConstExprValue *arr
51355150
{
51365151
assert(array_val->type->id == TypeTableEntryIdArray);
51375152

5138-
TypeTableEntry *ptr_type = get_pointer_to_type(g, array_val->type->data.array.child_type, is_const);
5153+
TypeTableEntry *ptr_type = get_pointer_to_type_extra(g, array_val->type->data.array.child_type,
5154+
is_const, false, PtrLenUnknown, get_abi_alignment(g, array_val->type->data.array.child_type),
5155+
0, 0);
51395156

51405157
const_val->special = ConstValSpecialStatic;
51415158
const_val->type = get_slice_type(g, ptr_type);
@@ -5759,6 +5776,7 @@ uint32_t type_id_hash(TypeId x) {
57595776
return hash_ptr(x.data.error_union.err_set_type) ^ hash_ptr(x.data.error_union.payload_type);
57605777
case TypeTableEntryIdPointer:
57615778
return hash_ptr(x.data.pointer.child_type) +
5779+
((x.data.pointer.ptr_len == PtrLenSingle) ? (uint32_t)1120226602 : (uint32_t)3200913342) +
57625780
(x.data.pointer.is_const ? (uint32_t)2749109194 : (uint32_t)4047371087) +
57635781
(x.data.pointer.is_volatile ? (uint32_t)536730450 : (uint32_t)1685612214) +
57645782
(((uint32_t)x.data.pointer.alignment) ^ (uint32_t)0x777fbe0e) +
@@ -5807,6 +5825,7 @@ bool type_id_eql(TypeId a, TypeId b) {
58075825

58085826
case TypeTableEntryIdPointer:
58095827
return a.data.pointer.child_type == b.data.pointer.child_type &&
5828+
a.data.pointer.ptr_len == b.data.pointer.ptr_len &&
58105829
a.data.pointer.is_const == b.data.pointer.is_const &&
58115830
a.data.pointer.is_volatile == b.data.pointer.is_volatile &&
58125831
a.data.pointer.alignment == b.data.pointer.alignment &&

‎src/analyze.hpp‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ ErrorMsg *add_error_note(CodeGen *g, ErrorMsg *parent_msg, AstNode *node, Buf *m
1616
TypeTableEntry *new_type_table_entry(TypeTableEntryId id);
1717
TypeTableEntry *get_pointer_to_type(CodeGen *g, TypeTableEntry *child_type, bool is_const);
1818
TypeTableEntry *get_pointer_to_type_extra(CodeGen *g, TypeTableEntry *child_type, bool is_const,
19-
bool is_volatile, uint32_t byte_alignment, uint32_t bit_offset, uint32_t unaligned_bit_count);
19+
bool is_volatile, PtrLen ptr_len, uint32_t byte_alignment, uint32_t bit_offset, uint32_t unaligned_bit_count);
2020
uint64_t type_size(CodeGen *g, TypeTableEntry *type_entry);
2121
uint64_t type_size_bits(CodeGen *g, TypeTableEntry *type_entry);
2222
TypeTableEntry **get_int_type_ptr(CodeGen *g, bool is_signed, uint32_t size_in_bits);

‎src/ast_render.cpp‎

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -625,7 +625,13 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) {
625625
case NodeTypePointerType:
626626
{
627627
if (!grouped) fprintf(ar->f, "(");
628-
fprintf(ar->f, "*");
628+
const char *star = "[*]";
629+
if (node->data.pointer_type.star_token != nullptr &&
630+
(node->data.pointer_type.star_token->id == TokenIdStar || node->data.pointer_type.star_token->id == TokenIdStarStar))
631+
{
632+
star = "*";
633+
}
634+
fprintf(ar->f, "%s", star);
629635
if (node->data.pointer_type.align_expr != nullptr) {
630636
fprintf(ar->f, "align(");
631637
render_node_grouped(ar, node->data.pointer_type.align_expr);

0 commit comments

Comments
 (0)