authorgravatar for me@robbielyman.comRobbie Lyman <me@robbielyman.com> 2026-09-09 14:38:09-04:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2026-10-09 23:14:28+02:00
log1df1f936b3f4b6f2e8e97248d5779cac1f60a911
tree25ba8d52a9cbe9beab28ed36b96c2dd73c9e3419
parentd69634c10d2e4ca4af834a0644136ac1af72cd26

feat(MultiArrayList): Pointer Stability

This commit builds on #19523 to add pointer stability to MultiArrayList, following the lead of the design for HashMap and ArrayList. Since initializing MultiArrayList with a struct literal is much more common within the compiler than for ArrayList, I have chosen to make the field a default init, although I think this is not the best design. Similar to ArrayList, I have chosen to interpret "pointer stability" broadly, meaning that existing pointers to (fields of) elements in the MultiArrayList continue to be valid pointers to the same object. Thus `appendAssumeCapacity()` does not invalidate pointers, but `pop()` and `swap()` do. Closes #19327.

2 files changed, 96 insertions(+), 5 deletions(-)

lib/std/multi_array_list.zig+95-5
...@@ -26,11 +26,13 @@ pub fn MultiArrayList(comptime T: type) type {...@@ -26,11 +26,13 @@ pub fn MultiArrayList(comptime T: type) type {
26 bytes: [*]u8 = undefined,26 bytes: [*]u8 = undefined,
27 len: usize = 0,27 len: usize = 0,
28 capacity: usize = 0,28 capacity: usize = 0,
29 pointer_stability: std.debug.SafetyLock = .{},
2930
30 pub const empty: Self = .{31 pub const empty: Self = .{
31 .bytes = undefined,32 .bytes = undefined,
32 .len = 0,33 .len = 0,
33 .capacity = 0,34 .capacity = 0,
35 .pointer_stability = .{},
34 };36 };
3537
36 /// Initialize with capacity to hold exactly `num` elements.38 /// Initialize with capacity to hold exactly `num` elements.
...@@ -80,13 +82,16 @@ pub fn MultiArrayList(comptime T: type) type {...@@ -80,13 +82,16 @@ pub fn MultiArrayList(comptime T: type) type {
80 ptrs: [field_names.len][*]u8,82 ptrs: [field_names.len][*]u8,
81 len: usize,83 len: usize,
82 capacity: usize,84 capacity: usize,
85 pointer_stability: std.debug.SafetyLock,
8386
84 pub const empty: Slice = .{87 pub const empty: Slice = .{
85 .ptrs = undefined,88 .ptrs = undefined,
86 .len = 0,89 .len = 0,
87 .capacity = 0,90 .capacity = 0,
91 .pointer_stability = .{},
88 };92 };
8993
94 /// The returned pointer may be invalidated by various operations to this Slice.
90 pub fn items(self: Slice, comptime field: Field) []FieldType(field) {95 pub fn items(self: Slice, comptime field: Field) []FieldType(field) {
91 const F = FieldType(field);96 const F = FieldType(field);
92 if (self.capacity == 0) {97 if (self.capacity == 0) {
...@@ -100,7 +105,9 @@ pub fn MultiArrayList(comptime T: type) type {...@@ -100,7 +105,9 @@ pub fn MultiArrayList(comptime T: type) type {
100 return casted_ptr[0..self.len];105 return casted_ptr[0..self.len];
101 }106 }
102107
108 /// Invalidates pre-existing pointers to fields of the element at index `index`.
103 pub fn set(self: *Slice, index: usize, elem: T) void {109 pub fn set(self: *Slice, index: usize, elem: T) void {
110 self.pointer_stability.assertUnlocked();
104 const e = switch (@typeInfo(T)) {111 const e = switch (@typeInfo(T)) {
105 .@"struct" => elem,112 .@"struct" => elem,
106 .@"union" => Elem.fromT(elem),113 .@"union" => Elem.fromT(elem),
...@@ -123,25 +130,31 @@ pub fn MultiArrayList(comptime T: type) type {...@@ -123,25 +130,31 @@ pub fn MultiArrayList(comptime T: type) type {
123 };130 };
124 }131 }
125132
133 /// Invalidates pre-existing pointers to fields of the element at indices `a` and `b`.
126 pub fn swap(self: Slice, a: usize, b: usize) void {134 pub fn swap(self: Slice, a: usize, b: usize) void {
135 self.pointer_stability.assertUnlocked();
127 inline for (@typeInfo(Field).@"enum".field_names) |field_name| {136 inline for (@typeInfo(Field).@"enum".field_names) |field_name| {
128 const its = self.items(@field(Field, field_name));137 const its = self.items(@field(Field, field_name));
129 std.mem.swap(@FieldType(T, field_name), &its[a], &its[b]);138 std.mem.swap(@FieldType(T, field_name), &its[a], &its[b]);
130 }139 }
131 }140 }
132141
142 /// The returned MultiArrayList inherits the locked/unlocked state of this Slice
133 pub fn toMultiArrayList(self: Slice) Self {143 pub fn toMultiArrayList(self: Slice) Self {
134 if (self.ptrs.len == 0 or self.capacity == 0) {144 if (self.ptrs.len == 0 or self.capacity == 0) {
135 return .{};145 return .empty;
136 }146 }
137 return .{147 return .{
138 .bytes = self.ptrs[sizes.fields[0]],148 .bytes = self.ptrs[sizes.fields[0]],
139 .len = self.len,149 .len = self.len,
140 .capacity = self.capacity,150 .capacity = self.capacity,
151 .pointer_stability = self.pointer_stability,
141 };152 };
142 }153 }
143154
155 /// Invalidates all element pointers
144 pub fn deinit(self: *Slice, gpa: Allocator) void {156 pub fn deinit(self: *Slice, gpa: Allocator) void {
157 self.pointer_stability.assertUnlocked();
145 var other = self.toMultiArrayList();158 var other = self.toMultiArrayList();
146 other.deinit(gpa);159 other.deinit(gpa);
147 self.* = undefined;160 self.* = undefined;
...@@ -160,9 +173,27 @@ pub fn MultiArrayList(comptime T: type) type {...@@ -160,9 +173,27 @@ pub fn MultiArrayList(comptime T: type) type {
160 .ptrs = ptrs,173 .ptrs = ptrs,
161 .len = len,174 .len = len,
162 .capacity = len,175 .capacity = len,
176 .pointer_stability = s.pointer_stability,
163 };177 };
164 }178 }
165179
180 /// Puts the Slice into a state where any method call that
181 /// would cause an existing item pointer to become invalidated will
182 /// instead trigger an assertion.
183 ///
184 /// `lockPointers` may be called multiple times. This allows multiple
185 /// independent users of the hash map to keep it locked simultaneously.
186 ///
187 /// `unlockPointers` returns the Slice to the previous state.
188 pub fn lockPointers(self: *Slice) void {
189 self.pointer_stability.lockShared();
190 }
191
192 /// Undoes one call to `lockPointers`.
193 pub fn unlockPointers(self: *Slice) void {
194 self.pointer_stability.unlockShared();
195 }
196
166 /// This function is used in the debugger pretty formatters in lib/lldb/ to fetch the197 /// This function is used in the debugger pretty formatters in lib/lldb/ to fetch the
167 /// child field order and entry type to facilitate fancy debug printing for this type.198 /// child field order and entry type to facilitate fancy debug printing for this type.
168 fn dbHelper(self: *Slice, child: *Elem, field: *Field, entry: *Entry) void {199 fn dbHelper(self: *Slice, child: *Elem, field: *Field, entry: *Entry) void {
...@@ -219,12 +250,32 @@ pub fn MultiArrayList(comptime T: type) type {...@@ -219,12 +250,32 @@ pub fn MultiArrayList(comptime T: type) type {
219 };250 };
220251
221 /// Release all allocated memory.252 /// Release all allocated memory.
253 /// Invalidates all element pointers.
222 pub fn deinit(self: *Self, gpa: Allocator) void {254 pub fn deinit(self: *Self, gpa: Allocator) void {
223 gpa.free(self.allocatedBytes());255 gpa.free(self.allocatedBytes());
224 self.* = undefined;256 self.* = undefined;
225 }257 }
226258
259 /// Puts the MultiArrayList into a state where any method call that
260 /// would cause an existing item pointer to become invalidated will
261 /// instead trigger an assertion.
262 ///
263 /// `lockPointers` may be called multiple times. This allows multiple
264 /// independent users of the hash map to keep it locked simultaneously.
265 ///
266 /// `unlockPointers` returns the MultiArrayList to the previous state.
267 pub fn lockPointers(self: *Self) void {
268 self.pointer_stability.lockShared();
269 }
270
271 /// Undoes one call to `lockPointers`.
272 pub fn unlockPointers(self: *Self) void {
273 self.pointer_stability.unlockShared();
274 }
275
227 /// The caller owns the returned memory. Empties this MultiArrayList.276 /// The caller owns the returned memory. Empties this MultiArrayList.
277 /// Does not invalidate element pointers.
278 /// The returned Slice inherits its locked/unlocked state from this MultiArrayList.
228 pub fn toOwnedSlice(self: *Self) Slice {279 pub fn toOwnedSlice(self: *Self) Slice {
229 const result = self.slice();280 const result = self.slice();
230 self.* = .empty;281 self.* = .empty;
...@@ -234,11 +285,14 @@ pub fn MultiArrayList(comptime T: type) type {...@@ -234,11 +285,14 @@ pub fn MultiArrayList(comptime T: type) type {
234 /// Compute pointers to the start of each field of the array.285 /// Compute pointers to the start of each field of the array.
235 /// If you need to access multiple fields, calling this may286 /// If you need to access multiple fields, calling this may
236 /// be more efficient than calling `items()` multiple times.287 /// be more efficient than calling `items()` multiple times.
288 /// Does not invalidate element pointers.
289 /// The returned Slice inherits its locked/unlocked state from this MultiArrayList.
237 pub fn slice(self: Self) Slice {290 pub fn slice(self: Self) Slice {
238 var result: Slice = .{291 var result: Slice = .{
239 .ptrs = undefined,292 .ptrs = undefined,
240 .len = self.len,293 .len = self.len,
241 .capacity = self.capacity,294 .capacity = self.capacity,
295 .pointer_stability = self.pointer_stability,
242 };296 };
243 var ptr: [*]u8 = self.bytes;297 var ptr: [*]u8 = self.bytes;
244 for (sizes.bytes, sizes.fields) |field_size, i| {298 for (sizes.bytes, sizes.fields) |field_size, i| {
...@@ -251,12 +305,15 @@ pub fn MultiArrayList(comptime T: type) type {...@@ -251,12 +305,15 @@ pub fn MultiArrayList(comptime T: type) type {
251 /// Get the slice of values for a specified field.305 /// Get the slice of values for a specified field.
252 /// If you need multiple fields, consider calling slice()306 /// If you need multiple fields, consider calling slice()
253 /// instead.307 /// instead.
308 /// The returned pointer may be invalidated by various operations to this MultiArrayList.
254 pub fn items(self: Self, comptime field: Field) []FieldType(field) {309 pub fn items(self: Self, comptime field: Field) []FieldType(field) {
255 return self.slice().items(field);310 return self.slice().items(field);
256 }311 }
257312
258 /// Overwrite one array element with new data.313 /// Overwrite one array element with new data.
314 /// Invalidates previously existing pointers to fields of that element.
259 pub fn set(self: *Self, index: usize, elem: T) void {315 pub fn set(self: *Self, index: usize, elem: T) void {
316 self.pointer_stability.assertUnlocked();
260 var slices = self.slice();317 var slices = self.slice();
261 slices.set(index, elem);318 slices.set(index, elem);
262 }319 }
...@@ -266,13 +323,17 @@ pub fn MultiArrayList(comptime T: type) type {...@@ -266,13 +323,17 @@ pub fn MultiArrayList(comptime T: type) type {
266 return self.slice().get(index);323 return self.slice().get(index);
267 }324 }
268325
326 /// Invalidates previously existing pointers to fields of the element
327 /// at indices `a` and `b`.
269 pub fn swap(self: Self, a: usize, b: usize) void {328 pub fn swap(self: Self, a: usize, b: usize) void {
329 self.pointer_stability.assertUnlocked();
270 return self.slice().swap(a, b);330 return self.slice().swap(a, b);
271 }331 }
272332
273 /// Extend the list by 1 element.333 /// Extend the list by 1 element.
274 ///334 ///
275 /// Allocates more memory as necessary.335 /// Allocates more memory as necessary.
336 /// Invalidates element pointers if more memory is needed.
276 pub fn append(self: *Self, gpa: Allocator, elem: T) Allocator.Error!void {337 pub fn append(self: *Self, gpa: Allocator, elem: T) Allocator.Error!void {
277 try self.ensureUnusedCapacity(gpa, 1);338 try self.ensureUnusedCapacity(gpa, 1);
278 self.appendAssumeCapacity(elem);339 self.appendAssumeCapacity(elem);
...@@ -281,6 +342,7 @@ pub fn MultiArrayList(comptime T: type) type {...@@ -281,6 +342,7 @@ pub fn MultiArrayList(comptime T: type) type {
281 /// Extend the list by 1 element.342 /// Extend the list by 1 element.
282 ///343 ///
283 /// Asserts that capacity is sufficient to hold an additional item.344 /// Asserts that capacity is sufficient to hold an additional item.
345 /// Never invalidates element pointers.
284 pub fn appendAssumeCapacity(self: *Self, elem: T) void {346 pub fn appendAssumeCapacity(self: *Self, elem: T) void {
285 assert(self.len < self.capacity);347 assert(self.len < self.capacity);
286 self.len += 1;348 self.len += 1;
...@@ -291,6 +353,7 @@ pub fn MultiArrayList(comptime T: type) type {...@@ -291,6 +353,7 @@ pub fn MultiArrayList(comptime T: type) type {
291 ///353 ///
292 /// If capacity is not sufficient to hold an additional354 /// If capacity is not sufficient to hold an additional
293 /// item, returns `error.OutOfMemory`.355 /// item, returns `error.OutOfMemory`.
356 /// Never invalidates element pointers.
294 pub fn appendBounded(self: *Self, elem: T) error{OutOfMemory}!void {357 pub fn appendBounded(self: *Self, elem: T) error{OutOfMemory}!void {
295 if (self.capacity - self.len < 1) return error.OutOfMemory;358 if (self.capacity - self.len < 1) return error.OutOfMemory;
296 return appendAssumeCapacity(self, elem);359 return appendAssumeCapacity(self, elem);
...@@ -300,6 +363,7 @@ pub fn MultiArrayList(comptime T: type) type {...@@ -300,6 +363,7 @@ pub fn MultiArrayList(comptime T: type) type {
300 /// index with uninitialized data.363 /// index with uninitialized data.
301 ///364 ///
302 /// Allocates more memory as necessary.365 /// Allocates more memory as necessary.
366 /// Invalidates element pointers if more memory is needed.
303 pub fn addOne(self: *Self, gpa: Allocator) Allocator.Error!usize {367 pub fn addOne(self: *Self, gpa: Allocator) Allocator.Error!usize {
304 try self.ensureUnusedCapacity(gpa, 1);368 try self.ensureUnusedCapacity(gpa, 1);
305 return self.addOneAssumeCapacity();369 return self.addOneAssumeCapacity();
...@@ -309,6 +373,7 @@ pub fn MultiArrayList(comptime T: type) type {...@@ -309,6 +373,7 @@ pub fn MultiArrayList(comptime T: type) type {
309 /// index with uninitialized data.373 /// index with uninitialized data.
310 ///374 ///
311 /// Asserts that capacity is sufficient to hold an additional item.375 /// Asserts that capacity is sufficient to hold an additional item.
376 /// Never invalidates element pointers.
312 pub fn addOneAssumeCapacity(self: *Self) usize {377 pub fn addOneAssumeCapacity(self: *Self) usize {
313 assert(self.len < self.capacity);378 assert(self.len < self.capacity);
314 const index = self.len;379 const index = self.len;
...@@ -321,6 +386,7 @@ pub fn MultiArrayList(comptime T: type) type {...@@ -321,6 +386,7 @@ pub fn MultiArrayList(comptime T: type) type {
321 ///386 ///
322 /// If capacity is not sufficient to hold an additional387 /// If capacity is not sufficient to hold an additional
323 /// item, returns `error.OutOfMemory`.388 /// item, returns `error.OutOfMemory`.
389 /// Never invalidates element pointers.
324 pub fn addOneBounded(self: *Self) error{OutOfMemory}!usize {390 pub fn addOneBounded(self: *Self) error{OutOfMemory}!usize {
325 if (self.capacity - self.len < 1) return error.OutOfMemory;391 if (self.capacity - self.len < 1) return error.OutOfMemory;
326 return addOneAssumeCapacity(self);392 return addOneAssumeCapacity(self);
...@@ -329,6 +395,7 @@ pub fn MultiArrayList(comptime T: type) type {...@@ -329,6 +395,7 @@ pub fn MultiArrayList(comptime T: type) type {
329 /// Remove and return the last element from the list, or return `null` if list is empty.395 /// Remove and return the last element from the list, or return `null` if list is empty.
330 /// Invalidates pointers to fields of the removed element.396 /// Invalidates pointers to fields of the removed element.
331 pub fn pop(self: *Self) ?T {397 pub fn pop(self: *Self) ?T {
398 self.pointer_stability.assertUnlocked();
332 if (self.len == 0) return null;399 if (self.len == 0) return null;
333 const val = self.get(self.len - 1);400 const val = self.get(self.len - 1);
334 self.len -= 1;401 self.len -= 1;
...@@ -340,6 +407,7 @@ pub fn MultiArrayList(comptime T: type) type {...@@ -340,6 +407,7 @@ pub fn MultiArrayList(comptime T: type) type {
340 /// sets the given index to the specified element.407 /// sets the given index to the specified element.
341 ///408 ///
342 /// Allocates more memory as necessary.409 /// Allocates more memory as necessary.
410 /// Invalidates pointers to shifted elements.
343 pub fn insert(self: *Self, gpa: Allocator, index: usize, elem: T) !void {411 pub fn insert(self: *Self, gpa: Allocator, index: usize, elem: T) !void {
344 try self.ensureUnusedCapacity(gpa, 1);412 try self.ensureUnusedCapacity(gpa, 1);
345 self.insertAssumeCapacity(index, elem);413 self.insertAssumeCapacity(index, elem);
...@@ -350,9 +418,11 @@ pub fn MultiArrayList(comptime T: type) type {...@@ -350,9 +418,11 @@ pub fn MultiArrayList(comptime T: type) type {
350 /// sets the given index to the specified element.418 /// sets the given index to the specified element.
351 ///419 ///
352 /// Asserts that capacity is sufficient to hold an additional item.420 /// Asserts that capacity is sufficient to hold an additional item.
421 /// Invalidates pointers to shifted elements.
353 pub fn insertAssumeCapacity(self: *Self, index: usize, elem: T) void {422 pub fn insertAssumeCapacity(self: *Self, index: usize, elem: T) void {
354 assert(self.len < self.capacity);423 assert(self.len < self.capacity);
355 assert(index <= self.len);424 assert(index <= self.len);
425 self.pointer_stability.assertUnlocked();
356 self.len += 1;426 self.len += 1;
357 const entry = switch (@typeInfo(T)) {427 const entry = switch (@typeInfo(T)) {
358 .@"struct" => elem,428 .@"struct" => elem,
...@@ -376,6 +446,7 @@ pub fn MultiArrayList(comptime T: type) type {...@@ -376,6 +446,7 @@ pub fn MultiArrayList(comptime T: type) type {
376 ///446 ///
377 /// If capacity is not sufficient to hold an additional447 /// If capacity is not sufficient to hold an additional
378 /// item, returns `error.OutOfMemory`.448 /// item, returns `error.OutOfMemory`.
449 /// Invalidates pointers to shifted elements.
379 pub fn insertBounded(self: *Self, index: usize, elem: T) error{OutOfMemory}!void {450 pub fn insertBounded(self: *Self, index: usize, elem: T) error{OutOfMemory}!void {
380 if (self.capacity - self.len < 1) return error.OutOfMemory;451 if (self.capacity - self.len < 1) return error.OutOfMemory;
381 return insertAssumeCapacity(self, index, elem);452 return insertAssumeCapacity(self, index, elem);
...@@ -384,7 +455,9 @@ pub fn MultiArrayList(comptime T: type) type {...@@ -384,7 +455,9 @@ pub fn MultiArrayList(comptime T: type) type {
384 /// Remove the specified item from the list, swapping the last455 /// Remove the specified item from the list, swapping the last
385 /// item in the list into its position. Fast, but does not456 /// item in the list into its position. Fast, but does not
386 /// retain list ordering.457 /// retain list ordering.
458 /// Invalidates pointers to fields of the item at the end of the list.
387 pub fn swapRemove(self: *Self, index: usize) void {459 pub fn swapRemove(self: *Self, index: usize) void {
460 self.pointer_stability.assertUnlocked();
388 const slices = self.slice();461 const slices = self.slice();
389 inline for (field_names, 0..) |_, i| {462 inline for (field_names, 0..) |_, i| {
390 const field_slice = slices.items(@as(Field, @fromBackingInt(@intCast(i))));463 const field_slice = slices.items(@as(Field, @fromBackingInt(@intCast(i))));
...@@ -396,7 +469,9 @@ pub fn MultiArrayList(comptime T: type) type {...@@ -396,7 +469,9 @@ pub fn MultiArrayList(comptime T: type) type {
396469
397 /// Remove the specified item from the list, shifting items470 /// Remove the specified item from the list, shifting items
398 /// after it to preserve order.471 /// after it to preserve order.
472 /// Invalidates pointers to shifted elements.
399 pub fn orderedRemove(self: *Self, index: usize) void {473 pub fn orderedRemove(self: *Self, index: usize) void {
474 self.pointer_stability.assertUnlocked();
400 const slices = self.slice();475 const slices = self.slice();
401 inline for (field_names, 0..) |_, field_index| {476 inline for (field_names, 0..) |_, field_index| {
402 const field_slice = slices.items(@as(Field, @fromBackingInt(@intCast(field_index))));477 const field_slice = slices.items(@as(Field, @fromBackingInt(@intCast(field_index))));
...@@ -423,6 +498,7 @@ pub fn MultiArrayList(comptime T: type) type {...@@ -423,6 +498,7 @@ pub fn MultiArrayList(comptime T: type) type {
423 /// Invalidates element pointers beyond the first deleted index.498 /// Invalidates element pointers beyond the first deleted index.
424 pub fn orderedRemoveMany(self: *Self, sorted_indexes: []const usize) void {499 pub fn orderedRemoveMany(self: *Self, sorted_indexes: []const usize) void {
425 if (sorted_indexes.len == 0) return;500 if (sorted_indexes.len == 0) return;
501 self.pointer_stability.assertUnlocked();
426 const slices = self.slice();502 const slices = self.slice();
427 var shift: usize = 1;503 var shift: usize = 1;
428 for (sorted_indexes[0 .. sorted_indexes.len - 1], sorted_indexes[1..]) |removed, end| {504 for (sorted_indexes[0 .. sorted_indexes.len - 1], sorted_indexes[1..]) |removed, end| {
...@@ -447,6 +523,7 @@ pub fn MultiArrayList(comptime T: type) type {...@@ -447,6 +523,7 @@ pub fn MultiArrayList(comptime T: type) type {
447523
448 /// Adjust the list's length to `new_len`.524 /// Adjust the list's length to `new_len`.
449 /// Does not initialize added items, if any.525 /// Does not initialize added items, if any.
526 /// Invalidates element pointers if additional memory is required.
450 pub fn resize(self: *Self, gpa: Allocator, new_len: usize) Allocator.Error!void {527 pub fn resize(self: *Self, gpa: Allocator, new_len: usize) Allocator.Error!void {
451 try self.ensureTotalCapacity(gpa, new_len);528 try self.ensureTotalCapacity(gpa, new_len);
452 self.len = new_len;529 self.len = new_len;
...@@ -455,11 +532,12 @@ pub fn MultiArrayList(comptime T: type) type {...@@ -455,11 +532,12 @@ pub fn MultiArrayList(comptime T: type) type {
455 /// Attempt to reduce allocated capacity to `new_len`.532 /// Attempt to reduce allocated capacity to `new_len`.
456 /// If `new_len` is greater than zero, this may fail to reduce the capacity,533 /// If `new_len` is greater than zero, this may fail to reduce the capacity,
457 /// but the data remains intact and the length is updated to new_len.534 /// but the data remains intact and the length is updated to new_len.
535 /// Invalidates element pointers if additional memory is required.
458 pub fn shrinkAndFree(self: *Self, gpa: Allocator, new_len: usize) void {536 pub fn shrinkAndFree(self: *Self, gpa: Allocator, new_len: usize) void {
459 if (new_len == 0) return clearAndFree(self, gpa);537 if (new_len == 0) return clearAndFree(self, gpa);
460
461 assert(new_len <= self.capacity);538 assert(new_len <= self.capacity);
462 assert(new_len <= self.len);539 assert(new_len <= self.len);
540 self.pointer_stability.assertUnlocked();
463541
464 const other_bytes = gpa.alignedAlloc(u8, sizes.big_align, capacityInBytes(new_len)) catch {542 const other_bytes = gpa.alignedAlloc(u8, sizes.big_align, capacityInBytes(new_len)) catch {
465 const self_slice = self.slice();543 const self_slice = self.slice();
...@@ -476,7 +554,7 @@ pub fn MultiArrayList(comptime T: type) type {...@@ -476,7 +554,7 @@ pub fn MultiArrayList(comptime T: type) type {
476 self.len = new_len;554 self.len = new_len;
477 return;555 return;
478 };556 };
479 var other = Self{557 var other: Self = .{
480 .bytes = other_bytes.ptr,558 .bytes = other_bytes.ptr,
481 .capacity = new_len,559 .capacity = new_len,
482 .len = new_len,560 .len = new_len,
...@@ -494,7 +572,9 @@ pub fn MultiArrayList(comptime T: type) type {...@@ -494,7 +572,9 @@ pub fn MultiArrayList(comptime T: type) type {
494 self.* = other;572 self.* = other;
495 }573 }
496574
575 /// Invalidates all element pointers.
497 pub fn clearAndFree(self: *Self, gpa: Allocator) void {576 pub fn clearAndFree(self: *Self, gpa: Allocator) void {
577 self.pointer_stability.assertUnlocked();
498 gpa.free(self.allocatedBytes());578 gpa.free(self.allocatedBytes());
499 self.* = .empty;579 self.* = .empty;
500 }580 }
...@@ -503,11 +583,13 @@ pub fn MultiArrayList(comptime T: type) type {...@@ -503,11 +583,13 @@ pub fn MultiArrayList(comptime T: type) type {
503 /// Invalidates pointers to elements `items[new_len..]`.583 /// Invalidates pointers to elements `items[new_len..]`.
504 /// Keeps capacity the same.584 /// Keeps capacity the same.
505 pub fn shrinkRetainingCapacity(self: *Self, new_len: usize) void {585 pub fn shrinkRetainingCapacity(self: *Self, new_len: usize) void {
586 self.pointer_stability.assertUnlocked();
506 self.len = new_len;587 self.len = new_len;
507 }588 }
508589
509 /// Invalidates all element pointers.590 /// Invalidates all element pointers.
510 pub fn clearRetainingCapacity(self: *Self) void {591 pub fn clearRetainingCapacity(self: *Self) void {
592 self.pointer_stability.assertUnlocked();
511 self.len = 0;593 self.len = 0;
512 }594 }
513595
...@@ -542,6 +624,7 @@ pub fn MultiArrayList(comptime T: type) type {...@@ -542,6 +624,7 @@ pub fn MultiArrayList(comptime T: type) type {
542 /// `new_capacity` must be greater or equal to `len`.624 /// `new_capacity` must be greater or equal to `len`.
543 pub fn setCapacity(self: *Self, gpa: Allocator, new_capacity: usize) Allocator.Error!void {625 pub fn setCapacity(self: *Self, gpa: Allocator, new_capacity: usize) Allocator.Error!void {
544 assert(new_capacity >= self.len);626 assert(new_capacity >= self.len);
627 self.pointer_stability.assertUnlocked();
545 const new_bytes = try gpa.alignedAlloc(u8, sizes.big_align, capacityInBytes(new_capacity));628 const new_bytes = try gpa.alignedAlloc(u8, sizes.big_align, capacityInBytes(new_capacity));
546 if (self.len == 0) {629 if (self.len == 0) {
547 gpa.free(self.allocatedBytes());630 gpa.free(self.allocatedBytes());
...@@ -549,7 +632,7 @@ pub fn MultiArrayList(comptime T: type) type {...@@ -549,7 +632,7 @@ pub fn MultiArrayList(comptime T: type) type {
549 self.capacity = new_capacity;632 self.capacity = new_capacity;
550 return;633 return;
551 }634 }
552 var other = Self{635 var other: Self = .{
553 .bytes = new_bytes.ptr,636 .bytes = new_bytes.ptr,
554 .capacity = new_capacity,637 .capacity = new_capacity,
555 .len = self.len,638 .len = self.len,
...@@ -568,8 +651,9 @@ pub fn MultiArrayList(comptime T: type) type {...@@ -568,8 +651,9 @@ pub fn MultiArrayList(comptime T: type) type {
568651
569 /// Create a copy of this list with a new backing store,652 /// Create a copy of this list with a new backing store,
570 /// using the specified allocator.653 /// using the specified allocator.
654 /// The returned MultiArrayList is in an unlocked state.
571 pub fn clone(self: Self, gpa: Allocator) Allocator.Error!Self {655 pub fn clone(self: Self, gpa: Allocator) Allocator.Error!Self {
572 var result = Self{};656 var result: Self = .{};
573 errdefer result.deinit(gpa);657 errdefer result.deinit(gpa);
574 try result.ensureTotalCapacity(gpa, self.len);658 try result.ensureTotalCapacity(gpa, self.len);
575 result.len = self.len;659 result.len = self.len;
...@@ -584,9 +668,11 @@ pub fn MultiArrayList(comptime T: type) type {...@@ -584,9 +668,11 @@ pub fn MultiArrayList(comptime T: type) type {
584 return result;668 return result;
585 }669 }
586670
671 /// Invalidates element pointers.
587 /// `ctx` has the following method:672 /// `ctx` has the following method:
588 /// `fn lessThan(ctx: @TypeOf(ctx), a_index: usize, b_index: usize) bool`673 /// `fn lessThan(ctx: @TypeOf(ctx), a_index: usize, b_index: usize) bool`
589 fn sortInternal(self: Self, a: usize, b: usize, ctx: anytype, comptime mode: std.sort.Mode) void {674 fn sortInternal(self: Self, a: usize, b: usize, ctx: anytype, comptime mode: std.sort.Mode) void {
675 self.pointer_stability.assertUnlocked();
590 const sort_context: struct {676 const sort_context: struct {
591 sub_ctx: @TypeOf(ctx),677 sub_ctx: @TypeOf(ctx),
592 slice: Slice,678 slice: Slice,
...@@ -616,6 +702,7 @@ pub fn MultiArrayList(comptime T: type) type {...@@ -616,6 +702,7 @@ pub fn MultiArrayList(comptime T: type) type {
616 }702 }
617703
618 /// This function guarantees a stable sort, i.e the relative order of equal elements is preserved during sorting.704 /// This function guarantees a stable sort, i.e the relative order of equal elements is preserved during sorting.
705 /// Invalidates element pointers.
619 /// Read more about stable sorting here: https://en.wikipedia.org/wiki/Sorting_algorithm#Stability706 /// Read more about stable sorting here: https://en.wikipedia.org/wiki/Sorting_algorithm#Stability
620 /// If this guarantee does not matter, `sortUnstable` might be a faster alternative.707 /// If this guarantee does not matter, `sortUnstable` might be a faster alternative.
621 /// `ctx` has the following method:708 /// `ctx` has the following method:
...@@ -625,6 +712,7 @@ pub fn MultiArrayList(comptime T: type) type {...@@ -625,6 +712,7 @@ pub fn MultiArrayList(comptime T: type) type {
625 }712 }
626713
627 /// Sorts only the subsection of items between indices `a` and `b` (excluding `b`)714 /// Sorts only the subsection of items between indices `a` and `b` (excluding `b`)
715 /// Invalidates element pointers.
628 /// This function guarantees a stable sort, i.e the relative order of equal elements is preserved during sorting.716 /// This function guarantees a stable sort, i.e the relative order of equal elements is preserved during sorting.
629 /// Read more about stable sorting here: https://en.wikipedia.org/wiki/Sorting_algorithm#Stability717 /// Read more about stable sorting here: https://en.wikipedia.org/wiki/Sorting_algorithm#Stability
630 /// If this guarantee does not matter, `sortSpanUnstable` might be a faster alternative.718 /// If this guarantee does not matter, `sortSpanUnstable` might be a faster alternative.
...@@ -635,6 +723,7 @@ pub fn MultiArrayList(comptime T: type) type {...@@ -635,6 +723,7 @@ pub fn MultiArrayList(comptime T: type) type {
635 }723 }
636724
637 /// This function does NOT guarantee a stable sort, i.e the relative order of equal elements may change during sorting.725 /// This function does NOT guarantee a stable sort, i.e the relative order of equal elements may change during sorting.
726 /// Invalidates element pointers.
638 /// Due to the weaker guarantees of this function, this may be faster than the stable `sort` method.727 /// Due to the weaker guarantees of this function, this may be faster than the stable `sort` method.
639 /// Read more about stable sorting here: https://en.wikipedia.org/wiki/Sorting_algorithm#Stability728 /// Read more about stable sorting here: https://en.wikipedia.org/wiki/Sorting_algorithm#Stability
640 /// `ctx` has the following method:729 /// `ctx` has the following method:
...@@ -644,6 +733,7 @@ pub fn MultiArrayList(comptime T: type) type {...@@ -644,6 +733,7 @@ pub fn MultiArrayList(comptime T: type) type {
644 }733 }
645734
646 /// Sorts only the subsection of items between indices `a` and `b` (excluding `b`)735 /// Sorts only the subsection of items between indices `a` and `b` (excluding `b`)
736 /// Invalidates element pointers.
647 /// This function does NOT guarantee a stable sort, i.e the relative order of equal elements may change during sorting.737 /// This function does NOT guarantee a stable sort, i.e the relative order of equal elements may change during sorting.
648 /// Due to the weaker guarantees of this function, this may be faster than the stable `sortSpan` method.738 /// Due to the weaker guarantees of this function, this may be faster than the stable `sortSpan` method.
649 /// Read more about stable sorting here: https://en.wikipedia.org/wiki/Sorting_algorithm#Stability739 /// Read more about stable sorting here: https://en.wikipedia.org/wiki/Sorting_algorithm#Stability
lib/std/zig/TokenSmith.zig+1
...@@ -246,6 +246,7 @@ pub fn list(t: *TokenSmith) TokenList.Slice {...@@ -246,6 +246,7 @@ pub fn list(t: *TokenSmith) TokenList.Slice {
246 .ptrs = undefined,246 .ptrs = undefined,
247 .len = t.tags_len,247 .len = t.tags_len,
248 .capacity = t.tags_len,248 .capacity = t.tags_len,
249 .pointer_stability = .{},
249 };250 };
250 comptime std.debug.assert(slice.ptrs.len == 2);251 comptime std.debug.assert(slice.ptrs.len == 2);
251 slice.ptrs[@backingInt(TokenList.Field.tag)] = @ptrCast(&t.tag_buf);252 slice.ptrs[@backingInt(TokenList.Field.tag)] = @ptrCast(&t.tag_buf);