authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-05-24 15:32:12-04:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2022-05-24 15:32:12-04:00
log60f0acd9b9c2bced47ba1c214460f34b73738f95
tree7cc1ba371eab54fb84d5f56a67c3dab0d035bf7a
parent8171972cbb477672ee1a99d953df4aaecb744a0c
parent953e2778d4402cf85b99061ef9bdb89aa3e5f2db
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #11706 from ziglang/string-literal-interning

stage2: string literal interning

8 files changed, 244 insertions(+), 26 deletions(-)

src/Module.zig+61-6
...@@ -70,6 +70,17 @@ import_table: std.StringArrayHashMapUnmanaged(*File) = .{},...@@ -70,6 +70,17 @@ import_table: std.StringArrayHashMapUnmanaged(*File) = .{},
70/// Keys are fully resolved file paths. This table owns the keys and values.70/// Keys are fully resolved file paths. This table owns the keys and values.
71embed_table: std.StringHashMapUnmanaged(*EmbedFile) = .{},71embed_table: std.StringHashMapUnmanaged(*EmbedFile) = .{},
7272
73/// This is a temporary addition to stage2 in order to match stage1 behavior,
74/// however the end-game once the lang spec is settled will be to use a global
75/// InternPool for comptime memoized objects, making this behavior consistent across all types,
76/// not only string literals. Or, we might decide to not guarantee string literals
77/// to have equal comptime pointers, in which case this field can be deleted (perhaps
78/// the commit that introduced it can simply be reverted).
79/// This table uses an optional index so that when a Decl is destroyed, the string literal
80/// is still reclaimable by a future Decl.
81string_literal_table: std.HashMapUnmanaged(StringLiteralContext.Key, Decl.OptionalIndex, StringLiteralContext, std.hash_map.default_max_load_percentage) = .{},
82string_literal_bytes: std.ArrayListUnmanaged(u8) = .{},
83
73/// The set of all the generic function instantiations. This is used so that when a generic84/// The set of all the generic function instantiations. This is used so that when a generic
74/// function is called twice with the same comptime parameter arguments, both calls dispatch85/// function is called twice with the same comptime parameter arguments, both calls dispatch
75/// to the same function.86/// to the same function.
...@@ -157,6 +168,39 @@ decls_free_list: std.ArrayListUnmanaged(Decl.Index) = .{},...@@ -157,6 +168,39 @@ decls_free_list: std.ArrayListUnmanaged(Decl.Index) = .{},
157168
158global_assembly: std.AutoHashMapUnmanaged(Decl.Index, []u8) = .{},169global_assembly: std.AutoHashMapUnmanaged(Decl.Index, []u8) = .{},
159170
171pub const StringLiteralContext = struct {
172 bytes: *std.ArrayListUnmanaged(u8),
173
174 pub const Key = struct {
175 index: u32,
176 len: u32,
177 };
178
179 pub fn eql(self: @This(), a: Key, b: Key) bool {
180 _ = self;
181 return a.index == b.index and a.len == b.len;
182 }
183
184 pub fn hash(self: @This(), x: Key) u64 {
185 const x_slice = self.bytes.items[x.index..][0..x.len];
186 return std.hash_map.hashString(x_slice);
187 }
188};
189
190pub const StringLiteralAdapter = struct {
191 bytes: *std.ArrayListUnmanaged(u8),
192
193 pub fn eql(self: @This(), a_slice: []const u8, b: StringLiteralContext.Key) bool {
194 const b_slice = self.bytes.items[b.index..][0..b.len];
195 return mem.eql(u8, a_slice, b_slice);
196 }
197
198 pub fn hash(self: @This(), adapted_key: []const u8) u64 {
199 _ = self;
200 return std.hash_map.hashString(adapted_key);
201 }
202};
203
160const MonomorphedFuncsSet = std.HashMapUnmanaged(204const MonomorphedFuncsSet = std.HashMapUnmanaged(
161 *Fn,205 *Fn,
162 void,206 void,
...@@ -507,7 +551,8 @@ pub const Decl = struct {...@@ -507,7 +551,8 @@ pub const Decl = struct {
507 decl.name = undefined;551 decl.name = undefined;
508 }552 }
509553
510 pub fn clearValues(decl: *Decl, gpa: Allocator) void {554 pub fn clearValues(decl: *Decl, mod: *Module) void {
555 const gpa = mod.gpa;
511 if (decl.getExternFn()) |extern_fn| {556 if (decl.getExternFn()) |extern_fn| {
512 extern_fn.deinit(gpa);557 extern_fn.deinit(gpa);
513 gpa.destroy(extern_fn);558 gpa.destroy(extern_fn);
...@@ -521,6 +566,13 @@ pub const Decl = struct {...@@ -521,6 +566,13 @@ pub const Decl = struct {
521 gpa.destroy(variable);566 gpa.destroy(variable);
522 }567 }
523 if (decl.value_arena) |arena_state| {568 if (decl.value_arena) |arena_state| {
569 if (decl.owns_tv) {
570 if (decl.val.castTag(.str_lit)) |str_lit| {
571 mod.string_literal_table.getPtrContext(str_lit.data, .{
572 .bytes = &mod.string_literal_bytes,
573 }).?.* = .none;
574 }
575 }
524 arena_state.promote(gpa).deinit();576 arena_state.promote(gpa).deinit();
525 decl.value_arena = null;577 decl.value_arena = null;
526 decl.has_tv = false;578 decl.has_tv = false;
...@@ -2839,6 +2891,9 @@ pub fn deinit(mod: *Module) void {...@@ -2839,6 +2891,9 @@ pub fn deinit(mod: *Module) void {
2839 mod.decls_free_list.deinit(gpa);2891 mod.decls_free_list.deinit(gpa);
2840 mod.allocated_decls.deinit(gpa);2892 mod.allocated_decls.deinit(gpa);
2841 mod.global_assembly.deinit(gpa);2893 mod.global_assembly.deinit(gpa);
2894
2895 mod.string_literal_table.deinit(gpa);
2896 mod.string_literal_bytes.deinit(gpa);
2842}2897}
28432898
2844pub fn destroyDecl(mod: *Module, decl_index: Decl.Index) void {2899pub fn destroyDecl(mod: *Module, decl_index: Decl.Index) void {
...@@ -2857,7 +2912,7 @@ pub fn destroyDecl(mod: *Module, decl_index: Decl.Index) void {...@@ -2857,7 +2912,7 @@ pub fn destroyDecl(mod: *Module, decl_index: Decl.Index) void {
2857 if (decl.getInnerNamespace()) |namespace| {2912 if (decl.getInnerNamespace()) |namespace| {
2858 namespace.destroyDecls(mod);2913 namespace.destroyDecls(mod);
2859 }2914 }
2860 decl.clearValues(gpa);2915 decl.clearValues(mod);
2861 }2916 }
2862 decl.dependants.deinit(gpa);2917 decl.dependants.deinit(gpa);
2863 decl.dependencies.deinit(gpa);2918 decl.dependencies.deinit(gpa);
...@@ -4034,7 +4089,7 @@ fn semaDecl(mod: *Module, decl_index: Decl.Index) !bool {...@@ -4034,7 +4089,7 @@ fn semaDecl(mod: *Module, decl_index: Decl.Index) !bool {
4034 if (decl.getFunction()) |prev_func| {4089 if (decl.getFunction()) |prev_func| {
4035 prev_is_inline = prev_func.state == .inline_only;4090 prev_is_inline = prev_func.state == .inline_only;
4036 }4091 }
4037 decl.clearValues(gpa);4092 decl.clearValues(mod);
4038 }4093 }
40394094
4040 decl.ty = try decl_tv.ty.copy(decl_arena_allocator);4095 decl.ty = try decl_tv.ty.copy(decl_arena_allocator);
...@@ -4080,7 +4135,7 @@ fn semaDecl(mod: *Module, decl_index: Decl.Index) !bool {...@@ -4080,7 +4135,7 @@ fn semaDecl(mod: *Module, decl_index: Decl.Index) !bool {
4080 var type_changed = true;4135 var type_changed = true;
4081 if (decl.has_tv) {4136 if (decl.has_tv) {
4082 type_changed = !decl.ty.eql(decl_tv.ty, mod);4137 type_changed = !decl.ty.eql(decl_tv.ty, mod);
4083 decl.clearValues(gpa);4138 decl.clearValues(mod);
4084 }4139 }
40854140
4086 decl.owns_tv = false;4141 decl.owns_tv = false;
...@@ -4694,7 +4749,7 @@ pub fn clearDecl(...@@ -4694,7 +4749,7 @@ pub fn clearDecl(
4694 if (decl.getInnerNamespace()) |namespace| {4749 if (decl.getInnerNamespace()) |namespace| {
4695 try namespace.deleteAllDecls(mod, outdated_decls);4750 try namespace.deleteAllDecls(mod, outdated_decls);
4696 }4751 }
4697 decl.clearValues(gpa);4752 decl.clearValues(mod);
4698 }4753 }
46994754
4700 if (decl.deletion_flag) {4755 if (decl.deletion_flag) {
...@@ -5623,7 +5678,7 @@ pub fn populateTestFunctions(mod: *Module) !void {...@@ -5623,7 +5678,7 @@ pub fn populateTestFunctions(mod: *Module) !void {
56235678
5624 // Since we are replacing the Decl's value we must perform cleanup on the5679 // Since we are replacing the Decl's value we must perform cleanup on the
5625 // previous value.5680 // previous value.
5626 decl.clearValues(gpa);5681 decl.clearValues(mod);
5627 decl.ty = new_ty;5682 decl.ty = new_ty;
5628 decl.val = new_val;5683 decl.val = new_val;
5629 decl.has_tv = true;5684 decl.has_tv = true;
src/Sema.zig+79-13
...@@ -3842,20 +3842,44 @@ fn zirStr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Ins...@@ -3842,20 +3842,44 @@ fn zirStr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Ins
3842fn addStrLit(sema: *Sema, block: *Block, zir_bytes: []const u8) CompileError!Air.Inst.Ref {3842fn addStrLit(sema: *Sema, block: *Block, zir_bytes: []const u8) CompileError!Air.Inst.Ref {
3843 // `zir_bytes` references memory inside the ZIR module, which can get deallocated3843 // `zir_bytes` references memory inside the ZIR module, which can get deallocated
3844 // after semantic analysis is complete, for example in the case of the initialization3844 // after semantic analysis is complete, for example in the case of the initialization
3845 // expression of a variable declaration. We need the memory to be in the new3845 // expression of a variable declaration.
3846 // anonymous Decl's arena.3846 const mod = sema.mod;
3847 var anon_decl = try block.startAnonDecl(LazySrcLoc.unneeded);3847 const gpa = sema.gpa;
3848 defer anon_decl.deinit();3848 const string_bytes = &mod.string_literal_bytes;
3849 const StringLiteralAdapter = Module.StringLiteralAdapter;
3850 const StringLiteralContext = Module.StringLiteralContext;
3851 try string_bytes.ensureUnusedCapacity(gpa, zir_bytes.len);
3852 const gop = try mod.string_literal_table.getOrPutContextAdapted(gpa, zir_bytes, StringLiteralAdapter{
3853 .bytes = string_bytes,
3854 }, StringLiteralContext{
3855 .bytes = string_bytes,
3856 });
3857 if (!gop.found_existing) {
3858 gop.key_ptr.* = .{
3859 .index = @intCast(u32, string_bytes.items.len),
3860 .len = @intCast(u32, zir_bytes.len),
3861 };
3862 string_bytes.appendSliceAssumeCapacity(zir_bytes);
3863 gop.value_ptr.* = .none;
3864 }
3865 const decl_index = gop.value_ptr.unwrap() orelse di: {
3866 var anon_decl = try block.startAnonDecl(LazySrcLoc.unneeded);
3867 defer anon_decl.deinit();
38493868
3850 const bytes = try anon_decl.arena().dupeZ(u8, zir_bytes);3869 const decl_index = try anon_decl.finish(
3870 try Type.Tag.array_u8_sentinel_0.create(anon_decl.arena(), gop.key_ptr.len),
3871 try Value.Tag.str_lit.create(anon_decl.arena(), gop.key_ptr.*),
3872 0, // default alignment
3873 );
38513874
3852 const new_decl = try anon_decl.finish(3875 // Needed so that `Decl.clearValues` will additionally set the corresponding
3853 try Type.Tag.array_u8_sentinel_0.create(anon_decl.arena(), bytes.len),3876 // string literal table value back to `Decl.OptionalIndex.none`.
3854 try Value.Tag.bytes.create(anon_decl.arena(), bytes[0 .. bytes.len + 1]),3877 mod.declPtr(decl_index).owns_tv = true;
3855 0, // default alignment
3856 );
38573878
3858 return sema.analyzeDeclRef(new_decl);3879 gop.value_ptr.* = decl_index.toOptional();
3880 break :di decl_index;
3881 };
3882 return sema.analyzeDeclRef(decl_index);
3859}3883}
38603884
3861fn zirInt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {3885fn zirInt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
...@@ -19762,6 +19786,35 @@ fn beginComptimePtrMutation(...@@ -19762,6 +19786,35 @@ fn beginComptimePtrMutation(
19762 .ty = elem_ty,19786 .ty = elem_ty,
19763 };19787 };
19764 },19788 },
19789 .str_lit => {
19790 // An array is memory-optimized to store a slice of bytes, but we are about
19791 // to modify an individual field and the representation has to change.
19792 // If we wanted to avoid this, there would need to be special detection
19793 // elsewhere to identify when writing a value to an array element that is stored
19794 // using the `str_lit` tag, and handle it without making a call to this function.
19795 const arena = parent.beginArena(sema.mod);
19796 defer parent.finishArena(sema.mod);
19797
19798 const str_lit = parent.val.castTag(.str_lit).?.data;
19799 const dest_len = parent.ty.arrayLenIncludingSentinel();
19800 const bytes = sema.mod.string_literal_bytes.items[str_lit.index..][0..str_lit.len];
19801 const elems = try arena.alloc(Value, @intCast(usize, dest_len));
19802 for (bytes) |byte, i| {
19803 elems[i] = try Value.Tag.int_u64.create(arena, byte);
19804 }
19805 if (parent.ty.sentinel()) |sent_val| {
19806 assert(elems.len == bytes.len + 1);
19807 elems[bytes.len] = sent_val;
19808 }
19809
19810 parent.val.* = try Value.Tag.aggregate.create(arena, elems);
19811
19812 return ComptimePtrMutationKit{
19813 .decl_ref_mut = parent.decl_ref_mut,
19814 .val = &elems[elem_ptr.index],
19815 .ty = elem_ty,
19816 };
19817 },
19765 .repeated => {19818 .repeated => {
19766 // An array is memory-optimized to store only a single element value, and19819 // An array is memory-optimized to store only a single element value, and
19767 // that value is understood to be the same for the entire length of the array.19820 // that value is understood to be the same for the entire length of the array.
...@@ -20097,10 +20150,23 @@ fn beginComptimePtrLoad(...@@ -20097,10 +20150,23 @@ fn beginComptimePtrLoad(
20097 }20150 }
20098 }20151 }
2009920152
20100 deref.pointee = if (elem_ptr.index < check_len) TypedValue{20153 if (elem_ptr.index >= check_len) {
20154 deref.pointee = null;
20155 break :blk deref;
20156 }
20157 if (elem_ptr.index == check_len - 1) {
20158 if (array_tv.ty.sentinel()) |sent| {
20159 deref.pointee = TypedValue{
20160 .ty = elem_ty,
20161 .val = sent,
20162 };
20163 break :blk deref;
20164 }
20165 }
20166 deref.pointee = TypedValue{
20101 .ty = elem_ty,20167 .ty = elem_ty,
20102 .val = try array_tv.val.elemValue(sema.mod, sema.arena, elem_ptr.index),20168 .val = try array_tv.val.elemValue(sema.mod, sema.arena, elem_ptr.index),
20103 } else null;20169 };
20104 break :blk deref;20170 break :blk deref;
20105 },20171 },
2010620172
src/TypedValue.zig+5
...@@ -295,6 +295,11 @@ pub fn print(...@@ -295,6 +295,11 @@ pub fn print(
295 return writer.print(".{s}", .{ty.enumFieldName(val.castTag(.enum_field_index).?.data)});295 return writer.print(".{s}", .{ty.enumFieldName(val.castTag(.enum_field_index).?.data)});
296 },296 },
297 .bytes => return writer.print("\"{}\"", .{std.zig.fmtEscapes(val.castTag(.bytes).?.data)}),297 .bytes => return writer.print("\"{}\"", .{std.zig.fmtEscapes(val.castTag(.bytes).?.data)}),
298 .str_lit => {
299 const str_lit = val.castTag(.str_lit).?.data;
300 const bytes = mod.string_literal_bytes.items[str_lit.index..][0..str_lit.len];
301 return writer.print("\"{}\"", .{std.zig.fmtEscapes(bytes)});
302 },
298 .repeated => {303 .repeated => {
299 if (level == 0) {304 if (level == 0) {
300 return writer.writeAll(".{ ... }");305 return writer.writeAll(".{ ... }");
src/codegen.zig+14-2
...@@ -203,11 +203,23 @@ pub fn generateSymbol(...@@ -203,11 +203,23 @@ pub fn generateSymbol(
203 },203 },
204 .Array => switch (typed_value.val.tag()) {204 .Array => switch (typed_value.val.tag()) {
205 .bytes => {205 .bytes => {
206 const payload = typed_value.val.castTag(.bytes).?;206 const bytes = typed_value.val.castTag(.bytes).?.data;
207 const len = @intCast(usize, typed_value.ty.arrayLenIncludingSentinel());207 const len = @intCast(usize, typed_value.ty.arrayLenIncludingSentinel());
208 // The bytes payload already includes the sentinel, if any208 // The bytes payload already includes the sentinel, if any
209 try code.ensureUnusedCapacity(len);209 try code.ensureUnusedCapacity(len);
210 code.appendSliceAssumeCapacity(payload.data[0..len]);210 code.appendSliceAssumeCapacity(bytes[0..len]);
211 return Result{ .appended = {} };
212 },
213 .str_lit => {
214 const str_lit = typed_value.val.castTag(.str_lit).?.data;
215 const mod = bin_file.options.module.?;
216 const bytes = mod.string_literal_bytes.items[str_lit.index..][0..str_lit.len];
217 try code.ensureUnusedCapacity(bytes.len + 1);
218 code.appendSliceAssumeCapacity(bytes);
219 if (typed_value.ty.sentinel()) |sent_val| {
220 const byte = @intCast(u8, sent_val.toUnsignedInt(target));
221 code.appendAssumeCapacity(byte);
222 }
211 return Result{ .appended = {} };223 return Result{ .appended = {} };
212 },224 },
213 .aggregate => {225 .aggregate => {
src/codegen/llvm.zig+31-1
...@@ -2936,9 +2936,39 @@ pub const DeclGen = struct {...@@ -2936,9 +2936,39 @@ pub const DeclGen = struct {
2936 return dg.context.constString(2936 return dg.context.constString(
2937 bytes.ptr,2937 bytes.ptr,
2938 @intCast(c_uint, tv.ty.arrayLenIncludingSentinel()),2938 @intCast(c_uint, tv.ty.arrayLenIncludingSentinel()),
2939 .True, // don't null terminate. bytes has the sentinel, if any.2939 .True, // Don't null terminate. Bytes has the sentinel, if any.
2940 );2940 );
2941 },2941 },
2942 .str_lit => {
2943 const str_lit = tv.val.castTag(.str_lit).?.data;
2944 const bytes = dg.module.string_literal_bytes.items[str_lit.index..][0..str_lit.len];
2945 if (tv.ty.sentinel()) |sent_val| {
2946 const byte = @intCast(u8, sent_val.toUnsignedInt(target));
2947 if (byte == 0 and bytes.len > 0) {
2948 return dg.context.constString(
2949 bytes.ptr,
2950 @intCast(c_uint, bytes.len),
2951 .False, // Yes, null terminate.
2952 );
2953 }
2954 var array = std.ArrayList(u8).init(dg.gpa);
2955 defer array.deinit();
2956 try array.ensureUnusedCapacity(bytes.len + 1);
2957 array.appendSliceAssumeCapacity(bytes);
2958 array.appendAssumeCapacity(byte);
2959 return dg.context.constString(
2960 array.items.ptr,
2961 @intCast(c_uint, array.items.len),
2962 .True, // Don't null terminate.
2963 );
2964 } else {
2965 return dg.context.constString(
2966 bytes.ptr,
2967 @intCast(c_uint, bytes.len),
2968 .True, // Don't null terminate. `bytes` has the sentinel, if any.
2969 );
2970 }
2971 },
2942 .aggregate => {2972 .aggregate => {
2943 const elem_vals = tv.val.castTag(.aggregate).?.data;2973 const elem_vals = tv.val.castTag(.aggregate).?.data;
2944 const elem_ty = tv.ty.elemType();2974 const elem_ty = tv.ty.elemType();
src/value.zig+41
...@@ -126,6 +126,8 @@ pub const Value = extern union {...@@ -126,6 +126,8 @@ pub const Value = extern union {
126 field_ptr,126 field_ptr,
127 /// A slice of u8 whose memory is managed externally.127 /// A slice of u8 whose memory is managed externally.
128 bytes,128 bytes,
129 /// Similar to bytes however it stores an index relative to `Module.string_literal_bytes`.
130 str_lit,
129 /// This value is repeated some number of times. The amount of times to repeat131 /// This value is repeated some number of times. The amount of times to repeat
130 /// is stored externally.132 /// is stored externally.
131 repeated,133 repeated,
...@@ -285,6 +287,7 @@ pub const Value = extern union {...@@ -285,6 +287,7 @@ pub const Value = extern union {
285 .enum_literal,287 .enum_literal,
286 => Payload.Bytes,288 => Payload.Bytes,
287289
290 .str_lit => Payload.StrLit,
288 .slice => Payload.Slice,291 .slice => Payload.Slice,
289292
290 .enum_field_index => Payload.U32,293 .enum_field_index => Payload.U32,
...@@ -538,6 +541,7 @@ pub const Value = extern union {...@@ -538,6 +541,7 @@ pub const Value = extern union {
538 };541 };
539 return Value{ .ptr_otherwise = &new_payload.base };542 return Value{ .ptr_otherwise = &new_payload.base };
540 },543 },
544 .str_lit => return self.copyPayloadShallow(arena, Payload.StrLit),
541 .repeated,545 .repeated,
542 .eu_payload,546 .eu_payload,
543 .opt_payload,547 .opt_payload,
...@@ -764,6 +768,12 @@ pub const Value = extern union {...@@ -764,6 +768,12 @@ pub const Value = extern union {
764 .enum_literal => return out_stream.print(".{}", .{std.zig.fmtId(val.castTag(.enum_literal).?.data)}),768 .enum_literal => return out_stream.print(".{}", .{std.zig.fmtId(val.castTag(.enum_literal).?.data)}),
765 .enum_field_index => return out_stream.print("(enum field {d})", .{val.castTag(.enum_field_index).?.data}),769 .enum_field_index => return out_stream.print("(enum field {d})", .{val.castTag(.enum_field_index).?.data}),
766 .bytes => return out_stream.print("\"{}\"", .{std.zig.fmtEscapes(val.castTag(.bytes).?.data)}),770 .bytes => return out_stream.print("\"{}\"", .{std.zig.fmtEscapes(val.castTag(.bytes).?.data)}),
771 .str_lit => {
772 const str_lit = val.castTag(.str_lit).?.data;
773 return out_stream.print("(.str_lit index={d} len={d})", .{
774 str_lit.index, str_lit.len,
775 });
776 },
767 .repeated => {777 .repeated => {
768 try out_stream.writeAll("(repeated) ");778 try out_stream.writeAll("(repeated) ");
769 val = val.castTag(.repeated).?.data;779 val = val.castTag(.repeated).?.data;
...@@ -824,6 +834,11 @@ pub const Value = extern union {...@@ -824,6 +834,11 @@ pub const Value = extern union {
824 const adjusted_bytes = bytes[0..adjusted_len];834 const adjusted_bytes = bytes[0..adjusted_len];
825 return allocator.dupe(u8, adjusted_bytes);835 return allocator.dupe(u8, adjusted_bytes);
826 },836 },
837 .str_lit => {
838 const str_lit = val.castTag(.str_lit).?.data;
839 const bytes = mod.string_literal_bytes.items[str_lit.index..][0..str_lit.len];
840 return allocator.dupe(u8, bytes);
841 },
827 .enum_literal => return allocator.dupe(u8, val.castTag(.enum_literal).?.data),842 .enum_literal => return allocator.dupe(u8, val.castTag(.enum_literal).?.data),
828 .repeated => {843 .repeated => {
829 const byte = @intCast(u8, val.castTag(.repeated).?.data.toUnsignedInt(target));844 const byte = @intCast(u8, val.castTag(.repeated).?.data.toUnsignedInt(target));
...@@ -2537,6 +2552,20 @@ pub const Value = extern union {...@@ -2537,6 +2552,20 @@ pub const Value = extern union {
2537 return initPayload(&buffer.base);2552 return initPayload(&buffer.base);
2538 }2553 }
2539 },2554 },
2555 .str_lit => {
2556 const str_lit = val.castTag(.str_lit).?.data;
2557 const bytes = mod.string_literal_bytes.items[str_lit.index..][0..str_lit.len];
2558 const byte = bytes[index];
2559 if (arena) |a| {
2560 return Tag.int_u64.create(a, byte);
2561 } else {
2562 buffer.* = .{
2563 .base = .{ .tag = .int_u64 },
2564 .data = byte,
2565 };
2566 return initPayload(&buffer.base);
2567 }
2568 },
25402569
2541 // No matter the index; all the elements are the same!2570 // No matter the index; all the elements are the same!
2542 .repeated => return val.castTag(.repeated).?.data,2571 .repeated => return val.castTag(.repeated).?.data,
...@@ -2570,6 +2599,13 @@ pub const Value = extern union {...@@ -2570,6 +2599,13 @@ pub const Value = extern union {
2570 return switch (val.tag()) {2599 return switch (val.tag()) {
2571 .empty_array_sentinel => if (start == 0 and end == 1) val else Value.initTag(.empty_array),2600 .empty_array_sentinel => if (start == 0 and end == 1) val else Value.initTag(.empty_array),
2572 .bytes => Tag.bytes.create(arena, val.castTag(.bytes).?.data[start..end]),2601 .bytes => Tag.bytes.create(arena, val.castTag(.bytes).?.data[start..end]),
2602 .str_lit => {
2603 const str_lit = val.castTag(.str_lit).?.data;
2604 return Tag.str_lit.create(arena, .{
2605 .index = @intCast(u32, str_lit.index + start),
2606 .len = @intCast(u32, end - start),
2607 });
2608 },
2573 .aggregate => Tag.aggregate.create(arena, val.castTag(.aggregate).?.data[start..end]),2609 .aggregate => Tag.aggregate.create(arena, val.castTag(.aggregate).?.data[start..end]),
2574 .slice => sliceArray(val.castTag(.slice).?.data.ptr, mod, arena, start, end),2610 .slice => sliceArray(val.castTag(.slice).?.data.ptr, mod, arena, start, end),
25752611
...@@ -4721,6 +4757,11 @@ pub const Value = extern union {...@@ -4721,6 +4757,11 @@ pub const Value = extern union {
4721 data: []const u8,4757 data: []const u8,
4722 };4758 };
47234759
4760 pub const StrLit = struct {
4761 base: Payload,
4762 data: Module.StringLiteralContext.Key,
4763 };
4764
4724 pub const Aggregate = struct {4765 pub const Aggregate = struct {
4725 base: Payload,4766 base: Payload,
4726 /// Field values. The types are according to the struct or array type.4767 /// Field values. The types are according to the struct or array type.
test/behavior/eval.zig+11-2
...@@ -643,9 +643,18 @@ fn assertEqualPtrs(ptr1: *const u8, ptr2: *const u8) !void {...@@ -643,9 +643,18 @@ fn assertEqualPtrs(ptr1: *const u8, ptr2: *const u8) !void {
643 try expect(ptr1 == ptr2);643 try expect(ptr1 == ptr2);
644}644}
645645
646// This one is still up for debate in the language specification.
647// Application code should not rely on this behavior until it is solidified.
648// Currently, stage1 has special case code to make this pass for string literals
649// but it does not work if the values are constructed with comptime code, or if
650// arrays of non-u8 elements are used instead.
651// The official language specification might not make this guarantee. However, if
652// it does make this guarantee, it will make it consistently for all types, not
653// only string literals. This is why stage2 currently has a string table for
654// string literals, to match stage1 and pass this test, however the end-game once
655// the lang spec issue is settled would be to use a global InternPool for comptime
656// memoized objects, making this behavior consistent across all types.
646test "string literal used as comptime slice is memoized" {657test "string literal used as comptime slice is memoized" {
647 if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO
648
649 const a = "link";658 const a = "link";
650 const b = "link";659 const b = "link";
651 comptime try expect(TypeWithCompTimeSlice(a).Node == TypeWithCompTimeSlice(b).Node);660 comptime try expect(TypeWithCompTimeSlice(a).Node == TypeWithCompTimeSlice(b).Node);
test/cases/llvm/pointer_with_different_address_spaces.zig+2-2
...@@ -1,12 +1,12 @@...@@ -1,12 +1,12 @@
1fn entry(a: *addrspace(.gs) i32) *addrspace(.fs) i32 {1fn entry(a: *addrspace(.gs) i32) *addrspace(.fs) i32 {
2 return a;2 return a;
3}3}
4pub fn main() void {4export fn entry2() void {
5 _ = entry;5 _ = entry;
6}6}
77
8// error8// error
9// output_mode=Exe9// output_mode=Obj
10// backend=stage2,llvm10// backend=stage2,llvm
11// target=x86_64-linux,x86_64-macos11// target=x86_64-linux,x86_64-macos
12//12//