mirror of
https://codeberg.org/ziglang/zig.git
synced 2025-12-06 13:54:21 +00:00
stage2: fix inferred comptime constant locals
`const` declarations inside comptime blocks were not getting properly evaluated at compile-time. To accomplish this there is a new ZIR instruction, `alloc_inferred_comptime`. Actually we already had one named that, but it got renamed to `alloc_inferred_comptime_mut` to match the naming convention with the other similar instructions.
This commit is contained in:
parent
210ee1067b
commit
7c1061784b
8 changed files with 91 additions and 94 deletions
|
|
@ -2084,10 +2084,11 @@ fn unusedResultExpr(gz: *GenZir, scope: *Scope, statement: Ast.Node.Index) Inner
|
|||
.param_anytype_comptime,
|
||||
.alloc,
|
||||
.alloc_mut,
|
||||
.alloc_comptime,
|
||||
.alloc_comptime_mut,
|
||||
.alloc_inferred,
|
||||
.alloc_inferred_mut,
|
||||
.alloc_inferred_comptime,
|
||||
.alloc_inferred_comptime_mut,
|
||||
.array_cat,
|
||||
.array_mul,
|
||||
.array_type,
|
||||
|
|
@ -2613,7 +2614,7 @@ fn varDecl(
|
|||
.type_inst = type_inst,
|
||||
.align_inst = align_inst,
|
||||
.is_const = true,
|
||||
.is_comptime = false,
|
||||
.is_comptime = gz.force_comptime,
|
||||
});
|
||||
init_scope.instructions_top = gz.instructions.items.len;
|
||||
}
|
||||
|
|
@ -2621,14 +2622,18 @@ fn varDecl(
|
|||
} else {
|
||||
const alloc = if (align_inst == .none) alloc: {
|
||||
init_scope.instructions_top = gz.instructions.items.len;
|
||||
break :alloc try init_scope.addNode(.alloc_inferred, node);
|
||||
const tag: Zir.Inst.Tag = if (gz.force_comptime)
|
||||
.alloc_inferred_comptime
|
||||
else
|
||||
.alloc_inferred;
|
||||
break :alloc try init_scope.addNode(tag, node);
|
||||
} else alloc: {
|
||||
const ref = try gz.addAllocExtended(.{
|
||||
.node = node,
|
||||
.type_inst = .none,
|
||||
.align_inst = align_inst,
|
||||
.is_const = true,
|
||||
.is_comptime = false,
|
||||
.is_comptime = gz.force_comptime,
|
||||
});
|
||||
init_scope.instructions_top = gz.instructions.items.len;
|
||||
break :alloc ref;
|
||||
|
|
@ -2716,7 +2721,10 @@ fn varDecl(
|
|||
const type_inst = try typeExpr(gz, scope, var_decl.ast.type_node);
|
||||
const alloc = alloc: {
|
||||
if (align_inst == .none) {
|
||||
const tag: Zir.Inst.Tag = if (is_comptime) .alloc_comptime else .alloc_mut;
|
||||
const tag: Zir.Inst.Tag = if (is_comptime)
|
||||
.alloc_comptime_mut
|
||||
else
|
||||
.alloc_mut;
|
||||
break :alloc try gz.addUnNode(tag, type_inst, node);
|
||||
} else {
|
||||
break :alloc try gz.addAllocExtended(.{
|
||||
|
|
@ -2732,7 +2740,10 @@ fn varDecl(
|
|||
} else a: {
|
||||
const alloc = alloc: {
|
||||
if (align_inst == .none) {
|
||||
const tag: Zir.Inst.Tag = if (is_comptime) .alloc_inferred_comptime else .alloc_inferred_mut;
|
||||
const tag: Zir.Inst.Tag = if (is_comptime)
|
||||
.alloc_inferred_comptime_mut
|
||||
else
|
||||
.alloc_inferred_mut;
|
||||
break :alloc try gz.addNode(tag, node);
|
||||
} else {
|
||||
break :alloc try gz.addAllocExtended(.{
|
||||
|
|
@ -5441,7 +5452,7 @@ fn forExpr(
|
|||
const len = try parent_gz.addUnNode(.indexable_ptr_len, array_ptr, for_full.ast.cond_expr);
|
||||
|
||||
const index_ptr = blk: {
|
||||
const alloc_tag: Zir.Inst.Tag = if (is_inline) .alloc_comptime else .alloc;
|
||||
const alloc_tag: Zir.Inst.Tag = if (is_inline) .alloc_comptime_mut else .alloc;
|
||||
const index_ptr = try parent_gz.addUnNode(alloc_tag, .usize_type, node);
|
||||
// initialize to zero
|
||||
_ = try parent_gz.addBin(.store, index_ptr, .zero_usize);
|
||||
|
|
|
|||
20
src/Sema.zig
20
src/Sema.zig
|
|
@ -584,9 +584,10 @@ fn analyzeBodyInner(
|
|||
.alloc => try sema.zirAlloc(block, inst),
|
||||
.alloc_inferred => try sema.zirAllocInferred(block, inst, Type.initTag(.inferred_alloc_const)),
|
||||
.alloc_inferred_mut => try sema.zirAllocInferred(block, inst, Type.initTag(.inferred_alloc_mut)),
|
||||
.alloc_inferred_comptime => try sema.zirAllocInferredComptime(inst),
|
||||
.alloc_inferred_comptime => try sema.zirAllocInferredComptime(inst, Type.initTag(.inferred_alloc_const)),
|
||||
.alloc_inferred_comptime_mut => try sema.zirAllocInferredComptime(inst, Type.initTag(.inferred_alloc_mut)),
|
||||
.alloc_mut => try sema.zirAllocMut(block, inst),
|
||||
.alloc_comptime => try sema.zirAllocComptime(block, inst),
|
||||
.alloc_comptime_mut => try sema.zirAllocComptime(block, inst),
|
||||
.anyframe_type => try sema.zirAnyframeType(block, inst),
|
||||
.array_cat => try sema.zirArrayCat(block, inst),
|
||||
.array_mul => try sema.zirArrayMul(block, inst),
|
||||
|
|
@ -2368,12 +2369,16 @@ fn zirAllocComptime(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileErr
|
|||
return sema.analyzeComptimeAlloc(block, var_ty, 0, ty_src);
|
||||
}
|
||||
|
||||
fn zirAllocInferredComptime(sema: *Sema, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
|
||||
fn zirAllocInferredComptime(
|
||||
sema: *Sema,
|
||||
inst: Zir.Inst.Index,
|
||||
inferred_alloc_ty: Type,
|
||||
) CompileError!Air.Inst.Ref {
|
||||
const src_node = sema.code.instructions.items(.data)[inst].node;
|
||||
const src: LazySrcLoc = .{ .node_offset = src_node };
|
||||
sema.src = src;
|
||||
return sema.addConstant(
|
||||
Type.initTag(.inferred_alloc_mut),
|
||||
inferred_alloc_ty,
|
||||
try Value.Tag.inferred_alloc_comptime.create(sema.arena, undefined),
|
||||
);
|
||||
}
|
||||
|
|
@ -2480,6 +2485,7 @@ fn zirResolveInferredAlloc(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Com
|
|||
const final_elem_ty = try decl.ty.copy(sema.arena);
|
||||
const final_ptr_ty = try Type.ptr(sema.arena, .{
|
||||
.pointee_type = final_elem_ty,
|
||||
.mutable = var_is_mut,
|
||||
.@"align" = iac.data.alignment,
|
||||
.@"addrspace" = target_util.defaultAddressSpace(target, .local),
|
||||
});
|
||||
|
|
@ -2500,9 +2506,6 @@ fn zirResolveInferredAlloc(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Com
|
|||
const peer_inst_list = inferred_alloc.data.stored_inst_list.items;
|
||||
const final_elem_ty = try sema.resolvePeerTypes(block, ty_src, peer_inst_list, .none);
|
||||
|
||||
try sema.requireRuntimeBlock(block, src);
|
||||
try sema.resolveTypeLayout(block, ty_src, final_elem_ty);
|
||||
|
||||
const final_ptr_ty = try Type.ptr(sema.arena, .{
|
||||
.pointee_type = final_elem_ty,
|
||||
.mutable = var_is_mut,
|
||||
|
|
@ -2564,6 +2567,9 @@ fn zirResolveInferredAlloc(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Com
|
|||
return;
|
||||
}
|
||||
|
||||
try sema.requireRuntimeBlock(block, src);
|
||||
try sema.resolveTypeLayout(block, ty_src, final_elem_ty);
|
||||
|
||||
// Change it to a normal alloc.
|
||||
sema.air_instructions.set(ptr_inst, .{
|
||||
.tag = .alloc,
|
||||
|
|
|
|||
14
src/Zir.zig
14
src/Zir.zig
|
|
@ -909,14 +909,18 @@ pub const Inst = struct {
|
|||
/// Allocates comptime-mutable memory.
|
||||
/// Uses the `un_node` union field. The operand is the type of the allocated object.
|
||||
/// The node source location points to a var decl node.
|
||||
alloc_comptime,
|
||||
alloc_comptime_mut,
|
||||
/// Same as `alloc` except the type is inferred.
|
||||
/// Uses the `node` union field.
|
||||
alloc_inferred,
|
||||
/// Same as `alloc_inferred` except mutable.
|
||||
alloc_inferred_mut,
|
||||
/// Same as `alloc_comptime` except the type is inferred.
|
||||
/// Allocates comptime const memory.
|
||||
/// Uses the `node` union field. The type of the allocated object is inferred.
|
||||
/// The node source location points to a var decl node.
|
||||
alloc_inferred_comptime,
|
||||
/// Same as `alloc_comptime_mut` except the type is inferred.
|
||||
alloc_inferred_comptime_mut,
|
||||
/// 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
|
||||
|
|
@ -957,10 +961,11 @@ pub const Inst = struct {
|
|||
.add_sat,
|
||||
.alloc,
|
||||
.alloc_mut,
|
||||
.alloc_comptime,
|
||||
.alloc_comptime_mut,
|
||||
.alloc_inferred,
|
||||
.alloc_inferred_mut,
|
||||
.alloc_inferred_comptime,
|
||||
.alloc_inferred_comptime_mut,
|
||||
.array_cat,
|
||||
.array_mul,
|
||||
.array_type,
|
||||
|
|
@ -1446,10 +1451,11 @@ pub const Inst = struct {
|
|||
|
||||
.alloc = .un_node,
|
||||
.alloc_mut = .un_node,
|
||||
.alloc_comptime = .un_node,
|
||||
.alloc_comptime_mut = .un_node,
|
||||
.alloc_inferred = .node,
|
||||
.alloc_inferred_mut = .node,
|
||||
.alloc_inferred_comptime = .node,
|
||||
.alloc_inferred_comptime_mut = .node,
|
||||
.resolve_inferred_alloc = .un_node,
|
||||
|
||||
.@"resume" = .un_node,
|
||||
|
|
|
|||
|
|
@ -155,7 +155,7 @@ const Writer = struct {
|
|||
|
||||
.alloc,
|
||||
.alloc_mut,
|
||||
.alloc_comptime,
|
||||
.alloc_comptime_mut,
|
||||
.indexable_ptr_len,
|
||||
.anyframe_type,
|
||||
.bit_not,
|
||||
|
|
@ -401,6 +401,7 @@ const Writer = struct {
|
|||
.alloc_inferred,
|
||||
.alloc_inferred_mut,
|
||||
.alloc_inferred_comptime,
|
||||
.alloc_inferred_comptime_mut,
|
||||
=> try self.writeNode(stream, inst),
|
||||
|
||||
.error_value,
|
||||
|
|
|
|||
|
|
@ -105,7 +105,6 @@ test {
|
|||
_ = @import("behavior/math.zig");
|
||||
_ = @import("behavior/maximum_minimum.zig");
|
||||
_ = @import("behavior/merge_error_sets.zig");
|
||||
_ = @import("behavior/null_llvm.zig");
|
||||
_ = @import("behavior/popcount.zig");
|
||||
_ = @import("behavior/saturating_arithmetic.zig");
|
||||
_ = @import("behavior/sizeof_and_typeof.zig");
|
||||
|
|
@ -156,7 +155,6 @@ test {
|
|||
_ = @import("behavior/ir_block_deps.zig");
|
||||
_ = @import("behavior/misc.zig");
|
||||
_ = @import("behavior/muladd.zig");
|
||||
_ = @import("behavior/null_stage1.zig");
|
||||
_ = @import("behavior/optional_stage1.zig");
|
||||
_ = @import("behavior/popcount_stage1.zig");
|
||||
_ = @import("behavior/reflection.zig");
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
const builtin = @import("builtin");
|
||||
const std = @import("std");
|
||||
const expect = std.testing.expect;
|
||||
|
||||
|
|
@ -140,3 +141,50 @@ const Particle = struct {
|
|||
c: u64,
|
||||
d: u64,
|
||||
};
|
||||
|
||||
test "null literal outside function" {
|
||||
const is_null = here_is_a_null_literal.context == null;
|
||||
try expect(is_null);
|
||||
|
||||
const is_non_null = here_is_a_null_literal.context != null;
|
||||
try expect(!is_non_null);
|
||||
}
|
||||
|
||||
const SillyStruct = struct {
|
||||
context: ?i32,
|
||||
};
|
||||
|
||||
const here_is_a_null_literal = SillyStruct{ .context = null };
|
||||
|
||||
test "unwrap optional which is field of global var" {
|
||||
if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
|
||||
if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
|
||||
|
||||
struct_with_optional.field = null;
|
||||
if (struct_with_optional.field) |payload| {
|
||||
_ = payload;
|
||||
unreachable;
|
||||
}
|
||||
struct_with_optional.field = 1234;
|
||||
if (struct_with_optional.field) |payload| {
|
||||
try expect(payload == 1234);
|
||||
} else {
|
||||
unreachable;
|
||||
}
|
||||
}
|
||||
const StructWithOptional = struct {
|
||||
field: ?i32,
|
||||
};
|
||||
|
||||
var struct_with_optional: StructWithOptional = undefined;
|
||||
|
||||
test "optional types" {
|
||||
comptime {
|
||||
const opt_type_struct = StructWithOptionalType{ .t = u8 };
|
||||
try expect(opt_type_struct.t != null and opt_type_struct.t.? == u8);
|
||||
}
|
||||
}
|
||||
|
||||
const StructWithOptionalType = struct {
|
||||
t: ?type,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,36 +0,0 @@
|
|||
const std = @import("std");
|
||||
const expect = std.testing.expect;
|
||||
|
||||
test "null literal outside function" {
|
||||
const is_null = here_is_a_null_literal.context == null;
|
||||
try expect(is_null);
|
||||
|
||||
const is_non_null = here_is_a_null_literal.context != null;
|
||||
try expect(!is_non_null);
|
||||
}
|
||||
|
||||
const SillyStruct = struct {
|
||||
context: ?i32,
|
||||
};
|
||||
|
||||
const here_is_a_null_literal = SillyStruct{ .context = null };
|
||||
|
||||
const StructWithOptional = struct {
|
||||
field: ?i32,
|
||||
};
|
||||
|
||||
var struct_with_optional: StructWithOptional = undefined;
|
||||
|
||||
test "unwrap optional which is field of global var" {
|
||||
struct_with_optional.field = null;
|
||||
if (struct_with_optional.field) |payload| {
|
||||
_ = payload;
|
||||
unreachable;
|
||||
}
|
||||
struct_with_optional.field = 1234;
|
||||
if (struct_with_optional.field) |payload| {
|
||||
try expect(payload == 1234);
|
||||
} else {
|
||||
unreachable;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,37 +0,0 @@
|
|||
const expect = @import("std").testing.expect;
|
||||
|
||||
test "if var maybe pointer" {
|
||||
try expect(shouldBeAPlus1(Particle{
|
||||
.a = 14,
|
||||
.b = 1,
|
||||
.c = 1,
|
||||
.d = 1,
|
||||
}) == 15);
|
||||
}
|
||||
fn shouldBeAPlus1(p: Particle) u64 {
|
||||
var maybe_particle: ?Particle = p;
|
||||
if (maybe_particle) |*particle| {
|
||||
particle.a += 1;
|
||||
}
|
||||
if (maybe_particle) |particle| {
|
||||
return particle.a;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
const Particle = struct {
|
||||
a: u64,
|
||||
b: u64,
|
||||
c: u64,
|
||||
d: u64,
|
||||
};
|
||||
|
||||
test "optional types" {
|
||||
comptime {
|
||||
const opt_type_struct = StructWithOptionalType{ .t = u8 };
|
||||
try expect(opt_type_struct.t != null and opt_type_struct.t.? == u8);
|
||||
}
|
||||
}
|
||||
|
||||
const StructWithOptionalType = struct {
|
||||
t: ?type,
|
||||
};
|
||||
Loading…
Add table
Reference in a new issue