╭─────────────╮ │scroll to top│ ╰─────────────╯ ╭ mutable_value.zig ────────────────────────────────────────────╮ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ ╰───────────────────────────────────────────────────────────────╯
╭ find file ─ ─ ─ ─ ─ ─ ─ ─ ─ ─╮ ╭────╮ ╭ clone ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ╮ │ │ │http│ ssh │ │ ╰ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─╯ ╰────╯ ╰ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ╯
312 interned_byte.* = .{ .interned = try pt.intern(.{ .int = .{ 313 .ty = .u8_type, 314 .storage = .{ .u64 = byte }, 315 } }) }; 316 } 317 mv.* = .{ .aggregate = .{ 318 .ty = bytes.ty, 319 .elems = elems, 320 } }; 321 }, 322 .repeated => |repeated| { 323 const len = ip.aggregateTypeLenIncludingSentinel(repeated.ty); 324 const elems = try arena.alloc(MutableValue, @intCast(len)); 325 @memset(elems, repeated.child.*); 326 mv.* = .{ .aggregate = .{ 327 .ty = repeated.ty, 328 .elems = elems, 329 } }; 330 }, 331 .slice, .aggregate => {}, 332 } 333 switch (mv.*) { 334 .aggregate => |*agg| return &agg.elems[field_idx], 335 .slice => |*slice| return switch (field_idx) { 336 Value.slice_ptr_index => slice.ptr, 337 Value.slice_len_index => slice.len, 338 else => unreachable, 339 }, 340 else => unreachable, 341 } 342 } 343 344 /// Modify a single field of a `MutableValue` which represents an aggregate or slice, leaving others 345 /// untouched. When an entire field must be modified, this should be used in preference to `elemPtr` 346 /// to allow for an optimal representation. 347 /// For slices, uses `Value.slice_ptr_index` and `Value.slice_len_index`. 348 pub fn setElem( 349 mv: *MutableValue, 350 pt: Zcu.PerThread, 351 arena: Allocator, 352 field_idx: usize, 353 field_val: MutableValue, 354 ) Allocator.Error!void { 355 const zcu = pt.zcu; 356 const ip = &zcu.intern_pool; 357 const is_trivial_int = field_val.isTrivialInt(zcu); 358 try mv.unintern(pt, arena, is_trivial_int, true); 359 switch (mv.*) { 360 .interned, 361 .eu_payload, 362 .opt_payload, 363 .un, 364 => unreachable, 365 .slice => |*s| switch (field_idx) { 366 Value.slice_ptr_index => s.ptr.* = field_val, 367 Value.slice_len_index => s.len.* = field_val, 368 else => unreachable, 369 }, 370 .bytes => |b| { 371 assert(is_trivial_int); 372 assert(field_val.typeOf(zcu).toIntern() == .u8_type); 373 b.data[field_idx] = @intCast(Value.fromInterned(field_val.interned).toUnsignedInt(zcu)); 374 }, 375 .repeated => |r| { 376 if (field_val.eqlTrivial(r.child.*)) return; 377 // We must switch to either the `aggregate` or the `bytes` representation. 378 const len_inc_sent = ip.aggregateTypeLenIncludingSentinel(r.ty); 379 if (Type.fromInterned(r.ty).zigTypeTag(zcu) != .@"struct" and 380 is_trivial_int and 381 Type.fromInterned(r.ty).childType(zcu).toIntern() == .u8_type and 382 r.child.isTrivialInt(zcu)) 383 { 384 // We can use the `bytes` representation. 385 const bytes = try arena.alloc(u8, @intCast(len_inc_sent)); 386 const repeated_byte = Value.fromInterned(r.child.interned).toUnsignedInt(zcu); 387 @memset(bytes, @intCast(repeated_byte)); 388 bytes[field_idx] = @intCast(Value.fromInterned(field_val.interned).toUnsignedInt(zcu)); 389 mv.* = .{ .bytes = .{ 390 .ty = r.ty, 391 .data = bytes, 392 } }; 393 } else { 394 // We must use the `aggregate` representation. 395 const mut_elems = try arena.alloc(MutableValue, @intCast(len_inc_sent)); 396 @memset(mut_elems, r.child.*); 397 mut_elems[field_idx] = field_val; 398 mv.* = .{ .aggregate = .{ 399 .ty = r.ty, 400 .elems = mut_elems, 401 } }; 402 } 403 }, 404 .aggregate => |a| { 405 a.elems[field_idx] = field_val; 406 const is_struct = Type.fromInterned(a.ty).zigTypeTag(zcu) == .@"struct"; 407 // Attempt to switch to a more efficient representation. 408 const is_repeated = for (a.elems) |e| { 409 if (!e.eqlTrivial(field_val)) break false; 410 } else true; 411 if (!is_struct and is_repeated) { 412 // Switch to `repeated` repr 413 const mut_repeated = try arena.create(MutableValue); 414 mut_repeated.* = field_val; 415 mv.* = .{ .repeated = .{ 416 .ty = a.ty, 417 .child = mut_repeated, 418 } }; 419 } else if (!is_struct and is_trivial_int and Type.fromInterned(a.ty).childType(zcu).toIntern() == .u8_type) { 420 // See if we can switch to `bytes` repr 421 for (a.elems) |e| { 422 if (!e.isTrivialInt(zcu)) break; 423 } else { 424 const bytes = try arena.alloc(u8, a.elems.len); 425 for (a.elems, bytes) |elem_val, *b| { 426 b.* = @intCast(Value.fromInterned(elem_val.interned).toUnsignedInt(zcu)); 427 } 428 mv.* = .{ .bytes = .{ 429 .ty = a.ty, 430 .data = bytes, 431 } }; 432 } 433 } 434 }, 435 } 436 } 437 438 /// Get the value of a single field of a `MutableValue` which represents an aggregate or slice. 439 /// For slices, uses `Value.slice_ptr_index` and `Value.slice_len_index`. 440 pub fn getElem( 441 mv: MutableValue, 442 pt: Zcu.PerThread, 443 field_idx: usize, 444 ) Allocator.Error!MutableValue { 445 return switch (mv) { 446 .eu_payload, 447 .opt_payload, 448 => unreachable, 449 .interned => |ip_index| { 450 const ty = Type.fromInterned(pt.zcu.intern_pool.typeOf(ip_index)); 451 switch (ty.zigTypeTag(pt.zcu)) { 452 .array, .vector => return .{ .interned = (try Value.fromInterned(ip_index).elemValue(pt, field_idx)).toIntern() }, 453 .@"struct", .@"union" => return .{ .interned = (try Value.fromInterned(ip_index).fieldValue(pt, field_idx)).toIntern() }, 454 .pointer => { 455 assert(ty.isSlice(pt.zcu)); 456 return switch (field_idx) { 457 Value.slice_ptr_index => .{ .interned = Value.fromInterned(ip_index).slicePtr(pt.zcu).toIntern() }, 458 Value.slice_len_index => .{ .interned = switch (pt.zcu.intern_pool.indexToKey(ip_index)) { 459 .undef => .undef_usize, 460 .slice => |s| s.len, 461 else => unreachable, 462 } }, 463 else => unreachable, 464 }; 465 }, 466 else => unreachable, 467 } 468 }, 469 .un => |un| { 470 // TODO assert the tag is correct 471 return un.payload.*; 472 }, 473 .slice => |s| switch (field_idx) { 474 Value.slice_ptr_index => s.ptr.*, 475 Value.slice_len_index => s.len.*, 476 else => unreachable, 477 }, 478 .bytes => |b| .{ .interned = try pt.intern(.{ .int = .{ 479 .ty = .u8_type, 480 .storage = .{ .u64 = b.data[field_idx] }, 481 } }) }, 482 .repeated => |r| r.child.*, 483 .aggregate => |a| a.elems[field_idx], 484 }; 485 } 486 487 fn isTrivialInt(mv: MutableValue, zcu: *Zcu) bool { 488 return switch (mv) { 489 else => false, 490 .interned => |ip_index| switch (zcu.intern_pool.indexToKey(ip_index)) { 491 else => false, 492 .int => true, 493 }, 494 }; 495 } 496 497 pub fn typeOf(mv: MutableValue, zcu: *Zcu) Type { 498 return switch (mv) { 499 .interned => |ip_index| Type.fromInterned(zcu.intern_pool.typeOf(ip_index)), 500 inline else => |x| Type.fromInterned(x.ty), 501 }; 502 } 503 504 pub fn unpackOptional(mv: MutableValue, zcu: *Zcu) union(enum) { 505 undef, 506 null, 507 payload: MutableValue, 508 } { 509 return switch (mv) { 510 .opt_payload => |pl| return .{ .payload = pl.child.* }, 511 .interned => |ip_index| switch (zcu.intern_pool.indexToKey(ip_index)) { 512 .undef => return .undef, 513 .opt => |opt| if (opt.val == .none) .null else .{ .payload = .{ .interned = opt.val } }, 514 else => unreachable, 515 }, 516 else => unreachable, 517 }; 518 } 519 520 pub fn unpackErrorUnion(mv: MutableValue, zcu: *Zcu) union(enum) { 521 undef, 522 err: InternPool.NullTerminatedString, 523 payload: MutableValue, 524 } { 525 return switch (mv) { 526 .eu_payload => |pl| return .{ .payload = pl.child.* }, 527 .interned => |ip_index| switch (zcu.intern_pool.indexToKey(ip_index)) { 528 .undef => return .undef, 529 .error_union => |eu| switch (eu.val) { 530 .err_name => |name| .{ .err = name }, 531 .payload => |pl| .{ .payload = .{ .interned = pl } }, 532 }, 533 else => unreachable, 534 }, 535 else => unreachable, 536 }; 537 } 538 539 /// Fast equality checking which may return false negatives. 540 /// Used for deciding when to switch aggregate representations without fully 541 /// interning many values. 542 fn eqlTrivial(a: MutableValue, b: MutableValue) bool { 543 const Tag = @typeInfo(MutableValue).@"union".tag_type.?; 544 if (@as(Tag, a) != @as(Tag, b)) return false; 545 return switch (a) { 546 .interned => |a_ip| a_ip == b.interned, 547 .eu_payload => |a_pl| a_pl.ty == b.eu_payload.ty and a_pl.child.eqlTrivial(b.eu_payload.child.*), 548 .opt_payload => |a_pl| a_pl.ty == b.opt_payload.ty and a_pl.child.eqlTrivial(b.opt_payload.child.*), 549 .repeated => |a_rep| a_rep.ty == b.repeated.ty and a_rep.child.eqlTrivial(b.repeated.child.*), 550 .bytes => |a_bytes| a_bytes.ty == b.bytes.ty and std.mem.eql(u8, a_bytes.data, b.bytes.data), 551 .aggregate => |a_agg| { 552 const b_agg = b.aggregate; 553 if (a_agg.ty != b_agg.ty) return false; 554 if (a_agg.elems.len != b_agg.elems.len) return false; 555 for (a_agg.elems, b_agg.elems) |a_elem, b_elem| { 556 if (!a_elem.eqlTrivial(b_elem)) return false; 557 } 558 return true; 559 }, 560 .slice => |a_slice| a_slice.ty == b.slice.ty and 561 a_slice.ptr.interned == b.slice.ptr.interned and 562 a_slice.len.interned == b.slice.len.interned, 563 .un => |a_un| a_un.ty == b.un.ty and a_un.tag == b.un.tag and a_un.payload.eqlTrivial(b.un.payload.*), 564 }; 565 } 566 }; 567