authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-12-31 01:54:02-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-12-31 01:54:02-07:00
loga46d24af1cf2885991c67bf39a2e639891c16121
treed4f3c81b798cbae420819434c810a52b6817a85b
parent3f7d9b5fc19e4081236b3b63aebbc80e1b17f5b5

stage2: inferred local variables

This patch introduces the following new things: Types: - inferred_alloc - This is a special value that tracks a set of types that have been stored to an inferred allocation. It does not support most of the normal type queries. However it does respond to `isConstPtr`, `ptrSize`, `zigTypeTag`, etc. - The payload for this type simply points to the corresponding Value payload. Values: - inferred_alloc - This is a special value that tracks a set of types that have been stored to an inferred allocation. It does not support any of the normal value queries. ZIR instructions: - store_to_inferred_ptr, - Same as `store` but the type of the value being stored will be used to infer the pointer type. - resolve_inferred_alloc - Each `store_to_inferred_ptr` puts the type of the stored value into a set, and then `resolve_inferred_alloc` triggers peer type resolution on the set. The operand is a `alloc_inferred` or `alloc_inferred_mut` instruction, which is the allocation that needs to have its type inferred. Changes to the C backend: * Implements the bitcast instruction. If the source and dest types are both pointers, uses a cast, otherwise uses memcpy. * Tests are run with -Wno-declaration-after-statement. Someday we can conform to this but not today. In ZIR form it looks like this: ```zir fn_body main { // unanalyzed %0 = dbg_stmt() =>%1 = alloc_inferred() %2 = declval_in_module(Decl(add)) %3 = deref(%2) %4 = param_type(%3, 0) %5 = const(TypedValue{ .ty = comptime_int, .val = 1}) %6 = as(%4, %5) %7 = param_type(%3, 1) %8 = const(TypedValue{ .ty = comptime_int, .val = 2}) %9 = as(%7, %8) %10 = call(%3, [%6, %9], modifier=auto) =>%11 = store_to_inferred_ptr(%1, %10) =>%12 = resolve_inferred_alloc(%1) %13 = dbg_stmt() %14 = ret_type() %15 = const(TypedValue{ .ty = comptime_int, .val = 3}) %16 = sub(%10, %15) %17 = as(%14, %16) %18 = return(%17) } // fn_body main ``` I have not played around with very many test cases yet. Some interesting ones that I want to look at before merging: ```zig var x = blk: { var y = foo(); y.a = 1; break :blk y; }; ``` In the above test case, x and y are supposed to alias. ```zig var x = if (bar()) blk: { var y = foo(); y.a = 1; break :blk y; } else blk: { var z = baz(); z.b = 1; break :blk z; }; ``` In the above test case, x, y, and z are supposed to alias. I also haven't tested with `var` instead of `const` yet.

11 files changed, 261 insertions(+), 58 deletions(-)

src/Module.zig+8-1
......@@ -3189,7 +3189,14 @@ pub fn floatSub(
31893189 }
31903190}
31913191
3192pub fn simplePtrType(self: *Module, scope: *Scope, src: usize, elem_ty: Type, mutable: bool, size: std.builtin.TypeInfo.Pointer.Size) Allocator.Error!Type {
3192pub fn simplePtrType(
3193 self: *Module,
3194 scope: *Scope,
3195 src: usize,
3196 elem_ty: Type,
3197 mutable: bool,
3198 size: std.builtin.TypeInfo.Pointer.Size,
3199) Allocator.Error!Type {
31933200 if (!mutable and size == .Slice and elem_ty.eql(Type.initTag(.u8))) {
31943201 return Type.initTag(.const_slice_u8);
31953202 }
src/astgen.zig+12-1
......@@ -585,6 +585,7 @@ fn varDecl(
585585
586586 switch (tree.token_ids[node.mut_token]) {
587587 .Keyword_const => {
588 var resolve_inferred_alloc: ?*zir.Inst = null;
588589 // Depending on the type of AST the initialization expression is, we may need an lvalue
589590 // or an rvalue as a result location. If it is an rvalue, we can use the instruction as
590591 // the variable, no memory location needed.
......@@ -595,6 +596,7 @@ fn varDecl(
595596 break :r ResultLoc{ .ptr = alloc };
596597 } else {
597598 const alloc = try addZIRNoOpT(mod, scope, name_src, .alloc_inferred);
599 resolve_inferred_alloc = &alloc.base;
598600 break :r ResultLoc{ .inferred_ptr = alloc };
599601 }
600602 } else r: {
......@@ -604,6 +606,9 @@ fn varDecl(
604606 break :r .none;
605607 };
606608 const init_inst = try expr(mod, scope, result_loc, init_node);
609 if (resolve_inferred_alloc) |inst| {
610 _ = try addZIRUnOp(mod, scope, name_src, .resolve_inferred_alloc, inst);
611 }
607612 const sub_scope = try block_arena.create(Scope.LocalVal);
608613 sub_scope.* = .{
609614 .parent = scope,
......@@ -614,15 +619,20 @@ fn varDecl(
614619 return &sub_scope.base;
615620 },
616621 .Keyword_var => {
622 var resolve_inferred_alloc: ?*zir.Inst = null;
617623 const var_data: struct { result_loc: ResultLoc, alloc: *zir.Inst } = if (node.getTypeNode()) |type_node| a: {
618624 const type_inst = try typeExpr(mod, scope, type_node);
619625 const alloc = try addZIRUnOp(mod, scope, name_src, .alloc_mut, type_inst);
620626 break :a .{ .alloc = alloc, .result_loc = .{ .ptr = alloc } };
621627 } else a: {
622628 const alloc = try addZIRNoOp(mod, scope, name_src, .alloc_inferred_mut);
629 resolve_inferred_alloc = alloc;
623630 break :a .{ .alloc = alloc, .result_loc = .{ .inferred_ptr = alloc.castTag(.alloc_inferred_mut).? } };
624631 };
625632 const init_inst = try expr(mod, scope, var_data.result_loc, init_node);
633 if (resolve_inferred_alloc) |inst| {
634 _ = try addZIRUnOp(mod, scope, name_src, .resolve_inferred_alloc, inst);
635 }
626636 const sub_scope = try block_arena.create(Scope.LocalPtr);
627637 sub_scope.* = .{
628638 .parent = scope,
......@@ -2717,7 +2727,8 @@ fn rlWrap(mod: *Module, scope: *Scope, rl: ResultLoc, result: *zir.Inst) InnerEr
27172727 return mod.fail(scope, result.src, "TODO implement rlWrap .bitcasted_ptr", .{});
27182728 },
27192729 .inferred_ptr => |alloc| {
2720 return addZIRBinOp(mod, scope, result.src, .store, &alloc.base, result);
2730 _ = try addZIRBinOp(mod, scope, result.src, .store_to_inferred_ptr, &alloc.base, result);
2731 return result;
27212732 },
27222733 .block_ptr => |block_ptr| {
27232734 return mod.fail(scope, result.src, "TODO implement rlWrap .block_ptr", .{});
src/codegen/c.zig+20
......@@ -275,6 +275,7 @@ pub fn generate(file: *C, module: *Module, decl: *Decl) !void {
275275 try writer.writeAll(" {");
276276
277277 const func: *Module.Fn = func_payload.data;
278 //func.dump(module.*);
278279 const instructions = func.analysis.success.instructions;
279280 if (instructions.len > 0) {
280281 try writer.writeAll("\n");
......@@ -285,6 +286,7 @@ pub fn generate(file: *C, module: *Module, decl: *Decl) !void {
285286 .arg => try genArg(&ctx),
286287 .assembly => try genAsm(&ctx, file, inst.castTag(.assembly).?),
287288 .block => try genBlock(&ctx, file, inst.castTag(.block).?),
289 .bitcast => try genBitcast(&ctx, file, inst.castTag(.bitcast).?),
288290 .breakpoint => try genBreakpoint(file, inst.castTag(.breakpoint).?),
289291 .call => try genCall(&ctx, file, inst.castTag(.call).?),
290292 .cmp_eq => try genBinOp(&ctx, file, inst.castTag(.cmp_eq).?, "=="),
......@@ -537,6 +539,24 @@ fn genBlock(ctx: *Context, file: *C, inst: *Inst.Block) !?[]u8 {
537539 return ctx.fail(ctx.decl.src(), "TODO: C backend: implement blocks", .{});
538540}
539541
542fn genBitcast(ctx: *Context, file: *C, inst: *Inst.UnOp) !?[]u8 {
543 const writer = file.main.writer();
544 try indent(file);
545 const local_name = try ctx.name();
546 const operand = try ctx.resolveInst(inst.operand);
547 try renderTypeAndName(ctx, writer, inst.base.ty, local_name, .Const);
548 if (inst.base.ty.zigTypeTag() == .Pointer and inst.operand.ty.zigTypeTag() == .Pointer) {
549 try writer.writeAll(" = (");
550 try renderType(ctx, writer, inst.base.ty);
551 try writer.print("){s};\n", .{operand});
552 } else {
553 try writer.writeAll(";\n");
554 try indent(file);
555 try writer.print("memcpy(&{s}, &{s}, sizeof {s});\n", .{ local_name, operand, local_name });
556 }
557 return local_name;
558}
559
540560fn genBreakpoint(file: *C, inst: *Inst.NoOp) !?[]u8 {
541561 try indent(file);
542562 try file.main.writer().writeAll("zig_breakpoint();\n");
src/ir.zig+1-1
......@@ -196,7 +196,7 @@ pub const Inst = struct {
196196 pub fn value(base: *Inst) ?Value {
197197 if (base.ty.onePossibleValue()) |opv| return opv;
198198
199 const inst = base.cast(Constant) orelse return null;
199 const inst = base.castTag(.constant) orelse return null;
200200 return inst.val;
201201 }
202202
src/link/cbe.h+1-1
......@@ -41,4 +41,4 @@
4141#include <stdint.h>
4242#define int128_t __int128
4343#define uint128_t unsigned __int128
44
44#include <string.h>
src/test.zig+1
......@@ -782,6 +782,7 @@ pub const TestContext = struct {
782782 "-std=c89",
783783 "-pedantic",
784784 "-Werror",
785 "-Wno-declaration-after-statement",
785786 "--",
786787 "-lc",
787788 exe_path,
src/type.zig+96-52
......@@ -78,6 +78,7 @@ pub const Type = extern union {
7878 .const_slice,
7979 .mut_slice,
8080 .pointer,
81 .inferred_alloc,
8182 => return .Pointer,
8283
8384 .optional,
......@@ -158,6 +159,8 @@ pub const Type = extern union {
158159 .optional_single_mut_pointer,
159160 => self.cast(Payload.ElemType),
160161
162 .inferred_alloc => unreachable,
163
161164 else => null,
162165 };
163166 }
......@@ -384,6 +387,7 @@ pub const Type = extern union {
384387 .enum_literal,
385388 .anyerror_void_error_union,
386389 .@"anyframe",
390 .inferred_alloc,
387391 => unreachable,
388392
389393 .array_u8,
......@@ -686,6 +690,7 @@ pub const Type = extern union {
686690 const name = ty.castTag(.error_set_single).?.data;
687691 return out_stream.print("error{{{s}}}", .{name});
688692 },
693 .inferred_alloc => return out_stream.writeAll("(inferred allocation type)"),
689694 }
690695 unreachable;
691696 }
......@@ -733,6 +738,7 @@ pub const Type = extern union {
733738 .single_const_pointer_to_comptime_int => return Value.initTag(.single_const_pointer_to_comptime_int_type),
734739 .const_slice_u8 => return Value.initTag(.const_slice_u8_type),
735740 .enum_literal => return Value.initTag(.enum_literal_type),
741 .inferred_alloc => unreachable,
736742 else => return Value.Tag.ty.create(allocator, self),
737743 }
738744 }
......@@ -803,6 +809,8 @@ pub const Type = extern union {
803809 .enum_literal,
804810 .empty_struct,
805811 => false,
812
813 .inferred_alloc => unreachable,
806814 };
807815 }
808816
......@@ -920,6 +928,7 @@ pub const Type = extern union {
920928 .@"undefined",
921929 .enum_literal,
922930 .empty_struct,
931 .inferred_alloc,
923932 => unreachable,
924933 };
925934 }
......@@ -943,6 +952,7 @@ pub const Type = extern union {
943952 .enum_literal => unreachable,
944953 .single_const_pointer_to_comptime_int => unreachable,
945954 .empty_struct => unreachable,
955 .inferred_alloc => unreachable,
946956
947957 .u8,
948958 .i8,
......@@ -1121,6 +1131,7 @@ pub const Type = extern union {
11211131 .single_const_pointer,
11221132 .single_mut_pointer,
11231133 .single_const_pointer_to_comptime_int,
1134 .inferred_alloc,
11241135 => true,
11251136
11261137 .pointer => self.castTag(.pointer).?.data.size == .One,
......@@ -1203,6 +1214,7 @@ pub const Type = extern union {
12031214 .single_const_pointer,
12041215 .single_mut_pointer,
12051216 .single_const_pointer_to_comptime_int,
1217 .inferred_alloc,
12061218 => .One,
12071219
12081220 .pointer => self.castTag(.pointer).?.data.size,
......@@ -1273,6 +1285,7 @@ pub const Type = extern union {
12731285 .error_set,
12741286 .error_set_single,
12751287 .empty_struct,
1288 .inferred_alloc,
12761289 => false,
12771290
12781291 .const_slice,
......@@ -1345,6 +1358,7 @@ pub const Type = extern union {
13451358 .error_set,
13461359 .error_set_single,
13471360 .empty_struct,
1361 .inferred_alloc,
13481362 => false,
13491363
13501364 .single_const_pointer,
......@@ -1426,6 +1440,7 @@ pub const Type = extern union {
14261440 .error_set,
14271441 .error_set_single,
14281442 .empty_struct,
1443 .inferred_alloc,
14291444 => false,
14301445
14311446 .pointer => {
......@@ -1502,6 +1517,7 @@ pub const Type = extern union {
15021517 .error_set,
15031518 .error_set_single,
15041519 .empty_struct,
1520 .inferred_alloc,
15051521 => false,
15061522
15071523 .pointer => {
......@@ -1569,58 +1585,58 @@ pub const Type = extern union {
15691585 /// Asserts the type is a pointer or array type.
15701586 pub fn elemType(self: Type) Type {
15711587 return switch (self.tag()) {
1572 .u8,
1573 .i8,
1574 .u16,
1575 .i16,
1576 .u32,
1577 .i32,
1578 .u64,
1579 .i64,
1580 .usize,
1581 .isize,
1582 .c_short,
1583 .c_ushort,
1584 .c_int,
1585 .c_uint,
1586 .c_long,
1587 .c_ulong,
1588 .c_longlong,
1589 .c_ulonglong,
1590 .c_longdouble,
1591 .f16,
1592 .f32,
1593 .f64,
1594 .f128,
1595 .c_void,
1596 .bool,
1597 .void,
1598 .type,
1599 .anyerror,
1600 .comptime_int,
1601 .comptime_float,
1602 .noreturn,
1603 .@"null",
1604 .@"undefined",
1605 .fn_noreturn_no_args,
1606 .fn_void_no_args,
1607 .fn_naked_noreturn_no_args,
1608 .fn_ccc_void_no_args,
1609 .function,
1610 .int_unsigned,
1611 .int_signed,
1612 .optional,
1613 .optional_single_const_pointer,
1614 .optional_single_mut_pointer,
1615 .enum_literal,
1616 .error_union,
1617 .@"anyframe",
1618 .anyframe_T,
1619 .anyerror_void_error_union,
1620 .error_set,
1621 .error_set_single,
1622 .empty_struct,
1623 => unreachable,
1588 .u8 => unreachable,
1589 .i8 => unreachable,
1590 .u16 => unreachable,
1591 .i16 => unreachable,
1592 .u32 => unreachable,
1593 .i32 => unreachable,
1594 .u64 => unreachable,
1595 .i64 => unreachable,
1596 .usize => unreachable,
1597 .isize => unreachable,
1598 .c_short => unreachable,
1599 .c_ushort => unreachable,
1600 .c_int => unreachable,
1601 .c_uint => unreachable,
1602 .c_long => unreachable,
1603 .c_ulong => unreachable,
1604 .c_longlong => unreachable,
1605 .c_ulonglong => unreachable,
1606 .c_longdouble => unreachable,
1607 .f16 => unreachable,
1608 .f32 => unreachable,
1609 .f64 => unreachable,
1610 .f128 => unreachable,
1611 .c_void => unreachable,
1612 .bool => unreachable,
1613 .void => unreachable,
1614 .type => unreachable,
1615 .anyerror => unreachable,
1616 .comptime_int => unreachable,
1617 .comptime_float => unreachable,
1618 .noreturn => unreachable,
1619 .@"null" => unreachable,
1620 .@"undefined" => unreachable,
1621 .fn_noreturn_no_args => unreachable,
1622 .fn_void_no_args => unreachable,
1623 .fn_naked_noreturn_no_args => unreachable,
1624 .fn_ccc_void_no_args => unreachable,
1625 .function => unreachable,
1626 .int_unsigned => unreachable,
1627 .int_signed => unreachable,
1628 .optional => unreachable,
1629 .optional_single_const_pointer => unreachable,
1630 .optional_single_mut_pointer => unreachable,
1631 .enum_literal => unreachable,
1632 .error_union => unreachable,
1633 .@"anyframe" => unreachable,
1634 .anyframe_T => unreachable,
1635 .anyerror_void_error_union => unreachable,
1636 .error_set => unreachable,
1637 .error_set_single => unreachable,
1638 .empty_struct => unreachable,
1639 .inferred_alloc => unreachable,
16241640
16251641 .array => self.castTag(.array).?.data.elem_type,
16261642 .array_sentinel => self.castTag(.array_sentinel).?.data.elem_type,
......@@ -1742,6 +1758,7 @@ pub const Type = extern union {
17421758 .error_set,
17431759 .error_set_single,
17441760 .empty_struct,
1761 .inferred_alloc,
17451762 => unreachable,
17461763
17471764 .array => self.castTag(.array).?.data.len,
......@@ -1808,6 +1825,7 @@ pub const Type = extern union {
18081825 .error_set,
18091826 .error_set_single,
18101827 .empty_struct,
1828 .inferred_alloc,
18111829 => unreachable,
18121830
18131831 .single_const_pointer,
......@@ -1891,6 +1909,7 @@ pub const Type = extern union {
18911909 .error_set,
18921910 .error_set_single,
18931911 .empty_struct,
1912 .inferred_alloc,
18941913 => false,
18951914
18961915 .int_signed,
......@@ -1966,6 +1985,7 @@ pub const Type = extern union {
19661985 .error_set,
19671986 .error_set_single,
19681987 .empty_struct,
1988 .inferred_alloc,
19691989 => false,
19701990
19711991 .int_unsigned,
......@@ -2031,6 +2051,7 @@ pub const Type = extern union {
20312051 .error_set,
20322052 .error_set_single,
20332053 .empty_struct,
2054 .inferred_alloc,
20342055 => unreachable,
20352056
20362057 .int_unsigned => .{
......@@ -2120,6 +2141,7 @@ pub const Type = extern union {
21202141 .error_set,
21212142 .error_set_single,
21222143 .empty_struct,
2144 .inferred_alloc,
21232145 => false,
21242146
21252147 .usize,
......@@ -2232,6 +2254,7 @@ pub const Type = extern union {
22322254 .error_set,
22332255 .error_set_single,
22342256 .empty_struct,
2257 .inferred_alloc,
22352258 => unreachable,
22362259 };
22372260 }
......@@ -2310,6 +2333,7 @@ pub const Type = extern union {
23102333 .error_set,
23112334 .error_set_single,
23122335 .empty_struct,
2336 .inferred_alloc,
23132337 => unreachable,
23142338 }
23152339 }
......@@ -2387,6 +2411,7 @@ pub const Type = extern union {
23872411 .error_set,
23882412 .error_set_single,
23892413 .empty_struct,
2414 .inferred_alloc,
23902415 => unreachable,
23912416 }
23922417 }
......@@ -2464,6 +2489,7 @@ pub const Type = extern union {
24642489 .error_set,
24652490 .error_set_single,
24662491 .empty_struct,
2492 .inferred_alloc,
24672493 => unreachable,
24682494 };
24692495 }
......@@ -2538,6 +2564,7 @@ pub const Type = extern union {
25382564 .error_set,
25392565 .error_set_single,
25402566 .empty_struct,
2567 .inferred_alloc,
25412568 => unreachable,
25422569 };
25432570 }
......@@ -2612,6 +2639,7 @@ pub const Type = extern union {
26122639 .error_set,
26132640 .error_set_single,
26142641 .empty_struct,
2642 .inferred_alloc,
26152643 => unreachable,
26162644 };
26172645 }
......@@ -2686,6 +2714,7 @@ pub const Type = extern union {
26862714 .error_set,
26872715 .error_set_single,
26882716 .empty_struct,
2717 .inferred_alloc,
26892718 => false,
26902719 };
26912720 }
......@@ -2778,6 +2807,7 @@ pub const Type = extern union {
27782807 ty = ty.castTag(.pointer).?.data.pointee_type;
27792808 continue;
27802809 },
2810 .inferred_alloc => unreachable,
27812811 };
27822812 }
27832813
......@@ -2846,6 +2876,7 @@ pub const Type = extern union {
28462876 .error_set,
28472877 .error_set_single,
28482878 .empty_struct,
2879 .inferred_alloc,
28492880 => return false,
28502881
28512882 .c_const_pointer,
......@@ -2931,6 +2962,7 @@ pub const Type = extern union {
29312962 .c_const_pointer,
29322963 .c_mut_pointer,
29332964 .pointer,
2965 .inferred_alloc,
29342966 => unreachable,
29352967
29362968 .empty_struct => self.castTag(.empty_struct).?.data,
......@@ -3068,6 +3100,10 @@ pub const Type = extern union {
30683100 error_set,
30693101 error_set_single,
30703102 empty_struct,
3103 /// This is a special value that tracks a set of types that have been stored
3104 /// to an inferred allocation. It does not support most of the normal type queries.
3105 /// However it does respond to `isConstPtr`, `ptrSize`, `zigTypeTag`, etc.
3106 inferred_alloc,
30713107
30723108 pub const last_no_payload_tag = Tag.const_slice_u8;
30733109 pub const no_payload_count = @enumToInt(last_no_payload_tag) + 1;
......@@ -3148,6 +3184,7 @@ pub const Type = extern union {
31483184 .error_set => Payload.Decl,
31493185 .error_set_single => Payload.Name,
31503186 .empty_struct => Payload.ContainerScope,
3187 .inferred_alloc => Payload.InferredAlloc,
31513188 };
31523189 }
31533190
......@@ -3261,6 +3298,13 @@ pub const Type = extern union {
32613298 base: Payload,
32623299 data: *Module.Scope.Container,
32633300 };
3301
3302 pub const InferredAlloc = struct {
3303 pub const base_tag = Tag.inferred_alloc;
3304
3305 base: Payload = .{ .tag = base_tag },
3306 data: *Value.Payload.InferredAlloc,
3307 };
32643308 };
32653309};
32663310
src/value.zig+36
......@@ -7,6 +7,7 @@ const BigIntMutable = std.math.big.int.Mutable;
77const Target = std.Target;
88const Allocator = std.mem.Allocator;
99const Module = @import("Module.zig");
10const ir = @import("ir.zig");
1011
1112/// This is the raw data, with no bookkeeping, no memory awareness,
1213/// no de-duplication, and no type system awareness.
......@@ -101,6 +102,9 @@ pub const Value = extern union {
101102 enum_literal,
102103 error_set,
103104 @"error",
105 /// This is a special value that tracks a set of types that have been stored
106 /// to an inferred allocation. It does not support any of the normal value queries.
107 inferred_alloc,
104108
105109 pub const last_no_payload_tag = Tag.bool_false;
106110 pub const no_payload_count = @enumToInt(last_no_payload_tag) + 1;
......@@ -189,6 +193,7 @@ pub const Value = extern union {
189193 .float_128 => Payload.Float_128,
190194 .error_set => Payload.ErrorSet,
191195 .@"error" => Payload.Error,
196 .inferred_alloc => Payload.InferredAlloc,
192197 };
193198 }
194199
......@@ -383,6 +388,8 @@ pub const Value = extern union {
383388
384389 // memory is managed by the declaration
385390 .error_set => return self.copyPayloadShallow(allocator, Payload.ErrorSet),
391
392 .inferred_alloc => unreachable,
386393 }
387394 }
388395
......@@ -501,6 +508,7 @@ pub const Value = extern union {
501508 return out_stream.writeAll("}");
502509 },
503510 .@"error" => return out_stream.print("error.{}", .{val.castTag(.@"error").?.data.name}),
511 .inferred_alloc => return out_stream.writeAll("(inferred allocation value)"),
504512 };
505513 }
506514
......@@ -613,6 +621,7 @@ pub const Value = extern union {
613621 .enum_literal,
614622 .@"error",
615623 .empty_struct_value,
624 .inferred_alloc,
616625 => unreachable,
617626 };
618627 }
......@@ -683,6 +692,7 @@ pub const Value = extern union {
683692 .error_set,
684693 .@"error",
685694 .empty_struct_value,
695 .inferred_alloc,
686696 => unreachable,
687697
688698 .undef => unreachable,
......@@ -768,6 +778,7 @@ pub const Value = extern union {
768778 .error_set,
769779 .@"error",
770780 .empty_struct_value,
781 .inferred_alloc,
771782 => unreachable,
772783
773784 .undef => unreachable,
......@@ -853,6 +864,7 @@ pub const Value = extern union {
853864 .error_set,
854865 .@"error",
855866 .empty_struct_value,
867 .inferred_alloc,
856868 => unreachable,
857869
858870 .undef => unreachable,
......@@ -966,6 +978,7 @@ pub const Value = extern union {
966978 .error_set,
967979 .@"error",
968980 .empty_struct_value,
981 .inferred_alloc,
969982 => unreachable,
970983
971984 .zero,
......@@ -1055,6 +1068,7 @@ pub const Value = extern union {
10551068 .error_set,
10561069 .@"error",
10571070 .empty_struct_value,
1071 .inferred_alloc,
10581072 => unreachable,
10591073
10601074 .zero,
......@@ -1213,6 +1227,7 @@ pub const Value = extern union {
12131227 .error_set,
12141228 .@"error",
12151229 .empty_struct_value,
1230 .inferred_alloc,
12161231 => unreachable,
12171232
12181233 .zero,
......@@ -1289,6 +1304,7 @@ pub const Value = extern union {
12891304 .error_set,
12901305 .@"error",
12911306 .empty_struct_value,
1307 .inferred_alloc,
12921308 => unreachable,
12931309
12941310 .zero,
......@@ -1525,6 +1541,8 @@ pub const Value = extern union {
15251541 hasher.update(payload.name);
15261542 std.hash.autoHash(&hasher, payload.value);
15271543 },
1544
1545 .inferred_alloc => unreachable,
15281546 }
15291547 return hasher.final();
15301548 }
......@@ -1602,6 +1620,7 @@ pub const Value = extern union {
16021620 .error_set,
16031621 .@"error",
16041622 .empty_struct_value,
1623 .inferred_alloc,
16051624 => unreachable,
16061625
16071626 .ref_val => self.castTag(.ref_val).?.data,
......@@ -1687,6 +1706,7 @@ pub const Value = extern union {
16871706 .error_set,
16881707 .@"error",
16891708 .empty_struct_value,
1709 .inferred_alloc,
16901710 => unreachable,
16911711
16921712 .empty_array => unreachable, // out of bounds array index
......@@ -1793,6 +1813,7 @@ pub const Value = extern union {
17931813
17941814 .undef => unreachable,
17951815 .unreachable_value => unreachable,
1816 .inferred_alloc => unreachable,
17961817 .null_value => true,
17971818 };
17981819 }
......@@ -1801,6 +1822,7 @@ pub const Value = extern union {
18011822 pub fn isFloat(self: Value) bool {
18021823 return switch (self.tag()) {
18031824 .undef => unreachable,
1825 .inferred_alloc => unreachable,
18041826
18051827 .float_16,
18061828 .float_32,
......@@ -1890,6 +1912,7 @@ pub const Value = extern union {
18901912
18911913 .undef => unreachable,
18921914 .unreachable_value => unreachable,
1915 .inferred_alloc => unreachable,
18931916 };
18941917 }
18951918
......@@ -2020,6 +2043,19 @@ pub const Value = extern union {
20202043 value: u16,
20212044 },
20222045 };
2046
2047 pub const InferredAlloc = struct {
2048 pub const base_tag = Tag.inferred_alloc;
2049
2050 base: Payload = .{ .tag = base_tag },
2051 data: struct {
2052 /// The value stored in the inferred allocation. This will go into
2053 /// peer type resolution. This is stored in a separate list so that
2054 /// the items are contiguous in memory and thus can be passed to
2055 /// `Module.resolvePeerTypes`.
2056 stored_inst_list: std.ArrayListUnmanaged(*ir.Inst) = .{},
2057 },
2058 };
20232059 };
20242060
20252061 /// Big enough to fit any non-BigInt value
src/zir.zig+12
......@@ -241,12 +241,20 @@ pub const Inst = struct {
241241 const_slice_type,
242242 /// Create a pointer type with attributes
243243 ptr_type,
244 /// Each `store_to_inferred_ptr` puts the type of the stored value into a set,
245 /// and then `resolve_inferred_alloc` triggers peer type resolution on the set.
246 /// The operand is a `alloc_inferred` or `alloc_inferred_mut` instruction, which
247 /// is the allocation that needs to have its type inferred.
248 resolve_inferred_alloc,
244249 /// Slice operation `array_ptr[start..end:sentinel]`
245250 slice,
246251 /// Slice operation with just start `lhs[rhs..]`
247252 slice_start,
248253 /// Write a value to a pointer. For loading, see `deref`.
249254 store,
255 /// Same as `store` but the type of the value being stored will be used to infer
256 /// the pointer type.
257 store_to_inferred_ptr,
250258 /// String Literal. Makes an anonymous Decl and then takes a pointer to it.
251259 str,
252260 /// Arithmetic subtraction. Asserts no integer overflow.
......@@ -319,6 +327,7 @@ pub const Inst = struct {
319327 .ref,
320328 .bitcast_ref,
321329 .typeof,
330 .resolve_inferred_alloc,
322331 .single_const_ptr_type,
323332 .single_mut_ptr_type,
324333 .many_const_ptr_type,
......@@ -355,6 +364,7 @@ pub const Inst = struct {
355364 .shl,
356365 .shr,
357366 .store,
367 .store_to_inferred_ptr,
358368 .sub,
359369 .subwrap,
360370 .cmp_lt,
......@@ -498,6 +508,7 @@ pub const Inst = struct {
498508 .mut_slice_type,
499509 .const_slice_type,
500510 .store,
511 .store_to_inferred_ptr,
501512 .str,
502513 .sub,
503514 .subwrap,
......@@ -522,6 +533,7 @@ pub const Inst = struct {
522533 .import,
523534 .switch_range,
524535 .typeof_peer,
536 .resolve_inferred_alloc,
525537 => false,
526538
527539 .@"break",
src/zir_sema.zig+59-2
......@@ -10,10 +10,12 @@
1010const std = @import("std");
1111const mem = std.mem;
1212const Allocator = std.mem.Allocator;
13const assert = std.debug.assert;
14const log = std.log.scoped(.sema);
15
1316const Value = @import("value.zig").Value;
1417const Type = @import("type.zig").Type;
1518const TypedValue = @import("TypedValue.zig");
16const assert = std.debug.assert;
1719const ir = @import("ir.zig");
1820const zir = @import("zir.zig");
1921const Module = @import("Module.zig");
......@@ -55,8 +57,10 @@ pub fn analyzeInst(mod: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError!
5557 .ensure_result_non_error => return analyzeInstEnsureResultNonError(mod, scope, old_inst.castTag(.ensure_result_non_error).?),
5658 .ensure_indexable => return analyzeInstEnsureIndexable(mod, scope, old_inst.castTag(.ensure_indexable).?),
5759 .ref => return analyzeInstRef(mod, scope, old_inst.castTag(.ref).?),
60 .resolve_inferred_alloc => return analyzeInstResolveInferredAlloc(mod, scope, old_inst.castTag(.resolve_inferred_alloc).?),
5861 .ret_ptr => return analyzeInstRetPtr(mod, scope, old_inst.castTag(.ret_ptr).?),
5962 .ret_type => return analyzeInstRetType(mod, scope, old_inst.castTag(.ret_type).?),
63 .store_to_inferred_ptr => return analyzeInstStoreToInferredPtr(mod, scope, old_inst.castTag(.store_to_inferred_ptr).?),
6064 .single_const_ptr_type => return analyzeInstSimplePtrType(mod, scope, old_inst.castTag(.single_const_ptr_type).?, false, .One),
6165 .single_mut_ptr_type => return analyzeInstSimplePtrType(mod, scope, old_inst.castTag(.single_mut_ptr_type).?, true, .One),
6266 .many_const_ptr_type => return analyzeInstSimplePtrType(mod, scope, old_inst.castTag(.many_const_ptr_type).?, false, .Many),
......@@ -428,13 +432,66 @@ fn analyzeInstAllocMut(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerE
428432}
429433
430434fn analyzeInstAllocInferred(mod: *Module, scope: *Scope, inst: *zir.Inst.NoOp) InnerError!*Inst {
431 return mod.fail(scope, inst.base.src, "TODO implement analyzeInstAllocInferred", .{});
435 const val_payload = try scope.arena().create(Value.Payload.InferredAlloc);
436 val_payload.* = .{
437 .data = .{},
438 };
439 // `Module.constInst` does not add the instruction to the block because it is
440 // not needed in the case of constant values. However here, we plan to "downgrade"
441 // to a normal instruction when we hit `resolve_inferred_alloc`. So we append
442 // to the block even though it is currently a `.constant`.
443 const result = try mod.constInst(scope, inst.base.src, .{
444 .ty = try Type.Tag.inferred_alloc.create(scope.arena(), val_payload),
445 .val = Value.initPayload(&val_payload.base),
446 });
447 const block = try mod.requireFunctionBlock(scope, inst.base.src);
448 try block.instructions.append(mod.gpa, result);
449 return result;
432450}
433451
434452fn analyzeInstAllocInferredMut(mod: *Module, scope: *Scope, inst: *zir.Inst.NoOp) InnerError!*Inst {
435453 return mod.fail(scope, inst.base.src, "TODO implement analyzeInstAllocInferredMut", .{});
436454}
437455
456fn analyzeInstResolveInferredAlloc(
457 mod: *Module,
458 scope: *Scope,
459 inst: *zir.Inst.UnOp,
460) InnerError!*Inst {
461 const ptr = try resolveInst(mod, scope, inst.positionals.operand);
462 const ptr_val = ptr.castTag(.constant).?.val;
463 const inferred_alloc = ptr_val.castTag(.inferred_alloc).?;
464 const peer_inst_list = inferred_alloc.data.stored_inst_list.items;
465 const final_elem_ty = try mod.resolvePeerTypes(scope, peer_inst_list);
466 const is_mut = true;
467 const final_ptr_ty = try mod.simplePtrType(scope, inst.base.src, final_elem_ty, is_mut, .One);
468
469 // Change it to a normal alloc.
470 ptr.ty = final_ptr_ty;
471 ptr.tag = .alloc;
472
473 return mod.constVoid(scope, inst.base.src);
474}
475
476fn analyzeInstStoreToInferredPtr(
477 mod: *Module,
478 scope: *Scope,
479 inst: *zir.Inst.BinOp,
480) InnerError!*Inst {
481 const ptr = try resolveInst(mod, scope, inst.positionals.lhs);
482 const value = try resolveInst(mod, scope, inst.positionals.rhs);
483 const inferred_alloc = ptr.castTag(.constant).?.val.castTag(.inferred_alloc).?;
484 // Add the stored instruction to the set we will use to resolve peer types
485 // for the inferred allocation.
486 try inferred_alloc.data.stored_inst_list.append(scope.arena(), value);
487 // Create a new alloc with exactly the type the pointer wants.
488 // Later it gets cleaned up by aliasing the alloc we are supposed to be storing to.
489 const ptr_ty = try mod.simplePtrType(scope, inst.base.src, value.ty, true, .One);
490 const b = try mod.requireRuntimeBlock(scope, inst.base.src);
491 const bitcasted_ptr = try mod.addUnOp(b, inst.base.src, ptr_ty, .bitcast, ptr);
492 return mod.storePtr(scope, inst.base.src, bitcasted_ptr, value);
493}
494
438495fn analyzeInstStore(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError!*Inst {
439496 const ptr = try resolveInst(mod, scope, inst.positionals.lhs);
440497 const value = try resolveInst(mod, scope, inst.positionals.rhs);
test/stage2/cbe.zig+15
......@@ -51,6 +51,21 @@ pub fn addCases(ctx: *TestContext) !void {
5151 , "");
5252 }
5353
54 {
55 var case = ctx.exeFromCompiledC("inferred local const", .{});
56
57 case.addCompareOutput(
58 \\fn add(a: i32, b: i32) i32 {
59 \\ return a + b;
60 \\}
61 \\
62 \\export fn main() c_int {
63 \\ const x = add(1, 2);
64 \\ return x - 3;
65 \\}
66 , "");
67 }
68
5469 ctx.c("empty start function", linux_x64,
5570 \\export fn _start() noreturn {
5671 \\ unreachable;