mirror of
https://github.com/ziglang/zig.git
synced 2025-12-17 03:33:06 +00:00
Merge pull request #7616 from ziglang/stage2-inferred-vars
stage2: inferred local variables
This commit is contained in:
commit
93bb1d93cd
@ -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))) {
|
if (!mutable and size == .Slice and elem_ty.eql(Type.initTag(.u8))) {
|
||||||
return Type.initTag(.const_slice_u8);
|
return Type.initTag(.const_slice_u8);
|
||||||
}
|
}
|
||||||
@ -3414,3 +3421,9 @@ pub fn getTarget(self: Module) Target {
|
|||||||
pub fn optimizeMode(self: Module) std.builtin.Mode {
|
pub fn optimizeMode(self: Module) std.builtin.Mode {
|
||||||
return self.comp.bin_file.options.optimize_mode;
|
return self.comp.bin_file.options.optimize_mode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn validateVarType(mod: *Module, scope: *Scope, src: usize, ty: Type) !void {
|
||||||
|
if (!ty.isValidVarType(false)) {
|
||||||
|
return mod.fail(scope, src, "variable of type '{}' must be const or comptime", .{ty});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -585,6 +585,7 @@ fn varDecl(
|
|||||||
|
|
||||||
switch (tree.token_ids[node.mut_token]) {
|
switch (tree.token_ids[node.mut_token]) {
|
||||||
.Keyword_const => {
|
.Keyword_const => {
|
||||||
|
var resolve_inferred_alloc: ?*zir.Inst = null;
|
||||||
// Depending on the type of AST the initialization expression is, we may need an lvalue
|
// 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
|
// or an rvalue as a result location. If it is an rvalue, we can use the instruction as
|
||||||
// the variable, no memory location needed.
|
// the variable, no memory location needed.
|
||||||
@ -595,6 +596,7 @@ fn varDecl(
|
|||||||
break :r ResultLoc{ .ptr = alloc };
|
break :r ResultLoc{ .ptr = alloc };
|
||||||
} else {
|
} else {
|
||||||
const alloc = try addZIRNoOpT(mod, scope, name_src, .alloc_inferred);
|
const alloc = try addZIRNoOpT(mod, scope, name_src, .alloc_inferred);
|
||||||
|
resolve_inferred_alloc = &alloc.base;
|
||||||
break :r ResultLoc{ .inferred_ptr = alloc };
|
break :r ResultLoc{ .inferred_ptr = alloc };
|
||||||
}
|
}
|
||||||
} else r: {
|
} else r: {
|
||||||
@ -604,6 +606,9 @@ fn varDecl(
|
|||||||
break :r .none;
|
break :r .none;
|
||||||
};
|
};
|
||||||
const init_inst = try expr(mod, scope, result_loc, init_node);
|
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);
|
const sub_scope = try block_arena.create(Scope.LocalVal);
|
||||||
sub_scope.* = .{
|
sub_scope.* = .{
|
||||||
.parent = scope,
|
.parent = scope,
|
||||||
@ -614,15 +619,20 @@ fn varDecl(
|
|||||||
return &sub_scope.base;
|
return &sub_scope.base;
|
||||||
},
|
},
|
||||||
.Keyword_var => {
|
.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 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 type_inst = try typeExpr(mod, scope, type_node);
|
||||||
const alloc = try addZIRUnOp(mod, scope, name_src, .alloc_mut, type_inst);
|
const alloc = try addZIRUnOp(mod, scope, name_src, .alloc_mut, type_inst);
|
||||||
break :a .{ .alloc = alloc, .result_loc = .{ .ptr = alloc } };
|
break :a .{ .alloc = alloc, .result_loc = .{ .ptr = alloc } };
|
||||||
} else a: {
|
} else a: {
|
||||||
const alloc = try addZIRNoOp(mod, scope, name_src, .alloc_inferred_mut);
|
const alloc = try addZIRNoOpT(mod, scope, name_src, .alloc_inferred_mut);
|
||||||
break :a .{ .alloc = alloc, .result_loc = .{ .inferred_ptr = alloc.castTag(.alloc_inferred_mut).? } };
|
resolve_inferred_alloc = &alloc.base;
|
||||||
|
break :a .{ .alloc = &alloc.base, .result_loc = .{ .inferred_ptr = alloc } };
|
||||||
};
|
};
|
||||||
const init_inst = try expr(mod, scope, var_data.result_loc, init_node);
|
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);
|
const sub_scope = try block_arena.create(Scope.LocalPtr);
|
||||||
sub_scope.* = .{
|
sub_scope.* = .{
|
||||||
.parent = 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", .{});
|
return mod.fail(scope, result.src, "TODO implement rlWrap .bitcasted_ptr", .{});
|
||||||
},
|
},
|
||||||
.inferred_ptr => |alloc| {
|
.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| {
|
.block_ptr => |block_ptr| {
|
||||||
return mod.fail(scope, result.src, "TODO implement rlWrap .block_ptr", .{});
|
return mod.fail(scope, result.src, "TODO implement rlWrap .block_ptr", .{});
|
||||||
|
|||||||
@ -285,6 +285,7 @@ pub fn generate(file: *C, module: *Module, decl: *Decl) !void {
|
|||||||
.arg => try genArg(&ctx),
|
.arg => try genArg(&ctx),
|
||||||
.assembly => try genAsm(&ctx, file, inst.castTag(.assembly).?),
|
.assembly => try genAsm(&ctx, file, inst.castTag(.assembly).?),
|
||||||
.block => try genBlock(&ctx, file, inst.castTag(.block).?),
|
.block => try genBlock(&ctx, file, inst.castTag(.block).?),
|
||||||
|
.bitcast => try genBitcast(&ctx, file, inst.castTag(.bitcast).?),
|
||||||
.breakpoint => try genBreakpoint(file, inst.castTag(.breakpoint).?),
|
.breakpoint => try genBreakpoint(file, inst.castTag(.breakpoint).?),
|
||||||
.call => try genCall(&ctx, file, inst.castTag(.call).?),
|
.call => try genCall(&ctx, file, inst.castTag(.call).?),
|
||||||
.cmp_eq => try genBinOp(&ctx, file, inst.castTag(.cmp_eq).?, "=="),
|
.cmp_eq => try genBinOp(&ctx, file, inst.castTag(.cmp_eq).?, "=="),
|
||||||
@ -295,6 +296,7 @@ pub fn generate(file: *C, module: *Module, decl: *Decl) !void {
|
|||||||
.cmp_neq => try genBinOp(&ctx, file, inst.castTag(.cmp_neq).?, "!="),
|
.cmp_neq => try genBinOp(&ctx, file, inst.castTag(.cmp_neq).?, "!="),
|
||||||
.dbg_stmt => try genDbgStmt(&ctx, inst.castTag(.dbg_stmt).?),
|
.dbg_stmt => try genDbgStmt(&ctx, inst.castTag(.dbg_stmt).?),
|
||||||
.intcast => try genIntCast(&ctx, file, inst.castTag(.intcast).?),
|
.intcast => try genIntCast(&ctx, file, inst.castTag(.intcast).?),
|
||||||
|
.load => try genLoad(&ctx, file, inst.castTag(.load).?),
|
||||||
.ret => try genRet(&ctx, file, inst.castTag(.ret).?),
|
.ret => try genRet(&ctx, file, inst.castTag(.ret).?),
|
||||||
.retvoid => try genRetVoid(file),
|
.retvoid => try genRetVoid(file),
|
||||||
.store => try genStore(&ctx, file, inst.castTag(.store).?),
|
.store => try genStore(&ctx, file, inst.castTag(.store).?),
|
||||||
@ -429,6 +431,16 @@ fn genRetVoid(file: *C) !?[]u8 {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn genLoad(ctx: *Context, file: *C, inst: *Inst.UnOp) !?[]u8 {
|
||||||
|
const operand = try ctx.resolveInst(inst.operand);
|
||||||
|
const writer = file.main.writer();
|
||||||
|
try indent(file);
|
||||||
|
const local_name = try ctx.name();
|
||||||
|
try renderTypeAndName(ctx, writer, inst.base.ty, local_name, .Const);
|
||||||
|
try writer.print(" = *{s};\n", .{operand});
|
||||||
|
return local_name;
|
||||||
|
}
|
||||||
|
|
||||||
fn genRet(ctx: *Context, file: *C, inst: *Inst.UnOp) !?[]u8 {
|
fn genRet(ctx: *Context, file: *C, inst: *Inst.UnOp) !?[]u8 {
|
||||||
try indent(file);
|
try indent(file);
|
||||||
const writer = file.main.writer();
|
const writer = file.main.writer();
|
||||||
@ -440,7 +452,6 @@ fn genIntCast(ctx: *Context, file: *C, inst: *Inst.UnOp) !?[]u8 {
|
|||||||
if (inst.base.isUnused())
|
if (inst.base.isUnused())
|
||||||
return null;
|
return null;
|
||||||
try indent(file);
|
try indent(file);
|
||||||
const op = inst.operand;
|
|
||||||
const writer = file.main.writer();
|
const writer = file.main.writer();
|
||||||
const name = try ctx.name();
|
const name = try ctx.name();
|
||||||
const from = try ctx.resolveInst(inst.operand);
|
const from = try ctx.resolveInst(inst.operand);
|
||||||
@ -537,6 +548,24 @@ fn genBlock(ctx: *Context, file: *C, inst: *Inst.Block) !?[]u8 {
|
|||||||
return ctx.fail(ctx.decl.src(), "TODO: C backend: implement blocks", .{});
|
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 {
|
fn genBreakpoint(file: *C, inst: *Inst.NoOp) !?[]u8 {
|
||||||
try indent(file);
|
try indent(file);
|
||||||
try file.main.writer().writeAll("zig_breakpoint();\n");
|
try file.main.writer().writeAll("zig_breakpoint();\n");
|
||||||
|
|||||||
@ -196,7 +196,7 @@ pub const Inst = struct {
|
|||||||
pub fn value(base: *Inst) ?Value {
|
pub fn value(base: *Inst) ?Value {
|
||||||
if (base.ty.onePossibleValue()) |opv| return opv;
|
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;
|
return inst.val;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -41,4 +41,4 @@
|
|||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#define int128_t __int128
|
#define int128_t __int128
|
||||||
#define uint128_t unsigned __int128
|
#define uint128_t unsigned __int128
|
||||||
|
#include <string.h>
|
||||||
|
|||||||
@ -782,6 +782,7 @@ pub const TestContext = struct {
|
|||||||
"-std=c89",
|
"-std=c89",
|
||||||
"-pedantic",
|
"-pedantic",
|
||||||
"-Werror",
|
"-Werror",
|
||||||
|
"-Wno-declaration-after-statement",
|
||||||
"--",
|
"--",
|
||||||
"-lc",
|
"-lc",
|
||||||
exe_path,
|
exe_path,
|
||||||
|
|||||||
179
src/type.zig
179
src/type.zig
@ -78,6 +78,8 @@ pub const Type = extern union {
|
|||||||
.const_slice,
|
.const_slice,
|
||||||
.mut_slice,
|
.mut_slice,
|
||||||
.pointer,
|
.pointer,
|
||||||
|
.inferred_alloc_const,
|
||||||
|
.inferred_alloc_mut,
|
||||||
=> return .Pointer,
|
=> return .Pointer,
|
||||||
|
|
||||||
.optional,
|
.optional,
|
||||||
@ -158,6 +160,9 @@ pub const Type = extern union {
|
|||||||
.optional_single_mut_pointer,
|
.optional_single_mut_pointer,
|
||||||
=> self.cast(Payload.ElemType),
|
=> self.cast(Payload.ElemType),
|
||||||
|
|
||||||
|
.inferred_alloc_const => unreachable,
|
||||||
|
.inferred_alloc_mut => unreachable,
|
||||||
|
|
||||||
else => null,
|
else => null,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -384,6 +389,8 @@ pub const Type = extern union {
|
|||||||
.enum_literal,
|
.enum_literal,
|
||||||
.anyerror_void_error_union,
|
.anyerror_void_error_union,
|
||||||
.@"anyframe",
|
.@"anyframe",
|
||||||
|
.inferred_alloc_const,
|
||||||
|
.inferred_alloc_mut,
|
||||||
=> unreachable,
|
=> unreachable,
|
||||||
|
|
||||||
.array_u8,
|
.array_u8,
|
||||||
@ -686,6 +693,8 @@ pub const Type = extern union {
|
|||||||
const name = ty.castTag(.error_set_single).?.data;
|
const name = ty.castTag(.error_set_single).?.data;
|
||||||
return out_stream.print("error{{{s}}}", .{name});
|
return out_stream.print("error{{{s}}}", .{name});
|
||||||
},
|
},
|
||||||
|
.inferred_alloc_const => return out_stream.writeAll("(inferred_alloc_const)"),
|
||||||
|
.inferred_alloc_mut => return out_stream.writeAll("(inferred_alloc_mut)"),
|
||||||
}
|
}
|
||||||
unreachable;
|
unreachable;
|
||||||
}
|
}
|
||||||
@ -733,6 +742,8 @@ pub const Type = extern union {
|
|||||||
.single_const_pointer_to_comptime_int => return Value.initTag(.single_const_pointer_to_comptime_int_type),
|
.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),
|
.const_slice_u8 => return Value.initTag(.const_slice_u8_type),
|
||||||
.enum_literal => return Value.initTag(.enum_literal_type),
|
.enum_literal => return Value.initTag(.enum_literal_type),
|
||||||
|
.inferred_alloc_const => unreachable,
|
||||||
|
.inferred_alloc_mut => unreachable,
|
||||||
else => return Value.Tag.ty.create(allocator, self),
|
else => return Value.Tag.ty.create(allocator, self),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -803,6 +814,9 @@ pub const Type = extern union {
|
|||||||
.enum_literal,
|
.enum_literal,
|
||||||
.empty_struct,
|
.empty_struct,
|
||||||
=> false,
|
=> false,
|
||||||
|
|
||||||
|
.inferred_alloc_const => unreachable,
|
||||||
|
.inferred_alloc_mut => unreachable,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -920,6 +934,8 @@ pub const Type = extern union {
|
|||||||
.@"undefined",
|
.@"undefined",
|
||||||
.enum_literal,
|
.enum_literal,
|
||||||
.empty_struct,
|
.empty_struct,
|
||||||
|
.inferred_alloc_const,
|
||||||
|
.inferred_alloc_mut,
|
||||||
=> unreachable,
|
=> unreachable,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -943,6 +959,8 @@ pub const Type = extern union {
|
|||||||
.enum_literal => unreachable,
|
.enum_literal => unreachable,
|
||||||
.single_const_pointer_to_comptime_int => unreachable,
|
.single_const_pointer_to_comptime_int => unreachable,
|
||||||
.empty_struct => unreachable,
|
.empty_struct => unreachable,
|
||||||
|
.inferred_alloc_const => unreachable,
|
||||||
|
.inferred_alloc_mut => unreachable,
|
||||||
|
|
||||||
.u8,
|
.u8,
|
||||||
.i8,
|
.i8,
|
||||||
@ -1121,6 +1139,8 @@ pub const Type = extern union {
|
|||||||
.single_const_pointer,
|
.single_const_pointer,
|
||||||
.single_mut_pointer,
|
.single_mut_pointer,
|
||||||
.single_const_pointer_to_comptime_int,
|
.single_const_pointer_to_comptime_int,
|
||||||
|
.inferred_alloc_const,
|
||||||
|
.inferred_alloc_mut,
|
||||||
=> true,
|
=> true,
|
||||||
|
|
||||||
.pointer => self.castTag(.pointer).?.data.size == .One,
|
.pointer => self.castTag(.pointer).?.data.size == .One,
|
||||||
@ -1203,6 +1223,8 @@ pub const Type = extern union {
|
|||||||
.single_const_pointer,
|
.single_const_pointer,
|
||||||
.single_mut_pointer,
|
.single_mut_pointer,
|
||||||
.single_const_pointer_to_comptime_int,
|
.single_const_pointer_to_comptime_int,
|
||||||
|
.inferred_alloc_const,
|
||||||
|
.inferred_alloc_mut,
|
||||||
=> .One,
|
=> .One,
|
||||||
|
|
||||||
.pointer => self.castTag(.pointer).?.data.size,
|
.pointer => self.castTag(.pointer).?.data.size,
|
||||||
@ -1273,6 +1295,8 @@ pub const Type = extern union {
|
|||||||
.error_set,
|
.error_set,
|
||||||
.error_set_single,
|
.error_set_single,
|
||||||
.empty_struct,
|
.empty_struct,
|
||||||
|
.inferred_alloc_const,
|
||||||
|
.inferred_alloc_mut,
|
||||||
=> false,
|
=> false,
|
||||||
|
|
||||||
.const_slice,
|
.const_slice,
|
||||||
@ -1345,6 +1369,8 @@ pub const Type = extern union {
|
|||||||
.error_set,
|
.error_set,
|
||||||
.error_set_single,
|
.error_set_single,
|
||||||
.empty_struct,
|
.empty_struct,
|
||||||
|
.inferred_alloc_const,
|
||||||
|
.inferred_alloc_mut,
|
||||||
=> false,
|
=> false,
|
||||||
|
|
||||||
.single_const_pointer,
|
.single_const_pointer,
|
||||||
@ -1426,6 +1452,8 @@ pub const Type = extern union {
|
|||||||
.error_set,
|
.error_set,
|
||||||
.error_set_single,
|
.error_set_single,
|
||||||
.empty_struct,
|
.empty_struct,
|
||||||
|
.inferred_alloc_const,
|
||||||
|
.inferred_alloc_mut,
|
||||||
=> false,
|
=> false,
|
||||||
|
|
||||||
.pointer => {
|
.pointer => {
|
||||||
@ -1502,6 +1530,8 @@ pub const Type = extern union {
|
|||||||
.error_set,
|
.error_set,
|
||||||
.error_set_single,
|
.error_set_single,
|
||||||
.empty_struct,
|
.empty_struct,
|
||||||
|
.inferred_alloc_const,
|
||||||
|
.inferred_alloc_mut,
|
||||||
=> false,
|
=> false,
|
||||||
|
|
||||||
.pointer => {
|
.pointer => {
|
||||||
@ -1569,58 +1599,59 @@ pub const Type = extern union {
|
|||||||
/// Asserts the type is a pointer or array type.
|
/// Asserts the type is a pointer or array type.
|
||||||
pub fn elemType(self: Type) Type {
|
pub fn elemType(self: Type) Type {
|
||||||
return switch (self.tag()) {
|
return switch (self.tag()) {
|
||||||
.u8,
|
.u8 => unreachable,
|
||||||
.i8,
|
.i8 => unreachable,
|
||||||
.u16,
|
.u16 => unreachable,
|
||||||
.i16,
|
.i16 => unreachable,
|
||||||
.u32,
|
.u32 => unreachable,
|
||||||
.i32,
|
.i32 => unreachable,
|
||||||
.u64,
|
.u64 => unreachable,
|
||||||
.i64,
|
.i64 => unreachable,
|
||||||
.usize,
|
.usize => unreachable,
|
||||||
.isize,
|
.isize => unreachable,
|
||||||
.c_short,
|
.c_short => unreachable,
|
||||||
.c_ushort,
|
.c_ushort => unreachable,
|
||||||
.c_int,
|
.c_int => unreachable,
|
||||||
.c_uint,
|
.c_uint => unreachable,
|
||||||
.c_long,
|
.c_long => unreachable,
|
||||||
.c_ulong,
|
.c_ulong => unreachable,
|
||||||
.c_longlong,
|
.c_longlong => unreachable,
|
||||||
.c_ulonglong,
|
.c_ulonglong => unreachable,
|
||||||
.c_longdouble,
|
.c_longdouble => unreachable,
|
||||||
.f16,
|
.f16 => unreachable,
|
||||||
.f32,
|
.f32 => unreachable,
|
||||||
.f64,
|
.f64 => unreachable,
|
||||||
.f128,
|
.f128 => unreachable,
|
||||||
.c_void,
|
.c_void => unreachable,
|
||||||
.bool,
|
.bool => unreachable,
|
||||||
.void,
|
.void => unreachable,
|
||||||
.type,
|
.type => unreachable,
|
||||||
.anyerror,
|
.anyerror => unreachable,
|
||||||
.comptime_int,
|
.comptime_int => unreachable,
|
||||||
.comptime_float,
|
.comptime_float => unreachable,
|
||||||
.noreturn,
|
.noreturn => unreachable,
|
||||||
.@"null",
|
.@"null" => unreachable,
|
||||||
.@"undefined",
|
.@"undefined" => unreachable,
|
||||||
.fn_noreturn_no_args,
|
.fn_noreturn_no_args => unreachable,
|
||||||
.fn_void_no_args,
|
.fn_void_no_args => unreachable,
|
||||||
.fn_naked_noreturn_no_args,
|
.fn_naked_noreturn_no_args => unreachable,
|
||||||
.fn_ccc_void_no_args,
|
.fn_ccc_void_no_args => unreachable,
|
||||||
.function,
|
.function => unreachable,
|
||||||
.int_unsigned,
|
.int_unsigned => unreachable,
|
||||||
.int_signed,
|
.int_signed => unreachable,
|
||||||
.optional,
|
.optional => unreachable,
|
||||||
.optional_single_const_pointer,
|
.optional_single_const_pointer => unreachable,
|
||||||
.optional_single_mut_pointer,
|
.optional_single_mut_pointer => unreachable,
|
||||||
.enum_literal,
|
.enum_literal => unreachable,
|
||||||
.error_union,
|
.error_union => unreachable,
|
||||||
.@"anyframe",
|
.@"anyframe" => unreachable,
|
||||||
.anyframe_T,
|
.anyframe_T => unreachable,
|
||||||
.anyerror_void_error_union,
|
.anyerror_void_error_union => unreachable,
|
||||||
.error_set,
|
.error_set => unreachable,
|
||||||
.error_set_single,
|
.error_set_single => unreachable,
|
||||||
.empty_struct,
|
.empty_struct => unreachable,
|
||||||
=> unreachable,
|
.inferred_alloc_const => unreachable,
|
||||||
|
.inferred_alloc_mut => unreachable,
|
||||||
|
|
||||||
.array => self.castTag(.array).?.data.elem_type,
|
.array => self.castTag(.array).?.data.elem_type,
|
||||||
.array_sentinel => self.castTag(.array_sentinel).?.data.elem_type,
|
.array_sentinel => self.castTag(.array_sentinel).?.data.elem_type,
|
||||||
@ -1742,6 +1773,8 @@ pub const Type = extern union {
|
|||||||
.error_set,
|
.error_set,
|
||||||
.error_set_single,
|
.error_set_single,
|
||||||
.empty_struct,
|
.empty_struct,
|
||||||
|
.inferred_alloc_const,
|
||||||
|
.inferred_alloc_mut,
|
||||||
=> unreachable,
|
=> unreachable,
|
||||||
|
|
||||||
.array => self.castTag(.array).?.data.len,
|
.array => self.castTag(.array).?.data.len,
|
||||||
@ -1808,6 +1841,8 @@ pub const Type = extern union {
|
|||||||
.error_set,
|
.error_set,
|
||||||
.error_set_single,
|
.error_set_single,
|
||||||
.empty_struct,
|
.empty_struct,
|
||||||
|
.inferred_alloc_const,
|
||||||
|
.inferred_alloc_mut,
|
||||||
=> unreachable,
|
=> unreachable,
|
||||||
|
|
||||||
.single_const_pointer,
|
.single_const_pointer,
|
||||||
@ -1891,6 +1926,8 @@ pub const Type = extern union {
|
|||||||
.error_set,
|
.error_set,
|
||||||
.error_set_single,
|
.error_set_single,
|
||||||
.empty_struct,
|
.empty_struct,
|
||||||
|
.inferred_alloc_const,
|
||||||
|
.inferred_alloc_mut,
|
||||||
=> false,
|
=> false,
|
||||||
|
|
||||||
.int_signed,
|
.int_signed,
|
||||||
@ -1966,6 +2003,8 @@ pub const Type = extern union {
|
|||||||
.error_set,
|
.error_set,
|
||||||
.error_set_single,
|
.error_set_single,
|
||||||
.empty_struct,
|
.empty_struct,
|
||||||
|
.inferred_alloc_const,
|
||||||
|
.inferred_alloc_mut,
|
||||||
=> false,
|
=> false,
|
||||||
|
|
||||||
.int_unsigned,
|
.int_unsigned,
|
||||||
@ -2031,6 +2070,8 @@ pub const Type = extern union {
|
|||||||
.error_set,
|
.error_set,
|
||||||
.error_set_single,
|
.error_set_single,
|
||||||
.empty_struct,
|
.empty_struct,
|
||||||
|
.inferred_alloc_const,
|
||||||
|
.inferred_alloc_mut,
|
||||||
=> unreachable,
|
=> unreachable,
|
||||||
|
|
||||||
.int_unsigned => .{
|
.int_unsigned => .{
|
||||||
@ -2120,6 +2161,8 @@ pub const Type = extern union {
|
|||||||
.error_set,
|
.error_set,
|
||||||
.error_set_single,
|
.error_set_single,
|
||||||
.empty_struct,
|
.empty_struct,
|
||||||
|
.inferred_alloc_const,
|
||||||
|
.inferred_alloc_mut,
|
||||||
=> false,
|
=> false,
|
||||||
|
|
||||||
.usize,
|
.usize,
|
||||||
@ -2232,6 +2275,8 @@ pub const Type = extern union {
|
|||||||
.error_set,
|
.error_set,
|
||||||
.error_set_single,
|
.error_set_single,
|
||||||
.empty_struct,
|
.empty_struct,
|
||||||
|
.inferred_alloc_const,
|
||||||
|
.inferred_alloc_mut,
|
||||||
=> unreachable,
|
=> unreachable,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -2310,6 +2355,8 @@ pub const Type = extern union {
|
|||||||
.error_set,
|
.error_set,
|
||||||
.error_set_single,
|
.error_set_single,
|
||||||
.empty_struct,
|
.empty_struct,
|
||||||
|
.inferred_alloc_const,
|
||||||
|
.inferred_alloc_mut,
|
||||||
=> unreachable,
|
=> unreachable,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2387,6 +2434,8 @@ pub const Type = extern union {
|
|||||||
.error_set,
|
.error_set,
|
||||||
.error_set_single,
|
.error_set_single,
|
||||||
.empty_struct,
|
.empty_struct,
|
||||||
|
.inferred_alloc_const,
|
||||||
|
.inferred_alloc_mut,
|
||||||
=> unreachable,
|
=> unreachable,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2464,6 +2513,8 @@ pub const Type = extern union {
|
|||||||
.error_set,
|
.error_set,
|
||||||
.error_set_single,
|
.error_set_single,
|
||||||
.empty_struct,
|
.empty_struct,
|
||||||
|
.inferred_alloc_const,
|
||||||
|
.inferred_alloc_mut,
|
||||||
=> unreachable,
|
=> unreachable,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -2538,6 +2589,8 @@ pub const Type = extern union {
|
|||||||
.error_set,
|
.error_set,
|
||||||
.error_set_single,
|
.error_set_single,
|
||||||
.empty_struct,
|
.empty_struct,
|
||||||
|
.inferred_alloc_const,
|
||||||
|
.inferred_alloc_mut,
|
||||||
=> unreachable,
|
=> unreachable,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -2612,6 +2665,8 @@ pub const Type = extern union {
|
|||||||
.error_set,
|
.error_set,
|
||||||
.error_set_single,
|
.error_set_single,
|
||||||
.empty_struct,
|
.empty_struct,
|
||||||
|
.inferred_alloc_const,
|
||||||
|
.inferred_alloc_mut,
|
||||||
=> unreachable,
|
=> unreachable,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -2686,6 +2741,8 @@ pub const Type = extern union {
|
|||||||
.error_set,
|
.error_set,
|
||||||
.error_set_single,
|
.error_set_single,
|
||||||
.empty_struct,
|
.empty_struct,
|
||||||
|
.inferred_alloc_const,
|
||||||
|
.inferred_alloc_mut,
|
||||||
=> false,
|
=> false,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -2778,6 +2835,8 @@ pub const Type = extern union {
|
|||||||
ty = ty.castTag(.pointer).?.data.pointee_type;
|
ty = ty.castTag(.pointer).?.data.pointee_type;
|
||||||
continue;
|
continue;
|
||||||
},
|
},
|
||||||
|
.inferred_alloc_const => unreachable,
|
||||||
|
.inferred_alloc_mut => unreachable,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2846,6 +2905,8 @@ pub const Type = extern union {
|
|||||||
.error_set,
|
.error_set,
|
||||||
.error_set_single,
|
.error_set_single,
|
||||||
.empty_struct,
|
.empty_struct,
|
||||||
|
.inferred_alloc_const,
|
||||||
|
.inferred_alloc_mut,
|
||||||
=> return false,
|
=> return false,
|
||||||
|
|
||||||
.c_const_pointer,
|
.c_const_pointer,
|
||||||
@ -2931,6 +2992,8 @@ pub const Type = extern union {
|
|||||||
.c_const_pointer,
|
.c_const_pointer,
|
||||||
.c_mut_pointer,
|
.c_mut_pointer,
|
||||||
.pointer,
|
.pointer,
|
||||||
|
.inferred_alloc_const,
|
||||||
|
.inferred_alloc_mut,
|
||||||
=> unreachable,
|
=> unreachable,
|
||||||
|
|
||||||
.empty_struct => self.castTag(.empty_struct).?.data,
|
.empty_struct => self.castTag(.empty_struct).?.data,
|
||||||
@ -3041,7 +3104,13 @@ pub const Type = extern union {
|
|||||||
single_const_pointer_to_comptime_int,
|
single_const_pointer_to_comptime_int,
|
||||||
anyerror_void_error_union,
|
anyerror_void_error_union,
|
||||||
@"anyframe",
|
@"anyframe",
|
||||||
const_slice_u8, // See last_no_payload_tag below.
|
const_slice_u8,
|
||||||
|
/// 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_mut,
|
||||||
|
/// Same as `inferred_alloc_mut` but the local is `var` not `const`.
|
||||||
|
inferred_alloc_const, // See last_no_payload_tag below.
|
||||||
// After this, the tag requires a payload.
|
// After this, the tag requires a payload.
|
||||||
|
|
||||||
array_u8,
|
array_u8,
|
||||||
@ -3069,7 +3138,7 @@ pub const Type = extern union {
|
|||||||
error_set_single,
|
error_set_single,
|
||||||
empty_struct,
|
empty_struct,
|
||||||
|
|
||||||
pub const last_no_payload_tag = Tag.const_slice_u8;
|
pub const last_no_payload_tag = Tag.inferred_alloc_const;
|
||||||
pub const no_payload_count = @enumToInt(last_no_payload_tag) + 1;
|
pub const no_payload_count = @enumToInt(last_no_payload_tag) + 1;
|
||||||
|
|
||||||
pub fn Type(comptime t: Tag) type {
|
pub fn Type(comptime t: Tag) type {
|
||||||
@ -3116,6 +3185,8 @@ pub const Type = extern union {
|
|||||||
.anyerror_void_error_union,
|
.anyerror_void_error_union,
|
||||||
.@"anyframe",
|
.@"anyframe",
|
||||||
.const_slice_u8,
|
.const_slice_u8,
|
||||||
|
.inferred_alloc_const,
|
||||||
|
.inferred_alloc_mut,
|
||||||
=> @compileError("Type Tag " ++ @tagName(t) ++ " has no payload"),
|
=> @compileError("Type Tag " ++ @tagName(t) ++ " has no payload"),
|
||||||
|
|
||||||
.array_u8,
|
.array_u8,
|
||||||
|
|||||||
@ -7,6 +7,7 @@ const BigIntMutable = std.math.big.int.Mutable;
|
|||||||
const Target = std.Target;
|
const Target = std.Target;
|
||||||
const Allocator = std.mem.Allocator;
|
const Allocator = std.mem.Allocator;
|
||||||
const Module = @import("Module.zig");
|
const Module = @import("Module.zig");
|
||||||
|
const ir = @import("ir.zig");
|
||||||
|
|
||||||
/// This is the raw data, with no bookkeeping, no memory awareness,
|
/// This is the raw data, with no bookkeeping, no memory awareness,
|
||||||
/// no de-duplication, and no type system awareness.
|
/// no de-duplication, and no type system awareness.
|
||||||
@ -101,6 +102,9 @@ pub const Value = extern union {
|
|||||||
enum_literal,
|
enum_literal,
|
||||||
error_set,
|
error_set,
|
||||||
@"error",
|
@"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 last_no_payload_tag = Tag.bool_false;
|
||||||
pub const no_payload_count = @enumToInt(last_no_payload_tag) + 1;
|
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,
|
.float_128 => Payload.Float_128,
|
||||||
.error_set => Payload.ErrorSet,
|
.error_set => Payload.ErrorSet,
|
||||||
.@"error" => Payload.Error,
|
.@"error" => Payload.Error,
|
||||||
|
.inferred_alloc => Payload.InferredAlloc,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -383,6 +388,8 @@ pub const Value = extern union {
|
|||||||
|
|
||||||
// memory is managed by the declaration
|
// memory is managed by the declaration
|
||||||
.error_set => return self.copyPayloadShallow(allocator, Payload.ErrorSet),
|
.error_set => return self.copyPayloadShallow(allocator, Payload.ErrorSet),
|
||||||
|
|
||||||
|
.inferred_alloc => unreachable,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -501,6 +508,7 @@ pub const Value = extern union {
|
|||||||
return out_stream.writeAll("}");
|
return out_stream.writeAll("}");
|
||||||
},
|
},
|
||||||
.@"error" => return out_stream.print("error.{}", .{val.castTag(.@"error").?.data.name}),
|
.@"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,
|
.enum_literal,
|
||||||
.@"error",
|
.@"error",
|
||||||
.empty_struct_value,
|
.empty_struct_value,
|
||||||
|
.inferred_alloc,
|
||||||
=> unreachable,
|
=> unreachable,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -683,6 +692,7 @@ pub const Value = extern union {
|
|||||||
.error_set,
|
.error_set,
|
||||||
.@"error",
|
.@"error",
|
||||||
.empty_struct_value,
|
.empty_struct_value,
|
||||||
|
.inferred_alloc,
|
||||||
=> unreachable,
|
=> unreachable,
|
||||||
|
|
||||||
.undef => unreachable,
|
.undef => unreachable,
|
||||||
@ -768,6 +778,7 @@ pub const Value = extern union {
|
|||||||
.error_set,
|
.error_set,
|
||||||
.@"error",
|
.@"error",
|
||||||
.empty_struct_value,
|
.empty_struct_value,
|
||||||
|
.inferred_alloc,
|
||||||
=> unreachable,
|
=> unreachable,
|
||||||
|
|
||||||
.undef => unreachable,
|
.undef => unreachable,
|
||||||
@ -853,6 +864,7 @@ pub const Value = extern union {
|
|||||||
.error_set,
|
.error_set,
|
||||||
.@"error",
|
.@"error",
|
||||||
.empty_struct_value,
|
.empty_struct_value,
|
||||||
|
.inferred_alloc,
|
||||||
=> unreachable,
|
=> unreachable,
|
||||||
|
|
||||||
.undef => unreachable,
|
.undef => unreachable,
|
||||||
@ -966,6 +978,7 @@ pub const Value = extern union {
|
|||||||
.error_set,
|
.error_set,
|
||||||
.@"error",
|
.@"error",
|
||||||
.empty_struct_value,
|
.empty_struct_value,
|
||||||
|
.inferred_alloc,
|
||||||
=> unreachable,
|
=> unreachable,
|
||||||
|
|
||||||
.zero,
|
.zero,
|
||||||
@ -1055,6 +1068,7 @@ pub const Value = extern union {
|
|||||||
.error_set,
|
.error_set,
|
||||||
.@"error",
|
.@"error",
|
||||||
.empty_struct_value,
|
.empty_struct_value,
|
||||||
|
.inferred_alloc,
|
||||||
=> unreachable,
|
=> unreachable,
|
||||||
|
|
||||||
.zero,
|
.zero,
|
||||||
@ -1213,6 +1227,7 @@ pub const Value = extern union {
|
|||||||
.error_set,
|
.error_set,
|
||||||
.@"error",
|
.@"error",
|
||||||
.empty_struct_value,
|
.empty_struct_value,
|
||||||
|
.inferred_alloc,
|
||||||
=> unreachable,
|
=> unreachable,
|
||||||
|
|
||||||
.zero,
|
.zero,
|
||||||
@ -1289,6 +1304,7 @@ pub const Value = extern union {
|
|||||||
.error_set,
|
.error_set,
|
||||||
.@"error",
|
.@"error",
|
||||||
.empty_struct_value,
|
.empty_struct_value,
|
||||||
|
.inferred_alloc,
|
||||||
=> unreachable,
|
=> unreachable,
|
||||||
|
|
||||||
.zero,
|
.zero,
|
||||||
@ -1525,6 +1541,8 @@ pub const Value = extern union {
|
|||||||
hasher.update(payload.name);
|
hasher.update(payload.name);
|
||||||
std.hash.autoHash(&hasher, payload.value);
|
std.hash.autoHash(&hasher, payload.value);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
.inferred_alloc => unreachable,
|
||||||
}
|
}
|
||||||
return hasher.final();
|
return hasher.final();
|
||||||
}
|
}
|
||||||
@ -1602,6 +1620,7 @@ pub const Value = extern union {
|
|||||||
.error_set,
|
.error_set,
|
||||||
.@"error",
|
.@"error",
|
||||||
.empty_struct_value,
|
.empty_struct_value,
|
||||||
|
.inferred_alloc,
|
||||||
=> unreachable,
|
=> unreachable,
|
||||||
|
|
||||||
.ref_val => self.castTag(.ref_val).?.data,
|
.ref_val => self.castTag(.ref_val).?.data,
|
||||||
@ -1687,6 +1706,7 @@ pub const Value = extern union {
|
|||||||
.error_set,
|
.error_set,
|
||||||
.@"error",
|
.@"error",
|
||||||
.empty_struct_value,
|
.empty_struct_value,
|
||||||
|
.inferred_alloc,
|
||||||
=> unreachable,
|
=> unreachable,
|
||||||
|
|
||||||
.empty_array => unreachable, // out of bounds array index
|
.empty_array => unreachable, // out of bounds array index
|
||||||
@ -1793,6 +1813,7 @@ pub const Value = extern union {
|
|||||||
|
|
||||||
.undef => unreachable,
|
.undef => unreachable,
|
||||||
.unreachable_value => unreachable,
|
.unreachable_value => unreachable,
|
||||||
|
.inferred_alloc => unreachable,
|
||||||
.null_value => true,
|
.null_value => true,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -1801,6 +1822,7 @@ pub const Value = extern union {
|
|||||||
pub fn isFloat(self: Value) bool {
|
pub fn isFloat(self: Value) bool {
|
||||||
return switch (self.tag()) {
|
return switch (self.tag()) {
|
||||||
.undef => unreachable,
|
.undef => unreachable,
|
||||||
|
.inferred_alloc => unreachable,
|
||||||
|
|
||||||
.float_16,
|
.float_16,
|
||||||
.float_32,
|
.float_32,
|
||||||
@ -1890,6 +1912,7 @@ pub const Value = extern union {
|
|||||||
|
|
||||||
.undef => unreachable,
|
.undef => unreachable,
|
||||||
.unreachable_value => unreachable,
|
.unreachable_value => unreachable,
|
||||||
|
.inferred_alloc => unreachable,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2020,6 +2043,19 @@ pub const Value = extern union {
|
|||||||
value: u16,
|
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
|
/// Big enough to fit any non-BigInt value
|
||||||
|
|||||||
12
src/zir.zig
12
src/zir.zig
@ -241,12 +241,20 @@ pub const Inst = struct {
|
|||||||
const_slice_type,
|
const_slice_type,
|
||||||
/// Create a pointer type with attributes
|
/// Create a pointer type with attributes
|
||||||
ptr_type,
|
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 operation `array_ptr[start..end:sentinel]`
|
||||||
slice,
|
slice,
|
||||||
/// Slice operation with just start `lhs[rhs..]`
|
/// Slice operation with just start `lhs[rhs..]`
|
||||||
slice_start,
|
slice_start,
|
||||||
/// Write a value to a pointer. For loading, see `deref`.
|
/// Write a value to a pointer. For loading, see `deref`.
|
||||||
store,
|
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.
|
/// String Literal. Makes an anonymous Decl and then takes a pointer to it.
|
||||||
str,
|
str,
|
||||||
/// Arithmetic subtraction. Asserts no integer overflow.
|
/// Arithmetic subtraction. Asserts no integer overflow.
|
||||||
@ -319,6 +327,7 @@ pub const Inst = struct {
|
|||||||
.ref,
|
.ref,
|
||||||
.bitcast_ref,
|
.bitcast_ref,
|
||||||
.typeof,
|
.typeof,
|
||||||
|
.resolve_inferred_alloc,
|
||||||
.single_const_ptr_type,
|
.single_const_ptr_type,
|
||||||
.single_mut_ptr_type,
|
.single_mut_ptr_type,
|
||||||
.many_const_ptr_type,
|
.many_const_ptr_type,
|
||||||
@ -355,6 +364,7 @@ pub const Inst = struct {
|
|||||||
.shl,
|
.shl,
|
||||||
.shr,
|
.shr,
|
||||||
.store,
|
.store,
|
||||||
|
.store_to_inferred_ptr,
|
||||||
.sub,
|
.sub,
|
||||||
.subwrap,
|
.subwrap,
|
||||||
.cmp_lt,
|
.cmp_lt,
|
||||||
@ -498,6 +508,7 @@ pub const Inst = struct {
|
|||||||
.mut_slice_type,
|
.mut_slice_type,
|
||||||
.const_slice_type,
|
.const_slice_type,
|
||||||
.store,
|
.store,
|
||||||
|
.store_to_inferred_ptr,
|
||||||
.str,
|
.str,
|
||||||
.sub,
|
.sub,
|
||||||
.subwrap,
|
.subwrap,
|
||||||
@ -522,6 +533,7 @@ pub const Inst = struct {
|
|||||||
.import,
|
.import,
|
||||||
.switch_range,
|
.switch_range,
|
||||||
.typeof_peer,
|
.typeof_peer,
|
||||||
|
.resolve_inferred_alloc,
|
||||||
=> false,
|
=> false,
|
||||||
|
|
||||||
.@"break",
|
.@"break",
|
||||||
|
|||||||
@ -10,10 +10,12 @@
|
|||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const mem = std.mem;
|
const mem = std.mem;
|
||||||
const Allocator = std.mem.Allocator;
|
const Allocator = std.mem.Allocator;
|
||||||
|
const assert = std.debug.assert;
|
||||||
|
const log = std.log.scoped(.sema);
|
||||||
|
|
||||||
const Value = @import("value.zig").Value;
|
const Value = @import("value.zig").Value;
|
||||||
const Type = @import("type.zig").Type;
|
const Type = @import("type.zig").Type;
|
||||||
const TypedValue = @import("TypedValue.zig");
|
const TypedValue = @import("TypedValue.zig");
|
||||||
const assert = std.debug.assert;
|
|
||||||
const ir = @import("ir.zig");
|
const ir = @import("ir.zig");
|
||||||
const zir = @import("zir.zig");
|
const zir = @import("zir.zig");
|
||||||
const Module = @import("Module.zig");
|
const Module = @import("Module.zig");
|
||||||
@ -28,8 +30,18 @@ pub fn analyzeInst(mod: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError!
|
|||||||
switch (old_inst.tag) {
|
switch (old_inst.tag) {
|
||||||
.alloc => return analyzeInstAlloc(mod, scope, old_inst.castTag(.alloc).?),
|
.alloc => return analyzeInstAlloc(mod, scope, old_inst.castTag(.alloc).?),
|
||||||
.alloc_mut => return analyzeInstAllocMut(mod, scope, old_inst.castTag(.alloc_mut).?),
|
.alloc_mut => return analyzeInstAllocMut(mod, scope, old_inst.castTag(.alloc_mut).?),
|
||||||
.alloc_inferred => return analyzeInstAllocInferred(mod, scope, old_inst.castTag(.alloc_inferred).?),
|
.alloc_inferred => return analyzeInstAllocInferred(
|
||||||
.alloc_inferred_mut => return analyzeInstAllocInferredMut(mod, scope, old_inst.castTag(.alloc_inferred_mut).?),
|
mod,
|
||||||
|
scope,
|
||||||
|
old_inst.castTag(.alloc_inferred).?,
|
||||||
|
.inferred_alloc_const,
|
||||||
|
),
|
||||||
|
.alloc_inferred_mut => return analyzeInstAllocInferred(
|
||||||
|
mod,
|
||||||
|
scope,
|
||||||
|
old_inst.castTag(.alloc_inferred_mut).?,
|
||||||
|
.inferred_alloc_mut,
|
||||||
|
),
|
||||||
.arg => return analyzeInstArg(mod, scope, old_inst.castTag(.arg).?),
|
.arg => return analyzeInstArg(mod, scope, old_inst.castTag(.arg).?),
|
||||||
.bitcast_ref => return analyzeInstBitCastRef(mod, scope, old_inst.castTag(.bitcast_ref).?),
|
.bitcast_ref => return analyzeInstBitCastRef(mod, scope, old_inst.castTag(.bitcast_ref).?),
|
||||||
.bitcast_result_ptr => return analyzeInstBitCastResultPtr(mod, scope, old_inst.castTag(.bitcast_result_ptr).?),
|
.bitcast_result_ptr => return analyzeInstBitCastResultPtr(mod, scope, old_inst.castTag(.bitcast_result_ptr).?),
|
||||||
@ -55,8 +67,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_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).?),
|
.ensure_indexable => return analyzeInstEnsureIndexable(mod, scope, old_inst.castTag(.ensure_indexable).?),
|
||||||
.ref => return analyzeInstRef(mod, scope, old_inst.castTag(.ref).?),
|
.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_ptr => return analyzeInstRetPtr(mod, scope, old_inst.castTag(.ret_ptr).?),
|
||||||
.ret_type => return analyzeInstRetType(mod, scope, old_inst.castTag(.ret_type).?),
|
.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_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),
|
.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),
|
.many_const_ptr_type => return analyzeInstSimplePtrType(mod, scope, old_inst.castTag(.many_const_ptr_type).?, false, .Many),
|
||||||
@ -419,20 +433,83 @@ fn analyzeInstAlloc(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerErro
|
|||||||
|
|
||||||
fn analyzeInstAllocMut(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst {
|
fn analyzeInstAllocMut(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst {
|
||||||
const var_type = try resolveType(mod, scope, inst.positionals.operand);
|
const var_type = try resolveType(mod, scope, inst.positionals.operand);
|
||||||
if (!var_type.isValidVarType(false)) {
|
try mod.validateVarType(scope, inst.base.src, var_type);
|
||||||
return mod.fail(scope, inst.base.src, "variable of type '{}' must be const or comptime", .{var_type});
|
|
||||||
}
|
|
||||||
const ptr_type = try mod.simplePtrType(scope, inst.base.src, var_type, true, .One);
|
const ptr_type = try mod.simplePtrType(scope, inst.base.src, var_type, true, .One);
|
||||||
const b = try mod.requireRuntimeBlock(scope, inst.base.src);
|
const b = try mod.requireRuntimeBlock(scope, inst.base.src);
|
||||||
return mod.addNoOp(b, inst.base.src, ptr_type, .alloc);
|
return mod.addNoOp(b, inst.base.src, ptr_type, .alloc);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn analyzeInstAllocInferred(mod: *Module, scope: *Scope, inst: *zir.Inst.NoOp) InnerError!*Inst {
|
fn analyzeInstAllocInferred(
|
||||||
return mod.fail(scope, inst.base.src, "TODO implement analyzeInstAllocInferred", .{});
|
mod: *Module,
|
||||||
|
scope: *Scope,
|
||||||
|
inst: *zir.Inst.NoOp,
|
||||||
|
mut_tag: Type.Tag,
|
||||||
|
) InnerError!*Inst {
|
||||||
|
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 = switch (mut_tag) {
|
||||||
|
.inferred_alloc_const => Type.initTag(.inferred_alloc_const),
|
||||||
|
.inferred_alloc_mut => Type.initTag(.inferred_alloc_mut),
|
||||||
|
else => unreachable,
|
||||||
|
},
|
||||||
|
.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 {
|
fn analyzeInstResolveInferredAlloc(
|
||||||
return mod.fail(scope, inst.base.src, "TODO implement analyzeInstAllocInferredMut", .{});
|
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 var_is_mut = switch (ptr.ty.tag()) {
|
||||||
|
.inferred_alloc_const => false,
|
||||||
|
.inferred_alloc_mut => true,
|
||||||
|
else => unreachable,
|
||||||
|
};
|
||||||
|
if (var_is_mut) {
|
||||||
|
try mod.validateVarType(scope, inst.base.src, final_elem_ty);
|
||||||
|
}
|
||||||
|
const final_ptr_ty = try mod.simplePtrType(scope, inst.base.src, final_elem_ty, true, .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 {
|
fn analyzeInstStore(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError!*Inst {
|
||||||
|
|||||||
@ -51,6 +51,23 @@ pub fn addCases(ctx: *TestContext) !void {
|
|||||||
, "");
|
, "");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
var case = ctx.exeFromCompiledC("inferred local const and var", .{});
|
||||||
|
|
||||||
|
case.addCompareOutput(
|
||||||
|
\\fn add(a: i32, b: i32) i32 {
|
||||||
|
\\ return a + b;
|
||||||
|
\\}
|
||||||
|
\\
|
||||||
|
\\export fn main() c_int {
|
||||||
|
\\ const x = add(1, 2);
|
||||||
|
\\ var y = add(3, 0);
|
||||||
|
\\ y -= x;
|
||||||
|
\\ return y;
|
||||||
|
\\}
|
||||||
|
, "");
|
||||||
|
}
|
||||||
|
|
||||||
ctx.c("empty start function", linux_x64,
|
ctx.c("empty start function", linux_x64,
|
||||||
\\export fn _start() noreturn {
|
\\export fn _start() noreturn {
|
||||||
\\ unreachable;
|
\\ unreachable;
|
||||||
|
|||||||
@ -1322,4 +1322,13 @@ pub fn addCases(ctx: *TestContext) !void {
|
|||||||
\\}
|
\\}
|
||||||
, &[_][]const u8{":2:5: error: unused for label"});
|
, &[_][]const u8{":2:5: error: unused for label"});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
var case = ctx.exe("bad inferred variable type", linux_x64);
|
||||||
|
case.addError(
|
||||||
|
\\export fn foo() void {
|
||||||
|
\\ var x = null;
|
||||||
|
\\}
|
||||||
|
, &[_][]const u8{":2:9: error: variable of type '@Type(.Null)' must be const or comptime"});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user