Commit a46d24af1c
Changed files (11)
test
stage2
src/codegen/c.zig
@@ -275,6 +275,7 @@ pub fn generate(file: *C, module: *Module, decl: *Decl) !void {
try writer.writeAll(" {");
const func: *Module.Fn = func_payload.data;
+ //func.dump(module.*);
const instructions = func.analysis.success.instructions;
if (instructions.len > 0) {
try writer.writeAll("\n");
@@ -285,6 +286,7 @@ pub fn generate(file: *C, module: *Module, decl: *Decl) !void {
.arg => try genArg(&ctx),
.assembly => try genAsm(&ctx, file, inst.castTag(.assembly).?),
.block => try genBlock(&ctx, file, inst.castTag(.block).?),
+ .bitcast => try genBitcast(&ctx, file, inst.castTag(.bitcast).?),
.breakpoint => try genBreakpoint(file, inst.castTag(.breakpoint).?),
.call => try genCall(&ctx, file, inst.castTag(.call).?),
.cmp_eq => try genBinOp(&ctx, file, inst.castTag(.cmp_eq).?, "=="),
@@ -537,6 +539,24 @@ fn genBlock(ctx: *Context, file: *C, inst: *Inst.Block) !?[]u8 {
return ctx.fail(ctx.decl.src(), "TODO: C backend: implement blocks", .{});
}
+fn genBitcast(ctx: *Context, file: *C, inst: *Inst.UnOp) !?[]u8 {
+ const writer = file.main.writer();
+ try indent(file);
+ const local_name = try ctx.name();
+ const operand = try ctx.resolveInst(inst.operand);
+ try renderTypeAndName(ctx, writer, inst.base.ty, local_name, .Const);
+ if (inst.base.ty.zigTypeTag() == .Pointer and inst.operand.ty.zigTypeTag() == .Pointer) {
+ try writer.writeAll(" = (");
+ try renderType(ctx, writer, inst.base.ty);
+ try writer.print("){s};\n", .{operand});
+ } else {
+ try writer.writeAll(";\n");
+ try indent(file);
+ try writer.print("memcpy(&{s}, &{s}, sizeof {s});\n", .{ local_name, operand, local_name });
+ }
+ return local_name;
+}
+
fn genBreakpoint(file: *C, inst: *Inst.NoOp) !?[]u8 {
try indent(file);
try file.main.writer().writeAll("zig_breakpoint();\n");
src/link/cbe.h
@@ -41,4 +41,4 @@
#include <stdint.h>
#define int128_t __int128
#define uint128_t unsigned __int128
-
+#include <string.h>
src/astgen.zig
@@ -585,6 +585,7 @@ fn varDecl(
switch (tree.token_ids[node.mut_token]) {
.Keyword_const => {
+ var resolve_inferred_alloc: ?*zir.Inst = null;
// Depending on the type of AST the initialization expression is, we may need an lvalue
// or an rvalue as a result location. If it is an rvalue, we can use the instruction as
// the variable, no memory location needed.
@@ -595,6 +596,7 @@ fn varDecl(
break :r ResultLoc{ .ptr = alloc };
} else {
const alloc = try addZIRNoOpT(mod, scope, name_src, .alloc_inferred);
+ resolve_inferred_alloc = &alloc.base;
break :r ResultLoc{ .inferred_ptr = alloc };
}
} else r: {
@@ -604,6 +606,9 @@ fn varDecl(
break :r .none;
};
const init_inst = try expr(mod, scope, result_loc, init_node);
+ if (resolve_inferred_alloc) |inst| {
+ _ = try addZIRUnOp(mod, scope, name_src, .resolve_inferred_alloc, inst);
+ }
const sub_scope = try block_arena.create(Scope.LocalVal);
sub_scope.* = .{
.parent = scope,
@@ -614,15 +619,20 @@ fn varDecl(
return &sub_scope.base;
},
.Keyword_var => {
+ var resolve_inferred_alloc: ?*zir.Inst = null;
const var_data: struct { result_loc: ResultLoc, alloc: *zir.Inst } = if (node.getTypeNode()) |type_node| a: {
const type_inst = try typeExpr(mod, scope, type_node);
const alloc = try addZIRUnOp(mod, scope, name_src, .alloc_mut, type_inst);
break :a .{ .alloc = alloc, .result_loc = .{ .ptr = alloc } };
} else a: {
const alloc = try addZIRNoOp(mod, scope, name_src, .alloc_inferred_mut);
+ resolve_inferred_alloc = alloc;
break :a .{ .alloc = alloc, .result_loc = .{ .inferred_ptr = alloc.castTag(.alloc_inferred_mut).? } };
};
const init_inst = try expr(mod, scope, var_data.result_loc, init_node);
+ if (resolve_inferred_alloc) |inst| {
+ _ = try addZIRUnOp(mod, scope, name_src, .resolve_inferred_alloc, inst);
+ }
const sub_scope = try block_arena.create(Scope.LocalPtr);
sub_scope.* = .{
.parent = scope,
@@ -2717,7 +2727,8 @@ fn rlWrap(mod: *Module, scope: *Scope, rl: ResultLoc, result: *zir.Inst) InnerEr
return mod.fail(scope, result.src, "TODO implement rlWrap .bitcasted_ptr", .{});
},
.inferred_ptr => |alloc| {
- return addZIRBinOp(mod, scope, result.src, .store, &alloc.base, result);
+ _ = try addZIRBinOp(mod, scope, result.src, .store_to_inferred_ptr, &alloc.base, result);
+ return result;
},
.block_ptr => |block_ptr| {
return mod.fail(scope, result.src, "TODO implement rlWrap .block_ptr", .{});
src/ir.zig
@@ -196,7 +196,7 @@ pub const Inst = struct {
pub fn value(base: *Inst) ?Value {
if (base.ty.onePossibleValue()) |opv| return opv;
- const inst = base.cast(Constant) orelse return null;
+ const inst = base.castTag(.constant) orelse return null;
return inst.val;
}
src/Module.zig
@@ -3189,7 +3189,14 @@ pub fn floatSub(
}
}
-pub fn simplePtrType(self: *Module, scope: *Scope, src: usize, elem_ty: Type, mutable: bool, size: std.builtin.TypeInfo.Pointer.Size) Allocator.Error!Type {
+pub fn simplePtrType(
+ self: *Module,
+ scope: *Scope,
+ src: usize,
+ elem_ty: Type,
+ mutable: bool,
+ size: std.builtin.TypeInfo.Pointer.Size,
+) Allocator.Error!Type {
if (!mutable and size == .Slice and elem_ty.eql(Type.initTag(.u8))) {
return Type.initTag(.const_slice_u8);
}
src/test.zig
@@ -782,6 +782,7 @@ pub const TestContext = struct {
"-std=c89",
"-pedantic",
"-Werror",
+ "-Wno-declaration-after-statement",
"--",
"-lc",
exe_path,
src/type.zig
@@ -78,6 +78,7 @@ pub const Type = extern union {
.const_slice,
.mut_slice,
.pointer,
+ .inferred_alloc,
=> return .Pointer,
.optional,
@@ -158,6 +159,8 @@ pub const Type = extern union {
.optional_single_mut_pointer,
=> self.cast(Payload.ElemType),
+ .inferred_alloc => unreachable,
+
else => null,
};
}
@@ -384,6 +387,7 @@ pub const Type = extern union {
.enum_literal,
.anyerror_void_error_union,
.@"anyframe",
+ .inferred_alloc,
=> unreachable,
.array_u8,
@@ -686,6 +690,7 @@ pub const Type = extern union {
const name = ty.castTag(.error_set_single).?.data;
return out_stream.print("error{{{s}}}", .{name});
},
+ .inferred_alloc => return out_stream.writeAll("(inferred allocation type)"),
}
unreachable;
}
@@ -733,6 +738,7 @@ pub const Type = extern union {
.single_const_pointer_to_comptime_int => return Value.initTag(.single_const_pointer_to_comptime_int_type),
.const_slice_u8 => return Value.initTag(.const_slice_u8_type),
.enum_literal => return Value.initTag(.enum_literal_type),
+ .inferred_alloc => unreachable,
else => return Value.Tag.ty.create(allocator, self),
}
}
@@ -803,6 +809,8 @@ pub const Type = extern union {
.enum_literal,
.empty_struct,
=> false,
+
+ .inferred_alloc => unreachable,
};
}
@@ -920,6 +928,7 @@ pub const Type = extern union {
.@"undefined",
.enum_literal,
.empty_struct,
+ .inferred_alloc,
=> unreachable,
};
}
@@ -943,6 +952,7 @@ pub const Type = extern union {
.enum_literal => unreachable,
.single_const_pointer_to_comptime_int => unreachable,
.empty_struct => unreachable,
+ .inferred_alloc => unreachable,
.u8,
.i8,
@@ -1121,6 +1131,7 @@ pub const Type = extern union {
.single_const_pointer,
.single_mut_pointer,
.single_const_pointer_to_comptime_int,
+ .inferred_alloc,
=> true,
.pointer => self.castTag(.pointer).?.data.size == .One,
@@ -1203,6 +1214,7 @@ pub const Type = extern union {
.single_const_pointer,
.single_mut_pointer,
.single_const_pointer_to_comptime_int,
+ .inferred_alloc,
=> .One,
.pointer => self.castTag(.pointer).?.data.size,
@@ -1273,6 +1285,7 @@ pub const Type = extern union {
.error_set,
.error_set_single,
.empty_struct,
+ .inferred_alloc,
=> false,
.const_slice,
@@ -1345,6 +1358,7 @@ pub const Type = extern union {
.error_set,
.error_set_single,
.empty_struct,
+ .inferred_alloc,
=> false,
.single_const_pointer,
@@ -1426,6 +1440,7 @@ pub const Type = extern union {
.error_set,
.error_set_single,
.empty_struct,
+ .inferred_alloc,
=> false,
.pointer => {
@@ -1502,6 +1517,7 @@ pub const Type = extern union {
.error_set,
.error_set_single,
.empty_struct,
+ .inferred_alloc,
=> false,
.pointer => {
@@ -1569,58 +1585,58 @@ pub const Type = extern union {
/// Asserts the type is a pointer or array type.
pub fn elemType(self: Type) Type {
return switch (self.tag()) {
- .u8,
- .i8,
- .u16,
- .i16,
- .u32,
- .i32,
- .u64,
- .i64,
- .usize,
- .isize,
- .c_short,
- .c_ushort,
- .c_int,
- .c_uint,
- .c_long,
- .c_ulong,
- .c_longlong,
- .c_ulonglong,
- .c_longdouble,
- .f16,
- .f32,
- .f64,
- .f128,
- .c_void,
- .bool,
- .void,
- .type,
- .anyerror,
- .comptime_int,
- .comptime_float,
- .noreturn,
- .@"null",
- .@"undefined",
- .fn_noreturn_no_args,
- .fn_void_no_args,
- .fn_naked_noreturn_no_args,
- .fn_ccc_void_no_args,
- .function,
- .int_unsigned,
- .int_signed,
- .optional,
- .optional_single_const_pointer,
- .optional_single_mut_pointer,
- .enum_literal,
- .error_union,
- .@"anyframe",
- .anyframe_T,
- .anyerror_void_error_union,
- .error_set,
- .error_set_single,
- .empty_struct,
- => unreachable,
+ .u8 => unreachable,
+ .i8 => unreachable,
+ .u16 => unreachable,
+ .i16 => unreachable,
+ .u32 => unreachable,
+ .i32 => unreachable,
+ .u64 => unreachable,
+ .i64 => unreachable,
+ .usize => unreachable,
+ .isize => unreachable,
+ .c_short => unreachable,
+ .c_ushort => unreachable,
+ .c_int => unreachable,
+ .c_uint => unreachable,
+ .c_long => unreachable,
+ .c_ulong => unreachable,
+ .c_longlong => unreachable,
+ .c_ulonglong => unreachable,
+ .c_longdouble => unreachable,
+ .f16 => unreachable,
+ .f32 => unreachable,
+ .f64 => unreachable,
+ .f128 => unreachable,
+ .c_void => unreachable,
+ .bool => unreachable,
+ .void => unreachable,
+ .type => unreachable,
+ .anyerror => unreachable,
+ .comptime_int => unreachable,
+ .comptime_float => unreachable,
+ .noreturn => unreachable,
+ .@"null" => unreachable,
+ .@"undefined" => unreachable,
+ .fn_noreturn_no_args => unreachable,
+ .fn_void_no_args => unreachable,
+ .fn_naked_noreturn_no_args => unreachable,
+ .fn_ccc_void_no_args => unreachable,
+ .function => unreachable,
+ .int_unsigned => unreachable,
+ .int_signed => unreachable,
+ .optional => unreachable,
+ .optional_single_const_pointer => unreachable,
+ .optional_single_mut_pointer => unreachable,
+ .enum_literal => unreachable,
+ .error_union => unreachable,
+ .@"anyframe" => unreachable,
+ .anyframe_T => unreachable,
+ .anyerror_void_error_union => unreachable,
+ .error_set => unreachable,
+ .error_set_single => unreachable,
+ .empty_struct => unreachable,
+ .inferred_alloc => unreachable,
.array => self.castTag(.array).?.data.elem_type,
.array_sentinel => self.castTag(.array_sentinel).?.data.elem_type,
@@ -1742,6 +1758,7 @@ pub const Type = extern union {
.error_set,
.error_set_single,
.empty_struct,
+ .inferred_alloc,
=> unreachable,
.array => self.castTag(.array).?.data.len,
@@ -1808,6 +1825,7 @@ pub const Type = extern union {
.error_set,
.error_set_single,
.empty_struct,
+ .inferred_alloc,
=> unreachable,
.single_const_pointer,
@@ -1891,6 +1909,7 @@ pub const Type = extern union {
.error_set,
.error_set_single,
.empty_struct,
+ .inferred_alloc,
=> false,
.int_signed,
@@ -1966,6 +1985,7 @@ pub const Type = extern union {
.error_set,
.error_set_single,
.empty_struct,
+ .inferred_alloc,
=> false,
.int_unsigned,
@@ -2031,6 +2051,7 @@ pub const Type = extern union {
.error_set,
.error_set_single,
.empty_struct,
+ .inferred_alloc,
=> unreachable,
.int_unsigned => .{
@@ -2120,6 +2141,7 @@ pub const Type = extern union {
.error_set,
.error_set_single,
.empty_struct,
+ .inferred_alloc,
=> false,
.usize,
@@ -2232,6 +2254,7 @@ pub const Type = extern union {
.error_set,
.error_set_single,
.empty_struct,
+ .inferred_alloc,
=> unreachable,
};
}
@@ -2310,6 +2333,7 @@ pub const Type = extern union {
.error_set,
.error_set_single,
.empty_struct,
+ .inferred_alloc,
=> unreachable,
}
}
@@ -2387,6 +2411,7 @@ pub const Type = extern union {
.error_set,
.error_set_single,
.empty_struct,
+ .inferred_alloc,
=> unreachable,
}
}
@@ -2464,6 +2489,7 @@ pub const Type = extern union {
.error_set,
.error_set_single,
.empty_struct,
+ .inferred_alloc,
=> unreachable,
};
}
@@ -2538,6 +2564,7 @@ pub const Type = extern union {
.error_set,
.error_set_single,
.empty_struct,
+ .inferred_alloc,
=> unreachable,
};
}
@@ -2612,6 +2639,7 @@ pub const Type = extern union {
.error_set,
.error_set_single,
.empty_struct,
+ .inferred_alloc,
=> unreachable,
};
}
@@ -2686,6 +2714,7 @@ pub const Type = extern union {
.error_set,
.error_set_single,
.empty_struct,
+ .inferred_alloc,
=> false,
};
}
@@ -2778,6 +2807,7 @@ pub const Type = extern union {
ty = ty.castTag(.pointer).?.data.pointee_type;
continue;
},
+ .inferred_alloc => unreachable,
};
}
@@ -2846,6 +2876,7 @@ pub const Type = extern union {
.error_set,
.error_set_single,
.empty_struct,
+ .inferred_alloc,
=> return false,
.c_const_pointer,
@@ -2931,6 +2962,7 @@ pub const Type = extern union {
.c_const_pointer,
.c_mut_pointer,
.pointer,
+ .inferred_alloc,
=> unreachable,
.empty_struct => self.castTag(.empty_struct).?.data,
@@ -3068,6 +3100,10 @@ pub const Type = extern union {
error_set,
error_set_single,
empty_struct,
+ /// 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.
+ inferred_alloc,
pub const last_no_payload_tag = Tag.const_slice_u8;
pub const no_payload_count = @enumToInt(last_no_payload_tag) + 1;
@@ -3148,6 +3184,7 @@ pub const Type = extern union {
.error_set => Payload.Decl,
.error_set_single => Payload.Name,
.empty_struct => Payload.ContainerScope,
+ .inferred_alloc => Payload.InferredAlloc,
};
}
@@ -3261,6 +3298,13 @@ pub const Type = extern union {
base: Payload,
data: *Module.Scope.Container,
};
+
+ pub const InferredAlloc = struct {
+ pub const base_tag = Tag.inferred_alloc;
+
+ base: Payload = .{ .tag = base_tag },
+ data: *Value.Payload.InferredAlloc,
+ };
};
};
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
src/zir.zig
@@ -241,12 +241,20 @@ pub const Inst = struct {
const_slice_type,
/// Create a pointer type with attributes
ptr_type,
+ /// 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.
+ resolve_inferred_alloc,
/// Slice operation `array_ptr[start..end:sentinel]`
slice,
/// Slice operation with just start `lhs[rhs..]`
slice_start,
/// Write a value to a pointer. For loading, see `deref`.
store,
+ /// Same as `store` but the type of the value being stored will be used to infer
+ /// the pointer type.
+ store_to_inferred_ptr,
/// String Literal. Makes an anonymous Decl and then takes a pointer to it.
str,
/// Arithmetic subtraction. Asserts no integer overflow.
@@ -319,6 +327,7 @@ pub const Inst = struct {
.ref,
.bitcast_ref,
.typeof,
+ .resolve_inferred_alloc,
.single_const_ptr_type,
.single_mut_ptr_type,
.many_const_ptr_type,
@@ -355,6 +364,7 @@ pub const Inst = struct {
.shl,
.shr,
.store,
+ .store_to_inferred_ptr,
.sub,
.subwrap,
.cmp_lt,
@@ -498,6 +508,7 @@ pub const Inst = struct {
.mut_slice_type,
.const_slice_type,
.store,
+ .store_to_inferred_ptr,
.str,
.sub,
.subwrap,
@@ -522,6 +533,7 @@ pub const Inst = struct {
.import,
.switch_range,
.typeof_peer,
+ .resolve_inferred_alloc,
=> false,
.@"break",
src/zir_sema.zig
@@ -10,10 +10,12 @@
const std = @import("std");
const mem = std.mem;
const Allocator = std.mem.Allocator;
+const assert = std.debug.assert;
+const log = std.log.scoped(.sema);
+
const Value = @import("value.zig").Value;
const Type = @import("type.zig").Type;
const TypedValue = @import("TypedValue.zig");
-const assert = std.debug.assert;
const ir = @import("ir.zig");
const zir = @import("zir.zig");
const Module = @import("Module.zig");
@@ -55,8 +57,10 @@ pub fn analyzeInst(mod: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError!
.ensure_result_non_error => return analyzeInstEnsureResultNonError(mod, scope, old_inst.castTag(.ensure_result_non_error).?),
.ensure_indexable => return analyzeInstEnsureIndexable(mod, scope, old_inst.castTag(.ensure_indexable).?),
.ref => return analyzeInstRef(mod, scope, old_inst.castTag(.ref).?),
+ .resolve_inferred_alloc => return analyzeInstResolveInferredAlloc(mod, scope, old_inst.castTag(.resolve_inferred_alloc).?),
.ret_ptr => return analyzeInstRetPtr(mod, scope, old_inst.castTag(.ret_ptr).?),
.ret_type => return analyzeInstRetType(mod, scope, old_inst.castTag(.ret_type).?),
+ .store_to_inferred_ptr => return analyzeInstStoreToInferredPtr(mod, scope, old_inst.castTag(.store_to_inferred_ptr).?),
.single_const_ptr_type => return analyzeInstSimplePtrType(mod, scope, old_inst.castTag(.single_const_ptr_type).?, false, .One),
.single_mut_ptr_type => return analyzeInstSimplePtrType(mod, scope, old_inst.castTag(.single_mut_ptr_type).?, true, .One),
.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
}
fn analyzeInstAllocInferred(mod: *Module, scope: *Scope, inst: *zir.Inst.NoOp) InnerError!*Inst {
- return mod.fail(scope, inst.base.src, "TODO implement analyzeInstAllocInferred", .{});
+ const val_payload = try scope.arena().create(Value.Payload.InferredAlloc);
+ val_payload.* = .{
+ .data = .{},
+ };
+ // `Module.constInst` does not add the instruction to the block because it is
+ // not needed in the case of constant values. However here, we plan to "downgrade"
+ // to a normal instruction when we hit `resolve_inferred_alloc`. So we append
+ // to the block even though it is currently a `.constant`.
+ const result = try mod.constInst(scope, inst.base.src, .{
+ .ty = try Type.Tag.inferred_alloc.create(scope.arena(), val_payload),
+ .val = Value.initPayload(&val_payload.base),
+ });
+ const block = try mod.requireFunctionBlock(scope, inst.base.src);
+ try block.instructions.append(mod.gpa, result);
+ return result;
}
fn analyzeInstAllocInferredMut(mod: *Module, scope: *Scope, inst: *zir.Inst.NoOp) InnerError!*Inst {
return mod.fail(scope, inst.base.src, "TODO implement analyzeInstAllocInferredMut", .{});
}
+fn analyzeInstResolveInferredAlloc(
+ mod: *Module,
+ scope: *Scope,
+ inst: *zir.Inst.UnOp,
+) InnerError!*Inst {
+ const ptr = try resolveInst(mod, scope, inst.positionals.operand);
+ const ptr_val = ptr.castTag(.constant).?.val;
+ const inferred_alloc = ptr_val.castTag(.inferred_alloc).?;
+ const peer_inst_list = inferred_alloc.data.stored_inst_list.items;
+ const final_elem_ty = try mod.resolvePeerTypes(scope, peer_inst_list);
+ const is_mut = true;
+ const final_ptr_ty = try mod.simplePtrType(scope, inst.base.src, final_elem_ty, is_mut, .One);
+
+ // Change it to a normal alloc.
+ ptr.ty = final_ptr_ty;
+ ptr.tag = .alloc;
+
+ return mod.constVoid(scope, inst.base.src);
+}
+
+fn analyzeInstStoreToInferredPtr(
+ mod: *Module,
+ scope: *Scope,
+ inst: *zir.Inst.BinOp,
+) InnerError!*Inst {
+ const ptr = try resolveInst(mod, scope, inst.positionals.lhs);
+ const value = try resolveInst(mod, scope, inst.positionals.rhs);
+ const inferred_alloc = ptr.castTag(.constant).?.val.castTag(.inferred_alloc).?;
+ // Add the stored instruction to the set we will use to resolve peer types
+ // for the inferred allocation.
+ try inferred_alloc.data.stored_inst_list.append(scope.arena(), value);
+ // Create a new alloc with exactly the type the pointer wants.
+ // Later it gets cleaned up by aliasing the alloc we are supposed to be storing to.
+ const ptr_ty = try mod.simplePtrType(scope, inst.base.src, value.ty, true, .One);
+ const b = try mod.requireRuntimeBlock(scope, inst.base.src);
+ const bitcasted_ptr = try mod.addUnOp(b, inst.base.src, ptr_ty, .bitcast, ptr);
+ return mod.storePtr(scope, inst.base.src, bitcasted_ptr, value);
+}
+
fn analyzeInstStore(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError!*Inst {
const ptr = try resolveInst(mod, scope, inst.positionals.lhs);
const value = try resolveInst(mod, scope, inst.positionals.rhs);
test/stage2/cbe.zig
@@ -51,6 +51,21 @@ pub fn addCases(ctx: *TestContext) !void {
, "");
}
+ {
+ var case = ctx.exeFromCompiledC("inferred local const", .{});
+
+ case.addCompareOutput(
+ \\fn add(a: i32, b: i32) i32 {
+ \\ return a + b;
+ \\}
+ \\
+ \\export fn main() c_int {
+ \\ const x = add(1, 2);
+ \\ return x - 3;
+ \\}
+ , "");
+ }
+
ctx.c("empty start function", linux_x64,
\\export fn _start() noreturn {
\\ unreachable;