diff options
| author | Andrew Kelley <andrew@ziglang.org> | 2020-12-31 01:54:02 -0700 |
|---|---|---|
| committer | Andrew Kelley <andrew@ziglang.org> | 2020-12-31 01:54:02 -0700 |
| commit | a46d24af1cf2885991c67bf39a2e639891c16121 (patch) | |
| tree | d4f3c81b798cbae420819434c810a52b6817a85b /src/value.zig | |
| parent | 3f7d9b5fc19e4081236b3b63aebbc80e1b17f5b5 (diff) | |
| download | zig-a46d24af1cf2885991c67bf39a2e639891c16121.tar.gz zig-a46d24af1cf2885991c67bf39a2e639891c16121.zip | |
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.
Diffstat (limited to 'src/value.zig')
| -rw-r--r-- | src/value.zig | 36 |
1 files changed, 36 insertions, 0 deletions
diff --git a/src/value.zig b/src/value.zig index 91b21511d4..4450099069 100644 --- a/src/value.zig +++ b/src/value.zig @@ -7,6 +7,7 @@ const BigIntMutable = std.math.big.int.Mutable; const Target = std.Target; const Allocator = std.mem.Allocator; const Module = @import("Module.zig"); +const ir = @import("ir.zig"); /// This is the raw data, with no bookkeeping, no memory awareness, /// no de-duplication, and no type system awareness. @@ -101,6 +102,9 @@ pub const Value = extern union { enum_literal, error_set, @"error", + /// 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. + inferred_alloc, pub const last_no_payload_tag = Tag.bool_false; pub const no_payload_count = @enumToInt(last_no_payload_tag) + 1; @@ -189,6 +193,7 @@ pub const Value = extern union { .float_128 => Payload.Float_128, .error_set => Payload.ErrorSet, .@"error" => Payload.Error, + .inferred_alloc => Payload.InferredAlloc, }; } @@ -383,6 +388,8 @@ pub const Value = extern union { // memory is managed by the declaration .error_set => return self.copyPayloadShallow(allocator, Payload.ErrorSet), + + .inferred_alloc => unreachable, } } @@ -501,6 +508,7 @@ pub const Value = extern union { return out_stream.writeAll("}"); }, .@"error" => return out_stream.print("error.{}", .{val.castTag(.@"error").?.data.name}), + .inferred_alloc => return out_stream.writeAll("(inferred allocation value)"), }; } @@ -613,6 +621,7 @@ pub const Value = extern union { .enum_literal, .@"error", .empty_struct_value, + .inferred_alloc, => unreachable, }; } @@ -683,6 +692,7 @@ pub const Value = extern union { .error_set, .@"error", .empty_struct_value, + .inferred_alloc, => unreachable, .undef => unreachable, @@ -768,6 +778,7 @@ pub const Value = extern union { .error_set, .@"error", .empty_struct_value, + .inferred_alloc, => unreachable, .undef => unreachable, @@ -853,6 +864,7 @@ pub const Value = extern union { .error_set, .@"error", .empty_struct_value, + .inferred_alloc, => unreachable, .undef => unreachable, @@ -966,6 +978,7 @@ pub const Value = extern union { .error_set, .@"error", .empty_struct_value, + .inferred_alloc, => unreachable, .zero, @@ -1055,6 +1068,7 @@ pub const Value = extern union { .error_set, .@"error", .empty_struct_value, + .inferred_alloc, => unreachable, .zero, @@ -1213,6 +1227,7 @@ pub const Value = extern union { .error_set, .@"error", .empty_struct_value, + .inferred_alloc, => unreachable, .zero, @@ -1289,6 +1304,7 @@ pub const Value = extern union { .error_set, .@"error", .empty_struct_value, + .inferred_alloc, => unreachable, .zero, @@ -1525,6 +1541,8 @@ pub const Value = extern union { hasher.update(payload.name); std.hash.autoHash(&hasher, payload.value); }, + + .inferred_alloc => unreachable, } return hasher.final(); } @@ -1602,6 +1620,7 @@ pub const Value = extern union { .error_set, .@"error", .empty_struct_value, + .inferred_alloc, => unreachable, .ref_val => self.castTag(.ref_val).?.data, @@ -1687,6 +1706,7 @@ pub const Value = extern union { .error_set, .@"error", .empty_struct_value, + .inferred_alloc, => unreachable, .empty_array => unreachable, // out of bounds array index @@ -1793,6 +1813,7 @@ pub const Value = extern union { .undef => unreachable, .unreachable_value => unreachable, + .inferred_alloc => unreachable, .null_value => true, }; } @@ -1801,6 +1822,7 @@ pub const Value = extern union { pub fn isFloat(self: Value) bool { return switch (self.tag()) { .undef => unreachable, + .inferred_alloc => unreachable, .float_16, .float_32, @@ -1890,6 +1912,7 @@ pub const Value = extern union { .undef => unreachable, .unreachable_value => unreachable, + .inferred_alloc => unreachable, }; } @@ -2020,6 +2043,19 @@ pub const Value = extern union { value: u16, }, }; + + pub const InferredAlloc = struct { + pub const base_tag = Tag.inferred_alloc; + + base: Payload = .{ .tag = base_tag }, + data: struct { + /// The value stored in the inferred allocation. This will go into + /// peer type resolution. This is stored in a separate list so that + /// the items are contiguous in memory and thus can be passed to + /// `Module.resolvePeerTypes`. + stored_inst_list: std.ArrayListUnmanaged(*ir.Inst) = .{}, + }, + }; }; /// Big enough to fit any non-BigInt value |
