mirror of
https://github.com/ziglang/zig.git
synced 2026-01-01 19:13:16 +00:00
commit
a30950706f
@ -170,6 +170,9 @@ pub const Decl = struct {
|
||||
/// This flag is set when this Decl is added to a check_for_deletion set, and cleared
|
||||
/// when removed.
|
||||
deletion_flag: bool,
|
||||
/// Whether the corresponding AST decl has a `pub` keyword.
|
||||
is_pub: bool,
|
||||
|
||||
/// An integer that can be checked against the corresponding incrementing
|
||||
/// generation field of Module. This is used to determine whether `complete` status
|
||||
/// represents pre- or post- re-analysis.
|
||||
@ -320,6 +323,16 @@ pub const Fn = struct {
|
||||
}
|
||||
};
|
||||
|
||||
pub const Var = struct {
|
||||
init: Value,
|
||||
owner_decl: *Decl,
|
||||
|
||||
has_init: bool,
|
||||
is_extern: bool,
|
||||
is_mutable: bool,
|
||||
is_threadlocal: bool,
|
||||
};
|
||||
|
||||
pub const Scope = struct {
|
||||
tag: Tag,
|
||||
|
||||
@ -1235,6 +1248,7 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {
|
||||
};
|
||||
defer fn_type_scope.instructions.deinit(self.gpa);
|
||||
|
||||
decl.is_pub = fn_proto.getTrailer("visib_token") != null;
|
||||
const body_node = fn_proto.getTrailer("body_node") orelse
|
||||
return self.failTok(&fn_type_scope.base, fn_proto.fn_token, "TODO implement extern functions", .{});
|
||||
|
||||
@ -1419,7 +1433,173 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {
|
||||
}
|
||||
return type_changed;
|
||||
},
|
||||
.VarDecl => @panic("TODO var decl"),
|
||||
.VarDecl => {
|
||||
const var_decl = @fieldParentPtr(ast.Node.VarDecl, "base", ast_node);
|
||||
|
||||
decl.analysis = .in_progress;
|
||||
|
||||
// We need the memory for the Type to go into the arena for the Decl
|
||||
var decl_arena = std.heap.ArenaAllocator.init(self.gpa);
|
||||
errdefer decl_arena.deinit();
|
||||
const decl_arena_state = try decl_arena.allocator.create(std.heap.ArenaAllocator.State);
|
||||
|
||||
var block_scope: Scope.Block = .{
|
||||
.parent = null,
|
||||
.func = null,
|
||||
.decl = decl,
|
||||
.instructions = .{},
|
||||
.arena = &decl_arena.allocator,
|
||||
};
|
||||
defer block_scope.instructions.deinit(self.gpa);
|
||||
|
||||
decl.is_pub = var_decl.getTrailer("visib_token") != null;
|
||||
const is_extern = blk: {
|
||||
const maybe_extern_token = var_decl.getTrailer("extern_export_token") orelse
|
||||
break :blk false;
|
||||
break :blk tree.token_ids[maybe_extern_token] == .Keyword_extern;
|
||||
};
|
||||
if (var_decl.getTrailer("lib_name")) |lib_name| {
|
||||
assert(is_extern);
|
||||
return self.failNode(&block_scope.base, lib_name, "TODO implement function library name", .{});
|
||||
}
|
||||
const is_mutable = tree.token_ids[var_decl.mut_token] == .Keyword_var;
|
||||
const is_threadlocal = if (var_decl.getTrailer("thread_local_token")) |some| blk: {
|
||||
if (!is_mutable) {
|
||||
return self.failTok(&block_scope.base, some, "threadlocal variable cannot be constant", .{});
|
||||
}
|
||||
break :blk true;
|
||||
} else false;
|
||||
assert(var_decl.getTrailer("comptime_token") == null);
|
||||
if (var_decl.getTrailer("align_node")) |align_expr| {
|
||||
return self.failNode(&block_scope.base, align_expr, "TODO implement function align expression", .{});
|
||||
}
|
||||
if (var_decl.getTrailer("section_node")) |sect_expr| {
|
||||
return self.failNode(&block_scope.base, sect_expr, "TODO implement function section expression", .{});
|
||||
}
|
||||
|
||||
const explicit_type = blk: {
|
||||
const type_node = var_decl.getTrailer("type_node") orelse
|
||||
break :blk null;
|
||||
|
||||
var type_scope_arena = std.heap.ArenaAllocator.init(self.gpa);
|
||||
defer type_scope_arena.deinit();
|
||||
var type_scope: Scope.GenZIR = .{
|
||||
.decl = decl,
|
||||
.arena = &type_scope_arena.allocator,
|
||||
.parent = decl.scope,
|
||||
};
|
||||
defer type_scope.instructions.deinit(self.gpa);
|
||||
|
||||
const src = tree.token_locs[type_node.firstToken()].start;
|
||||
const type_type = try astgen.addZIRInstConst(self, &type_scope.base, src, .{
|
||||
.ty = Type.initTag(.type),
|
||||
.val = Value.initTag(.type_type),
|
||||
});
|
||||
const var_type = try astgen.expr(self, &type_scope.base, .{ .ty = type_type }, type_node);
|
||||
_ = try astgen.addZIRUnOp(self, &type_scope.base, src, .@"return", var_type);
|
||||
|
||||
break :blk try zir_sema.analyzeBodyValueAsType(self, &block_scope, .{
|
||||
.instructions = type_scope.instructions.items,
|
||||
});
|
||||
};
|
||||
|
||||
var var_type: Type = undefined;
|
||||
const value: ?Value = if (var_decl.getTrailer("init_node")) |init_node| blk: {
|
||||
var gen_scope_arena = std.heap.ArenaAllocator.init(self.gpa);
|
||||
defer gen_scope_arena.deinit();
|
||||
var gen_scope: Scope.GenZIR = .{
|
||||
.decl = decl,
|
||||
.arena = &gen_scope_arena.allocator,
|
||||
.parent = decl.scope,
|
||||
};
|
||||
defer gen_scope.instructions.deinit(self.gpa);
|
||||
const src = tree.token_locs[init_node.firstToken()].start;
|
||||
|
||||
// TODO comptime scope here
|
||||
const init_inst = try astgen.expr(self, &gen_scope.base, .none, init_node);
|
||||
_ = try astgen.addZIRUnOp(self, &gen_scope.base, src, .@"return", init_inst);
|
||||
|
||||
var inner_block: Scope.Block = .{
|
||||
.parent = null,
|
||||
.func = null,
|
||||
.decl = decl,
|
||||
.instructions = .{},
|
||||
.arena = &gen_scope_arena.allocator,
|
||||
};
|
||||
defer inner_block.instructions.deinit(self.gpa);
|
||||
try zir_sema.analyzeBody(self, &inner_block.base, .{ .instructions = gen_scope.instructions.items });
|
||||
|
||||
for (inner_block.instructions.items) |inst| {
|
||||
if (inst.castTag(.ret)) |ret| {
|
||||
const coerced = if (explicit_type) |some|
|
||||
try self.coerce(&inner_block.base, some, ret.operand)
|
||||
else
|
||||
ret.operand;
|
||||
const val = try self.resolveConstValue(&inner_block.base, coerced);
|
||||
|
||||
var_type = explicit_type orelse try ret.operand.ty.copy(block_scope.arena);
|
||||
break :blk try val.copy(block_scope.arena);
|
||||
} else {
|
||||
return self.fail(&block_scope.base, inst.src, "unable to resolve comptime value", .{});
|
||||
}
|
||||
}
|
||||
unreachable;
|
||||
} else if (!is_extern) {
|
||||
return self.failTok(&block_scope.base, var_decl.firstToken(), "variables must be initialized", .{});
|
||||
} else if (explicit_type) |some| blk: {
|
||||
var_type = some;
|
||||
break :blk null;
|
||||
} else {
|
||||
return self.failTok(&block_scope.base, var_decl.firstToken(), "unable to infer variable type", .{});
|
||||
};
|
||||
|
||||
if (is_mutable and !var_type.isValidVarType(is_extern)) {
|
||||
return self.failTok(&block_scope.base, var_decl.firstToken(), "variable of type '{}' must be const", .{var_type});
|
||||
}
|
||||
|
||||
var type_changed = true;
|
||||
if (decl.typedValueManaged()) |tvm| {
|
||||
type_changed = !tvm.typed_value.ty.eql(var_type);
|
||||
|
||||
tvm.deinit(self.gpa);
|
||||
}
|
||||
|
||||
const new_variable = try decl_arena.allocator.create(Var);
|
||||
const var_payload = try decl_arena.allocator.create(Value.Payload.Variable);
|
||||
new_variable.* = .{
|
||||
.owner_decl = decl,
|
||||
.init = value orelse undefined,
|
||||
.has_init = value != null,
|
||||
.is_extern = is_extern,
|
||||
.is_mutable = is_mutable,
|
||||
.is_threadlocal = is_threadlocal,
|
||||
};
|
||||
var_payload.* = .{ .variable = new_variable };
|
||||
|
||||
decl_arena_state.* = decl_arena.state;
|
||||
decl.typed_value = .{
|
||||
.most_recent = .{
|
||||
.typed_value = .{
|
||||
.ty = var_type,
|
||||
.val = Value.initPayload(&var_payload.base),
|
||||
},
|
||||
.arena = decl_arena_state,
|
||||
},
|
||||
};
|
||||
decl.analysis = .complete;
|
||||
decl.generation = self.generation;
|
||||
|
||||
if (var_decl.getTrailer("extern_export_token")) |maybe_export_token| {
|
||||
if (tree.token_ids[maybe_export_token] == .Keyword_export) {
|
||||
const export_src = tree.token_locs[maybe_export_token].start;
|
||||
const name_loc = tree.token_locs[var_decl.name_token];
|
||||
const name = tree.tokenSliceLoc(name_loc);
|
||||
// The scope needs to have the decl in it.
|
||||
try self.analyzeExport(&block_scope.base, export_src, name, decl);
|
||||
}
|
||||
}
|
||||
return type_changed;
|
||||
},
|
||||
.Comptime => @panic("TODO comptime decl"),
|
||||
.Use => @panic("TODO usingnamespace decl"),
|
||||
else => unreachable,
|
||||
@ -1584,7 +1764,32 @@ fn analyzeRootSrcFile(self: *Module, root_scope: *Scope.File) !void {
|
||||
}
|
||||
}
|
||||
} else if (src_decl.castTag(.VarDecl)) |var_decl| {
|
||||
log.err("TODO: analyze var decl", .{});
|
||||
const name_loc = tree.token_locs[var_decl.name_token];
|
||||
const name = tree.tokenSliceLoc(name_loc);
|
||||
const name_hash = root_scope.fullyQualifiedNameHash(name);
|
||||
const contents_hash = std.zig.hashSrc(tree.getNodeSource(src_decl));
|
||||
if (self.decl_table.get(name_hash)) |decl| {
|
||||
// Update the AST Node index of the decl, even if its contents are unchanged, it may
|
||||
// have been re-ordered.
|
||||
decl.src_index = decl_i;
|
||||
if (deleted_decls.remove(decl) == null) {
|
||||
decl.analysis = .sema_failure;
|
||||
const err_msg = try ErrorMsg.create(self.gpa, name_loc.start, "redefinition of '{}'", .{decl.name});
|
||||
errdefer err_msg.destroy(self.gpa);
|
||||
try self.failed_decls.putNoClobber(self.gpa, decl, err_msg);
|
||||
} else if (!srcHashEql(decl.contents_hash, contents_hash)) {
|
||||
try self.markOutdatedDecl(decl);
|
||||
decl.contents_hash = contents_hash;
|
||||
}
|
||||
} else {
|
||||
const new_decl = try self.createNewDecl(&root_scope.base, name, decl_i, name_hash, contents_hash);
|
||||
root_scope.decls.appendAssumeCapacity(new_decl);
|
||||
if (var_decl.getTrailer("extern_export_token")) |maybe_export_token| {
|
||||
if (tree.token_ids[maybe_export_token] == .Keyword_export) {
|
||||
self.work_queue.writeItemAssumeCapacity(.{ .analyze_decl = new_decl });
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (src_decl.castTag(.Comptime)) |comptime_node| {
|
||||
log.err("TODO: analyze comptime decl", .{});
|
||||
} else if (src_decl.castTag(.ContainerField)) |container_field| {
|
||||
@ -1798,6 +2003,7 @@ fn allocateNewDecl(
|
||||
.wasm => .{ .wasm = null },
|
||||
},
|
||||
.generation = 0,
|
||||
.is_pub = false,
|
||||
};
|
||||
return new_decl;
|
||||
}
|
||||
@ -2217,20 +2423,46 @@ pub fn analyzeDeclRef(self: *Module, scope: *Scope, src: usize, decl: *Decl) Inn
|
||||
};
|
||||
|
||||
const decl_tv = try decl.typedValue();
|
||||
const ty_payload = try scope.arena().create(Type.Payload.Pointer);
|
||||
ty_payload.* = .{
|
||||
.base = .{ .tag = .single_const_pointer },
|
||||
.pointee_type = decl_tv.ty,
|
||||
};
|
||||
if (decl_tv.val.tag() == .variable) {
|
||||
return self.analyzeVarRef(scope, src, decl_tv);
|
||||
}
|
||||
const ty = try self.singlePtrType(scope, src, false, decl_tv.ty);
|
||||
const val_payload = try scope.arena().create(Value.Payload.DeclRef);
|
||||
val_payload.* = .{ .decl = decl };
|
||||
|
||||
return self.constInst(scope, src, .{
|
||||
.ty = Type.initPayload(&ty_payload.base),
|
||||
.ty = ty,
|
||||
.val = Value.initPayload(&val_payload.base),
|
||||
});
|
||||
}
|
||||
|
||||
fn analyzeVarRef(self: *Module, scope: *Scope, src: usize, tv: TypedValue) InnerError!*Inst {
|
||||
const variable = tv.val.cast(Value.Payload.Variable).?.variable;
|
||||
|
||||
const ty = try self.singlePtrType(scope, src, variable.is_mutable, tv.ty);
|
||||
if (!variable.is_mutable and !variable.is_extern and variable.has_init) {
|
||||
const val_payload = try scope.arena().create(Value.Payload.RefVal);
|
||||
val_payload.* = .{ .val = variable.init };
|
||||
return self.constInst(scope, src, .{
|
||||
.ty = ty,
|
||||
.val = Value.initPayload(&val_payload.base),
|
||||
});
|
||||
}
|
||||
|
||||
const b = try self.requireRuntimeBlock(scope, src);
|
||||
const inst = try b.arena.create(Inst.VarPtr);
|
||||
inst.* = .{
|
||||
.base = .{
|
||||
.tag = .varptr,
|
||||
.ty = ty,
|
||||
.src = src,
|
||||
},
|
||||
.variable = variable,
|
||||
};
|
||||
try b.instructions.append(self.gpa, &inst.base);
|
||||
return &inst.base;
|
||||
}
|
||||
|
||||
pub fn analyzeDeref(self: *Module, scope: *Scope, src: usize, ptr: *Inst, ptr_src: usize) InnerError!*Inst {
|
||||
const elem_ty = switch (ptr.ty.zigTypeTag()) {
|
||||
.Pointer => ptr.ty.elemType(),
|
||||
|
||||
@ -1223,9 +1223,10 @@ fn identifier(mod: *Module, scope: *Scope, rl: ResultLoc, ident: *ast.Node.OneTo
|
||||
}
|
||||
|
||||
if (mod.lookupDeclName(scope, ident_name)) |decl| {
|
||||
// TODO handle lvalues
|
||||
const result = try addZIRInst(mod, scope, src, zir.Inst.DeclValInModule, .{ .decl = decl }, .{});
|
||||
return rlWrap(mod, scope, rl, result);
|
||||
if (rl == .lvalue or rl == .ref)
|
||||
return result;
|
||||
return rlWrap(mod, scope, rl, try addZIRUnOp(mod, scope, src, .deref, result));
|
||||
}
|
||||
|
||||
return mod.failNode(scope, &ident.base, "use of undeclared identifier '{}'", .{ident_name});
|
||||
@ -1258,7 +1259,8 @@ fn multilineStrLiteral(mod: *Module, scope: *Scope, node: *ast.Node.MultilineStr
|
||||
// line lengths and new lines
|
||||
var len = lines.len - 1;
|
||||
for (lines) |line| {
|
||||
len += tree.tokenSlice(line).len - 2;
|
||||
// 2 for the '//' + 1 for '\n'
|
||||
len += tree.tokenSlice(line).len - 3;
|
||||
}
|
||||
|
||||
const bytes = try scope.arena().alloc(u8, len);
|
||||
@ -1268,9 +1270,9 @@ fn multilineStrLiteral(mod: *Module, scope: *Scope, node: *ast.Node.MultilineStr
|
||||
bytes[i] = '\n';
|
||||
i += 1;
|
||||
}
|
||||
const slice = tree.tokenSlice(line)[2..];
|
||||
mem.copy(u8, bytes[i..], slice);
|
||||
i += slice.len;
|
||||
const slice = tree.tokenSlice(line);
|
||||
mem.copy(u8, bytes[i..], slice[2..slice.len - 1]);
|
||||
i += slice.len - 3;
|
||||
}
|
||||
|
||||
return addZIRInst(mod, scope, src, zir.Inst.Str, .{ .bytes = bytes }, .{});
|
||||
|
||||
@ -684,6 +684,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
|
||||
.unreach => return MCValue{ .unreach = {} },
|
||||
.unwrap_optional => return self.genUnwrapOptional(inst.castTag(.unwrap_optional).?),
|
||||
.wrap_optional => return self.genWrapOptional(inst.castTag(.wrap_optional).?),
|
||||
.varptr => return self.genVarPtr(inst.castTag(.varptr).?),
|
||||
}
|
||||
}
|
||||
|
||||
@ -858,6 +859,16 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
|
||||
}
|
||||
}
|
||||
|
||||
fn genVarPtr(self: *Self, inst: *ir.Inst.VarPtr) !MCValue {
|
||||
// No side effects, so if it's unreferenced, do nothing.
|
||||
if (inst.base.isUnused())
|
||||
return MCValue.dead;
|
||||
|
||||
switch (arch) {
|
||||
else => return self.fail(inst.base.src, "TODO implement varptr for {}", .{self.target.cpu.arch}),
|
||||
}
|
||||
}
|
||||
|
||||
fn reuseOperand(inst: *ir.Inst, op_index: ir.Inst.DeathsBitIndex, mcv: MCValue) bool {
|
||||
if (!inst.operandDies(op_index) or !mcv.isMutable())
|
||||
return false;
|
||||
|
||||
@ -81,6 +81,7 @@ pub const Inst = struct {
|
||||
ref,
|
||||
ret,
|
||||
retvoid,
|
||||
varptr,
|
||||
/// Write a value to a pointer. LHS is pointer, RHS is value.
|
||||
store,
|
||||
sub,
|
||||
@ -135,6 +136,7 @@ pub const Inst = struct {
|
||||
.condbr => CondBr,
|
||||
.constant => Constant,
|
||||
.loop => Loop,
|
||||
.varptr => VarPtr,
|
||||
};
|
||||
}
|
||||
|
||||
@ -434,6 +436,20 @@ pub const Inst = struct {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
pub const VarPtr = struct {
|
||||
pub const base_tag = Tag.varptr;
|
||||
|
||||
base: Inst,
|
||||
variable: *Module.Var,
|
||||
|
||||
pub fn operandCount(self: *const VarPtr) usize {
|
||||
return 0;
|
||||
}
|
||||
pub fn getOperand(self: *const VarPtr, index: usize) ?*Inst {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
pub const Body = struct {
|
||||
|
||||
@ -457,7 +457,8 @@ pub const Type = extern union {
|
||||
try param_type.format("", .{}, out_stream);
|
||||
}
|
||||
try out_stream.writeAll(") ");
|
||||
try payload.return_type.format("", .{}, out_stream);
|
||||
ty = payload.return_type;
|
||||
continue;
|
||||
},
|
||||
|
||||
.array_u8 => {
|
||||
@ -1074,7 +1075,7 @@ pub const Type = extern union {
|
||||
}
|
||||
|
||||
/// Returns if type can be used for a runtime variable
|
||||
pub fn isValidVarType(self: Type) bool {
|
||||
pub fn isValidVarType(self: Type, is_extern: bool) bool {
|
||||
var ty = self;
|
||||
while (true) switch (ty.zigTypeTag()) {
|
||||
.Bool,
|
||||
@ -1087,6 +1088,7 @@ pub const Type = extern union {
|
||||
.Vector,
|
||||
=> return true,
|
||||
|
||||
.Opaque => return is_extern,
|
||||
.BoundFn,
|
||||
.ComptimeFloat,
|
||||
.ComptimeInt,
|
||||
@ -1096,12 +1098,11 @@ pub const Type = extern union {
|
||||
.Void,
|
||||
.Undefined,
|
||||
.Null,
|
||||
.Opaque,
|
||||
=> return false,
|
||||
|
||||
.Optional => {
|
||||
var buf: Payload.Pointer = undefined;
|
||||
return ty.optionalChild(&buf).isValidVarType();
|
||||
return ty.optionalChild(&buf).isValidVarType(is_extern);
|
||||
},
|
||||
.Pointer, .Array => ty = ty.elemType(),
|
||||
|
||||
|
||||
@ -79,6 +79,7 @@ pub const Value = extern union {
|
||||
int_big_positive,
|
||||
int_big_negative,
|
||||
function,
|
||||
variable,
|
||||
ref_val,
|
||||
decl_ref,
|
||||
elem_ptr,
|
||||
@ -196,6 +197,7 @@ pub const Value = extern union {
|
||||
@panic("TODO implement copying of big ints");
|
||||
},
|
||||
.function => return self.copyPayloadShallow(allocator, Payload.Function),
|
||||
.variable => return self.copyPayloadShallow(allocator, Payload.Variable),
|
||||
.ref_val => {
|
||||
const payload = @fieldParentPtr(Payload.RefVal, "base", self.ptr_otherwise);
|
||||
const new_payload = try allocator.create(Payload.RefVal);
|
||||
@ -216,7 +218,7 @@ pub const Value = extern union {
|
||||
};
|
||||
return Value{ .ptr_otherwise = &new_payload.base };
|
||||
},
|
||||
.enum_literal, .bytes => return self.copyPayloadShallow(allocator, Payload.Bytes),
|
||||
.bytes => return self.copyPayloadShallow(allocator, Payload.Bytes),
|
||||
.repeated => {
|
||||
const payload = @fieldParentPtr(Payload.Repeated, "base", self.ptr_otherwise);
|
||||
const new_payload = try allocator.create(Payload.Repeated);
|
||||
@ -230,6 +232,15 @@ pub const Value = extern union {
|
||||
.float_32 => return self.copyPayloadShallow(allocator, Payload.Float_32),
|
||||
.float_64 => return self.copyPayloadShallow(allocator, Payload.Float_64),
|
||||
.float_128 => return self.copyPayloadShallow(allocator, Payload.Float_128),
|
||||
.enum_literal => {
|
||||
const payload = @fieldParentPtr(Payload.Bytes, "base", self.ptr_otherwise);
|
||||
const new_payload = try allocator.create(Payload.Bytes);
|
||||
new_payload.* = .{
|
||||
.base = payload.base,
|
||||
.data = try allocator.dupe(u8, payload.data),
|
||||
};
|
||||
return Value{ .ptr_otherwise = &new_payload.base };
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@ -310,6 +321,7 @@ pub const Value = extern union {
|
||||
.int_big_positive => return out_stream.print("{}", .{val.cast(Payload.IntBigPositive).?.asBigInt()}),
|
||||
.int_big_negative => return out_stream.print("{}", .{val.cast(Payload.IntBigNegative).?.asBigInt()}),
|
||||
.function => return out_stream.writeAll("(function)"),
|
||||
.variable => return out_stream.writeAll("(variable)"),
|
||||
.ref_val => {
|
||||
const ref_val = val.cast(Payload.RefVal).?;
|
||||
try out_stream.writeAll("&const ");
|
||||
@ -410,6 +422,7 @@ pub const Value = extern union {
|
||||
.int_big_positive,
|
||||
.int_big_negative,
|
||||
.function,
|
||||
.variable,
|
||||
.ref_val,
|
||||
.decl_ref,
|
||||
.elem_ptr,
|
||||
@ -471,6 +484,7 @@ pub const Value = extern union {
|
||||
.enum_literal_type,
|
||||
.null_value,
|
||||
.function,
|
||||
.variable,
|
||||
.ref_val,
|
||||
.decl_ref,
|
||||
.elem_ptr,
|
||||
@ -548,6 +562,7 @@ pub const Value = extern union {
|
||||
.enum_literal_type,
|
||||
.null_value,
|
||||
.function,
|
||||
.variable,
|
||||
.ref_val,
|
||||
.decl_ref,
|
||||
.elem_ptr,
|
||||
@ -625,6 +640,7 @@ pub const Value = extern union {
|
||||
.enum_literal_type,
|
||||
.null_value,
|
||||
.function,
|
||||
.variable,
|
||||
.ref_val,
|
||||
.decl_ref,
|
||||
.elem_ptr,
|
||||
@ -728,6 +744,7 @@ pub const Value = extern union {
|
||||
.enum_literal_type,
|
||||
.null_value,
|
||||
.function,
|
||||
.variable,
|
||||
.ref_val,
|
||||
.decl_ref,
|
||||
.elem_ptr,
|
||||
@ -810,6 +827,7 @@ pub const Value = extern union {
|
||||
.enum_literal_type,
|
||||
.null_value,
|
||||
.function,
|
||||
.variable,
|
||||
.ref_val,
|
||||
.decl_ref,
|
||||
.elem_ptr,
|
||||
@ -974,6 +992,7 @@ pub const Value = extern union {
|
||||
.bool_false,
|
||||
.null_value,
|
||||
.function,
|
||||
.variable,
|
||||
.ref_val,
|
||||
.decl_ref,
|
||||
.elem_ptr,
|
||||
@ -1046,6 +1065,7 @@ pub const Value = extern union {
|
||||
.enum_literal_type,
|
||||
.null_value,
|
||||
.function,
|
||||
.variable,
|
||||
.ref_val,
|
||||
.decl_ref,
|
||||
.elem_ptr,
|
||||
@ -1182,6 +1202,7 @@ pub const Value = extern union {
|
||||
.bool_false,
|
||||
.null_value,
|
||||
.function,
|
||||
.variable,
|
||||
.int_u64,
|
||||
.int_i64,
|
||||
.int_big_positive,
|
||||
@ -1260,6 +1281,7 @@ pub const Value = extern union {
|
||||
.bool_false,
|
||||
.null_value,
|
||||
.function,
|
||||
.variable,
|
||||
.int_u64,
|
||||
.int_i64,
|
||||
.int_big_positive,
|
||||
@ -1355,6 +1377,7 @@ pub const Value = extern union {
|
||||
.bool_true,
|
||||
.bool_false,
|
||||
.function,
|
||||
.variable,
|
||||
.int_u64,
|
||||
.int_i64,
|
||||
.int_big_positive,
|
||||
@ -1429,6 +1452,11 @@ pub const Value = extern union {
|
||||
func: *Module.Fn,
|
||||
};
|
||||
|
||||
pub const Variable = struct {
|
||||
base: Payload = Payload{ .tag = .variable },
|
||||
variable: *Module.Var,
|
||||
};
|
||||
|
||||
pub const ArraySentinel0_u8_Type = struct {
|
||||
base: Payload = Payload{ .tag = .array_sentinel_0_u8_type },
|
||||
len: u64,
|
||||
|
||||
@ -1752,6 +1752,9 @@ const EmitZIR = struct {
|
||||
const decl_ref = try self.emitDeclRef(inst.src, declref.decl);
|
||||
try new_body.instructions.append(decl_ref);
|
||||
break :blk decl_ref;
|
||||
} else if (const_inst.val.cast(Value.Payload.Variable)) |var_pl| blk: {
|
||||
const owner_decl = var_pl.variable.owner_decl;
|
||||
break :blk try self.emitDeclVal(inst.src, mem.spanZ(owner_decl.name));
|
||||
} else blk: {
|
||||
break :blk (try self.emitTypedValue(inst.src, .{ .ty = inst.ty, .val = const_inst.val })).inst;
|
||||
};
|
||||
@ -1875,6 +1878,11 @@ const EmitZIR = struct {
|
||||
if (typed_value.val.cast(Value.Payload.DeclRef)) |decl_ref| {
|
||||
const decl = decl_ref.decl;
|
||||
return try self.emitUnnamedDecl(try self.emitDeclRef(src, decl));
|
||||
} else if (typed_value.val.cast(Value.Payload.Variable)) |variable| {
|
||||
return self.emitTypedValue(src, .{
|
||||
.ty = typed_value.ty,
|
||||
.val = variable.variable.init,
|
||||
});
|
||||
}
|
||||
if (typed_value.val.isUndef()) {
|
||||
const as_inst = try self.arena.allocator.create(Inst.BinOp);
|
||||
@ -1964,6 +1972,21 @@ const EmitZIR = struct {
|
||||
return self.emitPrimitive(src, .@"true")
|
||||
else
|
||||
return self.emitPrimitive(src, .@"false"),
|
||||
.EnumLiteral => {
|
||||
const enum_literal = @fieldParentPtr(Value.Payload.Bytes, "base", typed_value.val.ptr_otherwise);
|
||||
const inst = try self.arena.allocator.create(Inst.Str);
|
||||
inst.* = .{
|
||||
.base = .{
|
||||
.src = src,
|
||||
.tag = .enum_literal,
|
||||
},
|
||||
.positionals = .{
|
||||
.bytes = enum_literal.data,
|
||||
},
|
||||
.kw_args = .{},
|
||||
};
|
||||
return self.emitUnnamedDecl(&inst.base);
|
||||
},
|
||||
else => |t| std.debug.panic("TODO implement emitTypedValue for {}", .{@tagName(t)}),
|
||||
}
|
||||
}
|
||||
@ -2311,6 +2334,8 @@ const EmitZIR = struct {
|
||||
};
|
||||
break :blk &new_inst.base;
|
||||
},
|
||||
|
||||
.varptr => @panic("TODO"),
|
||||
};
|
||||
try self.metadata.put(new_inst, .{ .deaths = inst.deaths });
|
||||
try instructions.append(new_inst);
|
||||
@ -2432,6 +2457,51 @@ const EmitZIR = struct {
|
||||
};
|
||||
return self.emitUnnamedDecl(&inst.base);
|
||||
},
|
||||
.Array => {
|
||||
var len_pl = Value.Payload.Int_u64{ .int = ty.arrayLen() };
|
||||
const len = Value.initPayload(&len_pl.base);
|
||||
|
||||
const inst = if (ty.arraySentinel()) |sentinel| blk: {
|
||||
const inst = try self.arena.allocator.create(Inst.ArrayTypeSentinel);
|
||||
inst.* = .{
|
||||
.base = .{
|
||||
.src = src,
|
||||
.tag = .array_type,
|
||||
},
|
||||
.positionals = .{
|
||||
.len = (try self.emitTypedValue(src, .{
|
||||
.ty = Type.initTag(.usize),
|
||||
.val = len,
|
||||
})).inst,
|
||||
.sentinel = (try self.emitTypedValue(src, .{
|
||||
.ty = ty.elemType(),
|
||||
.val = sentinel,
|
||||
})).inst,
|
||||
.elem_type = (try self.emitType(src, ty.elemType())).inst,
|
||||
},
|
||||
.kw_args = .{},
|
||||
};
|
||||
break :blk &inst.base;
|
||||
} else blk: {
|
||||
const inst = try self.arena.allocator.create(Inst.BinOp);
|
||||
inst.* = .{
|
||||
.base = .{
|
||||
.src = src,
|
||||
.tag = .array_type,
|
||||
},
|
||||
.positionals = .{
|
||||
.lhs = (try self.emitTypedValue(src, .{
|
||||
.ty = Type.initTag(.usize),
|
||||
.val = len,
|
||||
})).inst,
|
||||
.rhs = (try self.emitType(src, ty.elemType())).inst,
|
||||
},
|
||||
.kw_args = .{},
|
||||
};
|
||||
break :blk &inst.base;
|
||||
};
|
||||
return self.emitUnnamedDecl(inst);
|
||||
},
|
||||
else => std.debug.panic("TODO implement emitType for {}", .{ty}),
|
||||
},
|
||||
}
|
||||
|
||||
@ -366,7 +366,7 @@ fn analyzeInstEnsureResultNonError(mod: *Module, scope: *Scope, inst: *zir.Inst.
|
||||
fn analyzeInstAlloc(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst {
|
||||
const var_type = try resolveType(mod, scope, inst.positionals.operand);
|
||||
// TODO this should happen only for var allocs
|
||||
if (!var_type.isValidVarType()) {
|
||||
if (!var_type.isValidVarType(false)) {
|
||||
return mod.fail(scope, inst.base.src, "variable of type '{}' must be const or comptime", .{var_type});
|
||||
}
|
||||
const ptr_type = try mod.singlePtrType(scope, inst.base.src, true, var_type);
|
||||
@ -581,8 +581,7 @@ fn analyzeInstDeclVal(mod: *Module, scope: *Scope, inst: *zir.Inst.DeclVal) Inne
|
||||
|
||||
fn analyzeInstDeclValInModule(mod: *Module, scope: *Scope, inst: *zir.Inst.DeclValInModule) InnerError!*Inst {
|
||||
const decl = inst.positionals.decl;
|
||||
const ptr = try mod.analyzeDeclRef(scope, inst.base.src, decl);
|
||||
return mod.analyzeDeref(scope, inst.base.src, ptr, inst.base.src);
|
||||
return mod.analyzeDeclRef(scope, inst.base.src, decl);
|
||||
}
|
||||
|
||||
fn analyzeInstCall(mod: *Module, scope: *Scope, inst: *zir.Inst.Call) InnerError!*Inst {
|
||||
@ -779,7 +778,7 @@ fn analyzeInstFnType(mod: *Module, scope: *Scope, fntype: *zir.Inst.FnType) Inne
|
||||
for (fntype.positionals.param_types) |param_type, i| {
|
||||
const resolved = try resolveType(mod, scope, param_type);
|
||||
// TODO skip for comptime params
|
||||
if (!resolved.isValidVarType()) {
|
||||
if (!resolved.isValidVarType(false)) {
|
||||
return mod.fail(scope, param_type.src, "parameter of type '{}' must be declared comptime", .{resolved});
|
||||
}
|
||||
param_types[i] = resolved;
|
||||
|
||||
@ -586,6 +586,7 @@ pub fn addCases(ctx: *TestContext) !void {
|
||||
"",
|
||||
);
|
||||
|
||||
// Character literals and multiline strings.
|
||||
case.addCompareOutput(
|
||||
\\export fn _start() noreturn {
|
||||
\\ const ignore =
|
||||
@ -617,6 +618,38 @@ pub fn addCases(ctx: *TestContext) !void {
|
||||
,
|
||||
"",
|
||||
);
|
||||
|
||||
// Global const.
|
||||
case.addCompareOutput(
|
||||
\\export fn _start() noreturn {
|
||||
\\ add(aa, bb);
|
||||
\\
|
||||
\\ exit();
|
||||
\\}
|
||||
\\
|
||||
\\const aa = 'ぁ';
|
||||
\\const bb = '\x03';
|
||||
\\
|
||||
\\fn add(a: u32, b: u32) void {
|
||||
\\ assert(a + b == 12356);
|
||||
\\}
|
||||
\\
|
||||
\\pub fn assert(ok: bool) void {
|
||||
\\ if (!ok) unreachable; // assertion failure
|
||||
\\}
|
||||
\\
|
||||
\\fn exit() noreturn {
|
||||
\\ asm volatile ("syscall"
|
||||
\\ :
|
||||
\\ : [number] "{rax}" (231),
|
||||
\\ [arg1] "{rdi}" (0)
|
||||
\\ : "rcx", "r11", "memory"
|
||||
\\ );
|
||||
\\ unreachable;
|
||||
\\}
|
||||
,
|
||||
"",
|
||||
);
|
||||
}
|
||||
|
||||
{
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user