mirror of
https://github.com/ziglang/zig.git
synced 2025-12-16 03:03:09 +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))) {
|
||||
return Type.initTag(.const_slice_u8);
|
||||
}
|
||||
@ -3414,3 +3421,9 @@ pub fn getTarget(self: Module) Target {
|
||||
pub fn optimizeMode(self: Module) std.builtin.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]) {
|
||||
.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);
|
||||
break :a .{ .alloc = alloc, .result_loc = .{ .inferred_ptr = alloc.castTag(.alloc_inferred_mut).? } };
|
||||
const alloc = try addZIRNoOpT(mod, scope, name_src, .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);
|
||||
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", .{});
|
||||
|
||||
@ -285,6 +285,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).?, "=="),
|
||||
@ -295,6 +296,7 @@ pub fn generate(file: *C, module: *Module, decl: *Decl) !void {
|
||||
.cmp_neq => try genBinOp(&ctx, file, inst.castTag(.cmp_neq).?, "!="),
|
||||
.dbg_stmt => try genDbgStmt(&ctx, inst.castTag(.dbg_stmt).?),
|
||||
.intcast => try genIntCast(&ctx, file, inst.castTag(.intcast).?),
|
||||
.load => try genLoad(&ctx, file, inst.castTag(.load).?),
|
||||
.ret => try genRet(&ctx, file, inst.castTag(.ret).?),
|
||||
.retvoid => try genRetVoid(file),
|
||||
.store => try genStore(&ctx, file, inst.castTag(.store).?),
|
||||
@ -429,6 +431,16 @@ fn genRetVoid(file: *C) !?[]u8 {
|
||||
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 {
|
||||
try indent(file);
|
||||
const writer = file.main.writer();
|
||||
@ -440,7 +452,6 @@ fn genIntCast(ctx: *Context, file: *C, inst: *Inst.UnOp) !?[]u8 {
|
||||
if (inst.base.isUnused())
|
||||
return null;
|
||||
try indent(file);
|
||||
const op = inst.operand;
|
||||
const writer = file.main.writer();
|
||||
const name = try ctx.name();
|
||||
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", .{});
|
||||
}
|
||||
|
||||
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");
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
|
||||
@ -41,4 +41,4 @@
|
||||
#include <stdint.h>
|
||||
#define int128_t __int128
|
||||
#define uint128_t unsigned __int128
|
||||
|
||||
#include <string.h>
|
||||
|
||||
@ -782,6 +782,7 @@ pub const TestContext = struct {
|
||||
"-std=c89",
|
||||
"-pedantic",
|
||||
"-Werror",
|
||||
"-Wno-declaration-after-statement",
|
||||
"--",
|
||||
"-lc",
|
||||
exe_path,
|
||||
|
||||
179
src/type.zig
179
src/type.zig
@ -78,6 +78,8 @@ pub const Type = extern union {
|
||||
.const_slice,
|
||||
.mut_slice,
|
||||
.pointer,
|
||||
.inferred_alloc_const,
|
||||
.inferred_alloc_mut,
|
||||
=> return .Pointer,
|
||||
|
||||
.optional,
|
||||
@ -158,6 +160,9 @@ pub const Type = extern union {
|
||||
.optional_single_mut_pointer,
|
||||
=> self.cast(Payload.ElemType),
|
||||
|
||||
.inferred_alloc_const => unreachable,
|
||||
.inferred_alloc_mut => unreachable,
|
||||
|
||||
else => null,
|
||||
};
|
||||
}
|
||||
@ -384,6 +389,8 @@ pub const Type = extern union {
|
||||
.enum_literal,
|
||||
.anyerror_void_error_union,
|
||||
.@"anyframe",
|
||||
.inferred_alloc_const,
|
||||
.inferred_alloc_mut,
|
||||
=> unreachable,
|
||||
|
||||
.array_u8,
|
||||
@ -686,6 +693,8 @@ pub const Type = extern union {
|
||||
const name = ty.castTag(.error_set_single).?.data;
|
||||
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;
|
||||
}
|
||||
@ -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),
|
||||
.const_slice_u8 => return Value.initTag(.const_slice_u8_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),
|
||||
}
|
||||
}
|
||||
@ -803,6 +814,9 @@ pub const Type = extern union {
|
||||
.enum_literal,
|
||||
.empty_struct,
|
||||
=> false,
|
||||
|
||||
.inferred_alloc_const => unreachable,
|
||||
.inferred_alloc_mut => unreachable,
|
||||
};
|
||||
}
|
||||
|
||||
@ -920,6 +934,8 @@ pub const Type = extern union {
|
||||
.@"undefined",
|
||||
.enum_literal,
|
||||
.empty_struct,
|
||||
.inferred_alloc_const,
|
||||
.inferred_alloc_mut,
|
||||
=> unreachable,
|
||||
};
|
||||
}
|
||||
@ -943,6 +959,8 @@ pub const Type = extern union {
|
||||
.enum_literal => unreachable,
|
||||
.single_const_pointer_to_comptime_int => unreachable,
|
||||
.empty_struct => unreachable,
|
||||
.inferred_alloc_const => unreachable,
|
||||
.inferred_alloc_mut => unreachable,
|
||||
|
||||
.u8,
|
||||
.i8,
|
||||
@ -1121,6 +1139,8 @@ pub const Type = extern union {
|
||||
.single_const_pointer,
|
||||
.single_mut_pointer,
|
||||
.single_const_pointer_to_comptime_int,
|
||||
.inferred_alloc_const,
|
||||
.inferred_alloc_mut,
|
||||
=> true,
|
||||
|
||||
.pointer => self.castTag(.pointer).?.data.size == .One,
|
||||
@ -1203,6 +1223,8 @@ pub const Type = extern union {
|
||||
.single_const_pointer,
|
||||
.single_mut_pointer,
|
||||
.single_const_pointer_to_comptime_int,
|
||||
.inferred_alloc_const,
|
||||
.inferred_alloc_mut,
|
||||
=> .One,
|
||||
|
||||
.pointer => self.castTag(.pointer).?.data.size,
|
||||
@ -1273,6 +1295,8 @@ pub const Type = extern union {
|
||||
.error_set,
|
||||
.error_set_single,
|
||||
.empty_struct,
|
||||
.inferred_alloc_const,
|
||||
.inferred_alloc_mut,
|
||||
=> false,
|
||||
|
||||
.const_slice,
|
||||
@ -1345,6 +1369,8 @@ pub const Type = extern union {
|
||||
.error_set,
|
||||
.error_set_single,
|
||||
.empty_struct,
|
||||
.inferred_alloc_const,
|
||||
.inferred_alloc_mut,
|
||||
=> false,
|
||||
|
||||
.single_const_pointer,
|
||||
@ -1426,6 +1452,8 @@ pub const Type = extern union {
|
||||
.error_set,
|
||||
.error_set_single,
|
||||
.empty_struct,
|
||||
.inferred_alloc_const,
|
||||
.inferred_alloc_mut,
|
||||
=> false,
|
||||
|
||||
.pointer => {
|
||||
@ -1502,6 +1530,8 @@ pub const Type = extern union {
|
||||
.error_set,
|
||||
.error_set_single,
|
||||
.empty_struct,
|
||||
.inferred_alloc_const,
|
||||
.inferred_alloc_mut,
|
||||
=> false,
|
||||
|
||||
.pointer => {
|
||||
@ -1569,58 +1599,59 @@ 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_const => unreachable,
|
||||
.inferred_alloc_mut => unreachable,
|
||||
|
||||
.array => self.castTag(.array).?.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_single,
|
||||
.empty_struct,
|
||||
.inferred_alloc_const,
|
||||
.inferred_alloc_mut,
|
||||
=> unreachable,
|
||||
|
||||
.array => self.castTag(.array).?.data.len,
|
||||
@ -1808,6 +1841,8 @@ pub const Type = extern union {
|
||||
.error_set,
|
||||
.error_set_single,
|
||||
.empty_struct,
|
||||
.inferred_alloc_const,
|
||||
.inferred_alloc_mut,
|
||||
=> unreachable,
|
||||
|
||||
.single_const_pointer,
|
||||
@ -1891,6 +1926,8 @@ pub const Type = extern union {
|
||||
.error_set,
|
||||
.error_set_single,
|
||||
.empty_struct,
|
||||
.inferred_alloc_const,
|
||||
.inferred_alloc_mut,
|
||||
=> false,
|
||||
|
||||
.int_signed,
|
||||
@ -1966,6 +2003,8 @@ pub const Type = extern union {
|
||||
.error_set,
|
||||
.error_set_single,
|
||||
.empty_struct,
|
||||
.inferred_alloc_const,
|
||||
.inferred_alloc_mut,
|
||||
=> false,
|
||||
|
||||
.int_unsigned,
|
||||
@ -2031,6 +2070,8 @@ pub const Type = extern union {
|
||||
.error_set,
|
||||
.error_set_single,
|
||||
.empty_struct,
|
||||
.inferred_alloc_const,
|
||||
.inferred_alloc_mut,
|
||||
=> unreachable,
|
||||
|
||||
.int_unsigned => .{
|
||||
@ -2120,6 +2161,8 @@ pub const Type = extern union {
|
||||
.error_set,
|
||||
.error_set_single,
|
||||
.empty_struct,
|
||||
.inferred_alloc_const,
|
||||
.inferred_alloc_mut,
|
||||
=> false,
|
||||
|
||||
.usize,
|
||||
@ -2232,6 +2275,8 @@ pub const Type = extern union {
|
||||
.error_set,
|
||||
.error_set_single,
|
||||
.empty_struct,
|
||||
.inferred_alloc_const,
|
||||
.inferred_alloc_mut,
|
||||
=> unreachable,
|
||||
};
|
||||
}
|
||||
@ -2310,6 +2355,8 @@ pub const Type = extern union {
|
||||
.error_set,
|
||||
.error_set_single,
|
||||
.empty_struct,
|
||||
.inferred_alloc_const,
|
||||
.inferred_alloc_mut,
|
||||
=> unreachable,
|
||||
}
|
||||
}
|
||||
@ -2387,6 +2434,8 @@ pub const Type = extern union {
|
||||
.error_set,
|
||||
.error_set_single,
|
||||
.empty_struct,
|
||||
.inferred_alloc_const,
|
||||
.inferred_alloc_mut,
|
||||
=> unreachable,
|
||||
}
|
||||
}
|
||||
@ -2464,6 +2513,8 @@ pub const Type = extern union {
|
||||
.error_set,
|
||||
.error_set_single,
|
||||
.empty_struct,
|
||||
.inferred_alloc_const,
|
||||
.inferred_alloc_mut,
|
||||
=> unreachable,
|
||||
};
|
||||
}
|
||||
@ -2538,6 +2589,8 @@ pub const Type = extern union {
|
||||
.error_set,
|
||||
.error_set_single,
|
||||
.empty_struct,
|
||||
.inferred_alloc_const,
|
||||
.inferred_alloc_mut,
|
||||
=> unreachable,
|
||||
};
|
||||
}
|
||||
@ -2612,6 +2665,8 @@ pub const Type = extern union {
|
||||
.error_set,
|
||||
.error_set_single,
|
||||
.empty_struct,
|
||||
.inferred_alloc_const,
|
||||
.inferred_alloc_mut,
|
||||
=> unreachable,
|
||||
};
|
||||
}
|
||||
@ -2686,6 +2741,8 @@ pub const Type = extern union {
|
||||
.error_set,
|
||||
.error_set_single,
|
||||
.empty_struct,
|
||||
.inferred_alloc_const,
|
||||
.inferred_alloc_mut,
|
||||
=> false,
|
||||
};
|
||||
}
|
||||
@ -2778,6 +2835,8 @@ pub const Type = extern union {
|
||||
ty = ty.castTag(.pointer).?.data.pointee_type;
|
||||
continue;
|
||||
},
|
||||
.inferred_alloc_const => unreachable,
|
||||
.inferred_alloc_mut => unreachable,
|
||||
};
|
||||
}
|
||||
|
||||
@ -2846,6 +2905,8 @@ pub const Type = extern union {
|
||||
.error_set,
|
||||
.error_set_single,
|
||||
.empty_struct,
|
||||
.inferred_alloc_const,
|
||||
.inferred_alloc_mut,
|
||||
=> return false,
|
||||
|
||||
.c_const_pointer,
|
||||
@ -2931,6 +2992,8 @@ pub const Type = extern union {
|
||||
.c_const_pointer,
|
||||
.c_mut_pointer,
|
||||
.pointer,
|
||||
.inferred_alloc_const,
|
||||
.inferred_alloc_mut,
|
||||
=> unreachable,
|
||||
|
||||
.empty_struct => self.castTag(.empty_struct).?.data,
|
||||
@ -3041,7 +3104,13 @@ pub const Type = extern union {
|
||||
single_const_pointer_to_comptime_int,
|
||||
anyerror_void_error_union,
|
||||
@"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.
|
||||
|
||||
array_u8,
|
||||
@ -3069,7 +3138,7 @@ pub const Type = extern union {
|
||||
error_set_single,
|
||||
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 fn Type(comptime t: Tag) type {
|
||||
@ -3116,6 +3185,8 @@ pub const Type = extern union {
|
||||
.anyerror_void_error_union,
|
||||
.@"anyframe",
|
||||
.const_slice_u8,
|
||||
.inferred_alloc_const,
|
||||
.inferred_alloc_mut,
|
||||
=> @compileError("Type Tag " ++ @tagName(t) ++ " has no payload"),
|
||||
|
||||
.array_u8,
|
||||
|
||||
@ -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
|
||||
|
||||
12
src/zir.zig
12
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",
|
||||
|
||||
@ -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");
|
||||
@ -28,8 +30,18 @@ pub fn analyzeInst(mod: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError!
|
||||
switch (old_inst.tag) {
|
||||
.alloc => return analyzeInstAlloc(mod, scope, old_inst.castTag(.alloc).?),
|
||||
.alloc_mut => return analyzeInstAllocMut(mod, scope, old_inst.castTag(.alloc_mut).?),
|
||||
.alloc_inferred => return analyzeInstAllocInferred(mod, scope, old_inst.castTag(.alloc_inferred).?),
|
||||
.alloc_inferred_mut => return analyzeInstAllocInferredMut(mod, scope, old_inst.castTag(.alloc_inferred_mut).?),
|
||||
.alloc_inferred => return analyzeInstAllocInferred(
|
||||
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).?),
|
||||
.bitcast_ref => return analyzeInstBitCastRef(mod, scope, old_inst.castTag(.bitcast_ref).?),
|
||||
.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_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),
|
||||
@ -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 {
|
||||
const var_type = try resolveType(mod, scope, inst.positionals.operand);
|
||||
if (!var_type.isValidVarType(false)) {
|
||||
return mod.fail(scope, inst.base.src, "variable of type '{}' must be const or comptime", .{var_type});
|
||||
}
|
||||
try mod.validateVarType(scope, inst.base.src, var_type);
|
||||
const ptr_type = try mod.simplePtrType(scope, inst.base.src, var_type, true, .One);
|
||||
const b = try mod.requireRuntimeBlock(scope, inst.base.src);
|
||||
return mod.addNoOp(b, inst.base.src, ptr_type, .alloc);
|
||||
}
|
||||
|
||||
fn analyzeInstAllocInferred(mod: *Module, scope: *Scope, inst: *zir.Inst.NoOp) InnerError!*Inst {
|
||||
return mod.fail(scope, inst.base.src, "TODO implement analyzeInstAllocInferred", .{});
|
||||
fn 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 {
|
||||
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 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 {
|
||||
|
||||
@ -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,
|
||||
\\export fn _start() noreturn {
|
||||
\\ unreachable;
|
||||
|
||||
@ -1322,4 +1322,13 @@ pub fn addCases(ctx: *TestContext) !void {
|
||||
\\}
|
||||
, &[_][]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