aboutsummaryrefslogtreecommitdiff
path: root/src/Sema.zig
diff options
context:
space:
mode:
authorAndrew Kelley <andrew@ziglang.org>2022-07-01 15:52:54 -0700
committerAndrew Kelley <andrew@ziglang.org>2022-07-01 15:52:54 -0700
commitc89dd15e1be4959800dc7092d7dd4375253db7bc (patch)
treeca184ae53592efa21e67128a5f891d642d7f1118 /src/Sema.zig
parent5466e87fce581f2ef90ac23bb80b1dbc05836fc6 (diff)
parent2360f8c490f3ec684ed64ff28e8c1fade249070b (diff)
downloadzig-c89dd15e1be4959800dc7092d7dd4375253db7bc.tar.gz
zig-c89dd15e1be4959800dc7092d7dd4375253db7bc.zip
Merge remote-tracking branch 'origin/master' into llvm14
Diffstat (limited to 'src/Sema.zig')
-rw-r--r--src/Sema.zig15869
1 files changed, 12252 insertions, 3617 deletions
diff --git a/src/Sema.zig b/src/Sema.zig
index 94b5a7f1d1..b946e29057 100644
--- a/src/Sema.zig
+++ b/src/Sema.zig
@@ -24,6 +24,7 @@ inst_map: InstMap = .{},
/// and `src_decl` of `Block` is the `Decl` of the callee.
/// This `Decl` owns the arena memory of this `Sema`.
owner_decl: *Decl,
+owner_decl_index: Decl.Index,
/// For an inline or comptime function call, this will be the root parent function
/// which contains the callsite. Corresponds to `owner_decl`.
owner_func: ?*Module.Fn,
@@ -37,7 +38,7 @@ func: ?*Module.Fn,
/// generic function which uses a type expression for the return type.
/// The type will be `void` in the case that `func` is `null`.
fn_ret_ty: Type,
-branch_quota: u32 = 1000,
+branch_quota: u32 = default_branch_quota,
branch_count: u32 = 0,
/// Populated when returning `error.ComptimeBreak`. Used to communicate the
/// break instruction up the stack to find the corresponding Block.
@@ -47,7 +48,7 @@ comptime_break_inst: Zir.Inst.Index = undefined,
/// access to the source location set by the previous instruction which did
/// contain a mapped source location.
src: LazySrcLoc = .{ .token_offset = 0 },
-decl_val_table: std.AutoHashMapUnmanaged(*Decl, Air.Inst.Ref) = .{},
+decl_val_table: std.AutoHashMapUnmanaged(Decl.Index, Air.Inst.Ref) = .{},
/// When doing a generic function instantiation, this array collects a
/// `Value` object for each parameter that is comptime known and thus elided
/// from the generated function. This memory is allocated by a parent `Sema` and
@@ -63,6 +64,16 @@ comptime_args_fn_inst: Zir.Inst.Index = 0,
/// extra hash table lookup in the `monomorphed_funcs` set.
/// Sema will set this to null when it takes ownership.
preallocated_new_func: ?*Module.Fn = null,
+/// The key is `constant` AIR instructions to types that must be fully resolved
+/// after the current function body analysis is done.
+/// TODO: after upgrading to use InternPool change the key here to be an
+/// InternPool value index.
+types_to_resolve: std.ArrayListUnmanaged(Air.Inst.Ref) = .{},
+/// These are lazily created runtime blocks from inline_block instructions.
+/// They are created when an inline_break passes through a runtime condition, because
+/// Sema must convert comptime control flow to runtime control flow, which means
+/// breaking from a block.
+post_hoc_blocks: std.AutoHashMapUnmanaged(Air.Inst.Index, *LabeledBlock) = .{},
const std = @import("std");
const mem = std.mem;
@@ -89,6 +100,9 @@ const RangeSet = @import("RangeSet.zig");
const target_util = @import("target.zig");
const Package = @import("Package.zig");
const crash_report = @import("crash_report.zig");
+const build_options = @import("build_options");
+
+pub const default_branch_quota = 1000;
pub const InstMap = std.AutoHashMapUnmanaged(Zir.Inst.Index, Air.Inst.Ref);
@@ -100,10 +114,6 @@ pub const Block = struct {
parent: ?*Block,
/// Shared among all child blocks.
sema: *Sema,
- /// This Decl is the Decl according to the Zig source code corresponding to this Block.
- /// This can vary during inline or comptime function calls. See `Sema.owner_decl`
- /// for the one that will be the same for all Block instances.
- src_decl: *Decl,
/// The namespace to use for lookups from this source block
/// When analyzing fields, this is different from src_decl.src_namepsace.
namespace: *Namespace,
@@ -119,21 +129,31 @@ pub const Block = struct {
/// If runtime_index is not 0 then one of these is guaranteed to be non null.
runtime_cond: ?LazySrcLoc = null,
runtime_loop: ?LazySrcLoc = null,
+ /// This Decl is the Decl according to the Zig source code corresponding to this Block.
+ /// This can vary during inline or comptime function calls. See `Sema.owner_decl`
+ /// for the one that will be the same for all Block instances.
+ src_decl: Decl.Index,
/// Non zero if a non-inline loop or a runtime conditional have been encountered.
/// Stores to to comptime variables are only allowed when var.runtime_index <= runtime_index.
- runtime_index: u32 = 0,
+ runtime_index: Value.RuntimeIndex = .zero,
is_comptime: bool,
+ is_typeof: bool = false,
+ is_coerce_result_ptr: bool = false,
/// when null, it is determined by build mode, changed by @setRuntimeSafety
want_safety: ?bool = null,
c_import_buf: ?*std.ArrayList(u8) = null,
+ /// type of `err` in `else => |err|`
+ switch_else_err_ty: ?Type = null,
+
const Param = struct {
/// `noreturn` means `anytype`.
ty: Type,
is_comptime: bool,
+ name: []const u8,
};
/// This `Block` maps a block ZIR instruction to the corresponding
@@ -151,6 +171,7 @@ pub const Block = struct {
pub const Inlining = struct {
comptime_result: Air.Inst.Ref,
merges: Merges,
+ err: ?*Module.ErrorMsg = null,
};
pub const Merges = struct {
@@ -180,11 +201,13 @@ pub const Block = struct {
.label = null,
.inlining = parent.inlining,
.is_comptime = parent.is_comptime,
+ .is_typeof = parent.is_typeof,
.runtime_cond = parent.runtime_cond,
.runtime_loop = parent.runtime_loop,
.runtime_index = parent.runtime_index,
.want_safety = parent.want_safety,
.c_import_buf = parent.c_import_buf,
+ .switch_else_err_ty = parent.switch_else_err_ty,
};
}
@@ -201,7 +224,7 @@ pub const Block = struct {
return block.namespace.file_scope;
}
- pub fn addTy(
+ fn addTy(
block: *Block,
tag: Air.Inst.Tag,
ty: Type,
@@ -212,7 +235,7 @@ pub const Block = struct {
});
}
- pub fn addTyOp(
+ fn addTyOp(
block: *Block,
tag: Air.Inst.Tag,
ty: Type,
@@ -227,7 +250,7 @@ pub const Block = struct {
});
}
- pub fn addBitCast(block: *Block, ty: Type, operand: Air.Inst.Ref) Allocator.Error!Air.Inst.Ref {
+ fn addBitCast(block: *Block, ty: Type, operand: Air.Inst.Ref) Allocator.Error!Air.Inst.Ref {
return block.addInst(.{
.tag = .bitcast,
.data = .{ .ty_op = .{
@@ -237,14 +260,14 @@ pub const Block = struct {
});
}
- pub fn addNoOp(block: *Block, tag: Air.Inst.Tag) error{OutOfMemory}!Air.Inst.Ref {
+ fn addNoOp(block: *Block, tag: Air.Inst.Tag) error{OutOfMemory}!Air.Inst.Ref {
return block.addInst(.{
.tag = tag,
.data = .{ .no_op = {} },
});
}
- pub fn addUnOp(
+ fn addUnOp(
block: *Block,
tag: Air.Inst.Tag,
operand: Air.Inst.Ref,
@@ -255,7 +278,7 @@ pub const Block = struct {
});
}
- pub fn addBr(
+ fn addBr(
block: *Block,
target_block: Air.Inst.Index,
operand: Air.Inst.Ref,
@@ -284,13 +307,10 @@ pub const Block = struct {
});
}
- fn addArg(block: *Block, ty: Type, name: u32) error{OutOfMemory}!Air.Inst.Ref {
+ fn addArg(block: *Block, ty: Type) error{OutOfMemory}!Air.Inst.Ref {
return block.addInst(.{
.tag = .arg,
- .data = .{ .ty_str = .{
- .ty = try block.sema.addType(ty),
- .str = name,
- } },
+ .data = .{ .ty = ty },
});
}
@@ -328,7 +348,7 @@ pub const Block = struct {
});
}
- pub fn addStructFieldVal(
+ fn addStructFieldVal(
block: *Block,
struct_val: Air.Inst.Ref,
field_index: u32,
@@ -346,7 +366,7 @@ pub const Block = struct {
});
}
- pub fn addSliceElemPtr(
+ fn addSliceElemPtr(
block: *Block,
slice: Air.Inst.Ref,
elem_index: Air.Inst.Ref,
@@ -364,7 +384,7 @@ pub const Block = struct {
});
}
- pub fn addPtrElemPtr(
+ fn addPtrElemPtr(
block: *Block,
array_ptr: Air.Inst.Ref,
elem_index: Air.Inst.Ref,
@@ -374,7 +394,7 @@ pub const Block = struct {
return block.addPtrElemPtrTypeRef(array_ptr, elem_index, ty_ref);
}
- pub fn addPtrElemPtrTypeRef(
+ fn addPtrElemPtrTypeRef(
block: *Block,
array_ptr: Air.Inst.Ref,
elem_index: Air.Inst.Ref,
@@ -392,19 +412,33 @@ pub const Block = struct {
});
}
- pub fn addVectorInit(
+ fn addCmpVector(block: *Block, lhs: Air.Inst.Ref, rhs: Air.Inst.Ref, cmp_op: std.math.CompareOperator, vector_ty: Air.Inst.Ref) !Air.Inst.Ref {
+ return block.addInst(.{
+ .tag = .cmp_vector,
+ .data = .{ .ty_pl = .{
+ .ty = vector_ty,
+ .payload = try block.sema.addExtra(Air.VectorCmp{
+ .lhs = lhs,
+ .rhs = rhs,
+ .op = Air.VectorCmp.encodeOp(cmp_op),
+ }),
+ } },
+ });
+ }
+
+ fn addAggregateInit(
block: *Block,
- vector_ty: Type,
+ aggregate_ty: Type,
elements: []const Air.Inst.Ref,
) !Air.Inst.Ref {
const sema = block.sema;
- const ty_ref = try sema.addType(vector_ty);
+ const ty_ref = try sema.addType(aggregate_ty);
try sema.air_extra.ensureUnusedCapacity(sema.gpa, elements.len);
const extra_index = @intCast(u32, sema.air_extra.items.len);
sema.appendRefsAssumeCapacity(elements);
return block.addInst(.{
- .tag = .vector_init,
+ .tag = .aggregate_init,
.data = .{ .ty_pl = .{
.ty = ty_ref,
.payload = extra_index,
@@ -412,6 +446,24 @@ pub const Block = struct {
});
}
+ fn addUnionInit(
+ block: *Block,
+ union_ty: Type,
+ field_index: u32,
+ init: Air.Inst.Ref,
+ ) !Air.Inst.Ref {
+ return block.addInst(.{
+ .tag = .union_init,
+ .data = .{ .ty_pl = .{
+ .ty = try block.sema.addType(union_ty),
+ .payload = try block.sema.addExtra(Air.UnionInit{
+ .field_index = field_index,
+ .init = init,
+ }),
+ } },
+ });
+ }
+
pub fn addInst(block: *Block, inst: Air.Inst) error{OutOfMemory}!Air.Inst.Ref {
return Air.indexToRef(try block.addInstAsIndex(inst));
}
@@ -463,23 +515,38 @@ pub const Block = struct {
wad.* = undefined;
}
- pub fn finish(wad: *WipAnonDecl, ty: Type, val: Value) !*Decl {
+ /// `alignment` value of 0 means to use ABI alignment.
+ pub fn finish(wad: *WipAnonDecl, ty: Type, val: Value, alignment: u32) !Decl.Index {
const sema = wad.block.sema;
// Do this ahead of time because `createAnonymousDecl` depends on calling
// `type.hasRuntimeBits()`.
_ = try sema.typeHasRuntimeBits(wad.block, wad.src, ty);
- const new_decl = try sema.mod.createAnonymousDecl(wad.block, .{
+ const new_decl_index = try sema.mod.createAnonymousDecl(wad.block, .{
.ty = ty,
.val = val,
});
- errdefer sema.mod.abortAnonDecl(new_decl);
+ const new_decl = sema.mod.declPtr(new_decl_index);
+ new_decl.@"align" = alignment;
+ errdefer sema.mod.abortAnonDecl(new_decl_index);
try new_decl.finalizeNewArena(&wad.new_decl_arena);
wad.finished = true;
- return new_decl;
+ return new_decl_index;
}
};
};
+const LabeledBlock = struct {
+ block: Block,
+ label: Block.Label,
+
+ fn destroy(lb: *LabeledBlock, gpa: Allocator) void {
+ lb.block.instructions.deinit(gpa);
+ lb.label.merges.results.deinit(gpa);
+ lb.label.merges.br_list.deinit(gpa);
+ gpa.destroy(lb);
+ }
+};
+
pub fn deinit(sema: *Sema) void {
const gpa = sema.gpa;
sema.air_instructions.deinit(gpa);
@@ -487,6 +554,15 @@ pub fn deinit(sema: *Sema) void {
sema.air_values.deinit(gpa);
sema.inst_map.deinit(gpa);
sema.decl_val_table.deinit(gpa);
+ sema.types_to_resolve.deinit(gpa);
+ {
+ var it = sema.post_hoc_blocks.iterator();
+ while (it.next()) |entry| {
+ const labeled_block = entry.value_ptr.*;
+ labeled_block.destroy(gpa);
+ }
+ sema.post_hoc_blocks.deinit(gpa);
+ }
sema.* = undefined;
}
@@ -501,28 +577,54 @@ fn resolveBody(
/// use to return from the body.
body_inst: Zir.Inst.Index,
) CompileError!Air.Inst.Ref {
- const break_inst = try sema.analyzeBody(block, body);
- const break_data = sema.code.instructions.items(.data)[break_inst].@"break";
+ const break_data = (try sema.analyzeBodyBreak(block, body)) orelse
+ return Air.Inst.Ref.unreachable_value;
// For comptime control flow, we need to detect when `analyzeBody` reports
// that we need to break from an outer block. In such case we
// use Zig's error mechanism to send control flow up the stack until
// we find the corresponding block to this break.
if (block.is_comptime and break_data.block_inst != body_inst) {
- sema.comptime_break_inst = break_inst;
+ sema.comptime_break_inst = break_data.inst;
return error.ComptimeBreak;
}
- return sema.resolveInst(break_data.operand);
+ return try sema.resolveInst(break_data.operand);
}
pub fn analyzeBody(
sema: *Sema,
block: *Block,
body: []const Zir.Inst.Index,
-) CompileError!Zir.Inst.Index {
- return sema.analyzeBodyInner(block, body) catch |err| switch (err) {
+) !void {
+ _ = sema.analyzeBodyInner(block, body) catch |err| switch (err) {
+ error.ComptimeBreak => unreachable, // unexpected comptime control flow
+ else => |e| return e,
+ };
+}
+
+const BreakData = struct {
+ block_inst: Zir.Inst.Index,
+ operand: Zir.Inst.Ref,
+ inst: Zir.Inst.Index,
+};
+
+pub fn analyzeBodyBreak(
+ sema: *Sema,
+ block: *Block,
+ body: []const Zir.Inst.Index,
+) CompileError!?BreakData {
+ const break_inst = sema.analyzeBodyInner(block, body) catch |err| switch (err) {
error.ComptimeBreak => sema.comptime_break_inst,
else => |e| return e,
};
+ if (block.instructions.items.len != 0 and
+ sema.typeOf(Air.indexToRef(block.instructions.items[block.instructions.items.len - 1])).isNoReturn())
+ return null;
+ const break_data = sema.code.instructions.items(.data)[break_inst].@"break";
+ return BreakData{
+ .block_inst = break_data.block_inst,
+ .operand = break_data.operand,
+ .inst = break_inst,
+ };
}
/// ZIR instructions which are always `noreturn` return this. This matches the
@@ -569,6 +671,8 @@ fn analyzeBodyInner(
crash_info.push();
defer crash_info.pop();
+ var dbg_block_begins: u32 = 0;
+
// We use a while(true) loop here to avoid a redundant way of breaking out of
// the loop. The only way to break out of the loop is with a `noreturn`
// instruction.
@@ -577,16 +681,18 @@ fn analyzeBodyInner(
crash_info.setBodyIndex(i);
const inst = body[i];
std.log.scoped(.sema_zir).debug("sema ZIR {s} %{d}", .{
- block.src_decl.src_namespace.file_scope.sub_file_path, inst,
+ sema.mod.declPtr(block.src_decl).src_namespace.file_scope.sub_file_path, inst,
});
const air_inst: Air.Inst.Ref = switch (tags[inst]) {
// zig fmt: off
.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),
+ .make_ptr_const => try sema.zirMakePtrConst(block, inst),
.anyframe_type => try sema.zirAnyframeType(block, inst),
.array_cat => try sema.zirArrayCat(block, inst),
.array_mul => try sema.zirArrayMul(block, inst),
@@ -621,7 +727,7 @@ fn analyzeBodyInner(
.elem_ptr_imm => try sema.zirElemPtrImm(block, inst),
.elem_val => try sema.zirElemVal(block, inst),
.elem_val_node => try sema.zirElemValNode(block, inst),
- .elem_type => try sema.zirElemType(block, inst),
+ .elem_type_index => try sema.zirElemTypeIndex(block, inst),
.enum_literal => try sema.zirEnumLiteral(block, inst),
.enum_to_int => try sema.zirEnumToInt(block, inst),
.int_to_enum => try sema.zirIntToEnum(block, inst),
@@ -640,9 +746,9 @@ fn analyzeBodyInner(
.field_val => try sema.zirFieldVal(block, inst),
.field_val_named => try sema.zirFieldValNamed(block, inst),
.field_call_bind => try sema.zirFieldCallBind(block, inst),
- .field_call_bind_named => try sema.zirFieldCallBindNamed(block, inst),
.func => try sema.zirFunc(block, inst, false),
.func_inferred => try sema.zirFunc(block, inst, true),
+ .func_fancy => try sema.zirFuncFancy(block, inst),
.import => try sema.zirImport(block, inst),
.indexable_ptr_len => try sema.zirIndexablePtrLen(block, inst),
.int => try sema.zirInt(block, inst),
@@ -655,17 +761,18 @@ fn analyzeBodyInner(
.is_non_null => try sema.zirIsNonNull(block, inst),
.is_non_null_ptr => try sema.zirIsNonNullPtr(block, inst),
.merge_error_sets => try sema.zirMergeErrorSets(block, inst),
- .negate => try sema.zirNegate(block, inst, .sub),
- .negate_wrap => try sema.zirNegate(block, inst, .subwrap),
+ .negate => try sema.zirNegate(block, inst),
+ .negate_wrap => try sema.zirNegateWrap(block, inst),
.optional_payload_safe => try sema.zirOptionalPayload(block, inst, true),
.optional_payload_safe_ptr => try sema.zirOptionalPayloadPtr(block, inst, true),
.optional_payload_unsafe => try sema.zirOptionalPayload(block, inst, false),
.optional_payload_unsafe_ptr => try sema.zirOptionalPayloadPtr(block, inst, false),
.optional_type => try sema.zirOptionalType(block, inst),
+ .param_type => try sema.zirParamType(block, inst),
.ptr_type => try sema.zirPtrType(block, inst),
.ptr_type_simple => try sema.zirPtrTypeSimple(block, inst),
.ref => try sema.zirRef(block, inst),
- .ret_err_value_code => try sema.zirRetErrValueCode(block, inst),
+ .ret_err_value_code => try sema.zirRetErrValueCode(inst),
.shr => try sema.zirShr(block, inst, .shr),
.shr_exact => try sema.zirShr(block, inst, .shr_exact),
.slice_end => try sema.zirSliceEnd(block, inst),
@@ -683,6 +790,7 @@ fn analyzeBodyInner(
.size_of => try sema.zirSizeOf(block, inst),
.bit_size_of => try sema.zirBitSizeOf(block, inst),
.typeof => try sema.zirTypeof(block, inst),
+ .typeof_builtin => try sema.zirTypeofBuiltin(block, inst),
.log2_int_type => try sema.zirLog2IntType(block, inst),
.typeof_log2_int_type => try sema.zirTypeofLog2IntType(block, inst),
.xor => try sema.zirBitwise(block, inst, .xor),
@@ -695,7 +803,7 @@ fn analyzeBodyInner(
.array_init_ref => try sema.zirArrayInit(block, inst, true),
.array_init_anon => try sema.zirArrayInitAnon(block, inst, false),
.array_init_anon_ref => try sema.zirArrayInitAnon(block, inst, true),
- .union_init_ptr => try sema.zirUnionInitPtr(block, inst),
+ .union_init => try sema.zirUnionInit(block, inst),
.field_type => try sema.zirFieldType(block, inst),
.field_type_ref => try sema.zirFieldTypeRef(block, inst),
.ptr_to_int => try sema.zirPtrToInt(block, inst),
@@ -713,13 +821,11 @@ fn analyzeBodyInner(
.int_to_ptr => try sema.zirIntToPtr(block, inst),
.float_cast => try sema.zirFloatCast(block, inst),
.int_cast => try sema.zirIntCast(block, inst),
- .err_set_cast => try sema.zirErrSetCast(block, inst),
.ptr_cast => try sema.zirPtrCast(block, inst),
.truncate => try sema.zirTruncate(block, inst),
.align_cast => try sema.zirAlignCast(block, inst),
.has_decl => try sema.zirHasDecl(block, inst),
.has_field => try sema.zirHasField(block, inst),
- .pop_count => try sema.zirPopCount(block, inst),
.byte_swap => try sema.zirByteSwap(block, inst),
.bit_reverse => try sema.zirBitReverse(block, inst),
.bit_offset_of => try sema.zirBitOffsetOf(block, inst),
@@ -734,30 +840,31 @@ fn analyzeBodyInner(
.atomic_rmw => try sema.zirAtomicRmw(block, inst),
.mul_add => try sema.zirMulAdd(block, inst),
.builtin_call => try sema.zirBuiltinCall(block, inst),
- .field_ptr_type => try sema.zirFieldPtrType(block, inst),
.field_parent_ptr => try sema.zirFieldParentPtr(block, inst),
.builtin_async_call => try sema.zirBuiltinAsyncCall(block, inst),
.@"resume" => try sema.zirResume(block, inst),
- .@"await" => try sema.zirAwait(block, inst, false),
- .await_nosuspend => try sema.zirAwait(block, inst, true),
- .extended => try sema.zirExtended(block, inst),
-
- .clz => try sema.zirClzCtz(block, inst, .clz, Value.clz),
- .ctz => try sema.zirClzCtz(block, inst, .ctz, Value.ctz),
-
- .sqrt => try sema.zirUnaryMath(block, inst),
- .sin => try sema.zirUnaryMath(block, inst),
- .cos => try sema.zirUnaryMath(block, inst),
- .exp => try sema.zirUnaryMath(block, inst),
- .exp2 => try sema.zirUnaryMath(block, inst),
- .log => try sema.zirUnaryMath(block, inst),
- .log2 => try sema.zirUnaryMath(block, inst),
- .log10 => try sema.zirUnaryMath(block, inst),
- .fabs => try sema.zirUnaryMath(block, inst),
- .floor => try sema.zirUnaryMath(block, inst),
- .ceil => try sema.zirUnaryMath(block, inst),
- .trunc => try sema.zirUnaryMath(block, inst),
- .round => try sema.zirUnaryMath(block, inst),
+ .@"await" => try sema.zirAwait(block, inst),
+ .array_base_ptr => try sema.zirArrayBasePtr(block, inst),
+ .field_base_ptr => try sema.zirFieldBasePtr(block, inst),
+
+ .clz => try sema.zirBitCount(block, inst, .clz, Value.clz),
+ .ctz => try sema.zirBitCount(block, inst, .ctz, Value.ctz),
+ .pop_count => try sema.zirBitCount(block, inst, .popcount, Value.popCount),
+
+ .sqrt => try sema.zirUnaryMath(block, inst, .sqrt, Value.sqrt),
+ .sin => try sema.zirUnaryMath(block, inst, .sin, Value.sin),
+ .cos => try sema.zirUnaryMath(block, inst, .cos, Value.cos),
+ .tan => try sema.zirUnaryMath(block, inst, .tan, Value.tan),
+ .exp => try sema.zirUnaryMath(block, inst, .exp, Value.exp),
+ .exp2 => try sema.zirUnaryMath(block, inst, .exp2, Value.exp2),
+ .log => try sema.zirUnaryMath(block, inst, .log, Value.log),
+ .log2 => try sema.zirUnaryMath(block, inst, .log2, Value.log2),
+ .log10 => try sema.zirUnaryMath(block, inst, .log10, Value.log10),
+ .fabs => try sema.zirUnaryMath(block, inst, .fabs, Value.fabs),
+ .floor => try sema.zirUnaryMath(block, inst, .floor, Value.floor),
+ .ceil => try sema.zirUnaryMath(block, inst, .ceil, Value.ceil),
+ .round => try sema.zirUnaryMath(block, inst, .round, Value.round),
+ .trunc => try sema.zirUnaryMath(block, inst, .trunc_float, Value.trunc),
.error_set_decl => try sema.zirErrorSetDecl(block, inst, .parent),
.error_set_decl_anon => try sema.zirErrorSetDecl(block, inst, .anon),
@@ -787,11 +894,14 @@ fn analyzeBodyInner(
.shl_exact => try sema.zirShl(block, inst, .shl_exact),
.shl_sat => try sema.zirShl(block, inst, .shl_sat),
+ .ret_ptr => try sema.zirRetPtr(block, inst),
+ .ret_type => try sema.zirRetType(block, inst),
+
// Instructions that we know to *always* be noreturn based solely on their tag.
// These functions match the return type of analyzeBody so that we can
// tail call them here.
.compile_error => break sema.zirCompileError(block, inst),
- .ret_coerce => break sema.zirRetCoerce(block, inst),
+ .ret_tok => break sema.zirRetTok(block, inst),
.ret_node => break sema.zirRetNode(block, inst),
.ret_load => break sema.zirRetLoad(block, inst),
.ret_err_value => break sema.zirRetErrValue(block, inst),
@@ -799,25 +909,94 @@ fn analyzeBodyInner(
.panic => break sema.zirPanic(block, inst),
// zig fmt: on
+ .extended => ext: {
+ const extended = datas[inst].extended;
+ break :ext switch (extended.opcode) {
+ // zig fmt: off
+ .variable => try sema.zirVarExtended( block, extended),
+ .struct_decl => try sema.zirStructDecl( block, extended, inst),
+ .enum_decl => try sema.zirEnumDecl( block, extended, inst),
+ .union_decl => try sema.zirUnionDecl( block, extended, inst),
+ .opaque_decl => try sema.zirOpaqueDecl( block, extended, inst),
+ .this => try sema.zirThis( block, extended),
+ .ret_addr => try sema.zirRetAddr( block, extended),
+ .builtin_src => try sema.zirBuiltinSrc( block, extended),
+ .error_return_trace => try sema.zirErrorReturnTrace( block, extended),
+ .frame => try sema.zirFrame( block, extended),
+ .frame_address => try sema.zirFrameAddress( block, extended),
+ .alloc => try sema.zirAllocExtended( block, extended),
+ .builtin_extern => try sema.zirBuiltinExtern( block, extended),
+ .@"asm" => try sema.zirAsm( block, extended),
+ .typeof_peer => try sema.zirTypeofPeer( block, extended),
+ .compile_log => try sema.zirCompileLog( block, extended),
+ .add_with_overflow => try sema.zirOverflowArithmetic(block, extended, extended.opcode),
+ .sub_with_overflow => try sema.zirOverflowArithmetic(block, extended, extended.opcode),
+ .mul_with_overflow => try sema.zirOverflowArithmetic(block, extended, extended.opcode),
+ .shl_with_overflow => try sema.zirOverflowArithmetic(block, extended, extended.opcode),
+ .c_undef => try sema.zirCUndef( block, extended),
+ .c_include => try sema.zirCInclude( block, extended),
+ .c_define => try sema.zirCDefine( block, extended),
+ .wasm_memory_size => try sema.zirWasmMemorySize( block, extended),
+ .wasm_memory_grow => try sema.zirWasmMemoryGrow( block, extended),
+ .prefetch => try sema.zirPrefetch( block, extended),
+ .field_call_bind_named => try sema.zirFieldCallBindNamed(block, extended),
+ .err_set_cast => try sema.zirErrSetCast( block, extended),
+ .await_nosuspend => try sema.zirAwaitNosuspend( block, extended),
+ // zig fmt: on
+ .fence => {
+ try sema.zirFence(block, extended);
+ i += 1;
+ continue;
+ },
+ .set_float_mode => {
+ try sema.zirSetFloatMode(block, extended);
+ i += 1;
+ continue;
+ },
+ .set_align_stack => {
+ try sema.zirSetAlignStack(block, extended);
+ i += 1;
+ continue;
+ },
+ .breakpoint => {
+ if (!block.is_comptime) {
+ _ = try block.addNoOp(.breakpoint);
+ }
+ i += 1;
+ continue;
+ },
+ };
+ },
+
// Instructions that we know can *never* be noreturn based solely on
// their tag. We avoid needlessly checking if they are noreturn and
// continue the loop.
// We also know that they cannot be referenced later, so we avoid
// putting them into the map.
- .breakpoint => {
- if (!block.is_comptime) {
- _ = try block.addNoOp(.breakpoint);
- }
+ .dbg_stmt => {
+ try sema.zirDbgStmt(block, inst);
i += 1;
continue;
},
- .fence => {
- try sema.zirFence(block, inst);
+ .dbg_var_ptr => {
+ try sema.zirDbgVar(block, inst, .dbg_var_ptr);
i += 1;
continue;
},
- .dbg_stmt => {
- try sema.zirDbgStmt(block, inst);
+ .dbg_var_val => {
+ try sema.zirDbgVar(block, inst, .dbg_var_val);
+ i += 1;
+ continue;
+ },
+ .dbg_block_begin => {
+ dbg_block_begins += 1;
+ try sema.zirDbgBlockBegin(block);
+ i += 1;
+ continue;
+ },
+ .dbg_block_end => {
+ dbg_block_begins -= 1;
+ try sema.zirDbgBlockEnd(block);
i += 1;
continue;
},
@@ -871,6 +1050,16 @@ fn analyzeBodyInner(
i += 1;
continue;
},
+ .validate_array_init_ty => {
+ try sema.validateArrayInitTy(block, inst);
+ i += 1;
+ continue;
+ },
+ .validate_struct_init_ty => {
+ try sema.validateStructInitTy(block, inst);
+ i += 1;
+ continue;
+ },
.validate_struct_init => {
try sema.zirValidateStructInit(block, inst, false);
i += 1;
@@ -891,6 +1080,11 @@ fn analyzeBodyInner(
i += 1;
continue;
},
+ .validate_deref => {
+ try sema.zirValidateDeref(block, inst);
+ i += 1;
+ continue;
+ },
.@"export" => {
try sema.zirExport(block, inst);
i += 1;
@@ -901,21 +1095,11 @@ fn analyzeBodyInner(
i += 1;
continue;
},
- .set_align_stack => {
- try sema.zirSetAlignStack(block, inst);
- i += 1;
- continue;
- },
.set_cold => {
try sema.zirSetCold(block, inst);
i += 1;
continue;
},
- .set_float_mode => {
- try sema.zirSetFloatMode(block, inst);
- i += 1;
- continue;
- },
.set_runtime_safety => {
try sema.zirSetRuntimeSafety(block, inst);
i += 1;
@@ -965,11 +1149,18 @@ fn analyzeBodyInner(
break sema.zirBreak(block, inst);
}
},
- .break_inline => break inst,
+ .break_inline => {
+ if (block.is_comptime) {
+ break inst;
+ } else {
+ sema.comptime_break_inst = inst;
+ return error.ComptimeBreak;
+ }
+ },
.repeat => {
if (block.is_comptime) {
// Send comptime control flow back to the beginning of this block.
- const src: LazySrcLoc = .{ .node_offset = datas[inst].node };
+ const src = LazySrcLoc.nodeOffset(datas[inst].node);
try sema.emitBackwardBranch(block, src);
if (wip_captures.scope.captures.count() != orig_captures) {
try wip_captures.reset(parent_capture_scope);
@@ -980,14 +1171,14 @@ fn analyzeBodyInner(
continue;
} else {
const src_node = sema.code.instructions.items(.data)[inst].node;
- const src: LazySrcLoc = .{ .node_offset = src_node };
+ const src = LazySrcLoc.nodeOffset(src_node);
try sema.requireRuntimeBlock(block, src);
break always_noreturn;
}
},
.repeat_inline => {
// Send comptime control flow back to the beginning of this block.
- const src: LazySrcLoc = .{ .node_offset = datas[inst].node };
+ const src = LazySrcLoc.nodeOffset(datas[inst].node);
try sema.emitBackwardBranch(block, src);
if (wip_captures.scope.captures.count() != orig_captures) {
try wip_captures.reset(parent_capture_scope);
@@ -1003,12 +1194,12 @@ fn analyzeBodyInner(
const inst_data = datas[inst].pl_node;
const extra = sema.code.extraData(Zir.Inst.Block, inst_data.payload_index);
const inline_body = sema.code.extra[extra.end..][0..extra.data.body_len];
- const break_inst = try sema.analyzeBody(block, inline_body);
- const break_data = datas[break_inst].@"break";
+ const break_data = (try sema.analyzeBodyBreak(block, inline_body)) orelse
+ break always_noreturn;
if (inst == break_data.block_inst) {
- break :blk sema.resolveInst(break_data.operand);
+ break :blk try sema.resolveInst(break_data.operand);
} else {
- break break_inst;
+ break break_data.inst;
}
},
.block => blk: {
@@ -1026,34 +1217,81 @@ fn analyzeBodyInner(
block.params.deinit(sema.gpa);
block.params = prev_params;
}
- const break_inst = try sema.analyzeBody(block, inline_body);
- const break_data = datas[break_inst].@"break";
+ const break_data = (try sema.analyzeBodyBreak(block, inline_body)) orelse
+ break always_noreturn;
if (inst == break_data.block_inst) {
- break :blk sema.resolveInst(break_data.operand);
+ break :blk try sema.resolveInst(break_data.operand);
} else {
- break break_inst;
+ break break_data.inst;
}
},
.block_inline => blk: {
// Directly analyze the block body without introducing a new block.
+ // However, in the case of a corresponding break_inline which reaches
+ // through a runtime conditional branch, we must retroactively emit
+ // a block, so we remember the block index here just in case.
+ const block_index = block.instructions.items.len;
const inst_data = datas[inst].pl_node;
const extra = sema.code.extraData(Zir.Inst.Block, inst_data.payload_index);
const inline_body = sema.code.extra[extra.end..][0..extra.data.body_len];
+ const gpa = sema.gpa;
// If this block contains a function prototype, we need to reset the
// current list of parameters and restore it later.
// Note: this probably needs to be resolved in a more general manner.
const prev_params = block.params;
block.params = .{};
defer {
- block.params.deinit(sema.gpa);
+ block.params.deinit(gpa);
block.params = prev_params;
}
- const break_inst = try sema.analyzeBody(block, inline_body);
- const break_data = datas[break_inst].@"break";
+ const opt_break_data = try sema.analyzeBodyBreak(block, inline_body);
+ // A runtime conditional branch that needs a post-hoc block to be
+ // emitted communicates this by mapping the block index into the inst map.
+ if (map.get(inst)) |new_block_ref| ph: {
+ // Comptime control flow populates the map, so we don't actually know
+ // if this is a post-hoc runtime block until we check the
+ // post_hoc_block map.
+ const new_block_inst = Air.refToIndex(new_block_ref) orelse break :ph;
+ const labeled_block = sema.post_hoc_blocks.get(new_block_inst) orelse
+ break :ph;
+
+ // In this case we need to move all the instructions starting at
+ // block_index from the current block into this new one.
+
+ if (opt_break_data) |break_data| {
+ // This is a comptime break which we now change to a runtime break
+ // since it crosses a runtime branch.
+ // It may pass through our currently being analyzed block_inline or it
+ // may point directly to it. In the latter case, this modifies the
+ // block that we are about to look up in the post_hoc_blocks map below.
+ try sema.addRuntimeBreak(block, break_data);
+ } else {
+ // Here the comptime control flow ends with noreturn; however
+ // we have runtime control flow continuing after this block.
+ // This branch is therefore handled by the `i += 1; continue;`
+ // logic below.
+ }
+
+ try labeled_block.block.instructions.appendSlice(gpa, block.instructions.items[block_index..]);
+ block.instructions.items.len = block_index;
+
+ const block_result = try sema.analyzeBlockBody(block, inst_data.src(), &labeled_block.block, &labeled_block.label.merges);
+ {
+ // Destroy the ad-hoc block entry so that it does not interfere with
+ // the next iteration of comptime control flow, if any.
+ labeled_block.destroy(gpa);
+ assert(sema.post_hoc_blocks.remove(new_block_inst));
+ }
+ try map.put(gpa, inst, block_result);
+ i += 1;
+ continue;
+ }
+
+ const break_data = opt_break_data orelse break always_noreturn;
if (inst == break_data.block_inst) {
- break :blk sema.resolveInst(break_data.operand);
+ break :blk try sema.resolveInst(break_data.operand);
} else {
- break break_inst;
+ break break_data.inst;
}
},
.condbr => blk: {
@@ -1066,12 +1304,12 @@ fn analyzeBodyInner(
const else_body = sema.code.extra[extra.end + then_body.len ..][0..extra.data.else_body_len];
const cond = try sema.resolveInstConst(block, cond_src, extra.data.condition);
const inline_body = if (cond.val.toBool()) then_body else else_body;
- const break_inst = try sema.analyzeBody(block, inline_body);
- const break_data = datas[break_inst].@"break";
+ const break_data = (try sema.analyzeBodyBreak(block, inline_body)) orelse
+ break always_noreturn;
if (inst == break_data.block_inst) {
- break :blk sema.resolveInst(break_data.operand);
+ break :blk try sema.resolveInst(break_data.operand);
} else {
- break break_inst;
+ break break_data.inst;
}
},
.condbr_inline => blk: {
@@ -1082,14 +1320,114 @@ fn analyzeBodyInner(
const else_body = sema.code.extra[extra.end + then_body.len ..][0..extra.data.else_body_len];
const cond = try sema.resolveInstConst(block, cond_src, extra.data.condition);
const inline_body = if (cond.val.toBool()) then_body else else_body;
- const break_inst = try sema.analyzeBody(block, inline_body);
- const break_data = datas[break_inst].@"break";
+ const break_data = (try sema.analyzeBodyBreak(block, inline_body)) orelse
+ break always_noreturn;
+ if (inst == break_data.block_inst) {
+ break :blk try sema.resolveInst(break_data.operand);
+ } else {
+ break break_data.inst;
+ }
+ },
+ .@"try" => blk: {
+ if (!block.is_comptime) break :blk try sema.zirTry(block, inst);
+ const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
+ const src = inst_data.src();
+ const operand_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node };
+ const extra = sema.code.extraData(Zir.Inst.Try, inst_data.payload_index);
+ const inline_body = sema.code.extra[extra.end..][0..extra.data.body_len];
+ const err_union = try sema.resolveInst(extra.data.operand);
+ const is_non_err = try sema.analyzeIsNonErrComptimeOnly(block, operand_src, err_union);
+ assert(is_non_err != .none);
+ const is_non_err_tv = try sema.resolveInstConst(block, operand_src, is_non_err);
+ if (is_non_err_tv.val.toBool()) {
+ const err_union_ty = sema.typeOf(err_union);
+ break :blk try sema.analyzeErrUnionPayload(block, src, err_union_ty, err_union, operand_src, false);
+ }
+ const break_data = (try sema.analyzeBodyBreak(block, inline_body)) orelse
+ break always_noreturn;
+ if (inst == break_data.block_inst) {
+ break :blk try sema.resolveInst(break_data.operand);
+ } else {
+ break break_data.inst;
+ }
+ },
+ //.try_inline => blk: {
+ // const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
+ // const src = inst_data.src();
+ // const operand_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node };
+ // const extra = sema.code.extraData(Zir.Inst.Try, inst_data.payload_index);
+ // const inline_body = sema.code.extra[extra.end..][0..extra.data.body_len];
+ // const operand = try sema.resolveInst(extra.data.operand);
+ // const operand_ty = sema.typeOf(operand);
+ // const is_ptr = operand_ty.zigTypeTag() == .Pointer;
+ // const err_union = if (is_ptr)
+ // try sema.analyzeLoad(block, src, operand, operand_src)
+ // else
+ // operand;
+ // const is_non_err = try sema.analyzeIsNonErrComptimeOnly(block, operand_src, err_union);
+ // assert(is_non_err != .none);
+ // const is_non_err_tv = try sema.resolveInstConst(block, operand_src, is_non_err);
+ // if (is_non_err_tv.val.toBool()) {
+ // if (is_ptr) {
+ // break :blk try sema.analyzeErrUnionPayloadPtr(block, src, operand, false, false);
+ // } else {
+ // const err_union_ty = sema.typeOf(err_union);
+ // break :blk try sema.analyzeErrUnionPayload(block, src, err_union_ty, operand, operand_src, false);
+ // }
+ // }
+ // const break_data = (try sema.analyzeBodyBreak(block, inline_body)) orelse
+ // break always_noreturn;
+ // if (inst == break_data.block_inst) {
+ // break :blk try sema.resolveInst(break_data.operand);
+ // } else {
+ // break break_data.inst;
+ // }
+ //},
+ .try_ptr => blk: {
+ if (!block.is_comptime) break :blk try sema.zirTryPtr(block, inst);
+ const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
+ const src = inst_data.src();
+ const operand_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node };
+ const extra = sema.code.extraData(Zir.Inst.Try, inst_data.payload_index);
+ const inline_body = sema.code.extra[extra.end..][0..extra.data.body_len];
+ const operand = try sema.resolveInst(extra.data.operand);
+ const err_union = try sema.analyzeLoad(block, src, operand, operand_src);
+ const is_non_err = try sema.analyzeIsNonErrComptimeOnly(block, operand_src, err_union);
+ assert(is_non_err != .none);
+ const is_non_err_tv = try sema.resolveInstConst(block, operand_src, is_non_err);
+ if (is_non_err_tv.val.toBool()) {
+ break :blk try sema.analyzeErrUnionPayloadPtr(block, src, operand, false, false);
+ }
+ const break_data = (try sema.analyzeBodyBreak(block, inline_body)) orelse
+ break always_noreturn;
if (inst == break_data.block_inst) {
- break :blk sema.resolveInst(break_data.operand);
+ break :blk try sema.resolveInst(break_data.operand);
} else {
- break break_inst;
+ break break_data.inst;
}
},
+ //.try_ptr_inline => blk: {
+ // const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
+ // const src = inst_data.src();
+ // const operand_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node };
+ // const extra = sema.code.extraData(Zir.Inst.Try, inst_data.payload_index);
+ // const inline_body = sema.code.extra[extra.end..][0..extra.data.body_len];
+ // const operand = try sema.resolveInst(extra.data.operand);
+ // const err_union = try sema.analyzeLoad(block, src, operand, operand_src);
+ // const is_non_err = try sema.analyzeIsNonErrComptimeOnly(block, operand_src, err_union);
+ // assert(is_non_err != .none);
+ // const is_non_err_tv = try sema.resolveInstConst(block, operand_src, is_non_err);
+ // if (is_non_err_tv.val.toBool()) {
+ // break :blk try sema.analyzeErrUnionPayloadPtr(block, src, operand, false, false);
+ // }
+ // const break_data = (try sema.analyzeBodyBreak(block, inline_body)) orelse
+ // break always_noreturn;
+ // if (inst == break_data.block_inst) {
+ // break :blk try sema.resolveInst(break_data.operand);
+ // } else {
+ // break break_data.inst;
+ // }
+ //},
};
if (sema.typeOf(air_inst).isNoReturn())
break always_noreturn;
@@ -1097,6 +1435,19 @@ fn analyzeBodyInner(
i += 1;
} else unreachable;
+ // balance out dbg_block_begins in case of early noreturn
+ const noreturn_inst = block.instructions.popOrNull();
+ while (dbg_block_begins > 0) {
+ dbg_block_begins -= 1;
+ if (block.is_comptime or sema.mod.comp.bin_file.options.strip) continue;
+
+ _ = try block.addInst(.{
+ .tag = .dbg_block_end,
+ .data = undefined,
+ });
+ }
+ if (noreturn_inst) |some| try block.instructions.append(sema.gpa, some);
+
if (!wip_captures.finalized) {
try wip_captures.finalize();
block.wip_capture_scope = parent_capture_scope;
@@ -1105,44 +1456,7 @@ fn analyzeBodyInner(
return result;
}
-fn zirExtended(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
- const extended = sema.code.instructions.items(.data)[inst].extended;
- switch (extended.opcode) {
- // zig fmt: off
- .func => return sema.zirFuncExtended( block, extended, inst),
- .variable => return sema.zirVarExtended( block, extended),
- .struct_decl => return sema.zirStructDecl( block, extended, inst),
- .enum_decl => return sema.zirEnumDecl( block, extended),
- .union_decl => return sema.zirUnionDecl( block, extended, inst),
- .opaque_decl => return sema.zirOpaqueDecl( block, extended),
- .ret_ptr => return sema.zirRetPtr( block, extended),
- .ret_type => return sema.zirRetType( block, extended),
- .this => return sema.zirThis( block, extended),
- .ret_addr => return sema.zirRetAddr( block, extended),
- .builtin_src => return sema.zirBuiltinSrc( block, extended),
- .error_return_trace => return sema.zirErrorReturnTrace( block, extended),
- .frame => return sema.zirFrame( block, extended),
- .frame_address => return sema.zirFrameAddress( block, extended),
- .alloc => return sema.zirAllocExtended( block, extended),
- .builtin_extern => return sema.zirBuiltinExtern( block, extended),
- .@"asm" => return sema.zirAsm( block, extended, inst),
- .typeof_peer => return sema.zirTypeofPeer( block, extended),
- .compile_log => return sema.zirCompileLog( block, extended),
- .add_with_overflow => return sema.zirOverflowArithmetic(block, extended, extended.opcode),
- .sub_with_overflow => return sema.zirOverflowArithmetic(block, extended, extended.opcode),
- .mul_with_overflow => return sema.zirOverflowArithmetic(block, extended, extended.opcode),
- .shl_with_overflow => return sema.zirOverflowArithmetic(block, extended, extended.opcode),
- .c_undef => return sema.zirCUndef( block, extended),
- .c_include => return sema.zirCInclude( block, extended),
- .c_define => return sema.zirCDefine( block, extended),
- .wasm_memory_size => return sema.zirWasmMemorySize( block, extended),
- .wasm_memory_grow => return sema.zirWasmMemoryGrow( block, extended),
- .prefetch => return sema.zirPrefetch( block, extended),
- // zig fmt: on
- }
-}
-
-pub fn resolveInst(sema: *Sema, zir_ref: Zir.Inst.Ref) Air.Inst.Ref {
+pub fn resolveInst(sema: *Sema, zir_ref: Zir.Inst.Ref) !Air.Inst.Ref {
var i: usize = @enumToInt(zir_ref);
// First section of indexes correspond to a set number of constant values.
@@ -1153,7 +1467,9 @@ pub fn resolveInst(sema: *Sema, zir_ref: Zir.Inst.Ref) Air.Inst.Ref {
i -= Zir.Inst.Ref.typed_value_map.len;
// Finally, the last section of indexes refers to the map of ZIR=>AIR.
- return sema.inst_map.get(@intCast(u32, i)).?;
+ const inst = sema.inst_map.get(@intCast(u32, i)).?;
+ if (sema.typeOf(inst).tag() == .generic_poison) return error.GenericPoison;
+ return inst;
}
fn resolveConstBool(
@@ -1162,28 +1478,28 @@ fn resolveConstBool(
src: LazySrcLoc,
zir_ref: Zir.Inst.Ref,
) !bool {
- const air_inst = sema.resolveInst(zir_ref);
+ const air_inst = try sema.resolveInst(zir_ref);
const wanted_type = Type.bool;
const coerced_inst = try sema.coerce(block, wanted_type, air_inst, src);
const val = try sema.resolveConstValue(block, src, coerced_inst);
return val.toBool();
}
-fn resolveConstString(
+pub fn resolveConstString(
sema: *Sema,
block: *Block,
src: LazySrcLoc,
zir_ref: Zir.Inst.Ref,
) ![]u8 {
- const air_inst = sema.resolveInst(zir_ref);
+ const air_inst = try sema.resolveInst(zir_ref);
const wanted_type = Type.initTag(.const_slice_u8);
const coerced_inst = try sema.coerce(block, wanted_type, air_inst, src);
const val = try sema.resolveConstValue(block, src, coerced_inst);
- return val.toAllocatedBytes(wanted_type, sema.arena);
+ return val.toAllocatedBytes(wanted_type, sema.arena, sema.mod);
}
pub fn resolveType(sema: *Sema, block: *Block, src: LazySrcLoc, zir_ref: Zir.Inst.Ref) !Type {
- const air_inst = sema.resolveInst(zir_ref);
+ const air_inst = try sema.resolveInst(zir_ref);
const ty = try sema.analyzeAsType(block, src, air_inst);
if (ty.tag() == .generic_poison) return error.GenericPoison;
return ty;
@@ -1203,6 +1519,45 @@ fn analyzeAsType(
return ty.copy(sema.arena);
}
+pub fn setupErrorReturnTrace(sema: *Sema, block: *Block, last_arg_index: usize) !void {
+ const backend_supports_error_return_tracing =
+ sema.mod.comp.bin_file.options.use_llvm;
+ if (!backend_supports_error_return_tracing) {
+ // TODO implement this feature in all the backends and then delete this branch
+ return;
+ }
+
+ var err_trace_block = block.makeSubBlock();
+ err_trace_block.is_comptime = false;
+ defer err_trace_block.instructions.deinit(sema.gpa);
+
+ const src: LazySrcLoc = .unneeded;
+
+ // var addrs: [err_return_trace_addr_count]usize = undefined;
+ const err_return_trace_addr_count = 32;
+ const addr_arr_ty = try Type.array(sema.arena, err_return_trace_addr_count, null, Type.usize, sema.mod);
+ const addrs_ptr = try err_trace_block.addTy(.alloc, try Type.Tag.single_mut_pointer.create(sema.arena, addr_arr_ty));
+
+ // var st: StackTrace = undefined;
+ const unresolved_stack_trace_ty = try sema.getBuiltinType(&err_trace_block, src, "StackTrace");
+ const stack_trace_ty = try sema.resolveTypeFields(&err_trace_block, src, unresolved_stack_trace_ty);
+ const st_ptr = try err_trace_block.addTy(.alloc, try Type.Tag.single_mut_pointer.create(sema.arena, stack_trace_ty));
+
+ // st.instruction_addresses = &addrs;
+ const addr_field_ptr = try sema.fieldPtr(&err_trace_block, src, st_ptr, "instruction_addresses", src);
+ try sema.storePtr2(&err_trace_block, src, addr_field_ptr, src, addrs_ptr, src, .store);
+
+ // st.index = 0;
+ const index_field_ptr = try sema.fieldPtr(&err_trace_block, src, st_ptr, "index", src);
+ const zero = try sema.addConstant(Type.usize, Value.zero);
+ try sema.storePtr2(&err_trace_block, src, index_field_ptr, src, zero, src, .store);
+
+ // @errorReturnTrace() = &st;
+ _ = try err_trace_block.addUnOp(.set_err_return_trace, st_ptr);
+
+ try block.instructions.insertSlice(sema.gpa, last_arg_index, err_trace_block.instructions.items);
+}
+
/// May return Value Tags: `variable`, `undef`.
/// See `resolveConstValue` for an alternative.
/// Value Tag `generic_poison` causes `error.GenericPoison` to be returned.
@@ -1266,6 +1621,7 @@ fn resolveDefinedValue(
) CompileError!?Value {
if (try sema.resolveMaybeUndefVal(block, src, air_ref)) |val| {
if (val.isUndef()) {
+ if (block.is_typeof) return null;
return sema.failWithUseOfUndef(block, src);
}
return val;
@@ -1290,6 +1646,24 @@ fn resolveMaybeUndefVal(
}
}
+/// Value Tag `variable` results in `null`.
+/// Value Tag `undef` results in the Value.
+/// Value Tag `generic_poison` causes `error.GenericPoison` to be returned.
+/// Value Tag `decl_ref` and `decl_ref_mut` or any nested such value results in `null`.
+fn resolveMaybeUndefValIntable(
+ sema: *Sema,
+ block: *Block,
+ src: LazySrcLoc,
+ inst: Air.Inst.Ref,
+) CompileError!?Value {
+ const val = (try sema.resolveMaybeUndefValAllowVariables(block, src, inst)) orelse return null;
+ switch (val.tag()) {
+ .variable, .decl_ref, .decl_ref_mut => return null,
+ .generic_poison => return error.GenericPoison,
+ else => return val,
+ }
+}
+
/// Returns all Value tags including `variable` and `undef`.
fn resolveMaybeUndefValAllowVariables(
sema: *Sema,
@@ -1333,11 +1707,25 @@ fn failWithDivideByZero(sema: *Sema, block: *Block, src: LazySrcLoc) CompileErro
}
fn failWithModRemNegative(sema: *Sema, block: *Block, src: LazySrcLoc, lhs_ty: Type, rhs_ty: Type) CompileError {
- return sema.fail(block, src, "remainder division with '{}' and '{}': signed integers and floats must use @rem or @mod", .{ lhs_ty, rhs_ty });
+ return sema.fail(block, src, "remainder division with '{}' and '{}': signed integers and floats must use @rem or @mod", .{
+ lhs_ty.fmt(sema.mod), rhs_ty.fmt(sema.mod),
+ });
}
fn failWithExpectedOptionalType(sema: *Sema, block: *Block, src: LazySrcLoc, optional_ty: Type) CompileError {
- return sema.fail(block, src, "expected optional type, found {}", .{optional_ty});
+ return sema.fail(block, src, "expected optional type, found '{}'", .{optional_ty.fmt(sema.mod)});
+}
+
+fn failWithArrayInitNotSupported(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) CompileError {
+ return sema.fail(block, src, "type '{}' does not support array initialization syntax", .{
+ ty.fmt(sema.mod),
+ });
+}
+
+fn failWithStructInitNotSupported(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) CompileError {
+ return sema.fail(block, src, "type '{}' does not support struct initialization syntax", .{
+ ty.fmt(sema.mod),
+ });
}
fn failWithErrorSetCodeMissing(
@@ -1348,7 +1736,24 @@ fn failWithErrorSetCodeMissing(
src_err_set_ty: Type,
) CompileError {
return sema.fail(block, src, "expected type '{}', found type '{}'", .{
- dest_err_set_ty, src_err_set_ty,
+ dest_err_set_ty.fmt(sema.mod), src_err_set_ty.fmt(sema.mod),
+ });
+}
+
+fn failWithIntegerOverflow(sema: *Sema, block: *Block, src: LazySrcLoc, int_ty: Type, val: Value, vector_index: usize) CompileError {
+ if (int_ty.zigTypeTag() == .Vector) {
+ const msg = msg: {
+ const msg = try sema.errMsg(block, src, "overflow of vector type '{}' with value '{}'", .{
+ int_ty.fmt(sema.mod), val.fmtValue(int_ty, sema.mod),
+ });
+ errdefer msg.destroy(sema.gpa);
+ try sema.errNote(block, src, msg, "when computing vector element at index '{d}'", .{vector_index});
+ break :msg msg;
+ };
+ return sema.failWithOwnedErrorMsg(block, msg);
+ }
+ return sema.fail(block, src, "overflow of integer type '{}' with value '{}'", .{
+ int_ty.fmt(sema.mod), val.fmtValue(int_ty, sema.mod),
});
}
@@ -1362,7 +1767,26 @@ fn errNote(
comptime format: []const u8,
args: anytype,
) error{OutOfMemory}!void {
- return sema.mod.errNoteNonLazy(src.toSrcLoc(block.src_decl), parent, format, args);
+ const mod = sema.mod;
+ const src_decl = mod.declPtr(block.src_decl);
+ return mod.errNoteNonLazy(src.toSrcLoc(src_decl), parent, format, args);
+}
+
+fn addFieldErrNote(
+ sema: *Sema,
+ block: *Block,
+ container_ty: Type,
+ field_index: usize,
+ parent: *Module.ErrorMsg,
+ comptime format: []const u8,
+ args: anytype,
+) !void {
+ const mod = sema.mod;
+ const decl_index = container_ty.getOwnerDecl();
+ const decl = mod.declPtr(decl_index);
+ const tree = try sema.getAstTree(block);
+ const field_src = enumFieldSrcLoc(decl, tree.*, container_ty.getNodeOffset(), field_index);
+ try mod.errNoteNonLazy(field_src.toSrcLoc(decl), parent, format, args);
}
fn errMsg(
@@ -1372,7 +1796,9 @@ fn errMsg(
comptime format: []const u8,
args: anytype,
) error{OutOfMemory}!*Module.ErrorMsg {
- return Module.ErrorMsg.create(sema.gpa, src.toSrcLoc(block.src_decl), format, args);
+ const mod = sema.mod;
+ const src_decl = mod.declPtr(block.src_decl);
+ return Module.ErrorMsg.create(sema.gpa, src.toSrcLoc(src_decl), format, args);
}
pub fn fail(
@@ -1383,10 +1809,10 @@ pub fn fail(
args: anytype,
) CompileError {
const err_msg = try sema.errMsg(block, src, format, args);
- return sema.failWithOwnedErrorMsg(err_msg);
+ return sema.failWithOwnedErrorMsg(block, err_msg);
}
-fn failWithOwnedErrorMsg(sema: *Sema, err_msg: *Module.ErrorMsg) CompileError {
+fn failWithOwnedErrorMsg(sema: *Sema, block: *Block, err_msg: *Module.ErrorMsg) CompileError {
@setCold(true);
if (crash_report.is_enabled and sema.mod.comp.debug_compile_errors) {
@@ -1399,6 +1825,7 @@ fn failWithOwnedErrorMsg(sema: *Sema, err_msg: *Module.ErrorMsg) CompileError {
}
const mod = sema.mod;
+ if (block.inlining) |some| some.err = err_msg;
{
errdefer err_msg.destroy(mod.gpa);
@@ -1414,41 +1841,27 @@ fn failWithOwnedErrorMsg(sema: *Sema, err_msg: *Module.ErrorMsg) CompileError {
sema.owner_decl.analysis = .sema_failure;
sema.owner_decl.generation = mod.generation;
}
- mod.failed_decls.putAssumeCapacityNoClobber(sema.owner_decl, err_msg);
- return error.AnalysisFail;
-}
-
-/// Appropriate to call when the coercion has already been done by result
-/// location semantics. Asserts the value fits in the provided `Int` type.
-/// Only supports `Int` types 64 bits or less.
-/// TODO don't ever call this since we're migrating towards ResultLoc.coerced_ty.
-fn resolveAlreadyCoercedInt(
- sema: *Sema,
- block: *Block,
- src: LazySrcLoc,
- zir_ref: Zir.Inst.Ref,
- comptime Int: type,
-) !Int {
- comptime assert(@typeInfo(Int).Int.bits <= 64);
- const air_inst = sema.resolveInst(zir_ref);
- const val = try sema.resolveConstValue(block, src, air_inst);
- switch (@typeInfo(Int).Int.signedness) {
- .signed => return @intCast(Int, val.toSignedInt()),
- .unsigned => return @intCast(Int, val.toUnsignedInt()),
+ const gop = mod.failed_decls.getOrPutAssumeCapacity(sema.owner_decl_index);
+ if (gop.found_existing) {
+ // If there are multiple errors for the same Decl, prefer the first one added.
+ err_msg.destroy(mod.gpa);
+ } else {
+ gop.value_ptr.* = err_msg;
}
+ return error.AnalysisFail;
}
-fn resolveAlign(
+pub fn resolveAlign(
sema: *Sema,
block: *Block,
src: LazySrcLoc,
zir_ref: Zir.Inst.Ref,
-) !u16 {
- const alignment_big = try sema.resolveInt(block, src, zir_ref, Type.initTag(.u16));
- const alignment = @intCast(u16, alignment_big); // We coerce to u16 in the prev line.
+) !u32 {
+ const alignment_big = try sema.resolveInt(block, src, zir_ref, Type.initTag(.u29));
+ const alignment = @intCast(u32, alignment_big); // We coerce to u16 in the prev line.
if (alignment == 0) return sema.fail(block, src, "alignment must be >= 1", .{});
if (!std.math.isPowerOfTwo(alignment)) {
- return sema.fail(block, src, "alignment value {d} is not a power of two", .{
+ return sema.fail(block, src, "alignment value '{d}' is not a power of two", .{
alignment,
});
}
@@ -1462,11 +1875,11 @@ fn resolveInt(
zir_ref: Zir.Inst.Ref,
dest_ty: Type,
) !u64 {
- const air_inst = sema.resolveInst(zir_ref);
+ const air_inst = try sema.resolveInst(zir_ref);
const coerced = try sema.coerce(block, dest_ty, air_inst, src);
const val = try sema.resolveConstValue(block, src, coerced);
-
- return val.toUnsignedInt();
+ const target = sema.mod.getTarget();
+ return (try val.getUnsignedIntAdvanced(target, sema.kit(block, src))).?;
}
// Returns a compile error if the value has tag `variable`. See `resolveInstValue` for
@@ -1477,7 +1890,7 @@ pub fn resolveInstConst(
src: LazySrcLoc,
zir_ref: Zir.Inst.Ref,
) CompileError!TypedValue {
- const air_ref = sema.resolveInst(zir_ref);
+ const air_ref = try sema.resolveInst(zir_ref);
const val = try sema.resolveConstValue(block, src, air_ref);
return TypedValue{
.ty = sema.typeOf(air_ref),
@@ -1493,7 +1906,7 @@ pub fn resolveInstValue(
src: LazySrcLoc,
zir_ref: Zir.Inst.Ref,
) CompileError!TypedValue {
- const air_ref = sema.resolveInst(zir_ref);
+ const air_ref = try sema.resolveInst(zir_ref);
const val = try sema.resolveValue(block, src, air_ref);
return TypedValue{
.ty = sema.typeOf(air_ref),
@@ -1505,11 +1918,13 @@ fn zirCoerceResultPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileE
const tracy = trace(@src());
defer tracy.end();
- const src: LazySrcLoc = sema.src;
- const bin_inst = sema.code.instructions.items(.data)[inst].bin;
- const pointee_ty = try sema.resolveType(block, src, bin_inst.lhs);
- const ptr = sema.resolveInst(bin_inst.rhs);
- const addr_space = target_util.defaultAddressSpace(sema.mod.getTarget(), .local);
+ const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
+ const src = inst_data.src();
+ const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
+ const pointee_ty = try sema.resolveType(block, src, extra.lhs);
+ const ptr = try sema.resolveInst(extra.rhs);
+ const target = sema.mod.getTarget();
+ const addr_space = target_util.defaultAddressSpace(target, .local);
if (Air.refToIndex(ptr)) |ptr_inst| {
if (sema.air_instructions.items(.tag)[ptr_inst] == .constant) {
@@ -1529,7 +1944,7 @@ fn zirCoerceResultPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileE
try inferred_alloc.stored_inst_list.append(sema.arena, operand);
try sema.requireRuntimeBlock(block, src);
- const ptr_ty = try Type.ptr(sema.arena, .{
+ const ptr_ty = try Type.ptr(sema.arena, sema.mod, .{
.pointee_type = pointee_ty,
.@"align" = inferred_alloc.alignment,
.@"addrspace" = addr_space,
@@ -1543,11 +1958,15 @@ fn zirCoerceResultPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileE
// The alloc will turn into a Decl.
var anon_decl = try block.startAnonDecl(src);
defer anon_decl.deinit();
- iac.data.decl = try anon_decl.finish(
+ iac.data.decl_index = try anon_decl.finish(
try pointee_ty.copy(anon_decl.arena()),
Value.undef,
+ iac.data.alignment,
);
- const ptr_ty = try Type.ptr(sema.arena, .{
+ if (iac.data.alignment != 0) {
+ try sema.resolveTypeLayout(block, src, pointee_ty);
+ }
+ const ptr_ty = try Type.ptr(sema.arena, sema.mod, .{
.pointee_type = pointee_ty,
.@"align" = iac.data.alignment,
.@"addrspace" = addr_space,
@@ -1555,33 +1974,27 @@ fn zirCoerceResultPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileE
return sema.addConstant(
ptr_ty,
try Value.Tag.decl_ref_mut.create(sema.arena, .{
- .decl = iac.data.decl,
+ .decl_index = iac.data.decl_index,
.runtime_index = block.runtime_index,
}),
);
},
- .decl_ref_mut => {
- const ptr_ty = try Type.ptr(sema.arena, .{
- .pointee_type = pointee_ty,
- .@"addrspace" = addr_space,
- });
- return sema.addConstant(ptr_ty, ptr_val);
- },
else => {},
}
}
}
- try sema.requireRuntimeBlock(block, src);
-
// Make a dummy store through the pointer to test the coercion.
// We will then use the generated instructions to decide what
// kind of transformations to make on the result pointer.
var trash_block = block.makeSubBlock();
+ trash_block.is_comptime = false;
+ trash_block.is_coerce_result_ptr = true;
defer trash_block.instructions.deinit(sema.gpa);
+ const dummy_ptr = try trash_block.addTy(.alloc, sema.typeOf(ptr));
const dummy_operand = try trash_block.addBitCast(pointee_ty, .void_value);
- try sema.storePtr(&trash_block, src, ptr, dummy_operand);
+ try sema.storePtr2(&trash_block, src, dummy_ptr, src, dummy_operand, src, .bitcast);
{
const air_tags = sema.air_instructions.items(.tag);
@@ -1592,13 +2005,18 @@ fn zirCoerceResultPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileE
//}
// The last one is always `store`.
- const trash_inst = trash_block.instructions.pop();
- assert(air_tags[trash_inst] == .store);
- assert(trash_inst == sema.air_instructions.len - 1);
- sema.air_instructions.len -= 1;
+ const trash_inst = trash_block.instructions.items[trash_block.instructions.items.len - 1];
+ if (air_tags[trash_inst] != .store) {
+ // no store instruction is generated for zero sized types
+ assert((try sema.typeHasOnePossibleValue(block, src, pointee_ty)) != null);
+ } else {
+ trash_block.instructions.items.len -= 1;
+ assert(trash_inst == sema.air_instructions.len - 1);
+ sema.air_instructions.len -= 1;
+ }
}
- const ptr_ty = try Type.ptr(sema.arena, .{
+ const ptr_ty = try Type.ptr(sema.arena, sema.mod, .{
.pointee_type = pointee_ty,
.@"addrspace" = addr_space,
});
@@ -1612,30 +2030,31 @@ fn zirCoerceResultPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileE
switch (air_tags[trash_inst]) {
.bitcast => {
if (Air.indexToRef(trash_inst) == dummy_operand) {
- return block.addBitCast(ptr_ty, new_ptr);
+ if (try sema.resolveDefinedValue(block, src, new_ptr)) |ptr_val| {
+ return sema.addConstant(ptr_ty, ptr_val);
+ }
+ return sema.bitCast(block, ptr_ty, new_ptr, src);
}
const ty_op = air_datas[trash_inst].ty_op;
- const operand_ty = sema.getTmpAir().typeOf(ty_op.operand);
- const ptr_operand_ty = try Type.ptr(sema.arena, .{
+ const operand_ty = sema.typeOf(ty_op.operand);
+ const ptr_operand_ty = try Type.ptr(sema.arena, sema.mod, .{
.pointee_type = operand_ty,
.@"addrspace" = addr_space,
});
- new_ptr = try block.addBitCast(ptr_operand_ty, new_ptr);
+ if (try sema.resolveDefinedValue(block, src, new_ptr)) |ptr_val| {
+ new_ptr = try sema.addConstant(ptr_operand_ty, ptr_val);
+ } else {
+ new_ptr = try sema.bitCast(block, ptr_operand_ty, new_ptr, src);
+ }
},
.wrap_optional => {
- const ty_op = air_datas[trash_inst].ty_op;
- const payload_ty = sema.getTmpAir().typeOf(ty_op.operand);
- const ptr_payload_ty = try Type.ptr(sema.arena, .{
- .pointee_type = payload_ty,
- .@"addrspace" = addr_space,
- });
- new_ptr = try block.addTyOp(.optional_payload_ptr_set, ptr_payload_ty, new_ptr);
+ new_ptr = try sema.analyzeOptionalPayloadPtr(block, src, new_ptr, false, true);
},
.wrap_errunion_err => {
return sema.fail(block, src, "TODO coerce_result_ptr wrap_errunion_err", .{});
},
.wrap_errunion_payload => {
- return sema.fail(block, src, "TODO coerce_result_ptr wrap_errunion_payload", .{});
+ new_ptr = try sema.analyzeErrUnionPayloadPtr(block, src, new_ptr, false, true);
},
else => {
if (std.debug.runtime_safety) {
@@ -1647,7 +2066,7 @@ fn zirCoerceResultPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileE
}
},
}
- } else unreachable; // TODO should not need else unreachable
+ }
}
pub fn analyzeStructDecl(
@@ -1687,27 +2106,28 @@ fn zirStructDecl(
const small = @bitCast(Zir.Inst.StructDecl.Small, extended.small);
const src: LazySrcLoc = if (small.has_src_node) blk: {
const node_offset = @bitCast(i32, sema.code.extra[extended.operand]);
- break :blk .{ .node_offset = node_offset };
+ break :blk LazySrcLoc.nodeOffset(node_offset);
} else sema.src;
var new_decl_arena = std.heap.ArenaAllocator.init(sema.gpa);
errdefer new_decl_arena.deinit();
const new_decl_arena_allocator = new_decl_arena.allocator();
+ const mod = sema.mod;
const struct_obj = try new_decl_arena_allocator.create(Module.Struct);
const struct_ty = try Type.Tag.@"struct".create(new_decl_arena_allocator, struct_obj);
const struct_val = try Value.Tag.ty.create(new_decl_arena_allocator, struct_ty);
- const type_name = try sema.createTypeName(block, small.name_strategy);
- const new_decl = try sema.mod.createAnonymousDeclNamed(block, .{
+ const new_decl_index = try sema.createAnonymousDeclTypeNamed(block, .{
.ty = Type.type,
.val = struct_val,
- }, type_name);
+ }, small.name_strategy, "struct", inst);
+ const new_decl = mod.declPtr(new_decl_index);
new_decl.owns_tv = true;
- errdefer sema.mod.abortAnonDecl(new_decl);
+ errdefer mod.abortAnonDecl(new_decl_index);
struct_obj.* = .{
- .owner_decl = new_decl,
+ .owner_decl = new_decl_index,
.fields = .{},
- .node_offset = src.node_offset,
+ .node_offset = src.node_offset.x,
.zir_index = inst,
.layout = small.layout,
.status = .none,
@@ -1723,31 +2143,97 @@ fn zirStructDecl(
});
try sema.analyzeStructDecl(new_decl, inst, struct_obj);
try new_decl.finalizeNewArena(&new_decl_arena);
- return sema.analyzeDeclVal(block, src, new_decl);
+ return sema.analyzeDeclVal(block, src, new_decl_index);
}
-fn createTypeName(sema: *Sema, block: *Block, name_strategy: Zir.Inst.NameStrategy) ![:0]u8 {
+fn createAnonymousDeclTypeNamed(
+ sema: *Sema,
+ block: *Block,
+ typed_value: TypedValue,
+ name_strategy: Zir.Inst.NameStrategy,
+ anon_prefix: []const u8,
+ inst: ?Zir.Inst.Index,
+) !Decl.Index {
+ const mod = sema.mod;
+ const namespace = block.namespace;
+ const src_scope = block.wip_capture_scope;
+ const src_decl = mod.declPtr(block.src_decl);
+ const new_decl_index = try mod.allocateNewDecl(namespace, src_decl.src_node, src_scope);
+ errdefer mod.destroyDecl(new_decl_index);
+
switch (name_strategy) {
.anon => {
// It would be neat to have "struct:line:column" but this name has
// to survive incremental updates, where it may have been shifted down
// or up to a different line, but unchanged, and thus not unnecessarily
// semantically analyzed.
- const name_index = sema.mod.getNextAnonNameIndex();
- return std.fmt.allocPrintZ(sema.gpa, "{s}__anon_{d}", .{
- block.src_decl.name, name_index,
+ // This name is also used as the key in the parent namespace so it cannot be
+ // renamed.
+ const name = try std.fmt.allocPrintZ(sema.gpa, "{s}__{s}_{d}", .{
+ src_decl.name, anon_prefix, @enumToInt(new_decl_index),
});
+ errdefer sema.gpa.free(name);
+ try mod.initNewAnonDecl(new_decl_index, src_decl.src_line, namespace, typed_value, name);
+ return new_decl_index;
+ },
+ .parent => {
+ const name = try sema.gpa.dupeZ(u8, mem.sliceTo(sema.mod.declPtr(block.src_decl).name, 0));
+ errdefer sema.gpa.free(name);
+ try mod.initNewAnonDecl(new_decl_index, src_decl.src_line, namespace, typed_value, name);
+ return new_decl_index;
},
- .parent => return sema.gpa.dupeZ(u8, mem.sliceTo(block.src_decl.name, 0)),
.func => {
- const name_index = sema.mod.getNextAnonNameIndex();
- const name = try std.fmt.allocPrintZ(sema.gpa, "{s}__anon_{d}", .{
- block.src_decl.name, name_index,
- });
- log.warn("TODO: handle NameStrategy.func correctly instead of using anon name '{s}'", .{
- name,
- });
- return name;
+ const fn_info = sema.code.getFnInfo(sema.func.?.zir_body_inst);
+ const zir_tags = sema.code.instructions.items(.tag);
+
+ var buf = std.ArrayList(u8).init(sema.gpa);
+ defer buf.deinit();
+ try buf.appendSlice(mem.sliceTo(sema.mod.declPtr(block.src_decl).name, 0));
+ try buf.appendSlice("(");
+
+ var arg_i: usize = 0;
+ for (fn_info.param_body) |zir_inst| switch (zir_tags[zir_inst]) {
+ .param, .param_comptime, .param_anytype, .param_anytype_comptime => {
+ const arg = sema.inst_map.get(zir_inst).?;
+ // The comptime call code in analyzeCall already did this, so we're
+ // just repeating it here and it's guaranteed to work.
+ const arg_val = sema.resolveConstMaybeUndefVal(block, .unneeded, arg) catch unreachable;
+
+ if (arg_i != 0) try buf.appendSlice(",");
+ try buf.writer().print("{}", .{arg_val.fmtValue(sema.typeOf(arg), sema.mod)});
+
+ arg_i += 1;
+ continue;
+ },
+ else => continue,
+ };
+
+ try buf.appendSlice(")");
+ const name = try buf.toOwnedSliceSentinel(0);
+ errdefer sema.gpa.free(name);
+ try mod.initNewAnonDecl(new_decl_index, src_decl.src_line, namespace, typed_value, name);
+ return new_decl_index;
+ },
+ .dbg_var => {
+ const ref = Zir.indexToRef(inst.?);
+ const zir_tags = sema.code.instructions.items(.tag);
+ const zir_data = sema.code.instructions.items(.data);
+ var i = inst.?;
+ while (i < zir_tags.len) : (i += 1) switch (zir_tags[i]) {
+ .dbg_var_ptr, .dbg_var_val => {
+ if (zir_data[i].str_op.operand != ref) continue;
+
+ const name = try std.fmt.allocPrintZ(sema.gpa, "{s}.{s}", .{
+ src_decl.name, zir_data[i].str_op.getStr(sema.code),
+ });
+ errdefer sema.gpa.free(name);
+
+ try mod.initNewAnonDecl(new_decl_index, src_decl.src_line, namespace, typed_value, name);
+ return new_decl_index;
+ },
+ else => {},
+ };
+ return sema.createAnonymousDeclTypeNamed(block, typed_value, .anon, anon_prefix, null);
},
}
}
@@ -1756,6 +2242,7 @@ fn zirEnumDecl(
sema: *Sema,
block: *Block,
extended: Zir.Inst.Extended.InstData,
+ inst: Zir.Inst.Index,
) CompileError!Air.Inst.Ref {
const tracy = trace(@src());
defer tracy.end();
@@ -1768,7 +2255,7 @@ fn zirEnumDecl(
const src: LazySrcLoc = if (small.has_src_node) blk: {
const node_offset = @bitCast(i32, sema.code.extra[extra_index]);
extra_index += 1;
- break :blk .{ .node_offset = node_offset };
+ break :blk LazySrcLoc.nodeOffset(node_offset);
} else sema.src;
const tag_type_ref = if (small.has_tag_type) blk: {
@@ -1807,20 +2294,21 @@ fn zirEnumDecl(
};
const enum_ty = Type.initPayload(&enum_ty_payload.base);
const enum_val = try Value.Tag.ty.create(new_decl_arena_allocator, enum_ty);
- const type_name = try sema.createTypeName(block, small.name_strategy);
- const new_decl = try mod.createAnonymousDeclNamed(block, .{
+ const new_decl_index = try sema.createAnonymousDeclTypeNamed(block, .{
.ty = Type.type,
.val = enum_val,
- }, type_name);
+ }, small.name_strategy, "enum", inst);
+ const new_decl = mod.declPtr(new_decl_index);
new_decl.owns_tv = true;
- errdefer mod.abortAnonDecl(new_decl);
+ errdefer mod.abortAnonDecl(new_decl_index);
enum_obj.* = .{
- .owner_decl = new_decl,
- .tag_ty = Type.initTag(.@"null"),
+ .owner_decl = new_decl_index,
+ .tag_ty = Type.@"null",
+ .tag_ty_inferred = true,
.fields = .{},
.values = .{},
- .node_offset = src.node_offset,
+ .node_offset = src.node_offset.x,
.namespace = .{
.parent = block.namespace,
.ty = enum_ty,
@@ -1840,9 +2328,10 @@ fn zirEnumDecl(
// TODO better source location
const ty = try sema.resolveType(block, src, tag_type_ref);
enum_obj.tag_ty = try ty.copy(new_decl_arena_allocator);
+ enum_obj.tag_ty_inferred = false;
}
try new_decl.finalizeNewArena(&new_decl_arena);
- return sema.analyzeDeclVal(block, src, new_decl);
+ return sema.analyzeDeclVal(block, src, new_decl_index);
}
extra_index += body.len;
@@ -1857,8 +2346,13 @@ fn zirEnumDecl(
// should be the enum itself.
const prev_owner_decl = sema.owner_decl;
+ const prev_owner_decl_index = sema.owner_decl_index;
sema.owner_decl = new_decl;
- defer sema.owner_decl = prev_owner_decl;
+ sema.owner_decl_index = new_decl_index;
+ defer {
+ sema.owner_decl = prev_owner_decl;
+ sema.owner_decl_index = prev_owner_decl_index;
+ }
const prev_owner_func = sema.owner_func;
sema.owner_func = null;
@@ -1874,7 +2368,7 @@ fn zirEnumDecl(
var enum_block: Block = .{
.parent = null,
.sema = sema,
- .src_decl = new_decl,
+ .src_decl = new_decl_index,
.namespace = &enum_obj.namespace,
.wip_capture_scope = wip_captures.scope,
.instructions = .{},
@@ -1884,21 +2378,27 @@ fn zirEnumDecl(
defer assert(enum_block.instructions.items.len == 0); // should all be comptime instructions
if (body.len != 0) {
- _ = try sema.analyzeBody(&enum_block, body);
+ try sema.analyzeBody(&enum_block, body);
}
try wip_captures.finalize();
- const tag_ty = blk: {
- if (tag_type_ref != .none) {
- // TODO better source location
- const ty = try sema.resolveType(block, src, tag_type_ref);
- break :blk try ty.copy(new_decl_arena_allocator);
- }
+ if (tag_type_ref != .none) {
+ // TODO better source location
+ const ty = try sema.resolveType(block, src, tag_type_ref);
+ enum_obj.tag_ty = try ty.copy(new_decl_arena_allocator);
+ enum_obj.tag_ty_inferred = false;
+ } else {
const bits = std.math.log2_int_ceil(usize, fields_len);
- break :blk try Type.Tag.int_unsigned.create(new_decl_arena_allocator, bits);
- };
- enum_obj.tag_ty = tag_ty;
+ enum_obj.tag_ty = try Type.Tag.int_unsigned.create(new_decl_arena_allocator, bits);
+ enum_obj.tag_ty_inferred = true;
+ }
+ }
+
+ if (small.nonexhaustive) {
+ if (fields_len > 1 and std.math.log2_int(u64, fields_len) == enum_obj.tag_ty.bitSize(sema.mod.getTarget())) {
+ return sema.fail(block, src, "non-exhaustive enum specifies every value", .{});
+ }
}
try enum_obj.fields.ensureTotalCapacity(new_decl_arena_allocator, fields_len);
@@ -1908,12 +2408,14 @@ fn zirEnumDecl(
if (any_values) {
try enum_obj.values.ensureTotalCapacityContext(new_decl_arena_allocator, fields_len, .{
.ty = enum_obj.tag_ty,
+ .mod = mod,
});
}
var bit_bag_index: usize = body_end;
var cur_bit_bag: u32 = undefined;
var field_i: u32 = 0;
+ var last_tag_val: ?Value = null;
while (field_i < fields_len) : (field_i += 1) {
if (field_i % 32 == 0) {
cur_bit_bag = sema.code.extra[bit_bag_index];
@@ -1934,15 +2436,15 @@ fn zirEnumDecl(
const gop = enum_obj.fields.getOrPutAssumeCapacity(field_name);
if (gop.found_existing) {
const tree = try sema.getAstTree(block);
- const field_src = enumFieldSrcLoc(block.src_decl, tree.*, src.node_offset, field_i);
- const other_tag_src = enumFieldSrcLoc(block.src_decl, tree.*, src.node_offset, gop.index);
+ const field_src = enumFieldSrcLoc(sema.mod.declPtr(block.src_decl), tree.*, src.node_offset.x, field_i);
+ const other_tag_src = enumFieldSrcLoc(sema.mod.declPtr(block.src_decl), tree.*, src.node_offset.x, gop.index);
const msg = msg: {
- const msg = try sema.errMsg(block, field_src, "duplicate enum tag", .{});
+ const msg = try sema.errMsg(block, field_src, "duplicate enum field '{s}'", .{field_name});
errdefer msg.destroy(gpa);
- try sema.errNote(block, other_tag_src, msg, "other tag here", .{});
+ try sema.errNote(block, other_tag_src, msg, "other field here", .{});
break :msg msg;
};
- return sema.failWithOwnedErrorMsg(msg);
+ return sema.failWithOwnedErrorMsg(block, msg);
}
if (has_tag_value) {
@@ -1952,18 +2454,28 @@ fn zirEnumDecl(
// that points to this default value expression rather than the struct.
// But only resolve the source location if we need to emit a compile error.
const tag_val = (try sema.resolveInstConst(block, src, tag_val_ref)).val;
+ last_tag_val = tag_val;
const copied_tag_val = try tag_val.copy(new_decl_arena_allocator);
enum_obj.values.putAssumeCapacityNoClobberContext(copied_tag_val, {}, .{
.ty = enum_obj.tag_ty,
+ .mod = mod,
});
} else if (any_values) {
- const tag_val = try Value.Tag.int_u64.create(new_decl_arena_allocator, field_i);
- enum_obj.values.putAssumeCapacityNoClobberContext(tag_val, {}, .{ .ty = enum_obj.tag_ty });
+ const tag_val = if (last_tag_val) |val|
+ try sema.intAdd(block, src, val, Value.one, enum_obj.tag_ty)
+ else
+ Value.zero;
+ last_tag_val = tag_val;
+ const copied_tag_val = try tag_val.copy(new_decl_arena_allocator);
+ enum_obj.values.putAssumeCapacityNoClobberContext(copied_tag_val, {}, .{
+ .ty = enum_obj.tag_ty,
+ .mod = mod,
+ });
}
}
try new_decl.finalizeNewArena(&new_decl_arena);
- return sema.analyzeDeclVal(block, src, new_decl);
+ return sema.analyzeDeclVal(block, src, new_decl_index);
}
fn zirUnionDecl(
@@ -1981,7 +2493,7 @@ fn zirUnionDecl(
const src: LazySrcLoc = if (small.has_src_node) blk: {
const node_offset = @bitCast(i32, sema.code.extra[extra_index]);
extra_index += 1;
- break :blk .{ .node_offset = node_offset };
+ break :blk LazySrcLoc.nodeOffset(node_offset);
} else sema.src;
extra_index += @boolToInt(small.has_tag_type);
@@ -2007,18 +2519,19 @@ fn zirUnionDecl(
};
const union_ty = Type.initPayload(&union_payload.base);
const union_val = try Value.Tag.ty.create(new_decl_arena_allocator, union_ty);
- const type_name = try sema.createTypeName(block, small.name_strategy);
- const new_decl = try sema.mod.createAnonymousDeclNamed(block, .{
+ const mod = sema.mod;
+ const new_decl_index = try sema.createAnonymousDeclTypeNamed(block, .{
.ty = Type.type,
.val = union_val,
- }, type_name);
+ }, small.name_strategy, "union", inst);
+ const new_decl = mod.declPtr(new_decl_index);
new_decl.owns_tv = true;
- errdefer sema.mod.abortAnonDecl(new_decl);
+ errdefer mod.abortAnonDecl(new_decl_index);
union_obj.* = .{
- .owner_decl = new_decl,
+ .owner_decl = new_decl_index,
.tag_ty = Type.initTag(.@"null"),
.fields = .{},
- .node_offset = src.node_offset,
+ .node_offset = src.node_offset.x,
.zir_index = inst,
.layout = small.layout,
.status = .none,
@@ -2032,16 +2545,17 @@ fn zirUnionDecl(
&union_obj.namespace, new_decl, new_decl.name,
});
- _ = try sema.mod.scanNamespace(&union_obj.namespace, extra_index, decls_len, new_decl);
+ _ = try mod.scanNamespace(&union_obj.namespace, extra_index, decls_len, new_decl);
try new_decl.finalizeNewArena(&new_decl_arena);
- return sema.analyzeDeclVal(block, src, new_decl);
+ return sema.analyzeDeclVal(block, src, new_decl_index);
}
fn zirOpaqueDecl(
sema: *Sema,
block: *Block,
extended: Zir.Inst.Extended.InstData,
+ inst: Zir.Inst.Index,
) CompileError!Air.Inst.Ref {
const tracy = trace(@src());
defer tracy.end();
@@ -2054,7 +2568,7 @@ fn zirOpaqueDecl(
const src: LazySrcLoc = if (small.has_src_node) blk: {
const node_offset = @bitCast(i32, sema.code.extra[extra_index]);
extra_index += 1;
- break :blk .{ .node_offset = node_offset };
+ break :blk LazySrcLoc.nodeOffset(node_offset);
} else sema.src;
const decls_len = if (small.has_decls_len) blk: {
@@ -2075,17 +2589,17 @@ fn zirOpaqueDecl(
};
const opaque_ty = Type.initPayload(&opaque_ty_payload.base);
const opaque_val = try Value.Tag.ty.create(new_decl_arena_allocator, opaque_ty);
- const type_name = try sema.createTypeName(block, small.name_strategy);
- const new_decl = try mod.createAnonymousDeclNamed(block, .{
+ const new_decl_index = try sema.createAnonymousDeclTypeNamed(block, .{
.ty = Type.type,
.val = opaque_val,
- }, type_name);
+ }, small.name_strategy, "opaque", inst);
+ const new_decl = mod.declPtr(new_decl_index);
new_decl.owns_tv = true;
- errdefer mod.abortAnonDecl(new_decl);
+ errdefer mod.abortAnonDecl(new_decl_index);
opaque_obj.* = .{
- .owner_decl = new_decl,
- .node_offset = src.node_offset,
+ .owner_decl = new_decl_index,
+ .node_offset = src.node_offset.x,
.namespace = .{
.parent = block.namespace,
.ty = opaque_ty,
@@ -2099,7 +2613,7 @@ fn zirOpaqueDecl(
extra_index = try mod.scanNamespace(&opaque_obj.namespace, extra_index, decls_len, new_decl);
try new_decl.finalizeNewArena(&new_decl_arena);
- return sema.analyzeDeclVal(block, src, new_decl);
+ return sema.analyzeDeclVal(block, src, new_decl_index);
}
fn zirErrorSetDecl(
@@ -2123,13 +2637,14 @@ fn zirErrorSetDecl(
const error_set = try new_decl_arena_allocator.create(Module.ErrorSet);
const error_set_ty = try Type.Tag.error_set.create(new_decl_arena_allocator, error_set);
const error_set_val = try Value.Tag.ty.create(new_decl_arena_allocator, error_set_ty);
- const type_name = try sema.createTypeName(block, name_strategy);
- const new_decl = try sema.mod.createAnonymousDeclNamed(block, .{
+ const mod = sema.mod;
+ const new_decl_index = try sema.createAnonymousDeclTypeNamed(block, .{
.ty = Type.type,
.val = error_set_val,
- }, type_name);
+ }, name_strategy, "error", inst);
+ const new_decl = mod.declPtr(new_decl_index);
new_decl.owns_tv = true;
- errdefer sema.mod.abortAnonDecl(new_decl);
+ errdefer mod.abortAnonDecl(new_decl_index);
var names = Module.ErrorSet.NameMap{};
try names.ensureUnusedCapacity(new_decl_arena_allocator, extra.data.fields_len);
@@ -2138,42 +2653,40 @@ fn zirErrorSetDecl(
const extra_index_end = extra_index + (extra.data.fields_len * 2);
while (extra_index < extra_index_end) : (extra_index += 2) { // +2 to skip over doc_string
const str_index = sema.code.extra[extra_index];
- const name = try new_decl_arena_allocator.dupe(u8, sema.code.nullTerminatedString(str_index));
-
- // TODO: This check should be performed in AstGen instead.
- const result = names.getOrPutAssumeCapacity(name);
- if (result.found_existing) {
- return sema.fail(block, src, "duplicate error set field {s}", .{name});
- }
+ const kv = try mod.getErrorValue(sema.code.nullTerminatedString(str_index));
+ const result = names.getOrPutAssumeCapacity(kv.key);
+ assert(!result.found_existing); // verified in AstGen
}
+
+ // names must be sorted.
+ Module.ErrorSet.sortNames(&names);
+
error_set.* = .{
- .owner_decl = new_decl,
+ .owner_decl = new_decl_index,
.node_offset = inst_data.src_node,
.names = names,
};
try new_decl.finalizeNewArena(&new_decl_arena);
- return sema.analyzeDeclVal(block, src, new_decl);
+ return sema.analyzeDeclVal(block, src, new_decl_index);
}
-fn zirRetPtr(
- sema: *Sema,
- block: *Block,
- extended: Zir.Inst.Extended.InstData,
-) CompileError!Air.Inst.Ref {
+fn zirRetPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
const tracy = trace(@src());
defer tracy.end();
- const src: LazySrcLoc = .{ .node_offset = @bitCast(i32, extended.operand) };
+ const inst_data = sema.code.instructions.items(.data)[inst].node;
+ const src = LazySrcLoc.nodeOffset(inst_data);
try sema.requireFunctionBlock(block, src);
- if (block.is_comptime) {
+ if (block.is_comptime or try sema.typeRequiresComptime(block, src, sema.fn_ret_ty)) {
const fn_ret_ty = try sema.resolveTypeFields(block, src, sema.fn_ret_ty);
return sema.analyzeComptimeAlloc(block, fn_ret_ty, 0, src);
}
- const ptr_type = try Type.ptr(sema.arena, .{
+ const target = sema.mod.getTarget();
+ const ptr_type = try Type.ptr(sema.arena, sema.mod, .{
.pointee_type = sema.fn_ret_ty,
- .@"addrspace" = target_util.defaultAddressSpace(sema.mod.getTarget(), .local),
+ .@"addrspace" = target_util.defaultAddressSpace(target, .local),
});
if (block.inlining != null) {
@@ -2191,19 +2704,16 @@ fn zirRef(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Ins
defer tracy.end();
const inst_data = sema.code.instructions.items(.data)[inst].un_tok;
- const operand = sema.resolveInst(inst_data.operand);
+ const operand = try sema.resolveInst(inst_data.operand);
return sema.analyzeRef(block, inst_data.src(), operand);
}
-fn zirRetType(
- sema: *Sema,
- block: *Block,
- extended: Zir.Inst.Extended.InstData,
-) CompileError!Air.Inst.Ref {
+fn zirRetType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
const tracy = trace(@src());
defer tracy.end();
- const src: LazySrcLoc = .{ .node_offset = @bitCast(i32, extended.operand) };
+ const inst_data = sema.code.instructions.items(.data)[inst].node;
+ const src = LazySrcLoc.nodeOffset(inst_data);
try sema.requireFunctionBlock(block, src);
return sema.addType(sema.fn_ret_ty);
}
@@ -2213,7 +2723,7 @@ fn zirEnsureResultUsed(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Compile
defer tracy.end();
const inst_data = sema.code.instructions.items(.data)[inst].un_node;
- const operand = sema.resolveInst(inst_data.operand);
+ const operand = try sema.resolveInst(inst_data.operand);
const src = inst_data.src();
return sema.ensureResultUsed(block, operand, src);
@@ -2228,6 +2738,7 @@ fn ensureResultUsed(
const operand_ty = sema.typeOf(operand);
switch (operand_ty.zigTypeTag()) {
.Void, .NoReturn => return,
+ .ErrorSet, .ErrorUnion => return sema.fail(block, src, "error is ignored. consider using `try`, `catch`, or `if`", .{}),
else => return sema.fail(block, src, "expression value is ignored", .{}),
}
}
@@ -2237,11 +2748,11 @@ fn zirEnsureResultNonError(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Com
defer tracy.end();
const inst_data = sema.code.instructions.items(.data)[inst].un_node;
- const operand = sema.resolveInst(inst_data.operand);
+ const operand = try sema.resolveInst(inst_data.operand);
const src = inst_data.src();
const operand_ty = sema.typeOf(operand);
switch (operand_ty.zigTypeTag()) {
- .ErrorSet, .ErrorUnion => return sema.fail(block, src, "error is discarded", .{}),
+ .ErrorSet, .ErrorUnion => return sema.fail(block, src, "error is discarded. consider using `try`, `catch`, or `if`", .{}),
else => return,
}
}
@@ -2252,7 +2763,7 @@ fn zirIndexablePtrLen(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileE
const inst_data = sema.code.instructions.items(.data)[inst].un_node;
const src = inst_data.src();
- const object = sema.resolveInst(inst_data.operand);
+ const object = try sema.resolveInst(inst_data.operand);
const object_ty = sema.typeOf(object);
const is_pointer_to = object_ty.isSinglePointer();
@@ -2268,7 +2779,7 @@ fn zirIndexablePtrLen(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileE
block,
src,
"type '{}' does not support indexing",
- .{array_ty},
+ .{array_ty.fmt(sema.mod)},
);
errdefer msg.destroy(sema.gpa);
try sema.errNote(
@@ -2280,7 +2791,7 @@ fn zirIndexablePtrLen(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileE
);
break :msg msg;
};
- return sema.failWithOwnedErrorMsg(msg);
+ return sema.failWithOwnedErrorMsg(block, msg);
}
return sema.fieldVal(block, src, object, "len", src);
@@ -2292,7 +2803,7 @@ fn zirAllocExtended(
extended: Zir.Inst.Extended.InstData,
) CompileError!Air.Inst.Ref {
const extra = sema.code.extraData(Zir.Inst.AllocExtended, extended.operand);
- const src: LazySrcLoc = .{ .node_offset = extra.data.src_node };
+ const src = LazySrcLoc.nodeOffset(extra.data.src_node);
const ty_src = src; // TODO better source location
const align_src = src; // TODO better source location
const small = @bitCast(Zir.Inst.AllocExtended.Small, extended.small);
@@ -2305,7 +2816,7 @@ fn zirAllocExtended(
break :blk try sema.resolveType(block, ty_src, type_ref);
} else undefined;
- const alignment: u16 = if (small.has_align) blk: {
+ const alignment: u32 = if (small.has_align) blk: {
const align_ref = @intToEnum(Zir.Inst.Ref, sema.code.extra[extra_index]);
extra_index += 1;
const alignment = try sema.resolveAlign(block, align_src, align_ref);
@@ -2317,14 +2828,14 @@ fn zirAllocExtended(
else
Type.initTag(.inferred_alloc_mut);
- if (small.is_comptime) {
+ if (block.is_comptime or small.is_comptime) {
if (small.has_type) {
return sema.analyzeComptimeAlloc(block, var_ty, alignment, ty_src);
} else {
return sema.addConstant(
inferred_alloc_ty,
try Value.Tag.inferred_alloc_comptime.create(sema.arena, .{
- .decl = undefined,
+ .decl_index = undefined,
.alignment = alignment,
}),
);
@@ -2335,13 +2846,14 @@ fn zirAllocExtended(
if (!small.is_const) {
try sema.validateVarType(block, ty_src, var_ty, false);
}
- const ptr_type = try Type.ptr(sema.arena, .{
+ const target = sema.mod.getTarget();
+ try sema.requireRuntimeBlock(block, src);
+ try sema.resolveTypeLayout(block, src, var_ty);
+ const ptr_type = try Type.ptr(sema.arena, sema.mod, .{
.pointee_type = var_ty,
.@"align" = alignment,
- .@"addrspace" = target_util.defaultAddressSpace(sema.mod.getTarget(), .local),
+ .@"addrspace" = target_util.defaultAddressSpace(target, .local),
});
- try sema.requireRuntimeBlock(block, src);
- try sema.resolveTypeLayout(block, src, var_ty);
return block.addTy(.alloc, ptr_type);
}
@@ -2368,13 +2880,90 @@ 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 zirMakePtrConst(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
+ const inst_data = sema.code.instructions.items(.data)[inst].un_node;
+ const src = inst_data.src();
+ const alloc = try sema.resolveInst(inst_data.operand);
+ const alloc_ty = sema.typeOf(alloc);
+
+ var ptr_info = alloc_ty.ptrInfo().data;
+ const elem_ty = ptr_info.pointee_type;
+
+ // Detect if all stores to an `.alloc` were comptime known.
+ ct: {
+ var search_index: usize = block.instructions.items.len;
+ const air_tags = sema.air_instructions.items(.tag);
+ const air_datas = sema.air_instructions.items(.data);
+
+ const store_inst = while (true) {
+ if (search_index == 0) break :ct;
+ search_index -= 1;
+
+ const candidate = block.instructions.items[search_index];
+ switch (air_tags[candidate]) {
+ .dbg_stmt => continue,
+ .store => break candidate,
+ else => break :ct,
+ }
+ } else unreachable; // TODO shouldn't need this
+
+ while (true) {
+ if (search_index == 0) break :ct;
+ search_index -= 1;
+
+ const candidate = block.instructions.items[search_index];
+ switch (air_tags[candidate]) {
+ .dbg_stmt => continue,
+ .alloc => {
+ if (Air.indexToRef(candidate) != alloc) break :ct;
+ break;
+ },
+ else => break :ct,
+ }
+ }
+
+ const store_op = air_datas[store_inst].bin_op;
+ const store_val = (try sema.resolveMaybeUndefVal(block, src, store_op.rhs)) orelse break :ct;
+ if (store_op.lhs != alloc) break :ct;
+
+ // Remove all the unnecessary runtime instructions.
+ block.instructions.shrinkRetainingCapacity(search_index);
+
+ var anon_decl = try block.startAnonDecl(src);
+ defer anon_decl.deinit();
+ return sema.analyzeDeclRef(try anon_decl.finish(
+ try elem_ty.copy(anon_decl.arena()),
+ try store_val.copy(anon_decl.arena()),
+ ptr_info.@"align",
+ ));
+ }
+
+ ptr_info.mutable = false;
+ const const_ptr_ty = try Type.ptr(sema.arena, sema.mod, ptr_info);
+
+ // Detect if a comptime value simply needs to have its type changed.
+ if (try sema.resolveMaybeUndefVal(block, inst_data.src(), alloc)) |val| {
+ return sema.addConstant(const_ptr_ty, val);
+ }
+
+ try sema.requireRuntimeBlock(block, src);
+ return block.addBitCast(const_ptr_ty, alloc);
+}
+
+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 };
+ const src = LazySrcLoc.nodeOffset(src_node);
sema.src = src;
return sema.addConstant(
- Type.initTag(.inferred_alloc_mut),
- try Value.Tag.inferred_alloc_comptime.create(sema.arena, undefined),
+ inferred_alloc_ty,
+ try Value.Tag.inferred_alloc_comptime.create(sema.arena, .{
+ .decl_index = undefined,
+ .alignment = 0,
+ }),
);
}
@@ -2389,12 +2978,13 @@ fn zirAlloc(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I
if (block.is_comptime) {
return sema.analyzeComptimeAlloc(block, var_ty, 0, ty_src);
}
- const ptr_type = try Type.ptr(sema.arena, .{
+ const target = sema.mod.getTarget();
+ const ptr_type = try Type.ptr(sema.arena, sema.mod, .{
.pointee_type = var_ty,
- .@"addrspace" = target_util.defaultAddressSpace(sema.mod.getTarget(), .local),
+ .@"addrspace" = target_util.defaultAddressSpace(target, .local),
});
try sema.requireRuntimeBlock(block, var_decl_src);
- try sema.resolveTypeLayout(block, ty_src, var_ty);
+ try sema.queueFullTypeResolution(var_ty);
return block.addTy(.alloc, ptr_type);
}
@@ -2410,12 +3000,13 @@ fn zirAllocMut(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
return sema.analyzeComptimeAlloc(block, var_ty, 0, ty_src);
}
try sema.validateVarType(block, ty_src, var_ty, false);
- const ptr_type = try Type.ptr(sema.arena, .{
+ const target = sema.mod.getTarget();
+ const ptr_type = try Type.ptr(sema.arena, sema.mod, .{
.pointee_type = var_ty,
- .@"addrspace" = target_util.defaultAddressSpace(sema.mod.getTarget(), .local),
+ .@"addrspace" = target_util.defaultAddressSpace(target, .local),
});
try sema.requireRuntimeBlock(block, var_decl_src);
- try sema.resolveTypeLayout(block, ty_src, var_ty);
+ try sema.queueFullTypeResolution(var_ty);
return block.addTy(.alloc, ptr_type);
}
@@ -2429,13 +3020,16 @@ fn zirAllocInferred(
defer tracy.end();
const src_node = sema.code.instructions.items(.data)[inst].node;
- const src: LazySrcLoc = .{ .node_offset = src_node };
+ const src = LazySrcLoc.nodeOffset(src_node);
sema.src = src;
if (block.is_comptime) {
return sema.addConstant(
inferred_alloc_ty,
- try Value.Tag.inferred_alloc_comptime.create(sema.arena, undefined),
+ try Value.Tag.inferred_alloc_comptime.create(sema.arena, .{
+ .decl_index = undefined,
+ .alignment = 0,
+ }),
);
}
@@ -2459,7 +3053,7 @@ fn zirResolveInferredAlloc(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Com
const inst_data = sema.code.instructions.items(.data)[inst].un_node;
const src = inst_data.src();
const ty_src: LazySrcLoc = .{ .node_offset_var_decl_ty = inst_data.src_node };
- const ptr = sema.resolveInst(inst_data.operand);
+ const ptr = try sema.resolveInst(inst_data.operand);
const ptr_inst = Air.refToIndex(ptr).?;
assert(sema.air_instructions.items(.tag)[ptr_inst] == .constant);
const value_index = sema.air_instructions.items(.data)[ptr_inst].ty_pl.payload;
@@ -2474,12 +3068,14 @@ fn zirResolveInferredAlloc(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Com
switch (ptr_val.tag()) {
.inferred_alloc_comptime => {
const iac = ptr_val.castTag(.inferred_alloc_comptime).?;
- const decl = iac.data.decl;
- try sema.mod.declareDeclDependency(sema.owner_decl, decl);
+ const decl_index = iac.data.decl_index;
+ try sema.mod.declareDeclDependency(sema.owner_decl_index, decl_index);
+ const decl = sema.mod.declPtr(decl_index);
const final_elem_ty = try decl.ty.copy(sema.arena);
- const final_ptr_ty = try Type.ptr(sema.arena, .{
+ const final_ptr_ty = try Type.ptr(sema.arena, sema.mod, .{
.pointee_type = final_elem_ty,
+ .mutable = var_is_mut,
.@"align" = iac.data.alignment,
.@"addrspace" = target_util.defaultAddressSpace(target, .local),
});
@@ -2488,11 +3084,11 @@ fn zirResolveInferredAlloc(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Com
if (var_is_mut) {
sema.air_values.items[value_index] = try Value.Tag.decl_ref_mut.create(sema.arena, .{
- .decl = decl,
+ .decl_index = decl_index,
.runtime_index = block.runtime_index,
});
} else {
- sema.air_values.items[value_index] = try Value.Tag.decl_ref.create(sema.arena, decl);
+ sema.air_values.items[value_index] = try Value.Tag.decl_ref.create(sema.arena, decl_index);
}
},
.inferred_alloc => {
@@ -2500,8 +3096,12 @@ 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, sema.mod, .{
+ .pointee_type = final_elem_ty,
+ .mutable = var_is_mut,
+ .@"align" = inferred_alloc.data.alignment,
+ .@"addrspace" = target_util.defaultAddressSpace(target, .local),
+ });
if (var_is_mut) {
try sema.validateVarType(block, ty_src, final_elem_ty, false);
@@ -2518,50 +3118,86 @@ fn zirResolveInferredAlloc(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Com
// instructions from the block, replacing the inst_map entry
// corresponding to the ZIR alloc instruction with a constant
// decl_ref pointing at our new Decl.
+ // dbg_stmt instructions may be interspersed into this pattern
+ // which must be ignored.
if (block.instructions.items.len < 3) break :ct;
- // zig fmt: off
- const const_inst = block.instructions.items[block.instructions.items.len - 3];
- const bitcast_inst = block.instructions.items[block.instructions.items.len - 2];
- const store_inst = block.instructions.items[block.instructions.items.len - 1];
- const air_tags = sema.air_instructions.items(.tag);
+ var search_index: usize = block.instructions.items.len;
+ const air_tags = sema.air_instructions.items(.tag);
const air_datas = sema.air_instructions.items(.data);
- if (air_tags[const_inst] != .constant) break :ct;
- if (air_tags[bitcast_inst] != .bitcast ) break :ct;
- if (air_tags[store_inst] != .store ) break :ct;
- // zig fmt: on
+
+ const store_inst = while (true) {
+ if (search_index == 0) break :ct;
+ search_index -= 1;
+
+ const candidate = block.instructions.items[search_index];
+ switch (air_tags[candidate]) {
+ .dbg_stmt => continue,
+ .store => break candidate,
+ else => break :ct,
+ }
+ } else unreachable; // TODO shouldn't need this
+
+ const bitcast_inst = while (true) {
+ if (search_index == 0) break :ct;
+ search_index -= 1;
+
+ const candidate = block.instructions.items[search_index];
+ switch (air_tags[candidate]) {
+ .dbg_stmt => continue,
+ .bitcast => break candidate,
+ else => break :ct,
+ }
+ } else unreachable; // TODO shouldn't need this
+
+ const const_inst = while (true) {
+ if (search_index == 0) break :ct;
+ search_index -= 1;
+
+ const candidate = block.instructions.items[search_index];
+ switch (air_tags[candidate]) {
+ .dbg_stmt => continue,
+ .constant => break candidate,
+ else => break :ct,
+ }
+ } else unreachable; // TODO shouldn't need this
+
const store_op = air_datas[store_inst].bin_op;
const store_val = (try sema.resolveMaybeUndefVal(block, src, store_op.rhs)) orelse break :ct;
if (store_op.lhs != Air.indexToRef(bitcast_inst)) break :ct;
if (air_datas[bitcast_inst].ty_op.operand != Air.indexToRef(const_inst)) break :ct;
- const bitcast_ty_ref = air_datas[bitcast_inst].ty_op.ty;
-
- const new_decl = d: {
+ const new_decl_index = d: {
var anon_decl = try block.startAnonDecl(src);
defer anon_decl.deinit();
- const new_decl = try anon_decl.finish(
+ const new_decl_index = try anon_decl.finish(
try final_elem_ty.copy(anon_decl.arena()),
try store_val.copy(anon_decl.arena()),
+ inferred_alloc.data.alignment,
);
- break :d new_decl;
+ break :d new_decl_index;
};
- try sema.mod.declareDeclDependency(sema.owner_decl, new_decl);
+ try sema.mod.declareDeclDependency(sema.owner_decl_index, new_decl_index);
// Even though we reuse the constant instruction, we still remove it from the
// block so that codegen does not see it.
- block.instructions.shrinkRetainingCapacity(block.instructions.items.len - 3);
- sema.air_values.items[value_index] = try Value.Tag.decl_ref.create(sema.arena, new_decl);
- air_datas[ptr_inst].ty_pl.ty = bitcast_ty_ref;
+ block.instructions.shrinkRetainingCapacity(search_index);
+ sema.air_values.items[value_index] = try Value.Tag.decl_ref.create(sema.arena, new_decl_index);
+ // if bitcast ty ref needs to be made const, make_ptr_const
+ // ZIR handles it later, so we can just use the ty ref here.
+ air_datas[ptr_inst].ty_pl.ty = air_datas[bitcast_inst].ty_op.ty;
+
+ // Unless the block is comptime, `alloc_inferred` always produces
+ // a runtime constant. The final inferred type needs to be
+ // fully resolved so it can be lowered in codegen.
+ try sema.resolveTypeFully(block, ty_src, final_elem_ty);
return;
}
+ try sema.requireRuntimeBlock(block, src);
+ try sema.queueFullTypeResolution(final_elem_ty);
+
// Change it to a normal alloc.
- const final_ptr_ty = try Type.ptr(sema.arena, .{
- .pointee_type = final_elem_ty,
- .@"align" = inferred_alloc.data.alignment,
- .@"addrspace" = target_util.defaultAddressSpace(target, .local),
- });
sema.air_instructions.set(ptr_inst, .{
.tag = .alloc,
.data = .{ .ty = final_ptr_ty },
@@ -2571,6 +3207,94 @@ fn zirResolveInferredAlloc(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Com
}
}
+fn zirArrayBasePtr(
+ sema: *Sema,
+ block: *Block,
+ inst: Zir.Inst.Index,
+) CompileError!Air.Inst.Ref {
+ const inst_data = sema.code.instructions.items(.data)[inst].un_node;
+ const src = inst_data.src();
+
+ const start_ptr = try sema.resolveInst(inst_data.operand);
+ var base_ptr = start_ptr;
+ while (true) switch (sema.typeOf(base_ptr).childType().zigTypeTag()) {
+ .ErrorUnion => base_ptr = try sema.analyzeErrUnionPayloadPtr(block, src, base_ptr, false, true),
+ .Optional => base_ptr = try sema.analyzeOptionalPayloadPtr(block, src, base_ptr, false, true),
+ else => break,
+ };
+
+ const elem_ty = sema.typeOf(base_ptr).childType();
+ switch (elem_ty.zigTypeTag()) {
+ .Array, .Vector => return base_ptr,
+ .Struct => if (elem_ty.isTuple()) {
+ // TODO validate element count
+ return base_ptr;
+ },
+ else => {},
+ }
+ return sema.failWithArrayInitNotSupported(block, src, sema.typeOf(start_ptr).childType());
+}
+
+fn zirFieldBasePtr(
+ sema: *Sema,
+ block: *Block,
+ inst: Zir.Inst.Index,
+) CompileError!Air.Inst.Ref {
+ const inst_data = sema.code.instructions.items(.data)[inst].un_node;
+ const src = inst_data.src();
+
+ const start_ptr = try sema.resolveInst(inst_data.operand);
+ var base_ptr = start_ptr;
+ while (true) switch (sema.typeOf(base_ptr).childType().zigTypeTag()) {
+ .ErrorUnion => base_ptr = try sema.analyzeErrUnionPayloadPtr(block, src, base_ptr, false, true),
+ .Optional => base_ptr = try sema.analyzeOptionalPayloadPtr(block, src, base_ptr, false, true),
+ else => break,
+ };
+
+ const elem_ty = sema.typeOf(base_ptr).childType();
+ switch (elem_ty.zigTypeTag()) {
+ .Struct, .Union => return base_ptr,
+ else => {},
+ }
+ return sema.failWithStructInitNotSupported(block, src, sema.typeOf(start_ptr).childType());
+}
+
+fn validateArrayInitTy(
+ sema: *Sema,
+ block: *Block,
+ inst: Zir.Inst.Index,
+) CompileError!void {
+ const inst_data = sema.code.instructions.items(.data)[inst].un_node;
+ const src = inst_data.src();
+ const ty = try sema.resolveType(block, src, inst_data.operand);
+
+ switch (ty.zigTypeTag()) {
+ .Array, .Vector => return,
+ .Struct => if (ty.isTuple()) {
+ // TODO validate element count
+ return;
+ },
+ else => {},
+ }
+ return sema.failWithArrayInitNotSupported(block, src, ty);
+}
+
+fn validateStructInitTy(
+ sema: *Sema,
+ block: *Block,
+ inst: Zir.Inst.Index,
+) CompileError!void {
+ const inst_data = sema.code.instructions.items(.data)[inst].un_node;
+ const src = inst_data.src();
+ const ty = try sema.resolveType(block, src, inst_data.operand);
+
+ switch (ty.zigTypeTag()) {
+ .Struct, .Union => return,
+ else => {},
+ }
+ return sema.failWithStructInitNotSupported(block, src, ty);
+}
+
fn zirValidateStructInit(
sema: *Sema,
block: *Block,
@@ -2586,7 +3310,7 @@ fn zirValidateStructInit(
const instrs = sema.code.extra[validate_extra.end..][0..validate_extra.data.body_len];
const field_ptr_data = sema.code.instructions.items(.data)[instrs[0]].pl_node;
const field_ptr_extra = sema.code.extraData(Zir.Inst.Field, field_ptr_data.payload_index).data;
- const object_ptr = sema.resolveInst(field_ptr_extra.lhs);
+ const object_ptr = try sema.resolveInst(field_ptr_extra.lhs);
const agg_ty = sema.typeOf(object_ptr).childType();
switch (agg_ty.zigTypeTag()) {
.Struct => return sema.validateStructInit(
@@ -2598,10 +3322,11 @@ fn zirValidateStructInit(
),
.Union => return sema.validateUnionInit(
block,
- agg_ty.cast(Type.Payload.Union).?.data,
+ agg_ty,
init_src,
instrs,
object_ptr,
+ is_comptime,
),
else => unreachable,
}
@@ -2610,15 +3335,40 @@ fn zirValidateStructInit(
fn validateUnionInit(
sema: *Sema,
block: *Block,
- union_obj: *Module.Union,
+ union_ty: Type,
init_src: LazySrcLoc,
instrs: []const Zir.Inst.Index,
union_ptr: Air.Inst.Ref,
+ is_comptime: bool,
) CompileError!void {
+ const union_obj = union_ty.cast(Type.Payload.Union).?.data;
+
if (instrs.len != 1) {
- // TODO add note for other field
- // TODO add note for union declared here
- return sema.fail(block, init_src, "only one union field can be active at once", .{});
+ const msg = msg: {
+ const msg = try sema.errMsg(
+ block,
+ init_src,
+ "cannot initialize multiple union fields at once, unions can only have one active field",
+ .{},
+ );
+ errdefer msg.destroy(sema.gpa);
+
+ for (instrs[1..]) |inst| {
+ const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
+ const inst_src: LazySrcLoc = .{ .node_offset_back2tok = inst_data.src_node };
+ try sema.errNote(block, inst_src, msg, "additional initializer here", .{});
+ }
+ try sema.addDeclaredHereNote(msg, union_ty);
+ break :msg msg;
+ };
+ return sema.failWithOwnedErrorMsg(block, msg);
+ }
+
+ if ((is_comptime or block.is_comptime) and
+ (try sema.resolveDefinedValue(block, init_src, union_ptr)) != null)
+ {
+ // In this case, comptime machinery already did everything. No work to do here.
+ return;
}
const field_ptr = instrs[0];
@@ -2626,40 +3376,83 @@ fn validateUnionInit(
const field_src: LazySrcLoc = .{ .node_offset_back2tok = field_ptr_data.src_node };
const field_ptr_extra = sema.code.extraData(Zir.Inst.Field, field_ptr_data.payload_index).data;
const field_name = sema.code.nullTerminatedString(field_ptr_extra.field_name_start);
- const field_index_big = union_obj.fields.getIndex(field_name) orelse
- return sema.failWithBadUnionFieldAccess(block, union_obj, field_src, field_name);
- const field_index = @intCast(u32, field_index_big);
-
- // Handle the possibility of the union value being comptime-known.
- const union_ptr_inst = Air.refToIndex(sema.resolveInst(field_ptr_extra.lhs)).?;
- switch (sema.air_instructions.items(.tag)[union_ptr_inst]) {
- .constant => return, // In this case the tag has already been set. No validation to do.
- .bitcast => {
- // TODO here we need to go back and see if we need to convert the union
- // to a comptime-known value. In such case, we must delete all the instructions
- // added to the current block starting with the bitcast.
- // If the bitcast result ptr is an alloc, the alloc should be replaced with
- // a constant decl_ref.
- // Otherwise, the bitcast should be preserved and a store instruction should be
- // emitted to store the constant union value through the bitcast.
- },
- .alloc => {},
- else => |t| {
- if (std.debug.runtime_safety) {
- std.debug.panic("unexpected AIR tag for union pointer: {s}", .{@tagName(t)});
- } else {
- unreachable;
+ const field_index = try sema.unionFieldIndex(block, union_ty, field_name, field_src);
+ const air_tags = sema.air_instructions.items(.tag);
+ const air_datas = sema.air_instructions.items(.data);
+ const field_ptr_air_ref = sema.inst_map.get(field_ptr).?;
+ const field_ptr_air_inst = Air.refToIndex(field_ptr_air_ref).?;
+
+ // Our task here is to determine if the union is comptime-known. In such case,
+ // we erase the runtime AIR instructions for initializing the union, and replace
+ // the mapping with the comptime value. Either way, we will need to populate the tag.
+
+ // We expect to see something like this in the current block AIR:
+ // %a = alloc(*const U)
+ // %b = bitcast(*U, %a)
+ // %c = field_ptr(..., %b)
+ // %e!= store(%c!, %d!)
+ // If %d is a comptime operand, the union is comptime.
+ // If the union is comptime, we want `first_block_index`
+ // to point at %c so that the bitcast becomes the last instruction in the block.
+ //
+ // In the case of a comptime-known pointer to a union, the
+ // the field_ptr instruction is missing, so we have to pattern-match
+ // based only on the store instructions.
+ // `first_block_index` needs to point to the `field_ptr` if it exists;
+ // the `store` otherwise.
+ //
+ // It's also possible for there to be no store instruction, in the case
+ // of nested `coerce_result_ptr` instructions. If we see the `field_ptr`
+ // but we have not found a `store`, treat as a runtime-known field.
+ var first_block_index = block.instructions.items.len;
+ var block_index = block.instructions.items.len - 1;
+ var init_val: ?Value = null;
+ while (block_index > 0) : (block_index -= 1) {
+ const store_inst = block.instructions.items[block_index];
+ if (store_inst == field_ptr_air_inst) break;
+ if (air_tags[store_inst] != .store) continue;
+ const bin_op = air_datas[store_inst].bin_op;
+ var lhs = bin_op.lhs;
+ if (Air.refToIndex(lhs)) |lhs_index| {
+ if (air_tags[lhs_index] == .bitcast) {
+ lhs = air_datas[lhs_index].ty_op.operand;
+ block_index -= 1;
}
- },
+ }
+ if (lhs != field_ptr_air_ref) continue;
+ while (block_index > 0) : (block_index -= 1) {
+ const block_inst = block.instructions.items[block_index - 1];
+ if (air_tags[block_inst] != .dbg_stmt) break;
+ }
+ if (block_index > 0 and
+ field_ptr_air_inst == block.instructions.items[block_index - 1])
+ {
+ first_block_index = @minimum(first_block_index, block_index - 1);
+ } else {
+ first_block_index = @minimum(first_block_index, block_index);
+ }
+ init_val = try sema.resolveMaybeUndefValAllowVariables(block, init_src, bin_op.rhs);
+ break;
}
- // Otherwise, we set the new union tag now.
- const new_tag = try sema.addConstant(
- union_obj.tag_ty,
- try Value.Tag.enum_field_index.create(sema.arena, field_index),
- );
+ const tag_val = try Value.Tag.enum_field_index.create(sema.arena, field_index);
+
+ if (init_val) |val| {
+ // Our task is to delete all the `field_ptr` and `store` instructions, and insert
+ // instead a single `store` to the result ptr with a comptime union value.
+ block.instructions.shrinkRetainingCapacity(first_block_index);
+
+ const union_val = try Value.Tag.@"union".create(sema.arena, .{
+ .tag = tag_val,
+ .val = val,
+ });
+ const union_init = try sema.addConstant(union_ty, union_val);
+ try sema.storePtr2(block, init_src, union_ptr, init_src, union_init, init_src, .store);
+ return;
+ }
try sema.requireRuntimeBlock(block, init_src);
+ const new_tag = try sema.addConstant(union_obj.tag_ty, tag_val);
_ = try block.addBinOp(.set_union_tag, union_ptr, new_tag);
}
@@ -2698,7 +3491,7 @@ fn validateStructInit(
try sema.errNote(block, other_field_src, msg, "other field here", .{});
break :msg msg;
};
- return sema.failWithOwnedErrorMsg(msg);
+ return sema.failWithOwnedErrorMsg(block, msg);
}
found_fields[field_index] = field_ptr;
}
@@ -2706,10 +3499,12 @@ fn validateStructInit(
var root_msg: ?*Module.ErrorMsg = null;
const fields = struct_obj.fields.values();
- const struct_ptr = sema.resolveInst(struct_ptr_zir_ref);
+ const struct_ptr = try sema.resolveInst(struct_ptr_zir_ref);
const struct_ty = sema.typeOf(struct_ptr).childType();
- if (is_comptime or block.is_comptime) {
+ if ((is_comptime or block.is_comptime) and
+ (try sema.resolveDefinedValue(block, init_src, struct_ptr)) != null)
+ {
// In this case the only thing we need to do is evaluate the implicit
// store instructions for default field values, and report any missing fields.
// Avoid the cost of the extra machinery for detecting a comptime struct init value.
@@ -2737,22 +3532,23 @@ fn validateStructInit(
}
if (root_msg) |msg| {
- const fqn = try struct_obj.getFullyQualifiedName(gpa);
+ const mod = sema.mod;
+ const fqn = try struct_obj.getFullyQualifiedName(mod);
defer gpa.free(fqn);
- try sema.mod.errNoteNonLazy(
- struct_obj.srcLoc(),
+ try mod.errNoteNonLazy(
+ struct_obj.srcLoc(mod),
msg,
"struct '{s}' declared here",
.{fqn},
);
- return sema.failWithOwnedErrorMsg(msg);
+ return sema.failWithOwnedErrorMsg(block, msg);
}
return;
}
var struct_is_comptime = true;
- var first_block_index: usize = std.math.maxInt(u32);
+ var first_block_index = block.instructions.items.len;
const air_tags = sema.air_instructions.items(.tag);
const air_datas = sema.air_instructions.items(.data);
@@ -2761,7 +3557,7 @@ fn validateStructInit(
// ends up being comptime-known.
const field_values = try sema.arena.alloc(Value, fields.len);
- for (found_fields) |field_ptr, i| {
+ field: for (found_fields) |field_ptr, i| {
const field = fields[i];
if (field_ptr != 0) {
@@ -2776,40 +3572,69 @@ fn validateStructInit(
const field_ptr_air_ref = sema.inst_map.get(field_ptr).?;
const field_ptr_air_inst = Air.refToIndex(field_ptr_air_ref).?;
- // Find the block index of the field_ptr so that we can look at the next
- // instruction after it within the same block.
+
+ //std.debug.print("validateStructInit (field_ptr_air_inst=%{d}):\n", .{
+ // field_ptr_air_inst,
+ //});
+ //for (block.instructions.items) |item| {
+ // std.debug.print(" %{d} = {s}\n", .{item, @tagName(air_tags[item])});
+ //}
+
+ // We expect to see something like this in the current block AIR:
+ // %a = field_ptr(...)
+ // store(%a, %b)
+ // If %b is a comptime operand, this field is comptime.
+ //
+ // However, in the case of a comptime-known pointer to a struct, the
+ // the field_ptr instruction is missing, so we have to pattern-match
+ // based only on the store instructions.
+ // `first_block_index` needs to point to the `field_ptr` if it exists;
+ // the `store` otherwise.
+ //
+ // It's also possible for there to be no store instruction, in the case
+ // of nested `coerce_result_ptr` instructions. If we see the `field_ptr`
+ // but we have not found a `store`, treat as a runtime-known field.
+
// Possible performance enhancement: save the `block_index` between iterations
// of the for loop.
- const next_air_inst = inst: {
- var block_index = block.instructions.items.len - 1;
- while (block.instructions.items[block_index] != field_ptr_air_inst) {
- block_index -= 1;
+ var block_index = block.instructions.items.len - 1;
+ while (block_index > 0) : (block_index -= 1) {
+ const store_inst = block.instructions.items[block_index];
+ if (store_inst == field_ptr_air_inst) {
+ struct_is_comptime = false;
+ continue :field;
}
- first_block_index = @minimum(first_block_index, block_index);
- break :inst block.instructions.items[block_index + 1];
- };
-
- // If the next instructon is a store with a comptime operand, this field
- // is comptime.
- switch (air_tags[next_air_inst]) {
- .store => {
- const bin_op = air_datas[next_air_inst].bin_op;
- if (bin_op.lhs != field_ptr_air_ref) {
- struct_is_comptime = false;
- continue;
- }
- if (try sema.resolveMaybeUndefValAllowVariables(block, field_src, bin_op.rhs)) |val| {
- field_values[i] = val;
- } else {
- struct_is_comptime = false;
+ if (air_tags[store_inst] != .store) continue;
+ const bin_op = air_datas[store_inst].bin_op;
+ var lhs = bin_op.lhs;
+ {
+ const lhs_index = Air.refToIndex(lhs) orelse continue;
+ if (air_tags[lhs_index] == .bitcast) {
+ lhs = air_datas[lhs_index].ty_op.operand;
+ block_index -= 1;
}
- continue;
- },
- else => {
+ }
+ if (lhs != field_ptr_air_ref) continue;
+ while (block_index > 0) : (block_index -= 1) {
+ const block_inst = block.instructions.items[block_index - 1];
+ if (air_tags[block_inst] != .dbg_stmt) break;
+ }
+ if (block_index > 0 and
+ field_ptr_air_inst == block.instructions.items[block_index - 1])
+ {
+ first_block_index = @minimum(first_block_index, block_index - 1);
+ } else {
+ first_block_index = @minimum(first_block_index, block_index);
+ }
+ if (try sema.resolveMaybeUndefValAllowVariables(block, field_src, bin_op.rhs)) |val| {
+ field_values[i] = val;
+ } else {
struct_is_comptime = false;
- continue;
- },
+ }
+ continue :field;
}
+ struct_is_comptime = false;
+ continue :field;
}
const field_name = struct_obj.fields.keys()[i];
@@ -2827,15 +3652,15 @@ fn validateStructInit(
}
if (root_msg) |msg| {
- const fqn = try struct_obj.getFullyQualifiedName(gpa);
+ const fqn = try struct_obj.getFullyQualifiedName(sema.mod);
defer gpa.free(fqn);
try sema.mod.errNoteNonLazy(
- struct_obj.srcLoc(),
+ struct_obj.srcLoc(sema.mod),
msg,
"struct '{s}' declared here",
.{fqn},
);
- return sema.failWithOwnedErrorMsg(msg);
+ return sema.failWithOwnedErrorMsg(block, msg);
}
if (struct_is_comptime) {
@@ -2852,7 +3677,7 @@ fn validateStructInit(
field_values[i] = fields[i].default_val;
}
- const struct_val = try Value.Tag.@"struct".create(sema.arena, field_values);
+ const struct_val = try Value.Tag.aggregate.create(sema.arena, field_values);
const struct_init = try sema.addConstant(struct_ty, struct_val);
try sema.storePtr2(block, init_src, struct_ptr, init_src, struct_init, init_src, .store);
return;
@@ -2885,7 +3710,7 @@ fn zirValidateArrayInit(
const instrs = sema.code.extra[validate_extra.end..][0..validate_extra.data.body_len];
const first_elem_ptr_data = sema.code.instructions.items(.data)[instrs[0]].pl_node;
const elem_ptr_extra = sema.code.extraData(Zir.Inst.ElemPtrImm, first_elem_ptr_data.payload_index).data;
- const array_ptr = sema.resolveInst(elem_ptr_extra.ptr);
+ const array_ptr = try sema.resolveInst(elem_ptr_extra.ptr);
const array_ty = sema.typeOf(array_ptr).childType();
const array_len = array_ty.arrayLen();
@@ -2895,14 +3720,16 @@ fn zirValidateArrayInit(
});
}
- if (is_comptime or block.is_comptime) {
+ if ((is_comptime or block.is_comptime) and
+ (try sema.resolveDefinedValue(block, init_src, array_ptr)) != null)
+ {
// In this case the comptime machinery will have evaluated the store instructions
// at comptime so we have almost nothing to do here. However, in case of a
// sentinel-terminated array, the sentinel will not have been populated by
// any ZIR instructions at comptime; we need to do that here.
if (array_ty.sentinel()) |sentinel_val| {
const array_len_ref = try sema.addIntUnsigned(Type.usize, array_len);
- const sentinel_ptr = try sema.elemPtrArray(block, init_src, array_ptr, array_len_ref, init_src);
+ const sentinel_ptr = try sema.elemPtrArray(block, init_src, array_ptr, init_src, array_len_ref, true);
const sentinel = try sema.addConstant(array_ty.childType(), sentinel_val);
try sema.storePtr2(block, init_src, sentinel_ptr, init_src, sentinel, init_src, .store);
}
@@ -2910,7 +3737,7 @@ fn zirValidateArrayInit(
}
var array_is_comptime = true;
- var first_block_index: usize = std.math.maxInt(u32);
+ var first_block_index = block.instructions.items.len;
// Collect the comptime element values in case the array literal ends up
// being comptime-known.
@@ -2920,38 +3747,74 @@ fn zirValidateArrayInit(
const air_tags = sema.air_instructions.items(.tag);
const air_datas = sema.air_instructions.items(.data);
- for (instrs) |elem_ptr, i| {
+ outer: for (instrs) |elem_ptr, i| {
const elem_ptr_data = sema.code.instructions.items(.data)[elem_ptr].pl_node;
- const elem_src: LazySrcLoc = .{ .node_offset = elem_ptr_data.src_node };
+ const elem_src = LazySrcLoc.nodeOffset(elem_ptr_data.src_node);
// Determine whether the value stored to this pointer is comptime-known.
- if (opt_opv) |opv| {
- element_vals[i] = opv;
- continue;
- }
-
const elem_ptr_air_ref = sema.inst_map.get(elem_ptr).?;
const elem_ptr_air_inst = Air.refToIndex(elem_ptr_air_ref).?;
// Find the block index of the elem_ptr so that we can look at the next
// instruction after it within the same block.
// Possible performance enhancement: save the `block_index` between iterations
// of the for loop.
- const next_air_inst = inst: {
- var block_index = block.instructions.items.len - 1;
- while (block.instructions.items[block_index] != elem_ptr_air_inst) {
- block_index -= 1;
+ var block_index = block.instructions.items.len - 1;
+ while (block.instructions.items[block_index] != elem_ptr_air_inst) {
+ if (block_index == 0) {
+ array_is_comptime = true;
+ continue :outer;
}
- first_block_index = @minimum(first_block_index, block_index);
- break :inst block.instructions.items[block_index + 1];
- };
+ block_index -= 1;
+ }
+ first_block_index = @minimum(first_block_index, block_index);
+
+ // Array has one possible value, so value is always comptime-known
+ if (opt_opv) |opv| {
+ element_vals[i] = opv;
+ continue;
+ }
// If the next instructon is a store with a comptime operand, this element
// is comptime.
+ const next_air_inst = block.instructions.items[block_index + 1];
switch (air_tags[next_air_inst]) {
.store => {
const bin_op = air_datas[next_air_inst].bin_op;
- if (bin_op.lhs != elem_ptr_air_ref) {
+ var lhs = bin_op.lhs;
+ if (Air.refToIndex(lhs)) |lhs_index| {
+ if (air_tags[lhs_index] == .bitcast) {
+ lhs = air_datas[lhs_index].ty_op.operand;
+ block_index -= 1;
+ }
+ }
+ if (lhs != elem_ptr_air_ref) {
+ array_is_comptime = false;
+ continue;
+ }
+ if (try sema.resolveMaybeUndefValAllowVariables(block, elem_src, bin_op.rhs)) |val| {
+ element_vals[i] = val;
+ } else {
+ array_is_comptime = false;
+ }
+ continue;
+ },
+ .bitcast => {
+ // %a = bitcast(*arr_ty, %array_base)
+ // %b = ptr_elem_ptr(%a, %index)
+ // %c = bitcast(*elem_ty, %b)
+ // %d = store(%c, %val)
+ if (air_datas[next_air_inst].ty_op.operand != elem_ptr_air_ref) {
+ array_is_comptime = false;
+ continue;
+ }
+ const store_inst = block.instructions.items[block_index + 2];
+ if (air_tags[store_inst] != .store) {
+ array_is_comptime = false;
+ continue;
+ }
+ const bin_op = air_datas[store_inst].bin_op;
+ if (bin_op.lhs != Air.indexToRef(next_air_inst)) {
array_is_comptime = false;
continue;
}
@@ -2970,6 +3833,13 @@ fn zirValidateArrayInit(
}
if (array_is_comptime) {
+ if (try sema.resolveDefinedValue(block, init_src, array_ptr)) |ptr_val| {
+ if (ptr_val.tag() == .comptime_field_ptr) {
+ // This store was validated by the individual elem ptrs.
+ return;
+ }
+ }
+
// Our task is to delete all the `elem_ptr` and `store` instructions, and insert
// instead a single `store` to the array_ptr with a comptime struct value.
// Also to populate the sentinel value, if any.
@@ -2979,12 +3849,34 @@ fn zirValidateArrayInit(
block.instructions.shrinkRetainingCapacity(first_block_index);
- const array_val = try Value.Tag.array.create(sema.arena, element_vals);
+ const array_val = try Value.Tag.aggregate.create(sema.arena, element_vals);
const array_init = try sema.addConstant(array_ty, array_val);
try sema.storePtr2(block, init_src, array_ptr, init_src, array_init, init_src, .store);
}
}
+fn zirValidateDeref(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void {
+ const inst_data = sema.code.instructions.items(.data)[inst].un_tok;
+ const src = inst_data.src();
+ const operand_src: LazySrcLoc = .{ .token_offset = inst_data.src_tok + 1 };
+ const operand = try sema.resolveInst(inst_data.operand);
+ const operand_ty = sema.typeOf(operand);
+
+ if (operand_ty.zigTypeTag() != .Pointer) {
+ return sema.fail(block, src, "cannot dereference non-pointer type '{}'", .{operand_ty.fmt(sema.mod)});
+ } else switch (operand_ty.ptrSize()) {
+ .One, .C => {},
+ .Many => return sema.fail(block, src, "index syntax required for unknown-length pointer type '{}'", .{operand_ty.fmt(sema.mod)}),
+ .Slice => return sema.fail(block, src, "index syntax required for slice type '{}'", .{operand_ty.fmt(sema.mod)}),
+ }
+
+ if (try sema.resolveMaybeUndefVal(block, operand_src, operand)) |val| {
+ if (val.isUndef()) {
+ return sema.fail(block, src, "cannot dereference undefined value", .{});
+ }
+ }
+}
+
fn failWithBadMemberAccess(
sema: *Sema,
block: *Block,
@@ -3001,13 +3893,13 @@ fn failWithBadMemberAccess(
};
const msg = msg: {
const msg = try sema.errMsg(block, field_src, "{s} '{}' has no member named '{s}'", .{
- kw_name, agg_ty, field_name,
+ kw_name, agg_ty.fmt(sema.mod), field_name,
});
errdefer msg.destroy(sema.gpa);
try sema.addDeclaredHereNote(msg, agg_ty);
break :msg msg;
};
- return sema.failWithOwnedErrorMsg(msg);
+ return sema.failWithOwnedErrorMsg(block, msg);
}
fn failWithBadStructFieldAccess(
@@ -3019,7 +3911,7 @@ fn failWithBadStructFieldAccess(
) CompileError {
const gpa = sema.gpa;
- const fqn = try struct_obj.getFullyQualifiedName(gpa);
+ const fqn = try struct_obj.getFullyQualifiedName(sema.mod);
defer gpa.free(fqn);
const msg = msg: {
@@ -3030,10 +3922,10 @@ fn failWithBadStructFieldAccess(
.{ field_name, fqn },
);
errdefer msg.destroy(gpa);
- try sema.mod.errNoteNonLazy(struct_obj.srcLoc(), msg, "struct declared here", .{});
+ try sema.mod.errNoteNonLazy(struct_obj.srcLoc(sema.mod), msg, "struct declared here", .{});
break :msg msg;
};
- return sema.failWithOwnedErrorMsg(msg);
+ return sema.failWithOwnedErrorMsg(block, msg);
}
fn failWithBadUnionFieldAccess(
@@ -3045,7 +3937,7 @@ fn failWithBadUnionFieldAccess(
) CompileError {
const gpa = sema.gpa;
- const fqn = try union_obj.getFullyQualifiedName(gpa);
+ const fqn = try union_obj.getFullyQualifiedName(sema.mod);
defer gpa.free(fqn);
const msg = msg: {
@@ -3056,14 +3948,14 @@ fn failWithBadUnionFieldAccess(
.{ field_name, fqn },
);
errdefer msg.destroy(gpa);
- try sema.mod.errNoteNonLazy(union_obj.srcLoc(), msg, "union declared here", .{});
+ try sema.mod.errNoteNonLazy(union_obj.srcLoc(sema.mod), msg, "union declared here", .{});
break :msg msg;
};
- return sema.failWithOwnedErrorMsg(msg);
+ return sema.failWithOwnedErrorMsg(block, msg);
}
fn addDeclaredHereNote(sema: *Sema, parent: *Module.ErrorMsg, decl_ty: Type) !void {
- const src_loc = decl_ty.declSrcLocOrNull() orelse return;
+ const src_loc = decl_ty.declSrcLocOrNull(sema.mod) orelse return;
const category = switch (decl_ty.zigTypeTag()) {
.Union => "union",
.Struct => "struct",
@@ -3080,24 +3972,31 @@ fn zirStoreToBlockPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileE
defer tracy.end();
const bin_inst = sema.code.instructions.items(.data)[inst].bin;
- if (bin_inst.lhs == .none) {
- // This is an elided instruction, but AstGen was not smart enough
- // to omit it.
+ const ptr = sema.inst_map.get(Zir.refToIndex(bin_inst.lhs).?) orelse {
+ // This is an elided instruction, but AstGen was unable to omit it.
return;
+ };
+ const operand = try sema.resolveInst(bin_inst.rhs);
+ const src: LazySrcLoc = sema.src;
+ blk: {
+ const ptr_inst = Air.refToIndex(ptr) orelse break :blk;
+ if (sema.air_instructions.items(.tag)[ptr_inst] != .constant) break :blk;
+ const air_datas = sema.air_instructions.items(.data);
+ const ptr_val = sema.air_values.items[air_datas[ptr_inst].ty_pl.payload];
+ switch (ptr_val.tag()) {
+ .inferred_alloc_comptime => {
+ const iac = ptr_val.castTag(.inferred_alloc_comptime).?;
+ return sema.storeToInferredAllocComptime(block, src, operand, iac);
+ },
+ .inferred_alloc => {
+ const inferred_alloc = ptr_val.castTag(.inferred_alloc).?;
+ return sema.storeToInferredAlloc(block, src, ptr, operand, inferred_alloc);
+ },
+ else => break :blk,
+ }
}
- const ptr = sema.resolveInst(bin_inst.lhs);
- const value = sema.resolveInst(bin_inst.rhs);
- const ptr_ty = try Type.ptr(sema.arena, .{
- .pointee_type = sema.typeOf(value),
- // TODO figure out which address space is appropriate here
- .@"addrspace" = target_util.defaultAddressSpace(sema.mod.getTarget(), .local),
- });
- // TODO detect when this store should be done at compile-time. For example,
- // if expressions should force it when the condition is compile-time known.
- const src: LazySrcLoc = .unneeded;
- try sema.requireRuntimeBlock(block, src);
- const bitcasted_ptr = try block.addBitCast(ptr_ty, ptr);
- return sema.storePtr(block, src, bitcasted_ptr, value);
+
+ return sema.storePtr(block, src, ptr, operand);
}
fn zirStoreToInferredPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void {
@@ -3106,56 +4005,81 @@ fn zirStoreToInferredPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Compi
const src: LazySrcLoc = sema.src;
const bin_inst = sema.code.instructions.items(.data)[inst].bin;
- const ptr = sema.resolveInst(bin_inst.lhs);
- const operand = sema.resolveInst(bin_inst.rhs);
- const operand_ty = sema.typeOf(operand);
+ const ptr = try sema.resolveInst(bin_inst.lhs);
+ const operand = try sema.resolveInst(bin_inst.rhs);
const ptr_inst = Air.refToIndex(ptr).?;
assert(sema.air_instructions.items(.tag)[ptr_inst] == .constant);
const air_datas = sema.air_instructions.items(.data);
const ptr_val = sema.air_values.items[air_datas[ptr_inst].ty_pl.payload];
- if (ptr_val.castTag(.inferred_alloc_comptime)) |iac| {
- // There will be only one store_to_inferred_ptr because we are running at comptime.
- // The alloc will turn into a Decl.
- if (try sema.resolveMaybeUndefValAllowVariables(block, src, operand)) |operand_val| {
- if (operand_val.tag() == .variable) {
- return sema.failWithNeededComptime(block, src);
- }
- var anon_decl = try block.startAnonDecl(src);
- defer anon_decl.deinit();
- iac.data.decl = try anon_decl.finish(
- try operand_ty.copy(anon_decl.arena()),
- try operand_val.copy(anon_decl.arena()),
- );
- // TODO set the alignment on the decl
- return;
- } else {
- return sema.failWithNeededComptime(block, src);
- }
+ switch (ptr_val.tag()) {
+ .inferred_alloc_comptime => {
+ const iac = ptr_val.castTag(.inferred_alloc_comptime).?;
+ return sema.storeToInferredAllocComptime(block, src, operand, iac);
+ },
+ .inferred_alloc => {
+ const inferred_alloc = ptr_val.castTag(.inferred_alloc).?;
+ return sema.storeToInferredAlloc(block, src, ptr, operand, inferred_alloc);
+ },
+ else => unreachable,
}
+}
- if (ptr_val.castTag(.inferred_alloc)) |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(sema.arena, operand);
- // Create a runtime bitcast instruction with exactly the type the pointer wants.
- const ptr_ty = try Type.ptr(sema.arena, .{
- .pointee_type = operand_ty,
- .@"align" = inferred_alloc.data.alignment,
- .@"addrspace" = target_util.defaultAddressSpace(sema.mod.getTarget(), .local),
- });
- const bitcasted_ptr = try block.addBitCast(ptr_ty, ptr);
- return sema.storePtr(block, src, bitcasted_ptr, operand);
+fn storeToInferredAlloc(
+ sema: *Sema,
+ block: *Block,
+ src: LazySrcLoc,
+ ptr: Air.Inst.Ref,
+ operand: Air.Inst.Ref,
+ inferred_alloc: *Value.Payload.InferredAlloc,
+) CompileError!void {
+ const operand_ty = sema.typeOf(operand);
+ // 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(sema.arena, operand);
+ // Create a runtime bitcast instruction with exactly the type the pointer wants.
+ const target = sema.mod.getTarget();
+ const ptr_ty = try Type.ptr(sema.arena, sema.mod, .{
+ .pointee_type = operand_ty,
+ .@"align" = inferred_alloc.data.alignment,
+ .@"addrspace" = target_util.defaultAddressSpace(target, .local),
+ });
+ const bitcasted_ptr = try block.addBitCast(ptr_ty, ptr);
+ return sema.storePtr(block, src, bitcasted_ptr, operand);
+}
+
+fn storeToInferredAllocComptime(
+ sema: *Sema,
+ block: *Block,
+ src: LazySrcLoc,
+ operand: Air.Inst.Ref,
+ iac: *Value.Payload.InferredAllocComptime,
+) CompileError!void {
+ const operand_ty = sema.typeOf(operand);
+ // There will be only one store_to_inferred_ptr because we are running at comptime.
+ // The alloc will turn into a Decl.
+ if (try sema.resolveMaybeUndefValAllowVariables(block, src, operand)) |operand_val| {
+ if (operand_val.tag() == .variable) {
+ return sema.failWithNeededComptime(block, src);
+ }
+ var anon_decl = try block.startAnonDecl(src);
+ defer anon_decl.deinit();
+ iac.data.decl_index = try anon_decl.finish(
+ try operand_ty.copy(anon_decl.arena()),
+ try operand_val.copy(anon_decl.arena()),
+ iac.data.alignment,
+ );
+ return;
+ } else {
+ return sema.failWithNeededComptime(block, src);
}
- unreachable;
}
fn zirSetEvalBranchQuota(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void {
const inst_data = sema.code.instructions.items(.data)[inst].un_node;
const src = inst_data.src();
- const quota = try sema.resolveAlreadyCoercedInt(block, src, inst_data.operand, u32);
- if (sema.branch_quota < quota)
- sema.branch_quota = quota;
+ const quota = @intCast(u32, try sema.resolveInt(block, src, inst_data.operand, Type.u32));
+ sema.branch_quota = @maximum(sema.branch_quota, quota);
}
fn zirStore(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void {
@@ -3163,8 +4087,8 @@ fn zirStore(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void
defer tracy.end();
const bin_inst = sema.code.instructions.items(.data)[inst].bin;
- const ptr = sema.resolveInst(bin_inst.lhs);
- const value = sema.resolveInst(bin_inst.rhs);
+ const ptr = try sema.resolveInst(bin_inst.lhs);
+ const value = try sema.resolveInst(bin_inst.rhs);
return sema.storePtr(block, sema.src, ptr, value);
}
@@ -3172,12 +4096,68 @@ fn zirStoreNode(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!v
const tracy = trace(@src());
defer tracy.end();
- const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
+ const zir_tags = sema.code.instructions.items(.tag);
+ const zir_datas = sema.code.instructions.items(.data);
+ const inst_data = zir_datas[inst].pl_node;
const src = inst_data.src();
const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
- const ptr = sema.resolveInst(extra.lhs);
- const value = sema.resolveInst(extra.rhs);
- return sema.storePtr(block, src, ptr, value);
+ const ptr = try sema.resolveInst(extra.lhs);
+ const operand = try sema.resolveInst(extra.rhs);
+
+ // Check for the possibility of this pattern:
+ // %a = ret_ptr
+ // %b = store(%a, %c)
+ // Where %c is an error union or error set. In such case we need to add
+ // to the current function's inferred error set, if any.
+ if ((sema.typeOf(operand).zigTypeTag() == .ErrorUnion or
+ sema.typeOf(operand).zigTypeTag() == .ErrorSet) and
+ sema.fn_ret_ty.zigTypeTag() == .ErrorUnion)
+ {
+ if (Zir.refToIndex(extra.lhs)) |ptr_index| {
+ if (zir_tags[ptr_index] == .ret_ptr) {
+ try sema.addToInferredErrorSet(operand);
+ }
+ }
+ }
+
+ return sema.storePtr(block, src, ptr, operand);
+}
+
+fn zirParamType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
+ const callee_src = sema.src;
+
+ const inst_data = sema.code.instructions.items(.data)[inst].param_type;
+ const callee = try sema.resolveInst(inst_data.callee);
+ const callee_ty = sema.typeOf(callee);
+ var param_index = inst_data.param_index;
+
+ const fn_ty = if (callee_ty.tag() == .bound_fn) fn_ty: {
+ const bound_fn_val = try sema.resolveConstValue(block, callee_src, callee);
+ const bound_fn = bound_fn_val.castTag(.bound_fn).?.data;
+ const fn_ty = sema.typeOf(bound_fn.func_inst);
+ param_index += 1;
+ break :fn_ty fn_ty;
+ } else callee_ty;
+
+ const fn_info = if (fn_ty.zigTypeTag() == .Pointer)
+ fn_ty.childType().fnInfo()
+ else
+ fn_ty.fnInfo();
+
+ if (param_index >= fn_info.param_types.len) {
+ if (fn_info.is_var_args) {
+ return sema.addType(Type.initTag(.var_args_param));
+ }
+ // TODO implement begin_call/end_call Zir instructions and check
+ // argument count before casting arguments to parameter types.
+ return sema.fail(block, callee_src, "wrong number of arguments", .{});
+ }
+
+ if (fn_info.param_types[param_index].tag() == .generic_poison) {
+ return sema.addType(Type.initTag(.var_args_param));
+ }
+
+ return sema.addType(fn_info.param_types[param_index]);
}
fn zirStr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
@@ -3191,19 +4171,44 @@ fn zirStr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Ins
fn addStrLit(sema: *Sema, block: *Block, zir_bytes: []const u8) CompileError!Air.Inst.Ref {
// `zir_bytes` references memory inside the ZIR module, which can get deallocated
// after semantic analysis is complete, for example in the case of the initialization
- // expression of a variable declaration. We need the memory to be in the new
- // anonymous Decl's arena.
- var anon_decl = try block.startAnonDecl(LazySrcLoc.unneeded);
- defer anon_decl.deinit();
+ // expression of a variable declaration.
+ const mod = sema.mod;
+ const gpa = sema.gpa;
+ const string_bytes = &mod.string_literal_bytes;
+ const StringLiteralAdapter = Module.StringLiteralAdapter;
+ const StringLiteralContext = Module.StringLiteralContext;
+ try string_bytes.ensureUnusedCapacity(gpa, zir_bytes.len);
+ const gop = try mod.string_literal_table.getOrPutContextAdapted(gpa, zir_bytes, StringLiteralAdapter{
+ .bytes = string_bytes,
+ }, StringLiteralContext{
+ .bytes = string_bytes,
+ });
+ if (!gop.found_existing) {
+ gop.key_ptr.* = .{
+ .index = @intCast(u32, string_bytes.items.len),
+ .len = @intCast(u32, zir_bytes.len),
+ };
+ string_bytes.appendSliceAssumeCapacity(zir_bytes);
+ gop.value_ptr.* = .none;
+ }
+ const decl_index = gop.value_ptr.unwrap() orelse di: {
+ var anon_decl = try block.startAnonDecl(LazySrcLoc.unneeded);
+ defer anon_decl.deinit();
- const bytes = try anon_decl.arena().dupeZ(u8, zir_bytes);
+ const decl_index = try anon_decl.finish(
+ try Type.Tag.array_u8_sentinel_0.create(anon_decl.arena(), gop.key_ptr.len),
+ try Value.Tag.str_lit.create(anon_decl.arena(), gop.key_ptr.*),
+ 0, // default alignment
+ );
- const new_decl = try anon_decl.finish(
- try Type.Tag.array_u8_sentinel_0.create(anon_decl.arena(), bytes.len),
- try Value.Tag.bytes.create(anon_decl.arena(), bytes[0 .. bytes.len + 1]),
- );
+ // Needed so that `Decl.clearValues` will additionally set the corresponding
+ // string literal table value back to `Decl.OptionalIndex.none`.
+ mod.declPtr(decl_index).owns_tv = true;
- return sema.analyzeDeclRef(new_decl);
+ gop.value_ptr.* = decl_index.toOptional();
+ break :di decl_index;
+ };
+ return sema.analyzeDeclRef(decl_index);
}
fn zirInt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
@@ -3277,23 +4282,26 @@ fn zirCompileLog(
const extra = sema.code.extraData(Zir.Inst.NodeMultiOp, extended.operand);
const src_node = extra.data.src_node;
- const src: LazySrcLoc = .{ .node_offset = src_node };
+ const src = LazySrcLoc.nodeOffset(src_node);
const args = sema.code.refSlice(extra.end, extended.small);
for (args) |arg_ref, i| {
if (i != 0) try writer.print(", ", .{});
- const arg = sema.resolveInst(arg_ref);
+ const arg = try sema.resolveInst(arg_ref);
const arg_ty = sema.typeOf(arg);
if (try sema.resolveMaybeUndefVal(block, src, arg)) |val| {
- try writer.print("@as({}, {})", .{ arg_ty, val });
+ try writer.print("@as({}, {})", .{
+ arg_ty.fmt(sema.mod), val.fmtValue(arg_ty, sema.mod),
+ });
} else {
- try writer.print("@as({}, [runtime value])", .{arg_ty});
+ try writer.print("@as({}, [runtime value])", .{arg_ty.fmt(sema.mod)});
}
}
try writer.print("\n", .{});
- const gop = try sema.mod.compile_log_decls.getOrPut(sema.gpa, sema.owner_decl);
+ const decl_index = if (sema.func) |some| some.owner_decl else sema.owner_decl_index;
+ const gop = try sema.mod.compile_log_decls.getOrPut(sema.gpa, decl_index);
if (!gop.found_existing) {
gop.value_ptr.* = src_node;
}
@@ -3303,7 +4311,7 @@ fn zirCompileLog(
fn zirPanic(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Zir.Inst.Index {
const inst_data = sema.code.instructions.items(.data)[inst].un_node;
const src: LazySrcLoc = inst_data.src();
- const msg_inst = sema.resolveInst(inst_data.operand);
+ const msg_inst = try sema.resolveInst(inst_data.operand);
return sema.panicWithMsg(block, src, msg_inst);
}
@@ -3348,7 +4356,7 @@ fn zirLoop(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileError
child_block.label = &label;
child_block.runtime_cond = null;
child_block.runtime_loop = src;
- child_block.runtime_index += 1;
+ child_block.runtime_index.increment();
const merges = &child_block.label.?.merges;
defer child_block.instructions.deinit(gpa);
@@ -3358,7 +4366,7 @@ fn zirLoop(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileError
var loop_block = child_block.makeSubBlock();
defer loop_block.instructions.deinit(gpa);
- _ = try sema.analyzeBody(&loop_block, body);
+ try sema.analyzeBody(&loop_block, body);
try child_block.instructions.append(gpa, loop_inst);
@@ -3400,9 +4408,11 @@ fn zirCImport(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileEr
};
defer child_block.instructions.deinit(sema.gpa);
- _ = try sema.analyzeBody(&child_block, body);
+ // Ignore the result, all the relevant operations have written to c_import_buf already.
+ _ = try sema.analyzeBodyBreak(&child_block, body);
- const c_import_res = sema.mod.comp.cImport(c_import_buf.items) catch |err|
+ const mod = sema.mod;
+ const c_import_res = mod.comp.cImport(c_import_buf.items) catch |err|
return sema.fail(&child_block, src, "C import failed: {s}", .{@errorName(err)});
if (c_import_res.errors.len != 0) {
@@ -3410,12 +4420,12 @@ fn zirCImport(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileEr
const msg = try sema.errMsg(&child_block, src, "C import failed", .{});
errdefer msg.destroy(sema.gpa);
- if (!sema.mod.comp.bin_file.options.link_libc)
+ if (!mod.comp.bin_file.options.link_libc)
try sema.errNote(&child_block, src, msg, "libc headers not available; compilation does not link against libc", .{});
for (c_import_res.errors) |_| {
// TODO integrate with LazySrcLoc
- // try sema.mod.errNoteNonLazy(.{}, msg, "{s}", .{clang_err.msg_ptr[0..clang_err.msg_len]});
+ // try mod.errNoteNonLazy(.{}, msg, "{s}", .{clang_err.msg_ptr[0..clang_err.msg_len]});
// if (clang_err.filename_ptr) |p| p[0..clang_err.filename_len] else "(no file)",
// clang_err.line + 1,
// clang_err.column + 1,
@@ -3423,7 +4433,7 @@ fn zirCImport(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileEr
@import("clang.zig").Stage2ErrorMsg.delete(c_import_res.errors.ptr, c_import_res.errors.len);
break :msg msg;
};
- return sema.failWithOwnedErrorMsg(msg);
+ return sema.failWithOwnedErrorMsg(parent_block, msg);
}
const c_import_pkg = Package.create(
sema.gpa,
@@ -3433,20 +4443,21 @@ fn zirCImport(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileEr
error.OutOfMemory => return error.OutOfMemory,
else => unreachable, // we pass null for root_src_dir_path
};
- const std_pkg = sema.mod.main_pkg.table.get("std").?;
- const builtin_pkg = sema.mod.main_pkg.table.get("builtin").?;
+ const std_pkg = mod.main_pkg.table.get("std").?;
+ const builtin_pkg = mod.main_pkg.table.get("builtin").?;
try c_import_pkg.add(sema.gpa, "builtin", builtin_pkg);
try c_import_pkg.add(sema.gpa, "std", std_pkg);
- const result = sema.mod.importPkg(c_import_pkg) catch |err|
+ const result = mod.importPkg(c_import_pkg) catch |err|
return sema.fail(&child_block, src, "C import failed: {s}", .{@errorName(err)});
- sema.mod.astGenFile(result.file) catch |err|
+ mod.astGenFile(result.file) catch |err|
return sema.fail(&child_block, src, "C import failed: {s}", .{@errorName(err)});
- try sema.mod.semaFile(result.file);
- const file_root_decl = result.file.root_decl.?;
- try sema.mod.declareDeclDependency(sema.owner_decl, file_root_decl);
+ try mod.semaFile(result.file);
+ const file_root_decl_index = result.file.root_decl.unwrap().?;
+ const file_root_decl = mod.declPtr(file_root_decl_index);
+ try mod.declareDeclDependency(sema.owner_decl_index, file_root_decl_index);
return sema.addConstant(file_root_decl.ty, file_root_decl.val);
}
@@ -3456,11 +4467,7 @@ fn zirSuspendBlock(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) Comp
return sema.fail(parent_block, src, "TODO: implement Sema.zirSuspendBlock", .{});
}
-fn zirBlock(
- sema: *Sema,
- parent_block: *Block,
- inst: Zir.Inst.Index,
-) CompileError!Air.Inst.Ref {
+fn zirBlock(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
const tracy = trace(@src());
defer tracy.end();
@@ -3498,16 +4505,14 @@ fn zirBlock(
.label = &label,
.inlining = parent_block.inlining,
.is_comptime = parent_block.is_comptime,
+ .want_safety = parent_block.want_safety,
};
- const merges = &child_block.label.?.merges;
defer child_block.instructions.deinit(gpa);
- defer merges.results.deinit(gpa);
- defer merges.br_list.deinit(gpa);
+ defer label.merges.results.deinit(gpa);
+ defer label.merges.br_list.deinit(gpa);
- _ = try sema.analyzeBody(&child_block, body);
-
- return sema.analyzeBlockBody(parent_block, src, &child_block, merges);
+ return sema.resolveBlockBody(parent_block, src, &child_block, body, inst, &label.merges);
}
fn resolveBlockBody(
@@ -3524,8 +4529,20 @@ fn resolveBlockBody(
if (child_block.is_comptime) {
return sema.resolveBody(child_block, body, body_inst);
} else {
- _ = try sema.analyzeBody(child_block, body);
- return sema.analyzeBlockBody(parent_block, src, child_block, merges);
+ if (sema.analyzeBodyInner(child_block, body)) |_| {
+ return sema.analyzeBlockBody(parent_block, src, child_block, merges);
+ } else |err| switch (err) {
+ error.ComptimeBreak => {
+ const break_inst = sema.comptime_break_inst;
+ const break_data = sema.code.instructions.items(.data)[break_inst].@"break";
+ if (break_data.block_inst == body_inst) {
+ return try sema.resolveInst(break_data.operand);
+ } else {
+ return error.ComptimeBreak;
+ }
+ },
+ else => |e| return e,
+ }
}
}
@@ -3540,6 +4557,7 @@ fn analyzeBlockBody(
defer tracy.end();
const gpa = sema.gpa;
+ const mod = sema.mod;
// Blocks must terminate with noreturn instruction.
assert(child_block.instructions.items.len != 0);
@@ -3571,6 +4589,25 @@ fn analyzeBlockBody(
// to emit a jump instruction to after the block when it encounters the break.
try parent_block.instructions.append(gpa, merges.block_inst);
const resolved_ty = try sema.resolvePeerTypes(parent_block, src, merges.results.items, .none);
+ // TODO add note "missing else causes void value"
+
+ const type_src = src; // TODO: better source location
+ const valid_rt = try sema.validateRunTimeType(child_block, type_src, resolved_ty, false);
+ if (!valid_rt) {
+ const msg = msg: {
+ const msg = try sema.errMsg(child_block, type_src, "value with comptime only type '{}' depends on runtime control flow", .{resolved_ty.fmt(mod)});
+ errdefer msg.destroy(sema.gpa);
+
+ const runtime_src = child_block.runtime_cond orelse child_block.runtime_loop.?;
+ try sema.errNote(child_block, runtime_src, msg, "runtime control flow here", .{});
+
+ const child_src_decl = mod.declPtr(child_block.src_decl);
+ try sema.explainWhyTypeIsComptime(child_block, type_src, msg, type_src.toSrcLoc(child_src_decl), resolved_ty);
+
+ break :msg msg;
+ };
+ return sema.failWithOwnedErrorMsg(child_block, msg);
+ }
const ty_inst = try sema.addType(resolved_ty);
try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.Block).Struct.fields.len +
child_block.instructions.items.len);
@@ -3587,7 +4624,7 @@ fn analyzeBlockBody(
const br_operand = sema.air_instructions.items(.data)[br].br.operand;
const br_operand_src = src;
const br_operand_ty = sema.typeOf(br_operand);
- if (br_operand_ty.eql(resolved_ty)) {
+ if (br_operand_ty.eql(resolved_ty, mod)) {
// No type coercion needed.
continue;
}
@@ -3645,9 +4682,9 @@ fn zirExport(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void
if (extra.namespace != .none) {
return sema.fail(block, src, "TODO: implement exporting with field access", .{});
}
- const decl = try sema.lookupIdentifier(block, operand_src, decl_name);
+ const decl_index = try sema.lookupIdentifier(block, operand_src, decl_name);
const options = try sema.resolveExportOptions(block, options_src, extra.options);
- try sema.analyzeExport(block, src, options, decl);
+ try sema.analyzeExport(block, src, options, decl_index);
}
fn zirExportValue(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void {
@@ -3661,11 +4698,11 @@ fn zirExportValue(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
const options_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node };
const operand = try sema.resolveInstConst(block, operand_src, extra.operand);
const options = try sema.resolveExportOptions(block, options_src, extra.options);
- const decl = switch (operand.val.tag()) {
+ const decl_index = switch (operand.val.tag()) {
.function => operand.val.castTag(.function).?.data.owner_decl,
else => return sema.fail(block, operand_src, "TODO implement exporting arbitrary Value objects", .{}), // TODO put this Value into an anonymous Decl and then export it.
};
- try sema.analyzeExport(block, src, options, decl);
+ try sema.analyzeExport(block, src, options, decl_index);
}
pub fn analyzeExport(
@@ -3673,16 +4710,23 @@ pub fn analyzeExport(
block: *Block,
src: LazySrcLoc,
borrowed_options: std.builtin.ExportOptions,
- exported_decl: *Decl,
+ exported_decl_index: Decl.Index,
) !void {
const Export = Module.Export;
const mod = sema.mod;
- try mod.ensureDeclAnalyzed(exported_decl);
+ if (borrowed_options.linkage == .Internal) {
+ return;
+ }
+
+ try mod.ensureDeclAnalyzed(exported_decl_index);
+ const exported_decl = mod.declPtr(exported_decl_index);
// TODO run the same checks as we do for C ABI struct fields
switch (exported_decl.ty.zigTypeTag()) {
- .Fn, .Int, .Struct, .Array, .Float => {},
- else => return sema.fail(block, src, "unable to export type '{}'", .{exported_decl.ty}),
+ .Fn, .Int, .Enum, .Struct, .Union, .Array, .Float, .Pointer, .Optional => {},
+ else => return sema.fail(block, src, "unable to export type '{}'", .{
+ exported_decl.ty.fmt(sema.mod),
+ }),
}
const gpa = mod.gpa;
@@ -3699,18 +4743,12 @@ pub fn analyzeExport(
const section: ?[]const u8 = if (borrowed_options.section) |s| try gpa.dupe(u8, s) else null;
errdefer if (section) |s| gpa.free(s);
- const src_decl = block.src_decl;
- const owner_decl = sema.owner_decl;
-
- log.debug("exporting Decl '{s}' as symbol '{s}' from Decl '{s}'", .{
- exported_decl.name, symbol_name, owner_decl.name,
- });
-
new_export.* = .{
.options = .{
.name = symbol_name,
.linkage = borrowed_options.linkage,
.section = section,
+ .visibility = borrowed_options.visibility,
},
.src = src,
.link = switch (mod.comp.bin_file.tag) {
@@ -3719,17 +4757,18 @@ pub fn analyzeExport(
.macho => .{ .macho = .{} },
.plan9 => .{ .plan9 = null },
.c => .{ .c = {} },
- .wasm => .{ .wasm = {} },
+ .wasm => .{ .wasm = .{} },
.spirv => .{ .spirv = {} },
+ .nvptx => .{ .nvptx = {} },
},
- .owner_decl = owner_decl,
- .src_decl = src_decl,
- .exported_decl = exported_decl,
+ .owner_decl = sema.owner_decl_index,
+ .src_decl = block.src_decl,
+ .exported_decl = exported_decl_index,
.status = .in_progress,
};
// Add to export_owners table.
- const eo_gop = mod.export_owners.getOrPutAssumeCapacity(owner_decl);
+ const eo_gop = mod.export_owners.getOrPutAssumeCapacity(sema.owner_decl_index);
if (!eo_gop.found_existing) {
eo_gop.value_ptr.* = &[0]*Export{};
}
@@ -3738,7 +4777,7 @@ pub fn analyzeExport(
errdefer eo_gop.value_ptr.* = gpa.shrink(eo_gop.value_ptr.*, eo_gop.value_ptr.len - 1);
// Add to exported_decl table.
- const de_gop = mod.decl_exports.getOrPutAssumeCapacity(exported_decl);
+ const de_gop = mod.decl_exports.getOrPutAssumeCapacity(exported_decl_index);
if (!de_gop.found_existing) {
de_gop.value_ptr.* = &[0]*Export{};
}
@@ -3747,11 +4786,11 @@ pub fn analyzeExport(
errdefer de_gop.value_ptr.* = gpa.shrink(de_gop.value_ptr.*, de_gop.value_ptr.len - 1);
}
-fn zirSetAlignStack(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void {
- const inst_data = sema.code.instructions.items(.data)[inst].un_node;
- const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
- const src: LazySrcLoc = inst_data.src();
- const alignment = try sema.resolveAlign(block, operand_src, inst_data.operand);
+fn zirSetAlignStack(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!void {
+ const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data;
+ const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = extra.node };
+ const src = LazySrcLoc.nodeOffset(extra.node);
+ const alignment = try sema.resolveAlign(block, operand_src, extra.operand);
if (alignment > 256) {
return sema.fail(block, src, "attempt to @setAlignStack({d}); maximum is 256", .{
alignment,
@@ -3760,7 +4799,8 @@ fn zirSetAlignStack(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileErr
const func = sema.owner_func orelse
return sema.fail(block, src, "@setAlignStack outside function body", .{});
- switch (func.owner_decl.ty.fnCallingConvention()) {
+ const fn_owner_decl = sema.mod.declPtr(func.owner_decl);
+ switch (fn_owner_decl.ty.fnCallingConvention()) {
.Naked => return sema.fail(block, src, "@setAlignStack in naked function", .{}),
.Inline => return sema.fail(block, src, "@setAlignStack in inline function", .{}),
else => {},
@@ -3771,10 +4811,10 @@ fn zirSetAlignStack(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileErr
const msg = msg: {
const msg = try sema.errMsg(block, src, "multiple @setAlignStack in the same function body", .{});
errdefer msg.destroy(sema.gpa);
- try sema.errNote(block, src, msg, "other instance here", .{});
+ try sema.errNote(block, gop.value_ptr.src, msg, "other instance here", .{});
break :msg msg;
};
- return sema.failWithOwnedErrorMsg(msg);
+ return sema.failWithOwnedErrorMsg(block, msg);
}
gop.value_ptr.* = .{ .alignment = alignment, .src = src };
}
@@ -3787,10 +4827,16 @@ fn zirSetCold(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!voi
func.is_cold = is_cold;
}
-fn zirSetFloatMode(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void {
- const inst_data = sema.code.instructions.items(.data)[inst].un_node;
- const src: LazySrcLoc = inst_data.src();
- return sema.fail(block, src, "TODO: implement Sema.zirSetFloatMode", .{});
+fn zirSetFloatMode(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!void {
+ const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data;
+ const src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = extra.node };
+ const float_mode = try sema.resolveBuiltinEnum(block, src, extra.operand, "FloatMode");
+ switch (float_mode) {
+ .Strict => return,
+ .Optimized => {
+ // TODO implement optimized float mode
+ },
+ }
}
fn zirSetRuntimeSafety(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void {
@@ -3799,12 +4845,12 @@ fn zirSetRuntimeSafety(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Compile
block.want_safety = try sema.resolveConstBool(block, operand_src, inst_data.operand);
}
-fn zirFence(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void {
+fn zirFence(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!void {
if (block.is_comptime) return;
- const inst_data = sema.code.instructions.items(.data)[inst].un_node;
- const order_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
- const order = try sema.resolveAtomicOrder(block, order_src, inst_data.operand);
+ const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data;
+ const order_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = extra.node };
+ const order = try sema.resolveAtomicOrder(block, order_src, extra.operand);
if (@enumToInt(order) < @enumToInt(std.builtin.AtomicOrder.Acquire)) {
return sema.fail(block, order_src, "atomic ordering must be Acquire or stricter", .{});
@@ -3821,7 +4867,7 @@ fn zirBreak(sema: *Sema, start_block: *Block, inst: Zir.Inst.Index) CompileError
defer tracy.end();
const inst_data = sema.code.instructions.items(.data)[inst].@"break";
- const operand = sema.resolveInst(inst_data.operand);
+ const operand = try sema.resolveInst(inst_data.operand);
const zir_block = inst_data.block_inst;
var block = start_block;
@@ -3831,6 +4877,11 @@ fn zirBreak(sema: *Sema, start_block: *Block, inst: Zir.Inst.Index) CompileError
const br_ref = try start_block.addBr(label.merges.block_inst, operand);
try label.merges.results.append(sema.gpa, operand);
try label.merges.br_list.append(sema.gpa, Air.refToIndex(br_ref).?);
+ block.runtime_index.increment();
+ if (block.runtime_cond == null and block.runtime_loop == null) {
+ block.runtime_cond = start_block.runtime_cond orelse start_block.runtime_loop;
+ block.runtime_loop = start_block.runtime_loop;
+ }
return inst;
}
}
@@ -3839,14 +4890,11 @@ fn zirBreak(sema: *Sema, start_block: *Block, inst: Zir.Inst.Index) CompileError
}
fn zirDbgStmt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void {
- const tracy = trace(@src());
- defer tracy.end();
-
// We do not set sema.src here because dbg_stmt instructions are only emitted for
// ZIR code that possibly will need to generate runtime code. So error messages
// and other source locations must not rely on sema.src being set from dbg_stmt
// instructions.
- if (block.is_comptime) return;
+ if (block.is_comptime or sema.mod.comp.bin_file.options.strip) return;
const inst_data = sema.code.instructions.items(.data)[inst].dbg_stmt;
_ = try block.addInst(.{
@@ -3858,12 +4906,82 @@ fn zirDbgStmt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!voi
});
}
+fn zirDbgBlockBegin(sema: *Sema, block: *Block) CompileError!void {
+ if (block.is_comptime or sema.mod.comp.bin_file.options.strip) return;
+
+ _ = try block.addInst(.{
+ .tag = .dbg_block_begin,
+ .data = undefined,
+ });
+}
+
+fn zirDbgBlockEnd(sema: *Sema, block: *Block) CompileError!void {
+ if (block.is_comptime or sema.mod.comp.bin_file.options.strip) return;
+
+ _ = try block.addInst(.{
+ .tag = .dbg_block_end,
+ .data = undefined,
+ });
+}
+
+fn zirDbgVar(
+ sema: *Sema,
+ block: *Block,
+ inst: Zir.Inst.Index,
+ air_tag: Air.Inst.Tag,
+) CompileError!void {
+ if (block.is_comptime or sema.mod.comp.bin_file.options.strip) return;
+
+ const str_op = sema.code.instructions.items(.data)[inst].str_op;
+ const operand = try sema.resolveInst(str_op.operand);
+ const name = str_op.getStr(sema.code);
+ try sema.addDbgVar(block, operand, air_tag, name);
+}
+
+fn addDbgVar(
+ sema: *Sema,
+ block: *Block,
+ operand: Air.Inst.Ref,
+ air_tag: Air.Inst.Tag,
+ name: []const u8,
+) CompileError!void {
+ const operand_ty = sema.typeOf(operand);
+ switch (air_tag) {
+ .dbg_var_ptr => {
+ if (!(try sema.typeHasRuntimeBits(block, sema.src, operand_ty.childType()))) return;
+ },
+ .dbg_var_val => {
+ if (!(try sema.typeHasRuntimeBits(block, sema.src, operand_ty))) return;
+ },
+ else => unreachable,
+ }
+
+ try sema.queueFullTypeResolution(operand_ty);
+
+ // Add the name to the AIR.
+ const name_extra_index = @intCast(u32, sema.air_extra.items.len);
+ const elements_used = name.len / 4 + 1;
+ try sema.air_extra.ensureUnusedCapacity(sema.gpa, elements_used);
+ const buffer = mem.sliceAsBytes(sema.air_extra.unusedCapacitySlice());
+ mem.copy(u8, buffer, name);
+ buffer[name.len] = 0;
+ sema.air_extra.items.len += elements_used;
+
+ _ = try block.addInst(.{
+ .tag = air_tag,
+ .data = .{ .pl_op = .{
+ .payload = name_extra_index,
+ .operand = operand,
+ } },
+ });
+}
+
fn zirDeclRef(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
const inst_data = sema.code.instructions.items(.data)[inst].str_tok;
const src = inst_data.src();
const decl_name = inst_data.get(sema.code);
- const decl = try sema.lookupIdentifier(block, src, decl_name);
- return sema.analyzeDeclRef(decl);
+ const decl_index = try sema.lookupIdentifier(block, src, decl_name);
+ return sema.analyzeDeclRef(decl_index);
}
fn zirDeclVal(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
@@ -3874,11 +4992,11 @@ fn zirDeclVal(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air
return sema.analyzeDeclVal(block, src, decl);
}
-fn lookupIdentifier(sema: *Sema, block: *Block, src: LazySrcLoc, name: []const u8) !*Decl {
+fn lookupIdentifier(sema: *Sema, block: *Block, src: LazySrcLoc, name: []const u8) !Decl.Index {
var namespace = block.namespace;
while (true) {
- if (try sema.lookupInNamespace(block, src, namespace, name, false)) |decl| {
- return decl;
+ if (try sema.lookupInNamespace(block, src, namespace, name, false)) |decl_index| {
+ return decl_index;
}
namespace = namespace.parent orelse break;
}
@@ -3894,12 +5012,13 @@ fn lookupInNamespace(
namespace: *Namespace,
ident_name: []const u8,
observe_usingnamespace: bool,
-) CompileError!?*Decl {
+) CompileError!?Decl.Index {
const mod = sema.mod;
- const namespace_decl = namespace.getDecl();
+ const namespace_decl_index = namespace.getDeclIndex();
+ const namespace_decl = sema.mod.declPtr(namespace_decl_index);
if (namespace_decl.analysis == .file_failure) {
- try mod.declareDeclDependency(sema.owner_decl, namespace_decl);
+ try mod.declareDeclDependency(sema.owner_decl_index, namespace_decl_index);
return error.AnalysisFail;
}
@@ -3911,7 +5030,7 @@ fn lookupInNamespace(
defer checked_namespaces.deinit(gpa);
// Keep track of name conflicts for error notes.
- var candidates: std.ArrayListUnmanaged(*Decl) = .{};
+ var candidates: std.ArrayListUnmanaged(Decl.Index) = .{};
defer candidates.deinit(gpa);
try checked_namespaces.put(gpa, namespace, {});
@@ -3919,23 +5038,27 @@ fn lookupInNamespace(
while (check_i < checked_namespaces.count()) : (check_i += 1) {
const check_ns = checked_namespaces.keys()[check_i];
- if (check_ns.decls.get(ident_name)) |decl| {
+ if (check_ns.decls.getKeyAdapted(ident_name, Module.DeclAdapter{ .mod = mod })) |decl_index| {
// Skip decls which are not marked pub, which are in a different
// file than the `a.b`/`@hasDecl` syntax.
+ const decl = mod.declPtr(decl_index);
if (decl.is_pub or src_file == decl.getFileScope()) {
- try candidates.append(gpa, decl);
+ try candidates.append(gpa, decl_index);
}
}
var it = check_ns.usingnamespace_set.iterator();
while (it.next()) |entry| {
- const sub_usingnamespace_decl = entry.key_ptr.*;
+ const sub_usingnamespace_decl_index = entry.key_ptr.*;
+ // Skip the decl we're currently analysing.
+ if (sub_usingnamespace_decl_index == sema.owner_decl_index) continue;
+ const sub_usingnamespace_decl = mod.declPtr(sub_usingnamespace_decl_index);
const sub_is_pub = entry.value_ptr.*;
if (!sub_is_pub and src_file != sub_usingnamespace_decl.getFileScope()) {
// Skip usingnamespace decls which are not marked pub, which are in
// a different file than the `a.b`/`@hasDecl` syntax.
continue;
}
- try sema.ensureDeclAnalyzed(sub_usingnamespace_decl);
+ try sema.ensureDeclAnalyzed(sub_usingnamespace_decl_index);
const ns_ty = sub_usingnamespace_decl.val.castTag(.ty).?.data;
const sub_ns = ns_ty.getNamespace().?;
try checked_namespaces.put(gpa, sub_ns, {});
@@ -3945,26 +5068,27 @@ fn lookupInNamespace(
switch (candidates.items.len) {
0 => {},
1 => {
- const decl = candidates.items[0];
- try mod.declareDeclDependency(sema.owner_decl, decl);
- return decl;
+ const decl_index = candidates.items[0];
+ try mod.declareDeclDependency(sema.owner_decl_index, decl_index);
+ return decl_index;
},
else => {
const msg = msg: {
const msg = try sema.errMsg(block, src, "ambiguous reference", .{});
errdefer msg.destroy(gpa);
- for (candidates.items) |candidate| {
+ for (candidates.items) |candidate_index| {
+ const candidate = mod.declPtr(candidate_index);
const src_loc = candidate.srcLoc();
try mod.errNoteNonLazy(src_loc, msg, "declared here", .{});
}
break :msg msg;
};
- return sema.failWithOwnedErrorMsg(msg);
+ return sema.failWithOwnedErrorMsg(block, msg);
},
}
- } else if (namespace.decls.get(ident_name)) |decl| {
- try mod.declareDeclDependency(sema.owner_decl, decl);
- return decl;
+ } else if (namespace.decls.getKeyAdapted(ident_name, Module.DeclAdapter{ .mod = mod })) |decl_index| {
+ try mod.declareDeclDependency(sema.owner_decl_index, decl_index);
+ return decl_index;
}
log.debug("{*} ({s}) depends on non-existence of '{s}' in {*} ({s})", .{
@@ -3973,7 +5097,7 @@ fn lookupInNamespace(
// TODO This dependency is too strong. Really, it should only be a dependency
// on the non-existence of `ident_name` in the namespace. We can lessen the number of
// outdated declarations by making this dependency more sophisticated.
- try mod.declareDeclDependency(sema.owner_decl, namespace_decl);
+ try mod.declareDeclDependency(sema.owner_decl_index, namespace_decl_index);
return null;
}
@@ -3994,7 +5118,7 @@ fn zirCall(
const modifier = @intToEnum(std.builtin.CallOptions.Modifier, extra.data.flags.packed_modifier);
const ensure_result_used = extra.data.flags.ensure_result_used;
- var func = sema.resolveInst(extra.data.callee);
+ var func = try sema.resolveInst(extra.data.callee);
var resolved_args: []Air.Inst.Ref = undefined;
const func_type = sema.typeOf(func);
@@ -4007,12 +5131,12 @@ fn zirCall(
resolved_args = try sema.arena.alloc(Air.Inst.Ref, args.len + 1);
resolved_args[0] = bound_data.arg0_inst;
for (args) |zir_arg, i| {
- resolved_args[i + 1] = sema.resolveInst(zir_arg);
+ resolved_args[i + 1] = try sema.resolveInst(zir_arg);
}
} else {
resolved_args = try sema.arena.alloc(Air.Inst.Ref, args.len);
for (args) |zir_arg, i| {
- resolved_args[i] = sema.resolveInst(zir_arg);
+ resolved_args[i] = try sema.resolveInst(zir_arg);
}
}
@@ -4023,31 +5147,44 @@ const GenericCallAdapter = struct {
generic_fn: *Module.Fn,
precomputed_hash: u64,
func_ty_info: Type.Payload.Function.Data,
+ /// Unlike comptime_args, the Type here is not always present.
+ /// .generic_poison is used to communicate non-anytype parameters.
comptime_tvs: []const TypedValue,
+ module: *Module,
pub fn eql(ctx: @This(), adapted_key: void, other_key: *Module.Fn) bool {
_ = adapted_key;
// The generic function Decl is guaranteed to be the first dependency
// of each of its instantiations.
- const generic_owner_decl = other_key.owner_decl.dependencies.keys()[0];
+ const other_owner_decl = ctx.module.declPtr(other_key.owner_decl);
+ const generic_owner_decl = other_owner_decl.dependencies.keys()[0];
if (ctx.generic_fn.owner_decl != generic_owner_decl) return false;
const other_comptime_args = other_key.comptime_args.?;
for (other_comptime_args[0..ctx.func_ty_info.param_types.len]) |other_arg, i| {
- if (other_arg.ty.tag() != .generic_poison) {
- // anytype parameter
- if (!other_arg.ty.eql(ctx.comptime_tvs[i].ty)) {
+ const this_arg = ctx.comptime_tvs[i];
+ const this_is_comptime = this_arg.val.tag() != .generic_poison;
+ const other_is_comptime = other_arg.val.tag() != .generic_poison;
+ const this_is_anytype = this_arg.ty.tag() != .generic_poison;
+ const other_is_anytype = other_key.anytype_args[i];
+
+ if (other_is_anytype != this_is_anytype) return false;
+ if (other_is_comptime != this_is_comptime) return false;
+
+ if (this_is_anytype) {
+ // Both are anytype parameters.
+ if (!this_arg.ty.eql(other_arg.ty, ctx.module)) {
return false;
}
- }
- if (other_arg.val.tag() != .generic_poison) {
- // comptime parameter
- if (ctx.comptime_tvs[i].val.tag() == .generic_poison) {
- // No match because the instantiation has a comptime parameter
- // but the callsite does not.
- return false;
+ if (this_is_comptime) {
+ // Both are comptime and anytype parameters with matching types.
+ if (!this_arg.val.eql(other_arg.val, other_arg.ty, ctx.module)) {
+ return false;
+ }
}
- if (!other_arg.val.eql(ctx.comptime_tvs[i].val, other_arg.ty)) {
+ } else if (this_is_comptime) {
+ // Both are comptime parameters but not anytype parameters.
+ if (!this_arg.val.eql(other_arg.val, other_arg.ty, ctx.module)) {
return false;
}
}
@@ -4063,22 +5200,6 @@ const GenericCallAdapter = struct {
}
};
-const GenericRemoveAdapter = struct {
- precomputed_hash: u64,
-
- pub fn eql(ctx: @This(), adapted_key: *Module.Fn, other_key: *Module.Fn) bool {
- _ = ctx;
- return adapted_key == other_key;
- }
-
- /// The implementation of the hash is in semantic analysis of function calls, so
- /// that any errors when computing the hash can be properly reported.
- pub fn hash(ctx: @This(), adapted_key: *Module.Fn) u64 {
- _ = adapted_key;
- return ctx.precomputed_hash;
- }
-};
-
fn analyzeCall(
sema: *Sema,
block: *Block,
@@ -4103,7 +5224,7 @@ fn analyzeCall(
},
else => {},
}
- return sema.fail(block, func_src, "type '{}' not a function", .{callee_ty});
+ return sema.fail(block, func_src, "type '{}' not a function", .{callee_ty.fmt(sema.mod)});
};
const func_ty_info = func_ty.fnInfo();
@@ -4139,32 +5260,71 @@ fn analyzeCall(
);
}
- switch (modifier) {
+ const call_tag: Air.Inst.Tag = switch (modifier) {
.auto,
.always_inline,
.compile_time,
- => {},
-
- .async_kw,
- .never_tail,
- .never_inline,
.no_async,
- .always_tail,
- => return sema.fail(block, call_src, "TODO implement call with modifier {}", .{
- modifier,
- }),
+ => Air.Inst.Tag.call,
+
+ .never_tail => Air.Inst.Tag.call_never_tail,
+ .never_inline => Air.Inst.Tag.call_never_inline,
+ .always_tail => Air.Inst.Tag.call_always_tail,
+
+ .async_kw => return sema.fail(block, call_src, "TODO implement async call", .{}),
+ };
+
+ if (modifier == .never_inline and func_ty_info.cc == .Inline) {
+ return sema.fail(block, call_src, "no-inline call of inline function", .{});
}
const gpa = sema.gpa;
- const is_comptime_call = block.is_comptime or modifier == .compile_time or
- try sema.typeRequiresComptime(block, func_src, func_ty_info.return_type);
- const is_inline_call = is_comptime_call or modifier == .always_inline or
+ var is_generic_call = func_ty_info.is_generic;
+ var is_comptime_call = block.is_comptime or modifier == .compile_time;
+ if (!is_comptime_call) {
+ if (sema.typeRequiresComptime(block, func_src, func_ty_info.return_type)) |ct| {
+ is_comptime_call = ct;
+ } else |err| switch (err) {
+ error.GenericPoison => is_generic_call = true,
+ else => |e| return e,
+ }
+ }
+ var is_inline_call = is_comptime_call or modifier == .always_inline or
func_ty_info.cc == .Inline;
+
+ if (!is_inline_call and is_generic_call) {
+ if (sema.instantiateGenericCall(
+ block,
+ func,
+ func_src,
+ call_src,
+ func_ty_info,
+ ensure_result_used,
+ uncasted_args,
+ call_tag,
+ )) |some| {
+ return some;
+ } else |err| switch (err) {
+ error.GenericPoison => {
+ is_inline_call = true;
+ },
+ error.ComptimeReturn => {
+ is_inline_call = true;
+ is_comptime_call = true;
+ },
+ else => |e| return e,
+ }
+ }
+
+ if (is_comptime_call and modifier == .never_inline) {
+ return sema.fail(block, call_src, "unable to perform 'never_inline' call at compile-time", .{});
+ }
+
const result: Air.Inst.Ref = if (is_inline_call) res: {
const func_val = try sema.resolveConstValue(block, func_src, func);
const module_fn = switch (func_val.tag()) {
- .decl_ref => func_val.castTag(.decl_ref).?.data.val.castTag(.function).?.data,
+ .decl_ref => mod.declPtr(func_val.castTag(.decl_ref).?.data).val.castTag(.function).?.data,
.function => func_val.castTag(.function).?.data,
.extern_fn => return sema.fail(block, call_src, "{s} call of extern function", .{
@as([]const u8, if (is_comptime_call) "comptime" else "inline"),
@@ -4195,7 +5355,8 @@ fn analyzeCall(
// In order to save a bit of stack space, directly modify Sema rather
// than create a child one.
const parent_zir = sema.code;
- sema.code = module_fn.owner_decl.getFileScope().zir;
+ const fn_owner_decl = mod.declPtr(module_fn.owner_decl);
+ sema.code = fn_owner_decl.getFileScope().zir;
defer sema.code = parent_zir;
const parent_inst_map = sema.inst_map;
@@ -4209,14 +5370,14 @@ fn analyzeCall(
sema.func = module_fn;
defer sema.func = parent_func;
- var wip_captures = try WipCaptureScope.init(gpa, sema.perm_arena, module_fn.owner_decl.src_scope);
+ var wip_captures = try WipCaptureScope.init(gpa, sema.perm_arena, fn_owner_decl.src_scope);
defer wip_captures.deinit();
var child_block: Block = .{
.parent = null,
.sema = sema,
.src_decl = module_fn.owner_decl,
- .namespace = module_fn.owner_decl.src_namespace,
+ .namespace = fn_owner_decl.src_namespace,
.wip_capture_scope = wip_captures.scope,
.instructions = .{},
.label = null,
@@ -4243,7 +5404,15 @@ fn analyzeCall(
delete_memoized_call_key = true;
}
- try sema.emitBackwardBranch(&child_block, call_src);
+ try sema.emitBackwardBranch(block, call_src);
+
+ // Whether this call should be memoized, set to false if the call can mutate
+ // comptime state.
+ var should_memoize = true;
+
+ var new_fn_info = fn_owner_decl.ty.fnInfo();
+ new_fn_info.param_types = try sema.arena.alloc(Type, new_fn_info.param_types.len);
+ new_fn_info.comptime_params = (try sema.arena.alloc(bool, new_fn_info.param_types.len)).ptr;
// This will have return instructions analyzed as break instructions to
// the block_inst above. Here we are performing "comptime/inline semantic analysis"
@@ -4264,6 +5433,7 @@ fn analyzeCall(
const param_body = sema.code.extra[extra.end..][0..extra.data.body_len];
const param_ty_inst = try sema.resolveBody(&child_block, param_body, inst);
const param_ty = try sema.analyzeAsType(&child_block, param_src, param_ty_inst);
+ new_fn_info.param_types[arg_i] = param_ty;
const arg_src = call_src; // TODO: better source location
const casted_arg = try sema.coerce(&child_block, param_ty, uncasted_args[arg_i], arg_src);
try sema.inst_map.putNoClobber(gpa, inst, casted_arg);
@@ -4276,8 +5446,14 @@ fn analyzeCall(
// parameter or return type.
return error.GenericPoison;
},
- else => {},
+ else => {
+ // Needed so that lazy values do not trigger
+ // assertion due to type not being resolved
+ // when the hash function is called.
+ try sema.resolveLazyValue(&child_block, arg_src, arg_val);
+ },
}
+ should_memoize = should_memoize and !arg_val.canMutateComptimeVarState();
memoized_call_key.args[arg_i] = .{
.ty = param_ty,
.val = arg_val,
@@ -4290,6 +5466,7 @@ fn analyzeCall(
.param_anytype, .param_anytype_comptime => {
// No coercion needed.
const uncasted_arg = uncasted_args[arg_i];
+ new_fn_info.param_types[arg_i] = sema.typeOf(uncasted_arg);
try sema.inst_map.putNoClobber(gpa, inst, uncasted_arg);
if (is_comptime_call) {
@@ -4301,8 +5478,14 @@ fn analyzeCall(
// parameter or return type.
return error.GenericPoison;
},
- else => {},
+ else => {
+ // Needed so that lazy values do not trigger
+ // assertion due to type not being resolved
+ // when the hash function is called.
+ try sema.resolveLazyValue(&child_block, arg_src, arg_val);
+ },
}
+ should_memoize = should_memoize and !arg_val.canMutateComptimeVarState();
memoized_call_key.args[arg_i] = .{
.ty = sema.typeOf(uncasted_arg),
.val = arg_val,
@@ -4319,26 +5502,30 @@ fn analyzeCall(
// on parameters, we must now do the same for the return type as we just did with
// each of the parameters, resolving the return type and providing it to the child
// `Sema` so that it can be used for the `ret_ptr` instruction.
- const ret_ty_inst = try sema.resolveBody(&child_block, fn_info.ret_ty_body, module_fn.zir_body_inst);
+ const ret_ty_inst = if (fn_info.ret_ty_body.len != 0)
+ try sema.resolveBody(&child_block, fn_info.ret_ty_body, module_fn.zir_body_inst)
+ else
+ try sema.resolveInst(fn_info.ret_ty_ref);
const ret_ty_src = func_src; // TODO better source location
const bare_return_type = try sema.analyzeAsType(&child_block, ret_ty_src, ret_ty_inst);
// Create a fresh inferred error set type for inline/comptime calls.
const fn_ret_ty = blk: {
- if (func_ty_info.return_type.castTag(.error_union)) |payload| {
- if (payload.data.error_set.tag() == .error_set_inferred) {
- const node = try sema.gpa.create(Module.Fn.InferredErrorSetListNode);
- node.data = .{ .func = module_fn };
- parent_func.?.inferred_error_sets.prepend(node);
-
- const error_set_ty = try Type.Tag.error_set_inferred.create(sema.arena, &node.data);
- break :blk try Type.Tag.error_union.create(sema.arena, .{
- .error_set = error_set_ty,
- .payload = bare_return_type,
- });
+ if (module_fn.hasInferredErrorSet(mod)) {
+ const node = try sema.gpa.create(Module.Fn.InferredErrorSetListNode);
+ node.data = .{ .func = module_fn };
+ if (parent_func) |some| {
+ some.inferred_error_sets.prepend(node);
}
+
+ const error_set_ty = try Type.Tag.error_set_inferred.create(sema.arena, &node.data);
+ break :blk try Type.Tag.error_union.create(sema.arena, .{
+ .error_set = error_set_ty,
+ .payload = bare_return_type,
+ });
}
break :blk bare_return_type;
};
+ new_fn_info.return_type = fn_ret_ty;
const parent_fn_ret_ty = sema.fn_ret_ty;
sema.fn_ret_ty = fn_ret_ty;
defer sema.fn_ret_ty = parent_fn_ret_ty;
@@ -4346,8 +5533,8 @@ fn analyzeCall(
// This `res2` is here instead of directly breaking from `res` due to a stage1
// bug generating invalid LLVM IR.
const res2: Air.Inst.Ref = res2: {
- if (is_comptime_call) {
- if (mod.memoized_calls.get(memoized_call_key)) |result| {
+ if (should_memoize and is_comptime_call) {
+ if (mod.memoized_calls.getContext(memoized_call_key, .{ .module = mod })) |result| {
const ty_inst = try sema.addType(fn_ret_ty);
try sema.air_values.append(gpa, result.val);
sema.air_instructions.set(block_inst, .{
@@ -4361,16 +5548,55 @@ fn analyzeCall(
}
}
+ const new_func_resolved_ty = try Type.Tag.function.create(sema.arena, new_fn_info);
+ if (!is_comptime_call) {
+ try sema.emitDbgInline(block, parent_func.?, module_fn, new_func_resolved_ty, .dbg_inline_begin);
+
+ for (fn_info.param_body) |param| switch (zir_tags[param]) {
+ .param, .param_comptime => {
+ const inst_data = sema.code.instructions.items(.data)[param].pl_tok;
+ const extra = sema.code.extraData(Zir.Inst.Param, inst_data.payload_index);
+ const param_name = sema.code.nullTerminatedString(extra.data.name);
+ const inst = sema.inst_map.get(param).?;
+
+ try sema.addDbgVar(&child_block, inst, .dbg_var_val, param_name);
+ },
+ .param_anytype, .param_anytype_comptime => {
+ const inst_data = sema.code.instructions.items(.data)[param].str_tok;
+ const param_name = inst_data.get(sema.code);
+ const inst = sema.inst_map.get(param).?;
+
+ try sema.addDbgVar(&child_block, inst, .dbg_var_val, param_name);
+ },
+ else => continue,
+ };
+ }
+
const result = result: {
- _ = sema.analyzeBody(&child_block, fn_info.body) catch |err| switch (err) {
+ sema.analyzeBody(&child_block, fn_info.body) catch |err| switch (err) {
error.ComptimeReturn => break :result inlining.comptime_result,
- error.ComptimeBreak => unreachable, // Can't break through a fn call.
+ error.AnalysisFail => {
+ const err_msg = inlining.err orelse return err;
+ try sema.errNote(block, call_src, err_msg, "called from here", .{});
+ if (block.inlining) |some| some.err = err_msg;
+ return err;
+ },
else => |e| return e,
};
break :result try sema.analyzeBlockBody(block, call_src, &child_block, merges);
};
- if (is_comptime_call) {
+ if (!is_comptime_call) {
+ try sema.emitDbgInline(
+ block,
+ module_fn,
+ parent_func.?,
+ mod.declPtr(parent_func.?.owner_decl).ty,
+ .dbg_inline_end,
+ );
+ }
+
+ if (should_memoize and is_comptime_call) {
const result_val = try sema.resolveConstMaybeUndefVal(block, call_src, result);
// TODO: check whether any external comptime memory was mutated by the
@@ -4386,10 +5612,10 @@ fn analyzeCall(
arg.* = try arg.*.copy(arena);
}
- try mod.memoized_calls.put(gpa, memoized_call_key, .{
+ try mod.memoized_calls.putContext(gpa, memoized_call_key, .{
.val = try result_val.copy(arena),
.arena = arena_allocator.state,
- });
+ }, .{ .module = mod });
delete_memoized_call_key = false;
}
}
@@ -4400,278 +5626,8 @@ fn analyzeCall(
try wip_captures.finalize();
break :res res2;
- } else if (func_ty_info.is_generic) res: {
- const func_val = try sema.resolveConstValue(block, func_src, func);
- const module_fn = switch (func_val.tag()) {
- .function => func_val.castTag(.function).?.data,
- .decl_ref => func_val.castTag(.decl_ref).?.data.val.castTag(.function).?.data,
- else => unreachable,
- };
- // Check the Module's generic function map with an adapted context, so that we
- // can match against `uncasted_args` rather than doing the work below to create a
- // generic Scope only to junk it if it matches an existing instantiation.
- const namespace = module_fn.owner_decl.src_namespace;
- const fn_zir = namespace.file_scope.zir;
- const fn_info = fn_zir.getFnInfo(module_fn.zir_body_inst);
- const zir_tags = fn_zir.instructions.items(.tag);
-
- // This hash must match `Module.MonomorphedFuncsContext.hash`.
- // For parameters explicitly marked comptime and simple parameter type expressions,
- // we know whether a parameter is elided from a monomorphed function, and can
- // use it in the hash here. However, for parameter type expressions that are not
- // explicitly marked comptime and rely on previous parameter comptime values, we
- // don't find out until after generating a monomorphed function whether the parameter
- // type ended up being a "must-be-comptime-known" type.
- var hasher = std.hash.Wyhash.init(0);
- std.hash.autoHash(&hasher, @ptrToInt(module_fn));
-
- const comptime_tvs = try sema.arena.alloc(TypedValue, func_ty_info.param_types.len);
-
- for (func_ty_info.param_types) |param_ty, i| {
- const is_comptime = func_ty_info.paramIsComptime(i);
- if (is_comptime) {
- const arg_src = call_src; // TODO better source location
- const casted_arg = try sema.coerce(block, param_ty, uncasted_args[i], arg_src);
- if (try sema.resolveMaybeUndefVal(block, arg_src, casted_arg)) |arg_val| {
- if (param_ty.tag() != .generic_poison) {
- arg_val.hash(param_ty, &hasher);
- }
- comptime_tvs[i] = .{
- // This will be different than `param_ty` in the case of `generic_poison`.
- .ty = sema.typeOf(casted_arg),
- .val = arg_val,
- };
- } else {
- return sema.failWithNeededComptime(block, arg_src);
- }
- } else {
- comptime_tvs[i] = .{
- .ty = sema.typeOf(uncasted_args[i]),
- .val = Value.initTag(.generic_poison),
- };
- }
- }
-
- const precomputed_hash = hasher.final();
-
- const adapter: GenericCallAdapter = .{
- .generic_fn = module_fn,
- .precomputed_hash = precomputed_hash,
- .func_ty_info = func_ty_info,
- .comptime_tvs = comptime_tvs,
- };
- const gop = try mod.monomorphed_funcs.getOrPutAdapted(gpa, {}, adapter);
- if (gop.found_existing) {
- const callee_func = gop.key_ptr.*;
- break :res try sema.finishGenericCall(
- block,
- call_src,
- callee_func,
- func_src,
- uncasted_args,
- fn_info,
- zir_tags,
- );
- }
- const new_module_func = try gpa.create(Module.Fn);
- gop.key_ptr.* = new_module_func;
- {
- errdefer gpa.destroy(new_module_func);
- const remove_adapter: GenericRemoveAdapter = .{
- .precomputed_hash = precomputed_hash,
- };
- errdefer assert(mod.monomorphed_funcs.removeAdapted(new_module_func, remove_adapter));
-
- try namespace.anon_decls.ensureUnusedCapacity(gpa, 1);
-
- // Create a Decl for the new function.
- const src_decl = namespace.getDecl();
- // TODO better names for generic function instantiations
- const name_index = mod.getNextAnonNameIndex();
- const decl_name = try std.fmt.allocPrintZ(gpa, "{s}__anon_{d}", .{
- module_fn.owner_decl.name, name_index,
- });
- const new_decl = try mod.allocateNewDecl(decl_name, namespace, module_fn.owner_decl.src_node, src_decl.src_scope);
- new_decl.src_line = module_fn.owner_decl.src_line;
- new_decl.is_pub = module_fn.owner_decl.is_pub;
- new_decl.is_exported = module_fn.owner_decl.is_exported;
- new_decl.has_align = module_fn.owner_decl.has_align;
- new_decl.has_linksection_or_addrspace = module_fn.owner_decl.has_linksection_or_addrspace;
- new_decl.@"addrspace" = module_fn.owner_decl.@"addrspace";
- new_decl.zir_decl_index = module_fn.owner_decl.zir_decl_index;
- new_decl.alive = true; // This Decl is called at runtime.
- new_decl.has_tv = true;
- new_decl.owns_tv = true;
- new_decl.analysis = .in_progress;
- new_decl.generation = mod.generation;
-
- namespace.anon_decls.putAssumeCapacityNoClobber(new_decl, {});
-
- // The generic function Decl is guaranteed to be the first dependency
- // of each of its instantiations.
- assert(new_decl.dependencies.keys().len == 0);
- try mod.declareDeclDependency(new_decl, module_fn.owner_decl);
-
- var new_decl_arena = std.heap.ArenaAllocator.init(sema.gpa);
- errdefer new_decl_arena.deinit();
- const new_decl_arena_allocator = new_decl_arena.allocator();
-
- // Re-run the block that creates the function, with the comptime parameters
- // pre-populated inside `inst_map`. This causes `param_comptime` and
- // `param_anytype_comptime` ZIR instructions to be ignored, resulting in a
- // new, monomorphized function, with the comptime parameters elided.
- var child_sema: Sema = .{
- .mod = mod,
- .gpa = gpa,
- .arena = sema.arena,
- .perm_arena = new_decl_arena_allocator,
- .code = fn_zir,
- .owner_decl = new_decl,
- .func = null,
- .fn_ret_ty = Type.void,
- .owner_func = null,
- .comptime_args = try new_decl_arena_allocator.alloc(TypedValue, uncasted_args.len),
- .comptime_args_fn_inst = module_fn.zir_body_inst,
- .preallocated_new_func = new_module_func,
- };
- defer child_sema.deinit();
-
- var wip_captures = try WipCaptureScope.init(gpa, sema.perm_arena, new_decl.src_scope);
- defer wip_captures.deinit();
-
- var child_block: Block = .{
- .parent = null,
- .sema = &child_sema,
- .src_decl = new_decl,
- .namespace = namespace,
- .wip_capture_scope = wip_captures.scope,
- .instructions = .{},
- .inlining = null,
- .is_comptime = true,
- };
- defer {
- child_block.instructions.deinit(gpa);
- child_block.params.deinit(gpa);
- }
-
- try child_sema.inst_map.ensureUnusedCapacity(gpa, @intCast(u32, uncasted_args.len));
- var arg_i: usize = 0;
- for (fn_info.param_body) |inst| {
- var is_comptime = false;
- var is_anytype = false;
- switch (zir_tags[inst]) {
- .param => {
- is_comptime = func_ty_info.paramIsComptime(arg_i);
- },
- .param_comptime => {
- is_comptime = true;
- },
- .param_anytype => {
- is_anytype = true;
- is_comptime = func_ty_info.paramIsComptime(arg_i);
- },
- .param_anytype_comptime => {
- is_anytype = true;
- is_comptime = true;
- },
- else => continue,
- }
- const arg_src = call_src; // TODO: better source location
- const arg = uncasted_args[arg_i];
- if (is_comptime) {
- if (try sema.resolveMaybeUndefVal(block, arg_src, arg)) |arg_val| {
- const child_arg = try child_sema.addConstant(sema.typeOf(arg), arg_val);
- child_sema.inst_map.putAssumeCapacityNoClobber(inst, child_arg);
- } else {
- return sema.failWithNeededComptime(block, arg_src);
- }
- } else if (is_anytype) {
- const arg_ty = sema.typeOf(arg);
- if (try sema.typeRequiresComptime(block, arg_src, arg_ty)) {
- const arg_val = try sema.resolveConstValue(block, arg_src, arg);
- const child_arg = try child_sema.addConstant(arg_ty, arg_val);
- child_sema.inst_map.putAssumeCapacityNoClobber(inst, child_arg);
- } else {
- // We insert into the map an instruction which is runtime-known
- // but has the type of the argument.
- const child_arg = try child_block.addArg(arg_ty, 0);
- child_sema.inst_map.putAssumeCapacityNoClobber(inst, child_arg);
- }
- }
- arg_i += 1;
- }
- const new_func_inst = child_sema.resolveBody(&child_block, fn_info.param_body, fn_info.param_body_inst) catch |err| {
- // TODO look up the compile error that happened here and attach a note to it
- // pointing here, at the generic instantiation callsite.
- if (sema.owner_func) |owner_func| {
- owner_func.state = .dependency_failure;
- } else {
- sema.owner_decl.analysis = .dependency_failure;
- }
- return err;
- };
- const new_func_val = child_sema.resolveConstValue(&child_block, .unneeded, new_func_inst) catch unreachable;
- const new_func = new_func_val.castTag(.function).?.data;
- assert(new_func == new_module_func);
-
- arg_i = 0;
- for (fn_info.param_body) |inst| {
- switch (zir_tags[inst]) {
- .param_comptime, .param_anytype_comptime, .param, .param_anytype => {},
- else => continue,
- }
- const arg = child_sema.inst_map.get(inst).?;
- const copied_arg_ty = try child_sema.typeOf(arg).copy(new_decl_arena_allocator);
- if (child_sema.resolveMaybeUndefValAllowVariables(
- &child_block,
- .unneeded,
- arg,
- ) catch unreachable) |arg_val| {
- child_sema.comptime_args[arg_i] = .{
- .ty = copied_arg_ty,
- .val = try arg_val.copy(new_decl_arena_allocator),
- };
- } else {
- child_sema.comptime_args[arg_i] = .{
- .ty = copied_arg_ty,
- .val = Value.initTag(.generic_poison),
- };
- }
-
- arg_i += 1;
- }
-
- try wip_captures.finalize();
-
- // Populate the Decl ty/val with the function and its type.
- new_decl.ty = try child_sema.typeOf(new_func_inst).copy(new_decl_arena_allocator);
- new_decl.val = try Value.Tag.function.create(new_decl_arena_allocator, new_func);
- new_decl.analysis = .complete;
-
- log.debug("generic function '{s}' instantiated with type {}", .{
- new_decl.name, new_decl.ty,
- });
- assert(!new_decl.ty.fnInfo().is_generic);
-
- // Queue up a `codegen_func` work item for the new Fn. The `comptime_args` field
- // will be populated, ensuring it will have `analyzeBody` called with the ZIR
- // parameters mapped appropriately.
- try mod.comp.bin_file.allocateDeclIndexes(new_decl);
- try mod.comp.work_queue.writeItem(.{ .codegen_func = new_func });
-
- try new_decl.finalizeNewArena(&new_decl_arena);
- }
-
- break :res try sema.finishGenericCall(
- block,
- call_src,
- new_module_func,
- func_src,
- uncasted_args,
- fn_info,
- zir_tags,
- );
} else res: {
+ assert(!func_ty_info.is_generic);
try sema.requireRuntimeBlock(block, call_src);
const args = try sema.arena.alloc(Air.Inst.Ref, uncasted_args.len);
@@ -4686,12 +5642,15 @@ fn analyzeCall(
}
}
- try sema.resolveTypeFully(block, call_src, func_ty_info.return_type);
+ try sema.queueFullTypeResolution(func_ty_info.return_type);
+ if (sema.owner_func != null and func_ty_info.return_type.isError()) {
+ sema.owner_func.?.calls_or_awaits_errorable_fn = true;
+ }
try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.Call).Struct.fields.len +
args.len);
const func_inst = try block.addInst(.{
- .tag = .call,
+ .tag = call_tag,
.data = .{ .pl_op = .{
.operand = func,
.payload = sema.addExtraAssumeCapacity(Air.Call{
@@ -4709,41 +5668,372 @@ fn analyzeCall(
return result;
}
-fn finishGenericCall(
+fn instantiateGenericCall(
sema: *Sema,
block: *Block,
- call_src: LazySrcLoc,
- callee: *Module.Fn,
+ func: Air.Inst.Ref,
func_src: LazySrcLoc,
+ call_src: LazySrcLoc,
+ func_ty_info: Type.Payload.Function.Data,
+ ensure_result_used: bool,
uncasted_args: []const Air.Inst.Ref,
- fn_info: Zir.FnInfo,
- zir_tags: []const Zir.Inst.Tag,
+ call_tag: Air.Inst.Tag,
) CompileError!Air.Inst.Ref {
- const callee_inst = try sema.analyzeDeclVal(block, func_src, callee.owner_decl);
+ const mod = sema.mod;
+ const gpa = sema.gpa;
- // Make a runtime call to the new function, making sure to omit the comptime args.
- try sema.requireRuntimeBlock(block, call_src);
+ const func_val = try sema.resolveConstValue(block, func_src, func);
+ const module_fn = switch (func_val.tag()) {
+ .function => func_val.castTag(.function).?.data,
+ .decl_ref => mod.declPtr(func_val.castTag(.decl_ref).?.data).val.castTag(.function).?.data,
+ else => unreachable,
+ };
+ // Check the Module's generic function map with an adapted context, so that we
+ // can match against `uncasted_args` rather than doing the work below to create a
+ // generic Scope only to junk it if it matches an existing instantiation.
+ const fn_owner_decl = mod.declPtr(module_fn.owner_decl);
+ const namespace = fn_owner_decl.src_namespace;
+ const fn_zir = namespace.file_scope.zir;
+ const fn_info = fn_zir.getFnInfo(module_fn.zir_body_inst);
+ const zir_tags = fn_zir.instructions.items(.tag);
+
+ // This hash must match `Module.MonomorphedFuncsContext.hash`.
+ // For parameters explicitly marked comptime and simple parameter type expressions,
+ // we know whether a parameter is elided from a monomorphed function, and can
+ // use it in the hash here. However, for parameter type expressions that are not
+ // explicitly marked comptime and rely on previous parameter comptime values, we
+ // don't find out until after generating a monomorphed function whether the parameter
+ // type ended up being a "must-be-comptime-known" type.
+ var hasher = std.hash.Wyhash.init(0);
+ std.hash.autoHash(&hasher, @ptrToInt(module_fn));
+
+ const comptime_tvs = try sema.arena.alloc(TypedValue, func_ty_info.param_types.len);
- const comptime_args = callee.comptime_args.?;
- const runtime_args_len = count: {
- var count: u32 = 0;
- var arg_i: usize = 0;
+ {
+ var i: usize = 0;
for (fn_info.param_body) |inst| {
+ var is_comptime = false;
+ var is_anytype = false;
switch (zir_tags[inst]) {
- .param_comptime, .param_anytype_comptime, .param, .param_anytype => {
- if (comptime_args[arg_i].val.tag() == .generic_poison) {
- count += 1;
- }
- arg_i += 1;
+ .param => {
+ is_comptime = func_ty_info.paramIsComptime(i);
+ },
+ .param_comptime => {
+ is_comptime = true;
+ },
+ .param_anytype => {
+ is_anytype = true;
+ is_comptime = func_ty_info.paramIsComptime(i);
+ },
+ .param_anytype_comptime => {
+ is_anytype = true;
+ is_comptime = true;
},
else => continue,
}
+
+ if (is_comptime) {
+ const arg_src = call_src; // TODO better source location
+ const arg_ty = sema.typeOf(uncasted_args[i]);
+ const arg_val = try sema.resolveValue(block, arg_src, uncasted_args[i]);
+ arg_val.hash(arg_ty, &hasher, mod);
+ if (is_anytype) {
+ arg_ty.hashWithHasher(&hasher, mod);
+ comptime_tvs[i] = .{
+ .ty = arg_ty,
+ .val = arg_val,
+ };
+ } else {
+ comptime_tvs[i] = .{
+ .ty = Type.initTag(.generic_poison),
+ .val = arg_val,
+ };
+ }
+ } else if (is_anytype) {
+ const arg_ty = sema.typeOf(uncasted_args[i]);
+ arg_ty.hashWithHasher(&hasher, mod);
+ comptime_tvs[i] = .{
+ .ty = arg_ty,
+ .val = Value.initTag(.generic_poison),
+ };
+ } else {
+ comptime_tvs[i] = .{
+ .ty = Type.initTag(.generic_poison),
+ .val = Value.initTag(.generic_poison),
+ };
+ }
+
+ i += 1;
}
- break :count count;
+ }
+
+ const precomputed_hash = hasher.final();
+
+ const adapter: GenericCallAdapter = .{
+ .generic_fn = module_fn,
+ .precomputed_hash = precomputed_hash,
+ .func_ty_info = func_ty_info,
+ .comptime_tvs = comptime_tvs,
+ .module = mod,
};
+ const gop = try mod.monomorphed_funcs.getOrPutAdapted(gpa, {}, adapter);
+ const callee = if (!gop.found_existing) callee: {
+ const new_module_func = try gpa.create(Module.Fn);
+ // This ensures that we can operate on the hash map before the Module.Fn
+ // struct is fully initialized.
+ new_module_func.hash = precomputed_hash;
+ gop.key_ptr.* = new_module_func;
+ errdefer gpa.destroy(new_module_func);
+ errdefer assert(mod.monomorphed_funcs.remove(new_module_func));
+
+ try namespace.anon_decls.ensureUnusedCapacity(gpa, 1);
+
+ // Create a Decl for the new function.
+ const src_decl_index = namespace.getDeclIndex();
+ const src_decl = mod.declPtr(src_decl_index);
+ const new_decl_index = try mod.allocateNewDecl(namespace, fn_owner_decl.src_node, src_decl.src_scope);
+ errdefer mod.destroyDecl(new_decl_index);
+ const new_decl = mod.declPtr(new_decl_index);
+ // TODO better names for generic function instantiations
+ const decl_name = try std.fmt.allocPrintZ(gpa, "{s}__anon_{d}", .{
+ fn_owner_decl.name, @enumToInt(new_decl_index),
+ });
+ new_decl.name = decl_name;
+ new_decl.src_line = fn_owner_decl.src_line;
+ new_decl.is_pub = fn_owner_decl.is_pub;
+ new_decl.is_exported = fn_owner_decl.is_exported;
+ new_decl.has_align = fn_owner_decl.has_align;
+ new_decl.has_linksection_or_addrspace = fn_owner_decl.has_linksection_or_addrspace;
+ new_decl.@"addrspace" = fn_owner_decl.@"addrspace";
+ new_decl.zir_decl_index = fn_owner_decl.zir_decl_index;
+ new_decl.alive = true; // This Decl is called at runtime.
+ new_decl.analysis = .in_progress;
+ new_decl.generation = mod.generation;
+
+ namespace.anon_decls.putAssumeCapacityNoClobber(new_decl_index, {});
+ errdefer assert(namespace.anon_decls.orderedRemove(new_decl_index));
+
+ // The generic function Decl is guaranteed to be the first dependency
+ // of each of its instantiations.
+ assert(new_decl.dependencies.keys().len == 0);
+ try mod.declareDeclDependency(new_decl_index, module_fn.owner_decl);
+ // Resolving the new function type below will possibly declare more decl dependencies
+ // and so we remove them all here in case of error.
+ errdefer {
+ for (new_decl.dependencies.keys()) |dep_index| {
+ const dep = mod.declPtr(dep_index);
+ dep.removeDependant(new_decl_index);
+ }
+ }
+
+ var new_decl_arena = std.heap.ArenaAllocator.init(sema.gpa);
+ errdefer new_decl_arena.deinit();
+ const new_decl_arena_allocator = new_decl_arena.allocator();
+
+ // Re-run the block that creates the function, with the comptime parameters
+ // pre-populated inside `inst_map`. This causes `param_comptime` and
+ // `param_anytype_comptime` ZIR instructions to be ignored, resulting in a
+ // new, monomorphized function, with the comptime parameters elided.
+ var child_sema: Sema = .{
+ .mod = mod,
+ .gpa = gpa,
+ .arena = sema.arena,
+ .perm_arena = new_decl_arena_allocator,
+ .code = fn_zir,
+ .owner_decl = new_decl,
+ .owner_decl_index = new_decl_index,
+ .func = null,
+ .fn_ret_ty = Type.void,
+ .owner_func = null,
+ .comptime_args = try new_decl_arena_allocator.alloc(TypedValue, uncasted_args.len),
+ .comptime_args_fn_inst = module_fn.zir_body_inst,
+ .preallocated_new_func = new_module_func,
+ };
+ defer child_sema.deinit();
+
+ var wip_captures = try WipCaptureScope.init(gpa, sema.perm_arena, new_decl.src_scope);
+ defer wip_captures.deinit();
+
+ var child_block: Block = .{
+ .parent = null,
+ .sema = &child_sema,
+ .src_decl = new_decl_index,
+ .namespace = namespace,
+ .wip_capture_scope = wip_captures.scope,
+ .instructions = .{},
+ .inlining = null,
+ .is_comptime = true,
+ };
+ defer {
+ child_block.instructions.deinit(gpa);
+ child_block.params.deinit(gpa);
+ }
+
+ try child_sema.inst_map.ensureUnusedCapacity(gpa, @intCast(u32, uncasted_args.len));
+ var arg_i: usize = 0;
+ for (fn_info.param_body) |inst| {
+ var is_comptime = false;
+ var is_anytype = false;
+ switch (zir_tags[inst]) {
+ .param => {
+ is_comptime = func_ty_info.paramIsComptime(arg_i);
+ },
+ .param_comptime => {
+ is_comptime = true;
+ },
+ .param_anytype => {
+ is_anytype = true;
+ is_comptime = func_ty_info.paramIsComptime(arg_i);
+ },
+ .param_anytype_comptime => {
+ is_anytype = true;
+ is_comptime = true;
+ },
+ else => continue,
+ }
+ const arg_src = call_src; // TODO: better source location
+ const arg = uncasted_args[arg_i];
+ if (is_comptime) {
+ if (try sema.resolveMaybeUndefVal(block, arg_src, arg)) |arg_val| {
+ const child_arg = try child_sema.addConstant(sema.typeOf(arg), arg_val);
+ child_sema.inst_map.putAssumeCapacityNoClobber(inst, child_arg);
+ } else {
+ return sema.failWithNeededComptime(block, arg_src);
+ }
+ } else if (is_anytype) {
+ const arg_ty = sema.typeOf(arg);
+ if (try sema.typeRequiresComptime(block, arg_src, arg_ty)) {
+ const arg_val = try sema.resolveConstValue(block, arg_src, arg);
+ const child_arg = try child_sema.addConstant(arg_ty, arg_val);
+ child_sema.inst_map.putAssumeCapacityNoClobber(inst, child_arg);
+ } else {
+ // We insert into the map an instruction which is runtime-known
+ // but has the type of the argument.
+ const child_arg = try child_block.addArg(arg_ty);
+ child_sema.inst_map.putAssumeCapacityNoClobber(inst, child_arg);
+ }
+ }
+ arg_i += 1;
+ }
+ const new_func_inst = child_sema.resolveBody(&child_block, fn_info.param_body, fn_info.param_body_inst) catch |err| {
+ // TODO look up the compile error that happened here and attach a note to it
+ // pointing here, at the generic instantiation callsite.
+ if (sema.owner_func) |owner_func| {
+ owner_func.state = .dependency_failure;
+ } else {
+ sema.owner_decl.analysis = .dependency_failure;
+ }
+ return err;
+ };
+ const new_func_val = child_sema.resolveConstValue(&child_block, .unneeded, new_func_inst) catch unreachable;
+ const new_func = new_func_val.castTag(.function).?.data;
+ errdefer new_func.deinit(gpa);
+ assert(new_func == new_module_func);
+
+ const anytype_args = try new_decl_arena_allocator.alloc(bool, func_ty_info.param_types.len);
+ new_func.anytype_args = anytype_args.ptr;
+ arg_i = 0;
+ for (fn_info.param_body) |inst| {
+ var is_comptime = false;
+ var is_anytype = false;
+ switch (zir_tags[inst]) {
+ .param => {
+ is_comptime = func_ty_info.paramIsComptime(arg_i);
+ },
+ .param_comptime => {
+ is_comptime = true;
+ },
+ .param_anytype => {
+ is_anytype = true;
+ is_comptime = func_ty_info.paramIsComptime(arg_i);
+ },
+ .param_anytype_comptime => {
+ is_anytype = true;
+ is_comptime = true;
+ },
+ else => continue,
+ }
+
+ // We populate the Type here regardless because it is needed by
+ // `GenericCallAdapter.eql` as well as function body analysis.
+ // Whether it is anytype is communicated by `anytype_args`.
+ const arg = child_sema.inst_map.get(inst).?;
+ const copied_arg_ty = try child_sema.typeOf(arg).copy(new_decl_arena_allocator);
+ anytype_args[arg_i] = is_anytype;
+
+ const arg_src = call_src; // TODO: better source location
+ if (try sema.typeRequiresComptime(block, arg_src, copied_arg_ty)) {
+ is_comptime = true;
+ }
+
+ if (is_comptime) {
+ const arg_val = (child_sema.resolveMaybeUndefValAllowVariables(
+ &child_block,
+ .unneeded,
+ arg,
+ ) catch unreachable).?;
+ child_sema.comptime_args[arg_i] = .{
+ .ty = copied_arg_ty,
+ .val = try arg_val.copy(new_decl_arena_allocator),
+ };
+ } else {
+ child_sema.comptime_args[arg_i] = .{
+ .ty = copied_arg_ty,
+ .val = Value.initTag(.generic_poison),
+ };
+ }
+
+ arg_i += 1;
+ }
+
+ try wip_captures.finalize();
+
+ // Populate the Decl ty/val with the function and its type.
+ new_decl.ty = try child_sema.typeOf(new_func_inst).copy(new_decl_arena_allocator);
+ // If the call evaluated to a return type that requires comptime, never mind
+ // our generic instantiation. Instead we need to perform a comptime call.
+ const new_fn_info = new_decl.ty.fnInfo();
+ if (try sema.typeRequiresComptime(block, call_src, new_fn_info.return_type)) {
+ return error.ComptimeReturn;
+ }
+ // Similarly, if the call evaluated to a generic type we need to instead
+ // call it inline.
+ if (new_fn_info.is_generic or new_fn_info.cc == .Inline) {
+ return error.GenericPoison;
+ }
+
+ new_decl.val = try Value.Tag.function.create(new_decl_arena_allocator, new_func);
+ new_decl.@"align" = 0;
+ new_decl.has_tv = true;
+ new_decl.owns_tv = true;
+ new_decl.analysis = .complete;
+
+ log.debug("generic function '{s}' instantiated with type {}", .{
+ new_decl.name, new_decl.ty.fmtDebug(),
+ });
+
+ // Queue up a `codegen_func` work item for the new Fn. The `comptime_args` field
+ // will be populated, ensuring it will have `analyzeBody` called with the ZIR
+ // parameters mapped appropriately.
+ try mod.comp.bin_file.allocateDeclIndexes(new_decl_index);
+ try mod.comp.work_queue.writeItem(.{ .codegen_func = new_func });
+
+ try new_decl.finalizeNewArena(&new_decl_arena);
+ break :callee new_func;
+ } else gop.key_ptr.*;
+
+ callee.branch_quota = @maximum(callee.branch_quota, sema.branch_quota);
+
+ const callee_inst = try sema.analyzeDeclVal(block, func_src, callee.owner_decl);
+
+ // Make a runtime call to the new function, making sure to omit the comptime args.
+ try sema.requireRuntimeBlock(block, call_src);
+
+ const comptime_args = callee.comptime_args.?;
+ const new_fn_info = mod.declPtr(callee.owner_decl).ty.fnInfo();
+ const runtime_args_len = @intCast(u32, new_fn_info.param_types.len);
const runtime_args = try sema.arena.alloc(Air.Inst.Ref, runtime_args_len);
{
- const new_fn_ty = callee.owner_decl.ty;
var runtime_i: u32 = 0;
var total_i: u32 = 0;
for (fn_info.param_body) |inst| {
@@ -4751,25 +6041,32 @@ fn finishGenericCall(
.param_comptime, .param_anytype_comptime, .param, .param_anytype => {},
else => continue,
}
- const is_runtime = comptime_args[total_i].val.tag() == .generic_poison;
+ const is_runtime = comptime_args[total_i].val.tag() == .generic_poison and
+ comptime_args[total_i].ty.hasRuntimeBits() and
+ !comptime_args[total_i].ty.comptimeOnly();
if (is_runtime) {
- const param_ty = new_fn_ty.fnParamType(runtime_i);
+ const param_ty = new_fn_info.param_types[runtime_i];
const arg_src = call_src; // TODO: better source location
const uncasted_arg = uncasted_args[total_i];
- try sema.resolveTypeFully(block, arg_src, param_ty);
const casted_arg = try sema.coerce(block, param_ty, uncasted_arg, arg_src);
+ try sema.queueFullTypeResolution(param_ty);
runtime_args[runtime_i] = casted_arg;
runtime_i += 1;
}
total_i += 1;
}
- try sema.resolveTypeFully(block, call_src, new_fn_ty.fnReturnType());
+ try sema.queueFullTypeResolution(new_fn_info.return_type);
+ }
+
+ if (sema.owner_func != null and new_fn_info.return_type.isError()) {
+ sema.owner_func.?.calls_or_awaits_errorable_fn = true;
}
+
try sema.air_extra.ensureUnusedCapacity(sema.gpa, @typeInfo(Air.Call).Struct.fields.len +
runtime_args_len);
const func_inst = try block.addInst(.{
- .tag = .call,
+ .tag = call_tag,
.data = .{ .pl_op = .{
.operand = callee_inst,
.payload = sema.addExtraAssumeCapacity(Air.Call{
@@ -4778,9 +6075,36 @@ fn finishGenericCall(
} },
});
sema.appendRefsAssumeCapacity(runtime_args);
+
+ if (ensure_result_used) {
+ try sema.ensureResultUsed(block, func_inst, call_src);
+ }
return func_inst;
}
+fn emitDbgInline(
+ sema: *Sema,
+ block: *Block,
+ old_func: *Module.Fn,
+ new_func: *Module.Fn,
+ new_func_ty: Type,
+ tag: Air.Inst.Tag,
+) CompileError!void {
+ if (sema.mod.comp.bin_file.options.strip) return;
+
+ // Recursive inline call; no dbg_inline needed.
+ if (old_func == new_func) return;
+
+ try sema.air_values.append(sema.gpa, try Value.Tag.function.create(sema.arena, new_func));
+ _ = try block.addInst(.{
+ .tag = tag,
+ .data = .{ .ty_pl = .{
+ .ty = try sema.addType(new_func_ty),
+ .payload = @intCast(u32, sema.air_values.items.len - 1),
+ } },
+ });
+}
+
fn zirIntType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
_ = block;
const tracy = trace(@src());
@@ -4804,12 +6128,17 @@ fn zirOptionalType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileErro
return sema.addType(opt_type);
}
-fn zirElemType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
- const inst_data = sema.code.instructions.items(.data)[inst].un_node;
- const src = inst_data.src();
- const array_type = try sema.resolveType(block, src, inst_data.operand);
- const elem_type = array_type.elemType();
- return sema.addType(elem_type);
+fn zirElemTypeIndex(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
+ const bin = sema.code.instructions.items(.data)[inst].bin;
+ const indexable_ty = try sema.resolveType(block, .unneeded, bin.lhs);
+ assert(indexable_ty.isIndexable()); // validated by a previous instruction
+ if (indexable_ty.zigTypeTag() == .Struct) {
+ const elem_type = indexable_ty.tupleFields().types[@enumToInt(bin.rhs)];
+ return sema.addType(elem_type);
+ } else {
+ const elem_type = indexable_ty.elemType2();
+ return sema.addType(elem_type);
+ }
}
fn zirVectorType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
@@ -4817,11 +6146,11 @@ fn zirVectorType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!
const elem_type_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
const len_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node };
const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
- const len = try sema.resolveAlreadyCoercedInt(block, len_src, extra.lhs, u32);
+ const len = try sema.resolveInt(block, len_src, extra.lhs, Type.u32);
const elem_type = try sema.resolveType(block, elem_type_src, extra.rhs);
try sema.checkVectorElemType(block, elem_type_src, elem_type);
const vector_type = try Type.Tag.vector.create(sema.arena, .{
- .len = len,
+ .len = @intCast(u32, len),
.elem_type = elem_type,
});
return sema.addType(vector_type);
@@ -4832,9 +6161,11 @@ fn zirArrayType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A
defer tracy.end();
const bin_inst = sema.code.instructions.items(.data)[inst].bin;
- const len = try sema.resolveInt(block, .unneeded, bin_inst.lhs, Type.usize);
- const elem_type = try sema.resolveType(block, .unneeded, bin_inst.rhs);
- const array_ty = try Type.array(sema.arena, len, null, elem_type);
+ const len_src = sema.src; // TODO better source location
+ const elem_src = sema.src; // TODO better source location
+ const len = try sema.resolveInt(block, len_src, bin_inst.lhs, Type.usize);
+ const elem_type = try sema.resolveType(block, elem_src, bin_inst.rhs);
+ const array_ty = try Type.array(sema.arena, len, null, elem_type, sema.mod);
return sema.addType(array_ty);
}
@@ -4850,10 +6181,10 @@ fn zirArrayTypeSentinel(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Compil
const elem_src: LazySrcLoc = .{ .node_offset_array_type_elem = inst_data.src_node };
const len = try sema.resolveInt(block, len_src, extra.len, Type.usize);
const elem_type = try sema.resolveType(block, elem_src, extra.elem_type);
- const uncasted_sentinel = sema.resolveInst(extra.sentinel);
+ const uncasted_sentinel = try sema.resolveInst(extra.sentinel);
const sentinel = try sema.coerce(block, elem_type, uncasted_sentinel, sentinel_src);
const sentinel_val = try sema.resolveConstValue(block, sentinel_src, sentinel);
- const array_ty = try Type.array(sema.arena, len, sentinel_val, elem_type);
+ const array_ty = try Type.array(sema.arena, len, sentinel_val, elem_type, sema.mod);
return sema.addType(array_ty);
}
@@ -4878,15 +6209,15 @@ fn zirErrorUnionType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileEr
const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node };
const rhs_src: LazySrcLoc = .{ .node_offset_bin_rhs = inst_data.src_node };
- const error_union = try sema.resolveType(block, lhs_src, extra.lhs);
+ const error_set = try sema.resolveType(block, lhs_src, extra.lhs);
const payload = try sema.resolveType(block, rhs_src, extra.rhs);
- if (error_union.zigTypeTag() != .ErrorSet) {
- return sema.fail(block, lhs_src, "expected error set type, found {}", .{
- error_union.elemType(),
+ if (error_set.zigTypeTag() != .ErrorSet) {
+ return sema.fail(block, lhs_src, "expected error set type, found '{}'", .{
+ error_set.fmt(sema.mod),
});
}
- const err_union_ty = try Module.errorUnionType(sema.arena, error_union, payload);
+ const err_union_ty = try Type.errorUnion(sema.arena, error_set, payload, sema.mod);
return sema.addType(err_union_ty);
}
@@ -4915,20 +6246,41 @@ fn zirErrorToInt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!
const inst_data = sema.code.instructions.items(.data)[inst].un_node;
const src = inst_data.src();
const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
- const op = sema.resolveInst(inst_data.operand);
+ const op = try sema.resolveInst(inst_data.operand);
const op_coerced = try sema.coerce(block, Type.anyerror, op, operand_src);
- const result_ty = Type.initTag(.u16);
+ const result_ty = Type.u16;
if (try sema.resolveMaybeUndefVal(block, src, op_coerced)) |val| {
if (val.isUndef()) {
return sema.addConstUndef(result_ty);
}
- const payload = try sema.arena.create(Value.Payload.U64);
- payload.* = .{
- .base = .{ .tag = .int_u64 },
- .data = (try sema.mod.getErrorValue(val.castTag(.@"error").?.data.name)).value,
- };
- return sema.addConstant(result_ty, Value.initPayload(&payload.base));
+ switch (val.tag()) {
+ .@"error" => {
+ const payload = try sema.arena.create(Value.Payload.U64);
+ payload.* = .{
+ .base = .{ .tag = .int_u64 },
+ .data = (try sema.mod.getErrorValue(val.castTag(.@"error").?.data.name)).value,
+ };
+ return sema.addConstant(result_ty, Value.initPayload(&payload.base));
+ },
+
+ // This is not a valid combination with the type `anyerror`.
+ .the_only_possible_value => unreachable,
+
+ // Assume it's already encoded as an integer.
+ else => return sema.addConstant(result_ty, val),
+ }
+ }
+
+ const op_ty = sema.typeOf(op);
+ try sema.resolveInferredErrorSetTy(block, src, op_ty);
+ if (!op_ty.isAnyError()) {
+ const names = op_ty.errorSetNames();
+ switch (names.len) {
+ 0 => return sema.addConstant(result_ty, Value.zero),
+ 1 => return sema.addIntUnsigned(result_ty, sema.mod.global_error_set.get(names[0]).?),
+ else => {},
+ }
}
try sema.requireRuntimeBlock(block, src);
@@ -4942,31 +6294,31 @@ fn zirIntToError(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!
const inst_data = sema.code.instructions.items(.data)[inst].un_node;
const src = inst_data.src();
const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
+ const uncasted_operand = try sema.resolveInst(inst_data.operand);
+ const operand = try sema.coerce(block, Type.u16, uncasted_operand, operand_src);
+ const target = sema.mod.getTarget();
- const op = sema.resolveInst(inst_data.operand);
-
- if (try sema.resolveDefinedValue(block, operand_src, op)) |value| {
- const int = value.toUnsignedInt();
+ if (try sema.resolveDefinedValue(block, operand_src, operand)) |value| {
+ const int = try sema.usizeCast(block, operand_src, value.toUnsignedInt(target));
if (int > sema.mod.global_error_set.count() or int == 0)
- return sema.fail(block, operand_src, "integer value {d} represents no error", .{int});
+ return sema.fail(block, operand_src, "integer value '{d}' represents no error", .{int});
const payload = try sema.arena.create(Value.Payload.Error);
payload.* = .{
.base = .{ .tag = .@"error" },
- .data = .{ .name = sema.mod.error_name_list.items[@intCast(usize, int)] },
+ .data = .{ .name = sema.mod.error_name_list.items[int] },
};
return sema.addConstant(Type.anyerror, Value.initPayload(&payload.base));
}
try sema.requireRuntimeBlock(block, src);
if (block.wantSafety()) {
- return sema.fail(block, src, "TODO: get max errors in compilation", .{});
- // const is_gt_max = @panic("TODO get max errors in compilation");
- // try sema.addSafetyCheck(block, is_gt_max, .invalid_error_code);
+ const is_lt_len = try block.addUnOp(.cmp_lt_errors_len, operand);
+ try sema.addSafetyCheck(block, is_lt_len, .invalid_error_code);
}
return block.addInst(.{
.tag = .bitcast,
.data = .{ .ty_op = .{
.ty = Air.Inst.Ref.anyerror_type,
- .operand = op,
+ .operand = operand,
} },
});
}
@@ -4980,8 +6332,8 @@ fn zirMergeErrorSets(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileEr
const src: LazySrcLoc = .{ .node_offset_bin_op = inst_data.src_node };
const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node };
const rhs_src: LazySrcLoc = .{ .node_offset_bin_rhs = inst_data.src_node };
- const lhs = sema.resolveInst(extra.lhs);
- const rhs = sema.resolveInst(extra.rhs);
+ const lhs = try sema.resolveInst(extra.lhs);
+ const rhs = try sema.resolveInst(extra.rhs);
if (sema.typeOf(lhs).zigTypeTag() == .Bool and sema.typeOf(rhs).zigTypeTag() == .Bool) {
const msg = msg: {
const msg = try sema.errMsg(block, lhs_src, "expected error set type, found 'bool'", .{});
@@ -4989,44 +6341,36 @@ fn zirMergeErrorSets(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileEr
try sema.errNote(block, src, msg, "'||' merges error sets; 'or' performs boolean OR", .{});
break :msg msg;
};
- return sema.failWithOwnedErrorMsg(msg);
+ return sema.failWithOwnedErrorMsg(block, msg);
}
const lhs_ty = try sema.analyzeAsType(block, lhs_src, lhs);
const rhs_ty = try sema.analyzeAsType(block, rhs_src, rhs);
if (lhs_ty.zigTypeTag() != .ErrorSet)
- return sema.fail(block, lhs_src, "expected error set type, found {}", .{lhs_ty});
+ return sema.fail(block, lhs_src, "expected error set type, found '{}'", .{lhs_ty.fmt(sema.mod)});
if (rhs_ty.zigTypeTag() != .ErrorSet)
- return sema.fail(block, rhs_src, "expected error set type, found {}", .{rhs_ty});
+ return sema.fail(block, rhs_src, "expected error set type, found '{}'", .{rhs_ty.fmt(sema.mod)});
// Anything merged with anyerror is anyerror.
if (lhs_ty.tag() == .anyerror or rhs_ty.tag() == .anyerror) {
return Air.Inst.Ref.anyerror_type;
}
- // Resolve both error sets now.
- const lhs_names = lhs_ty.errorSetNames();
- const rhs_names = rhs_ty.errorSetNames();
-
- // TODO do we really want to create a Decl for this?
- // The reason we do it right now is for memory management.
- var anon_decl = try block.startAnonDecl(src);
- defer anon_decl.deinit();
- var names = Module.ErrorSet.NameMap{};
- // TODO: Guess is an upper bound, but maybe this needs to be reduced by computing the exact size first.
- try names.ensureUnusedCapacity(anon_decl.arena(), @intCast(u32, lhs_names.len + rhs_names.len));
- for (lhs_names) |name| {
- names.putAssumeCapacityNoClobber(name, {});
+ if (lhs_ty.castTag(.error_set_inferred)) |payload| {
+ try sema.resolveInferredErrorSet(block, src, payload.data);
+ // isAnyError might have changed from a false negative to a true positive after resolution.
+ if (lhs_ty.isAnyError()) {
+ return Air.Inst.Ref.anyerror_type;
+ }
}
- for (rhs_names) |name| {
- names.putAssumeCapacity(name, {});
+ if (rhs_ty.castTag(.error_set_inferred)) |payload| {
+ try sema.resolveInferredErrorSet(block, src, payload.data);
+ // isAnyError might have changed from a false negative to a true positive after resolution.
+ if (rhs_ty.isAnyError()) {
+ return Air.Inst.Ref.anyerror_type;
+ }
}
- const err_set_ty = try Type.Tag.error_set_merged.create(anon_decl.arena(), names);
- const err_set_decl = try anon_decl.finish(
- Type.type,
- try Value.Tag.ty.create(anon_decl.arena(), err_set_ty),
- );
- try sema.mod.declareDeclDependency(sema.owner_decl, err_set_decl);
+ const err_set_ty = try lhs_ty.errorSetMerge(sema.arena, rhs_ty);
return sema.addType(err_set_ty);
}
@@ -5048,25 +6392,25 @@ fn zirEnumToInt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A
const inst_data = sema.code.instructions.items(.data)[inst].un_node;
const src = inst_data.src();
const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
- const operand = sema.resolveInst(inst_data.operand);
+ const operand = try sema.resolveInst(inst_data.operand);
const operand_ty = sema.typeOf(operand);
const enum_tag: Air.Inst.Ref = switch (operand_ty.zigTypeTag()) {
.Enum => operand,
- .Union => {
- //if (!operand_ty.unionHasTag()) {
- // return sema.fail(
- // block,
- // operand_src,
- // "untagged union '{}' cannot be converted to integer",
- // .{dest_ty_src},
- // );
- //}
- return sema.fail(block, operand_src, "TODO zirEnumToInt for tagged unions", .{});
+ .Union => blk: {
+ const tag_ty = operand_ty.unionTagType() orelse {
+ return sema.fail(
+ block,
+ operand_src,
+ "untagged union '{}' cannot be converted to integer",
+ .{src},
+ );
+ };
+ break :blk try sema.unionToTag(block, tag_ty, operand, operand_src);
},
else => {
- return sema.fail(block, operand_src, "expected enum or tagged union, found {}", .{
- operand_ty,
+ return sema.fail(block, operand_src, "expected enum or tagged union, found '{}'", .{
+ operand_ty.fmt(sema.mod),
});
},
};
@@ -5090,18 +6434,18 @@ fn zirEnumToInt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A
}
fn zirIntToEnum(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
- const target = sema.mod.getTarget();
const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
const src = inst_data.src();
const dest_ty_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node };
const dest_ty = try sema.resolveType(block, dest_ty_src, extra.lhs);
- const operand = sema.resolveInst(extra.rhs);
+ const operand = try sema.resolveInst(extra.rhs);
if (dest_ty.zigTypeTag() != .Enum) {
- return sema.fail(block, dest_ty_src, "expected enum, found {}", .{dest_ty});
+ return sema.fail(block, dest_ty_src, "expected enum, found '{}'", .{dest_ty.fmt(sema.mod)});
}
+ _ = try sema.checkIntType(block, operand_src, sema.typeOf(operand));
if (try sema.resolveMaybeUndefVal(block, operand_src, operand)) |int_val| {
if (dest_ty.isNonexhaustiveEnum()) {
@@ -5110,24 +6454,24 @@ fn zirIntToEnum(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A
if (int_val.isUndef()) {
return sema.failWithUseOfUndef(block, operand_src);
}
- if (!dest_ty.enumHasInt(int_val, target)) {
+ if (!(try sema.enumHasInt(block, src, dest_ty, int_val))) {
const msg = msg: {
const msg = try sema.errMsg(
block,
src,
- "enum '{}' has no tag with value {}",
- .{ dest_ty, int_val },
+ "enum '{}' has no tag with value '{}'",
+ .{ dest_ty.fmt(sema.mod), int_val.fmtValue(sema.typeOf(operand), sema.mod) },
);
errdefer msg.destroy(sema.gpa);
try sema.mod.errNoteNonLazy(
- dest_ty.declSrcLoc(),
+ dest_ty.declSrcLoc(sema.mod),
msg,
"enum declared here",
.{},
);
break :msg msg;
};
- return sema.failWithOwnedErrorMsg(msg);
+ return sema.failWithOwnedErrorMsg(block, msg);
}
return sema.addConstant(dest_ty, int_val);
}
@@ -5148,32 +6492,63 @@ fn zirOptionalPayloadPtr(
defer tracy.end();
const inst_data = sema.code.instructions.items(.data)[inst].un_node;
- const optional_ptr = sema.resolveInst(inst_data.operand);
+ const optional_ptr = try sema.resolveInst(inst_data.operand);
+ const src = inst_data.src();
+
+ return sema.analyzeOptionalPayloadPtr(block, src, optional_ptr, safety_check, false);
+}
+
+fn analyzeOptionalPayloadPtr(
+ sema: *Sema,
+ block: *Block,
+ src: LazySrcLoc,
+ optional_ptr: Air.Inst.Ref,
+ safety_check: bool,
+ initializing: bool,
+) CompileError!Air.Inst.Ref {
const optional_ptr_ty = sema.typeOf(optional_ptr);
assert(optional_ptr_ty.zigTypeTag() == .Pointer);
- const src = inst_data.src();
const opt_type = optional_ptr_ty.elemType();
if (opt_type.zigTypeTag() != .Optional) {
- return sema.fail(block, src, "expected optional type, found {}", .{opt_type});
+ return sema.fail(block, src, "expected optional type, found '{}'", .{opt_type.fmt(sema.mod)});
}
const child_type = try opt_type.optionalChildAlloc(sema.arena);
- const child_pointer = try Type.ptr(sema.arena, .{
+ const child_pointer = try Type.ptr(sema.arena, sema.mod, .{
.pointee_type = child_type,
.mutable = !optional_ptr_ty.isConstPtr(),
.@"addrspace" = optional_ptr_ty.ptrAddressSpace(),
});
- if (try sema.resolveDefinedValue(block, src, optional_ptr)) |pointer_val| {
- if (try sema.pointerDeref(block, src, pointer_val, optional_ptr_ty)) |val| {
+ if (try sema.resolveDefinedValue(block, src, optional_ptr)) |ptr_val| {
+ if (initializing) {
+ if (!ptr_val.isComptimeMutablePtr()) {
+ // If the pointer resulting from this function was stored at comptime,
+ // the optional non-null bit would be set that way. But in this case,
+ // we need to emit a runtime instruction to do it.
+ try sema.requireRuntimeBlock(block, src);
+ _ = try block.addTyOp(.optional_payload_ptr_set, child_pointer, optional_ptr);
+ }
+ return sema.addConstant(
+ child_pointer,
+ try Value.Tag.opt_payload_ptr.create(sema.arena, .{
+ .container_ptr = ptr_val,
+ .container_ty = optional_ptr_ty.childType(),
+ }),
+ );
+ }
+ if (try sema.pointerDeref(block, src, ptr_val, optional_ptr_ty)) |val| {
if (val.isNull()) {
return sema.fail(block, src, "unable to unwrap null", .{});
}
// The same Value represents the pointer to the optional and the payload.
return sema.addConstant(
child_pointer,
- try Value.Tag.opt_payload_ptr.create(sema.arena, pointer_val),
+ try Value.Tag.opt_payload_ptr.create(sema.arena, .{
+ .container_ptr = ptr_val,
+ .container_ty = optional_ptr_ty.childType(),
+ }),
);
}
}
@@ -5183,7 +6558,11 @@ fn zirOptionalPayloadPtr(
const is_non_null = try block.addUnOp(.is_non_null_ptr, optional_ptr);
try sema.addSafetyCheck(block, is_non_null, .unwrap_null);
}
- return block.addTyOp(.optional_payload_ptr, child_pointer, optional_ptr);
+ const air_tag: Air.Inst.Tag = if (initializing)
+ .optional_payload_ptr_set
+ else
+ .optional_payload_ptr;
+ return block.addTyOp(air_tag, child_pointer, optional_ptr);
}
/// Value in, value out.
@@ -5198,7 +6577,7 @@ fn zirOptionalPayload(
const inst_data = sema.code.instructions.items(.data)[inst].un_node;
const src = inst_data.src();
- const operand = sema.resolveInst(inst_data.operand);
+ const operand = try sema.resolveInst(inst_data.operand);
const operand_ty = sema.typeOf(operand);
const result_ty = switch (operand_ty.zigTypeTag()) {
.Optional => try operand_ty.optionalChildAlloc(sema.arena),
@@ -5207,7 +6586,7 @@ fn zirOptionalPayload(
return sema.failWithExpectedOptionalType(block, src, operand_ty);
}
const ptr_info = operand_ty.ptrInfo().data;
- break :t try Type.ptr(sema.arena, .{
+ break :t try Type.ptr(sema.arena, sema.mod, .{
.pointee_type = try ptr_info.pointee_type.copy(sema.arena),
.@"align" = ptr_info.@"align",
.@"addrspace" = ptr_info.@"addrspace",
@@ -5250,27 +6629,45 @@ fn zirErrUnionPayload(
const inst_data = sema.code.instructions.items(.data)[inst].un_node;
const src = inst_data.src();
- const operand = sema.resolveInst(inst_data.operand);
+ const operand = try sema.resolveInst(inst_data.operand);
const operand_src = src;
- const operand_ty = sema.typeOf(operand);
- if (operand_ty.zigTypeTag() != .ErrorUnion)
- return sema.fail(block, operand_src, "expected error union type, found '{}'", .{operand_ty});
+ const err_union_ty = sema.typeOf(operand);
+ if (err_union_ty.zigTypeTag() != .ErrorUnion) {
+ return sema.fail(block, operand_src, "expected error union type, found '{}'", .{
+ err_union_ty.fmt(sema.mod),
+ });
+ }
+ return sema.analyzeErrUnionPayload(block, src, err_union_ty, operand, operand_src, safety_check);
+}
- if (try sema.resolveDefinedValue(block, src, operand)) |val| {
+fn analyzeErrUnionPayload(
+ sema: *Sema,
+ block: *Block,
+ src: LazySrcLoc,
+ err_union_ty: Type,
+ operand: Zir.Inst.Ref,
+ operand_src: LazySrcLoc,
+ safety_check: bool,
+) CompileError!Air.Inst.Ref {
+ const payload_ty = err_union_ty.errorUnionPayload();
+ if (try sema.resolveDefinedValue(block, operand_src, operand)) |val| {
if (val.getError()) |name| {
return sema.fail(block, src, "caught unexpected error '{s}'", .{name});
}
const data = val.castTag(.eu_payload).?.data;
- const result_ty = operand_ty.errorUnionPayload();
- return sema.addConstant(result_ty, data);
+ return sema.addConstant(payload_ty, data);
}
+
try sema.requireRuntimeBlock(block, src);
- if (safety_check and block.wantSafety()) {
- const is_non_err = try block.addUnOp(.is_err, operand);
- try sema.addSafetyCheck(block, is_non_err, .unwrap_errunion);
+
+ // If the error set has no fields then no safety check is needed.
+ if (safety_check and block.wantSafety() and
+ !err_union_ty.errorUnionSet().errorSetIsEmpty())
+ {
+ try sema.panicUnwrapError(block, src, operand, .unwrap_errunion_err, .is_non_err);
}
- const result_ty = operand_ty.errorUnionPayload();
- return block.addTyOp(.unwrap_errunion_payload, result_ty, operand);
+
+ return block.addTyOp(.unwrap_errunion_payload, payload_ty, operand);
}
/// Pointer in, pointer out.
@@ -5284,39 +6681,83 @@ fn zirErrUnionPayloadPtr(
defer tracy.end();
const inst_data = sema.code.instructions.items(.data)[inst].un_node;
+ const operand = try sema.resolveInst(inst_data.operand);
const src = inst_data.src();
- const operand = sema.resolveInst(inst_data.operand);
+
+ return sema.analyzeErrUnionPayloadPtr(block, src, operand, safety_check, false);
+}
+
+fn analyzeErrUnionPayloadPtr(
+ sema: *Sema,
+ block: *Block,
+ src: LazySrcLoc,
+ operand: Air.Inst.Ref,
+ safety_check: bool,
+ initializing: bool,
+) CompileError!Air.Inst.Ref {
const operand_ty = sema.typeOf(operand);
assert(operand_ty.zigTypeTag() == .Pointer);
- if (operand_ty.elemType().zigTypeTag() != .ErrorUnion)
- return sema.fail(block, src, "expected error union type, found {}", .{operand_ty.elemType()});
+ if (operand_ty.elemType().zigTypeTag() != .ErrorUnion) {
+ return sema.fail(block, src, "expected error union type, found '{}'", .{
+ operand_ty.elemType().fmt(sema.mod),
+ });
+ }
- const payload_ty = operand_ty.elemType().errorUnionPayload();
- const operand_pointer_ty = try Type.ptr(sema.arena, .{
+ const err_union_ty = operand_ty.elemType();
+ const payload_ty = err_union_ty.errorUnionPayload();
+ const operand_pointer_ty = try Type.ptr(sema.arena, sema.mod, .{
.pointee_type = payload_ty,
.mutable = !operand_ty.isConstPtr(),
.@"addrspace" = operand_ty.ptrAddressSpace(),
});
- if (try sema.resolveDefinedValue(block, src, operand)) |pointer_val| {
- if (try sema.pointerDeref(block, src, pointer_val, operand_ty)) |val| {
+ if (try sema.resolveDefinedValue(block, src, operand)) |ptr_val| {
+ if (initializing) {
+ if (!ptr_val.isComptimeMutablePtr()) {
+ // If the pointer resulting from this function was stored at comptime,
+ // the error union error code would be set that way. But in this case,
+ // we need to emit a runtime instruction to do it.
+ try sema.requireRuntimeBlock(block, src);
+ _ = try block.addTyOp(.errunion_payload_ptr_set, operand_pointer_ty, operand);
+ }
+ return sema.addConstant(
+ operand_pointer_ty,
+ try Value.Tag.eu_payload_ptr.create(sema.arena, .{
+ .container_ptr = ptr_val,
+ .container_ty = operand_ty.elemType(),
+ }),
+ );
+ }
+ if (try sema.pointerDeref(block, src, ptr_val, operand_ty)) |val| {
if (val.getError()) |name| {
return sema.fail(block, src, "caught unexpected error '{s}'", .{name});
}
+
return sema.addConstant(
operand_pointer_ty,
- try Value.Tag.eu_payload_ptr.create(sema.arena, pointer_val),
+ try Value.Tag.eu_payload_ptr.create(sema.arena, .{
+ .container_ptr = ptr_val,
+ .container_ty = operand_ty.elemType(),
+ }),
);
}
}
try sema.requireRuntimeBlock(block, src);
- if (safety_check and block.wantSafety()) {
- const is_non_err = try block.addUnOp(.is_err, operand);
- try sema.addSafetyCheck(block, is_non_err, .unwrap_errunion);
+
+ // If the error set has no fields then no safety check is needed.
+ if (safety_check and block.wantSafety() and
+ !err_union_ty.errorUnionSet().errorSetIsEmpty())
+ {
+ try sema.panicUnwrapError(block, src, operand, .unwrap_errunion_err_ptr, .is_non_err_ptr);
}
- return block.addTyOp(.unwrap_errunion_payload_ptr, operand_pointer_ty, operand);
+
+ const air_tag: Air.Inst.Tag = if (initializing)
+ .errunion_payload_ptr_set
+ else
+ .unwrap_errunion_payload_ptr;
+ return block.addTyOp(air_tag, operand_pointer_ty, operand);
}
/// Value in, value out
@@ -5326,10 +6767,13 @@ fn zirErrUnionCode(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileErro
const inst_data = sema.code.instructions.items(.data)[inst].un_node;
const src = inst_data.src();
- const operand = sema.resolveInst(inst_data.operand);
+ const operand = try sema.resolveInst(inst_data.operand);
const operand_ty = sema.typeOf(operand);
- if (operand_ty.zigTypeTag() != .ErrorUnion)
- return sema.fail(block, src, "expected error union type, found '{}'", .{operand_ty});
+ if (operand_ty.zigTypeTag() != .ErrorUnion) {
+ return sema.fail(block, src, "expected error union type, found '{}'", .{
+ operand_ty.fmt(sema.mod),
+ });
+ }
const result_ty = operand_ty.errorUnionSet();
@@ -5349,12 +6793,15 @@ fn zirErrUnionCodePtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileE
const inst_data = sema.code.instructions.items(.data)[inst].un_node;
const src = inst_data.src();
- const operand = sema.resolveInst(inst_data.operand);
+ const operand = try sema.resolveInst(inst_data.operand);
const operand_ty = sema.typeOf(operand);
assert(operand_ty.zigTypeTag() == .Pointer);
- if (operand_ty.elemType().zigTypeTag() != .ErrorUnion)
- return sema.fail(block, src, "expected error union type, found {}", .{operand_ty.elemType()});
+ if (operand_ty.elemType().zigTypeTag() != .ErrorUnion) {
+ return sema.fail(block, src, "expected error union type, found '{}'", .{
+ operand_ty.elemType().fmt(sema.mod),
+ });
+ }
const result_ty = operand_ty.elemType().errorUnionSet();
@@ -5375,10 +6822,13 @@ fn zirEnsureErrPayloadVoid(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Com
const inst_data = sema.code.instructions.items(.data)[inst].un_tok;
const src = inst_data.src();
- const operand = sema.resolveInst(inst_data.operand);
+ const operand = try sema.resolveInst(inst_data.operand);
const operand_ty = sema.typeOf(operand);
- if (operand_ty.zigTypeTag() != .ErrorUnion)
- return sema.fail(block, src, "expected error union type, found '{}'", .{operand_ty});
+ if (operand_ty.zigTypeTag() != .ErrorUnion) {
+ return sema.fail(block, src, "expected error union type, found '{}'", .{
+ operand_ty.fmt(sema.mod),
+ });
+ }
if (operand_ty.errorUnionPayload().zigTypeTag() != .Void) {
return sema.fail(block, src, "expression value is ignored", .{});
}
@@ -5395,9 +6845,34 @@ fn zirFunc(
const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
const extra = sema.code.extraData(Zir.Inst.Func, inst_data.payload_index);
+ const target = sema.mod.getTarget();
+ const ret_ty_src = inst_data.src(); // TODO better source location
+
var extra_index = extra.end;
- const ret_ty_body = sema.code.extra[extra_index..][0..extra.data.ret_body_len];
- extra_index += ret_ty_body.len;
+
+ const ret_ty: Type = switch (extra.data.ret_body_len) {
+ 0 => Type.void,
+ 1 => blk: {
+ const ret_ty_ref = @intToEnum(Zir.Inst.Ref, sema.code.extra[extra_index]);
+ extra_index += 1;
+ if (sema.resolveType(block, ret_ty_src, ret_ty_ref)) |ret_ty| {
+ break :blk ret_ty;
+ } else |err| switch (err) {
+ error.GenericPoison => {
+ break :blk Type.initTag(.generic_poison);
+ },
+ else => |e| return e,
+ }
+ },
+ else => blk: {
+ const ret_ty_body = sema.code.extra[extra_index..][0..extra.data.ret_body_len];
+ extra_index += ret_ty_body.len;
+
+ const ret_ty_val = try sema.resolveGenericBody(block, ret_ty_src, ret_ty_body, inst, Type.type);
+ var buffer: Value.ToTypeBuffer = undefined;
+ break :blk try ret_ty_val.toType(&buffer).copy(sema.arena);
+ },
+ };
var src_locs: Zir.Inst.Func.SrcLocs = undefined;
const has_body = extra.data.body_len != 0;
@@ -5406,7 +6881,11 @@ fn zirFunc(
src_locs = sema.code.extraData(Zir.Inst.Func.SrcLocs, extra_index).data;
}
- const cc: std.builtin.CallingConvention = if (sema.owner_decl.is_exported)
+ // If this instruction has a body it means it's the type of the `owner_decl`
+ // otherwise it's a function type without a `callconv` attribute and should
+ // never be `.C`.
+ // NOTE: revisit when doing #1717
+ const cc: std.builtin.CallingConvention = if (sema.owner_decl.is_exported and has_body)
.C
else
.Unspecified;
@@ -5415,67 +6894,160 @@ fn zirFunc(
block,
inst_data.src_node,
inst,
- ret_ty_body,
+ 0,
+ target_util.defaultAddressSpace(target, .function),
+ FuncLinkSection.default,
cc,
- Value.@"null",
+ ret_ty,
false,
inferred_error_set,
false,
has_body,
src_locs,
null,
+ 0,
);
}
+fn resolveGenericBody(
+ sema: *Sema,
+ block: *Block,
+ src: LazySrcLoc,
+ body: []const Zir.Inst.Index,
+ func_inst: Zir.Inst.Index,
+ dest_ty: Type,
+) !Value {
+ assert(body.len != 0);
+
+ const err = err: {
+ // Make sure any nested param instructions don't clobber our work.
+ const prev_params = block.params;
+ block.params = .{};
+ defer {
+ block.params.deinit(sema.gpa);
+ block.params = prev_params;
+ }
+ const uncasted = sema.resolveBody(block, body, func_inst) catch |err| break :err err;
+ const result = sema.coerce(block, dest_ty, uncasted, src) catch |err| break :err err;
+ const val = sema.resolveConstValue(block, src, result) catch |err| break :err err;
+ return val;
+ };
+ switch (err) {
+ error.GenericPoison => {
+ if (dest_ty.tag() == .type) {
+ return Value.initTag(.generic_poison_type);
+ } else {
+ return Value.initTag(.generic_poison);
+ }
+ },
+ else => |e| return e,
+ }
+}
+
+/// Given a library name, examines if the library name should end up in
+/// `link.File.Options.system_libs` table (for example, libc is always
+/// specified via dedicated flag `link.File.Options.link_libc` instead),
+/// and puts it there if it doesn't exist.
+/// It also dupes the library name which can then be saved as part of the
+/// respective `Decl` (either `ExternFn` or `Var`).
+/// The liveness of the duped library name is tied to liveness of `Module`.
+/// To deallocate, call `deinit` on the respective `Decl` (`ExternFn` or `Var`).
+fn handleExternLibName(
+ sema: *Sema,
+ block: *Block,
+ src_loc: LazySrcLoc,
+ lib_name: []const u8,
+) CompileError![:0]u8 {
+ blk: {
+ const mod = sema.mod;
+ const target = mod.getTarget();
+ log.debug("extern fn symbol expected in lib '{s}'", .{lib_name});
+ if (target_util.is_libc_lib_name(target, lib_name)) {
+ if (!mod.comp.bin_file.options.link_libc) {
+ return sema.fail(
+ block,
+ src_loc,
+ "dependency on libc must be explicitly specified in the build command",
+ .{},
+ );
+ }
+ mod.comp.bin_file.options.link_libc = true;
+ break :blk;
+ }
+ if (target_util.is_libcpp_lib_name(target, lib_name)) {
+ if (!mod.comp.bin_file.options.link_libcpp) {
+ return sema.fail(
+ block,
+ src_loc,
+ "dependency on libc++ must be explicitly specified in the build command",
+ .{},
+ );
+ }
+ mod.comp.bin_file.options.link_libcpp = true;
+ break :blk;
+ }
+ if (mem.eql(u8, lib_name, "unwind")) {
+ mod.comp.bin_file.options.link_libunwind = true;
+ break :blk;
+ }
+ if (!target.isWasm() and !mod.comp.bin_file.options.pic) {
+ return sema.fail(
+ block,
+ src_loc,
+ "dependency on dynamic library '{s}' requires enabling Position Independent Code. Fixed by `-l{s}` or `-fPIC`.",
+ .{ lib_name, lib_name },
+ );
+ }
+ mod.comp.stage1AddLinkLib(lib_name) catch |err| {
+ return sema.fail(block, src_loc, "unable to add link lib '{s}': {s}", .{
+ lib_name, @errorName(err),
+ });
+ };
+ }
+ return sema.gpa.dupeZ(u8, lib_name);
+}
+
+const FuncLinkSection = union(enum) {
+ generic,
+ default,
+ explicit: [*:0]const u8,
+};
+
fn funcCommon(
sema: *Sema,
block: *Block,
src_node_offset: i32,
func_inst: Zir.Inst.Index,
- ret_ty_body: []const Zir.Inst.Index,
- cc: std.builtin.CallingConvention,
- align_val: Value,
+ /// null means generic poison
+ alignment: ?u32,
+ /// null means generic poison
+ address_space: ?std.builtin.AddressSpace,
+ /// outer null means generic poison; inner null means default link section
+ section: FuncLinkSection,
+ /// null means generic poison
+ cc: ?std.builtin.CallingConvention,
+ /// this might be Type.generic_poison
+ bare_return_type: Type,
var_args: bool,
inferred_error_set: bool,
is_extern: bool,
has_body: bool,
src_locs: Zir.Inst.Func.SrcLocs,
opt_lib_name: ?[]const u8,
+ noalias_bits: u32,
) CompileError!Air.Inst.Ref {
const ret_ty_src: LazySrcLoc = .{ .node_offset_fn_type_ret_ty = src_node_offset };
+ const cc_src: LazySrcLoc = .{ .node_offset_fn_type_cc = src_node_offset };
- // The return type body might be a type expression that depends on generic parameters.
- // In such case we need to use a generic_poison value for the return type and mark
- // the function as generic.
- var is_generic = false;
- const bare_return_type: Type = ret_ty: {
- if (ret_ty_body.len == 0) break :ret_ty Type.void;
-
- const err = err: {
- // Make sure any nested param instructions don't clobber our work.
- const prev_params = block.params;
- block.params = .{};
- defer {
- block.params.deinit(sema.gpa);
- block.params = prev_params;
- }
- if (sema.resolveBody(block, ret_ty_body, func_inst)) |ret_ty_inst| {
- if (sema.analyzeAsType(block, ret_ty_src, ret_ty_inst)) |ret_ty| {
- break :ret_ty ret_ty;
- } else |err| break :err err;
- } else |err| break :err err;
- };
- switch (err) {
- error.GenericPoison => {
- // The type is not available until the generic instantiation.
- is_generic = true;
- break :ret_ty Type.initTag(.generic_poison);
- },
- else => |e| return e,
- }
- };
-
- const mod = sema.mod;
+ var is_generic = bare_return_type.tag() == .generic_poison or
+ alignment == null or
+ address_space == null or
+ section == .generic or
+ cc == null;
+ // Check for generic params.
+ for (block.params.items) |param| {
+ if (param.ty.tag() == .generic_poison) is_generic = true;
+ }
const new_func: *Module.Fn = new_func: {
if (!has_body) break :new_func undefined;
@@ -5492,36 +7064,28 @@ fn funcCommon(
errdefer if (maybe_inferred_error_set_node) |node| sema.gpa.destroy(node);
// Note: no need to errdefer since this will still be in its default state at the end of the function.
- const target = mod.getTarget();
-
+ const target = sema.mod.getTarget();
const fn_ty: Type = fn_ty: {
- const alignment: u32 = if (align_val.tag() == .null_value) 0 else a: {
- const alignment = @intCast(u32, align_val.toUnsignedInt());
- if (alignment == target_util.defaultFunctionAlignment(target)) {
- break :a 0;
- } else {
- break :a alignment;
- }
- };
-
// Hot path for some common function types.
// TODO can we eliminate some of these Type tag values? seems unnecessarily complicated.
- if (!is_generic and block.params.items.len == 0 and !var_args and
- alignment == 0 and !inferred_error_set)
+ if (!is_generic and block.params.items.len == 0 and !var_args and !inferred_error_set and
+ alignment.? == 0 and
+ address_space.? == target_util.defaultAddressSpace(target, .function) and
+ section == .default)
{
- if (bare_return_type.zigTypeTag() == .NoReturn and cc == .Unspecified) {
+ if (bare_return_type.zigTypeTag() == .NoReturn and cc.? == .Unspecified) {
break :fn_ty Type.initTag(.fn_noreturn_no_args);
}
- if (bare_return_type.zigTypeTag() == .Void and cc == .Unspecified) {
+ if (bare_return_type.zigTypeTag() == .Void and cc.? == .Unspecified) {
break :fn_ty Type.initTag(.fn_void_no_args);
}
- if (bare_return_type.zigTypeTag() == .NoReturn and cc == .Naked) {
+ if (bare_return_type.zigTypeTag() == .NoReturn and cc.? == .Naked) {
break :fn_ty Type.initTag(.fn_naked_noreturn_no_args);
}
- if (bare_return_type.zigTypeTag() == .Void and cc == .C) {
+ if (bare_return_type.zigTypeTag() == .Void and cc.? == .C) {
break :fn_ty Type.initTag(.fn_ccc_void_no_args);
}
}
@@ -5529,17 +7093,31 @@ fn funcCommon(
const param_types = try sema.arena.alloc(Type, block.params.items.len);
const comptime_params = try sema.arena.alloc(bool, block.params.items.len);
for (block.params.items) |param, i| {
- const param_src: LazySrcLoc = .{ .node_offset = src_node_offset }; // TODO better src
+ const param_src = LazySrcLoc.nodeOffset(src_node_offset); // TODO better soruce location
param_types[i] = param.ty;
comptime_params[i] = param.is_comptime or
try sema.typeRequiresComptime(block, param_src, param.ty);
is_generic = is_generic or comptime_params[i] or param.ty.tag() == .generic_poison;
+ if (is_extern and is_generic) {
+ // TODO add note: function is generic because of this parameter
+ return sema.fail(block, param_src, "extern function cannot be generic", .{});
+ }
}
- is_generic = is_generic or
- try sema.typeRequiresComptime(block, ret_ty_src, bare_return_type);
+ const ret_poison = if (!is_generic) rp: {
+ if (sema.typeRequiresComptime(block, ret_ty_src, bare_return_type)) |ret_comptime| {
+ is_generic = ret_comptime;
+ break :rp bare_return_type.tag() == .generic_poison;
+ } else |err| switch (err) {
+ error.GenericPoison => {
+ is_generic = true;
+ break :rp true;
+ },
+ else => |e| return e,
+ }
+ } else bare_return_type.tag() == .generic_poison;
- const return_type = if (!inferred_error_set or bare_return_type.tag() == .generic_poison)
+ const return_type = if (!inferred_error_set or ret_poison)
bare_return_type
else blk: {
const node = try sema.gpa.create(Module.Fn.InferredErrorSetListNode);
@@ -5553,68 +7131,101 @@ fn funcCommon(
});
};
+ // These locals are pulled out from the init expression below to work around
+ // a stage1 compiler bug.
+ // In the case of generic calling convention, or generic alignment, we use
+ // default values which are only meaningful for the generic function, *not*
+ // the instantiation, which can depend on comptime parameters.
+ // Related proposal: https://github.com/ziglang/zig/issues/11834
+ const cc_workaround = cc orelse .Unspecified;
+ const align_workaround = alignment orelse 0;
+
+ const arch = sema.mod.getTarget().cpu.arch;
+ if (switch (cc_workaround) {
+ .Unspecified, .C, .Naked, .Async, .Inline => null,
+ .Interrupt => switch (arch) {
+ .i386, .x86_64, .avr, .msp430 => null,
+ else => @as([]const u8, "i386, x86_64, AVR, and MSP430"),
+ },
+ .Signal => switch (arch) {
+ .avr => null,
+ else => @as([]const u8, "AVR"),
+ },
+ .Stdcall, .Fastcall, .Thiscall => switch (arch) {
+ .i386 => null,
+ else => @as([]const u8, "i386"),
+ },
+ .Vectorcall => switch (arch) {
+ .i386, .aarch64, .aarch64_be, .aarch64_32 => null,
+ else => @as([]const u8, "i386 and AArch64"),
+ },
+ .APCS, .AAPCS, .AAPCSVFP => switch (arch) {
+ .arm, .armeb, .aarch64, .aarch64_be, .aarch64_32 => null,
+ else => @as([]const u8, "ARM"),
+ },
+ .SysV, .Win64 => switch (arch) {
+ .x86_64 => null,
+ else => @as([]const u8, "x86_64"),
+ },
+ .PtxKernel => switch (arch) {
+ .nvptx, .nvptx64 => null,
+ else => @as([]const u8, "nvptx and nvptx64"),
+ },
+ }) |allowed_platform| {
+ return sema.fail(block, cc_src, "callconv '{s}' is only available on {s}, not {s}", .{
+ @tagName(cc_workaround),
+ allowed_platform,
+ @tagName(arch),
+ });
+ }
+
break :fn_ty try Type.Tag.function.create(sema.arena, .{
.param_types = param_types,
.comptime_params = comptime_params.ptr,
.return_type = return_type,
- .cc = cc,
- .alignment = alignment,
+ .cc = cc_workaround,
+ .cc_is_generic = cc == null,
+ .alignment = align_workaround,
+ .align_is_generic = alignment == null,
+ .section_is_generic = section == .generic,
+ .addrspace_is_generic = address_space == null,
.is_var_args = var_args,
.is_generic = is_generic,
+ .noalias_bits = noalias_bits,
});
};
- if (opt_lib_name) |lib_name| blk: {
- const lib_name_src: LazySrcLoc = .{ .node_offset_lib_name = src_node_offset };
- log.debug("extern fn symbol expected in lib '{s}'", .{lib_name});
- if (target_util.is_libc_lib_name(target, lib_name)) {
- if (!mod.comp.bin_file.options.link_libc) {
- return sema.fail(
- block,
- lib_name_src,
- "dependency on libc must be explicitly specified in the build command",
- .{},
- );
- }
- mod.comp.bin_file.options.link_libc = true;
- break :blk;
- }
- if (target_util.is_libcpp_lib_name(target, lib_name)) {
- if (!mod.comp.bin_file.options.link_libcpp) {
- return sema.fail(
- block,
- lib_name_src,
- "dependency on libc++ must be explicitly specified in the build command",
- .{},
- );
- }
- mod.comp.bin_file.options.link_libcpp = true;
- break :blk;
- }
- if (mem.eql(u8, lib_name, "unwind")) {
- mod.comp.bin_file.options.link_libunwind = true;
- break :blk;
- }
- if (!target.isWasm() and !mod.comp.bin_file.options.pic) {
- return sema.fail(
- block,
- lib_name_src,
- "dependency on dynamic library '{s}' requires enabling Position Independent Code. Fixed by `-l{s}` or `-fPIC`.",
- .{ lib_name, lib_name },
- );
+ if (sema.owner_decl.owns_tv) {
+ switch (section) {
+ .generic => sema.owner_decl.@"linksection" = undefined,
+ .default => sema.owner_decl.@"linksection" = null,
+ .explicit => |s| sema.owner_decl.@"linksection" = s,
}
- mod.comp.stage1AddLinkLib(lib_name) catch |err| {
- return sema.fail(block, lib_name_src, "unable to add link lib '{s}': {s}", .{
- lib_name, @errorName(err),
- });
- };
+ if (alignment) |a| sema.owner_decl.@"align" = a;
+ if (address_space) |a| sema.owner_decl.@"addrspace" = a;
}
if (is_extern) {
- return sema.addConstant(
- fn_ty,
- try Value.Tag.extern_fn.create(sema.arena, sema.owner_decl),
- );
+ const new_extern_fn = try sema.gpa.create(Module.ExternFn);
+ errdefer sema.gpa.destroy(new_extern_fn);
+
+ new_extern_fn.* = Module.ExternFn{
+ .owner_decl = sema.owner_decl_index,
+ .lib_name = null,
+ };
+
+ if (opt_lib_name) |lib_name| {
+ new_extern_fn.lib_name = try sema.handleExternLibName(block, .{
+ .node_offset_lib_name = src_node_offset,
+ }, lib_name);
+ }
+
+ const extern_fn_payload = try sema.arena.create(Value.Payload.ExternFn);
+ extern_fn_payload.* = .{
+ .base = .{ .tag = .extern_fn },
+ .data = new_extern_fn,
+ };
+ return sema.addConstant(fn_ty, Value.initPayload(&extern_fn_payload.base));
}
if (!has_body) {
@@ -5628,16 +7239,26 @@ fn funcCommon(
break :blk if (sema.comptime_args.len == 0) null else sema.comptime_args.ptr;
} else null;
+ const param_names = try sema.gpa.alloc([:0]const u8, block.params.items.len);
+ for (param_names) |*param_name, i| {
+ param_name.* = try sema.gpa.dupeZ(u8, block.params.items[i].name);
+ }
+
+ const hash = new_func.hash;
const fn_payload = try sema.arena.create(Value.Payload.Function);
new_func.* = .{
.state = anal_state,
.zir_body_inst = func_inst,
- .owner_decl = sema.owner_decl,
+ .owner_decl = sema.owner_decl_index,
.comptime_args = comptime_args,
+ .anytype_args = undefined,
+ .hash = hash,
.lbrace_line = src_locs.lbrace_line,
.rbrace_line = src_locs.rbrace_line,
.lbrace_column = @truncate(u16, src_locs.columns),
.rbrace_column = @truncate(u16, src_locs.columns >> 16),
+ .param_names = param_names,
+ .branch_quota = default_branch_quota,
};
if (maybe_inferred_error_set_node) |node| {
new_func.inferred_error_sets.prepend(node);
@@ -5662,24 +7283,32 @@ fn zirParam(
const param_name = sema.code.nullTerminatedString(extra.data.name);
const body = sema.code.extra[extra.end..][0..extra.data.body_len];
- // TODO check if param_name shadows a Decl. This only needs to be done if
- // usingnamespace is implemented.
- _ = param_name;
-
// We could be in a generic function instantiation, or we could be evaluating a generic
// function without any comptime args provided.
const param_ty = param_ty: {
const err = err: {
// Make sure any nested param instructions don't clobber our work.
const prev_params = block.params;
+ const prev_preallocated_new_func = sema.preallocated_new_func;
block.params = .{};
+ sema.preallocated_new_func = null;
defer {
block.params.deinit(sema.gpa);
block.params = prev_params;
+ sema.preallocated_new_func = prev_preallocated_new_func;
}
if (sema.resolveBody(block, body, inst)) |param_ty_inst| {
if (sema.analyzeAsType(block, src, param_ty_inst)) |param_ty| {
+ if (param_ty.zigTypeTag() == .Fn and param_ty.fnInfo().is_generic) {
+ // zirFunc will not emit error.GenericPoison to build a
+ // partial type for generic functions but we still need to
+ // detect if a function parameter is a generic function
+ // to force the parent function to also be generic.
+ if (!sema.inst_map.contains(inst)) {
+ break :err error.GenericPoison;
+ }
+ }
break :param_ty param_ty;
} else |err| break :err err;
} else |err| break :err err;
@@ -5692,6 +7321,7 @@ fn zirParam(
try block.params.append(sema.gpa, .{
.ty = Type.initTag(.generic_poison),
.is_comptime = comptime_syntax,
+ .name = param_name,
});
try sema.inst_map.putNoClobber(sema.gpa, inst, .generic_poison);
return;
@@ -5714,9 +7344,21 @@ fn zirParam(
assert(sema.inst_map.remove(inst));
}
+ if (sema.preallocated_new_func != null) {
+ if (try sema.typeHasOnePossibleValue(block, src, param_ty)) |opv| {
+ // In this case we are instantiating a generic function call with a non-comptime
+ // non-anytype parameter that ended up being a one-possible-type.
+ // We don't want the parameter to be part of the instantiated function type.
+ const result = try sema.addConstant(param_ty, opv);
+ try sema.inst_map.put(sema.gpa, inst, result);
+ return;
+ }
+ }
+
try block.params.append(sema.gpa, .{
.ty = param_ty,
.is_comptime = is_comptime,
+ .name = param_name,
});
const result = try sema.addConstant(param_ty, Value.initTag(.generic_poison));
try sema.inst_map.putNoClobber(sema.gpa, inst, result);
@@ -5732,10 +7374,6 @@ fn zirParamAnytype(
const src = inst_data.src();
const param_name = inst_data.get(sema.code);
- // TODO check if param_name shadows a Decl. This only needs to be done if
- // usingnamespace is implemented.
- _ = param_name;
-
if (sema.inst_map.get(inst)) |air_ref| {
const param_ty = sema.typeOf(air_ref);
if (comptime_syntax or try sema.typeRequiresComptime(block, src, param_ty)) {
@@ -5743,10 +7381,14 @@ fn zirParamAnytype(
// function type of the function instruction in this block.
return;
}
+ if (null != try sema.typeHasOnePossibleValue(block, src, param_ty)) {
+ return;
+ }
// The map is already populated but we do need to add a runtime parameter.
try block.params.append(sema.gpa, .{
.ty = param_ty,
.is_comptime = false,
+ .name = param_name,
});
return;
}
@@ -5756,6 +7398,7 @@ fn zirParamAnytype(
try block.params.append(sema.gpa, .{
.ty = Type.initTag(.generic_poison),
.is_comptime = comptime_syntax,
+ .name = param_name,
});
try sema.inst_map.put(sema.gpa, inst, .generic_poison);
}
@@ -5765,7 +7408,7 @@ fn zirAs(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst
defer tracy.end();
const bin_inst = sema.code.instructions.items(.data)[inst].bin;
- return sema.analyzeAs(block, .unneeded, bin_inst.lhs, bin_inst.rhs);
+ return sema.analyzeAs(block, sema.src, bin_inst.lhs, bin_inst.rhs);
}
fn zirAsNode(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
@@ -5786,7 +7429,8 @@ fn analyzeAs(
zir_operand: Zir.Inst.Ref,
) CompileError!Air.Inst.Ref {
const dest_ty = try sema.resolveType(block, src, zir_dest_type);
- const operand = sema.resolveInst(zir_operand);
+ const operand = try sema.resolveInst(zir_operand);
+ if (dest_ty.tag() == .var_args_param) return operand;
return sema.coerce(block, dest_ty, operand, src);
}
@@ -5796,10 +7440,10 @@ fn zirPtrToInt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
const inst_data = sema.code.instructions.items(.data)[inst].un_node;
const ptr_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
- const ptr = sema.resolveInst(inst_data.operand);
+ const ptr = try sema.resolveInst(inst_data.operand);
const ptr_ty = sema.typeOf(ptr);
if (!ptr_ty.isPtrAtRuntime()) {
- return sema.fail(block, ptr_src, "expected pointer, found '{}'", .{ptr_ty});
+ return sema.fail(block, ptr_src, "expected pointer, found '{}'", .{ptr_ty.fmt(sema.mod)});
}
if (try sema.resolveMaybeUndefVal(block, ptr_src, ptr)) |ptr_val| {
return sema.addConstant(Type.usize, ptr_val);
@@ -5817,7 +7461,7 @@ fn zirFieldVal(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
const field_name_src: LazySrcLoc = .{ .node_offset_field_name = inst_data.src_node };
const extra = sema.code.extraData(Zir.Inst.Field, inst_data.payload_index).data;
const field_name = sema.code.nullTerminatedString(extra.field_name_start);
- const object = sema.resolveInst(extra.lhs);
+ const object = try sema.resolveInst(extra.lhs);
return sema.fieldVal(block, src, object, field_name, field_name_src);
}
@@ -5830,7 +7474,7 @@ fn zirFieldPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
const field_name_src: LazySrcLoc = .{ .node_offset_field_name = inst_data.src_node };
const extra = sema.code.extraData(Zir.Inst.Field, inst_data.payload_index).data;
const field_name = sema.code.nullTerminatedString(extra.field_name_start);
- const object_ptr = sema.resolveInst(extra.lhs);
+ const object_ptr = try sema.resolveInst(extra.lhs);
return sema.fieldPtr(block, src, object_ptr, field_name, field_name_src);
}
@@ -5843,7 +7487,7 @@ fn zirFieldCallBind(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileErr
const field_name_src: LazySrcLoc = .{ .node_offset_field_name = inst_data.src_node };
const extra = sema.code.extraData(Zir.Inst.Field, inst_data.payload_index).data;
const field_name = sema.code.nullTerminatedString(extra.field_name_start);
- const object_ptr = sema.resolveInst(extra.lhs);
+ const object_ptr = try sema.resolveInst(extra.lhs);
return sema.fieldCallBind(block, src, object_ptr, field_name, field_name_src);
}
@@ -5855,7 +7499,7 @@ fn zirFieldValNamed(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileErr
const src = inst_data.src();
const field_name_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node };
const extra = sema.code.extraData(Zir.Inst.FieldNamed, inst_data.payload_index).data;
- const object = sema.resolveInst(extra.lhs);
+ const object = try sema.resolveInst(extra.lhs);
const field_name = try sema.resolveConstString(block, field_name_src, extra.field_name);
return sema.fieldVal(block, src, object, field_name, field_name_src);
}
@@ -5868,20 +7512,19 @@ fn zirFieldPtrNamed(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileErr
const src = inst_data.src();
const field_name_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node };
const extra = sema.code.extraData(Zir.Inst.FieldNamed, inst_data.payload_index).data;
- const object_ptr = sema.resolveInst(extra.lhs);
+ const object_ptr = try sema.resolveInst(extra.lhs);
const field_name = try sema.resolveConstString(block, field_name_src, extra.field_name);
return sema.fieldPtr(block, src, object_ptr, field_name, field_name_src);
}
-fn zirFieldCallBindNamed(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
+fn zirFieldCallBindNamed(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref {
const tracy = trace(@src());
defer tracy.end();
- const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
- const src = inst_data.src();
- const field_name_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node };
- const extra = sema.code.extraData(Zir.Inst.FieldNamed, inst_data.payload_index).data;
- const object_ptr = sema.resolveInst(extra.lhs);
+ const extra = sema.code.extraData(Zir.Inst.FieldNamedNode, extended.operand).data;
+ const src = LazySrcLoc.nodeOffset(extra.node);
+ const field_name_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = extra.node };
+ const object_ptr = try sema.resolveInst(extra.lhs);
const field_name = try sema.resolveConstString(block, field_name_src, extra.field_name);
return sema.fieldCallBind(block, src, object_ptr, field_name, field_name_src);
}
@@ -5891,25 +7534,146 @@ fn zirIntCast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air
defer tracy.end();
const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
- const src = inst_data.src();
const dest_ty_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node };
const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
const dest_ty = try sema.resolveType(block, dest_ty_src, extra.lhs);
- const operand = sema.resolveInst(extra.rhs);
+ const operand = try sema.resolveInst(extra.rhs);
- const dest_is_comptime_int = try sema.checkIntType(block, dest_ty_src, dest_ty);
- _ = try sema.checkIntType(block, operand_src, sema.typeOf(operand));
+ return sema.intCast(block, dest_ty, dest_ty_src, operand, operand_src, true);
+}
+
+fn intCast(
+ sema: *Sema,
+ block: *Block,
+ dest_ty: Type,
+ dest_ty_src: LazySrcLoc,
+ operand: Air.Inst.Ref,
+ operand_src: LazySrcLoc,
+ runtime_safety: bool,
+) CompileError!Air.Inst.Ref {
+ const operand_ty = sema.typeOf(operand);
+ const dest_scalar_ty = try sema.checkIntOrVectorAllowComptime(block, dest_ty, dest_ty_src);
+ const operand_scalar_ty = try sema.checkIntOrVectorAllowComptime(block, operand_ty, operand_src);
if (try sema.isComptimeKnown(block, operand_src, operand)) {
return sema.coerce(block, dest_ty, operand, operand_src);
- } else if (dest_is_comptime_int) {
- return sema.fail(block, src, "unable to cast runtime value to 'comptime_int'", .{});
+ } else if (dest_scalar_ty.zigTypeTag() == .ComptimeInt) {
+ return sema.fail(block, operand_src, "unable to cast runtime value to 'comptime_int'", .{});
+ }
+
+ try sema.checkVectorizableBinaryOperands(block, operand_src, dest_ty, operand_ty, dest_ty_src, operand_src);
+ const is_vector = dest_ty.zigTypeTag() == .Vector;
+
+ if ((try sema.typeHasOnePossibleValue(block, dest_ty_src, dest_ty))) |opv| {
+ // requirement: intCast(u0, input) iff input == 0
+ if (runtime_safety and block.wantSafety()) {
+ try sema.requireRuntimeBlock(block, operand_src);
+ const target = sema.mod.getTarget();
+ const wanted_info = dest_scalar_ty.intInfo(target);
+ const wanted_bits = wanted_info.bits;
+
+ if (wanted_bits == 0) {
+ const zero_inst = try sema.addConstant(sema.typeOf(operand), Value.zero);
+ const is_in_range = try block.addBinOp(.cmp_eq, operand, zero_inst);
+ try sema.addSafetyCheck(block, is_in_range, .cast_truncated_data);
+ }
+ }
+
+ return sema.addConstant(dest_ty, opv);
}
try sema.requireRuntimeBlock(block, operand_src);
- // TODO insert safety check to make sure the value fits in the dest type
+ if (runtime_safety and block.wantSafety()) {
+ const target = sema.mod.getTarget();
+ const actual_info = operand_scalar_ty.intInfo(target);
+ const wanted_info = dest_scalar_ty.intInfo(target);
+ const actual_bits = actual_info.bits;
+ const wanted_bits = wanted_info.bits;
+ const actual_value_bits = actual_bits - @boolToInt(actual_info.signedness == .signed);
+ const wanted_value_bits = wanted_bits - @boolToInt(wanted_info.signedness == .signed);
+
+ // range shrinkage
+ // requirement: int value fits into target type
+ if (wanted_value_bits < actual_value_bits) {
+ const dest_max_val_scalar = try dest_scalar_ty.maxInt(sema.arena, target);
+ const dest_max_val = if (is_vector)
+ try Value.Tag.repeated.create(sema.arena, dest_max_val_scalar)
+ else
+ dest_max_val_scalar;
+ const dest_max = try sema.addConstant(operand_ty, dest_max_val);
+ const diff = try block.addBinOp(.subwrap, dest_max, operand);
+
+ if (actual_info.signedness == .signed) {
+ // Reinterpret the sign-bit as part of the value. This will make
+ // negative differences (`operand` > `dest_max`) appear too big.
+ const unsigned_operand_ty = try Type.Tag.int_unsigned.create(sema.arena, actual_bits);
+ const diff_unsigned = try block.addBitCast(unsigned_operand_ty, diff);
+
+ // If the destination type is signed, then we need to double its
+ // range to account for negative values.
+ const dest_range_val = if (wanted_info.signedness == .signed) range_val: {
+ const range_minus_one = try dest_max_val.shl(Value.one, unsigned_operand_ty, sema.arena, target);
+ break :range_val try sema.intAdd(block, operand_src, range_minus_one, Value.one, unsigned_operand_ty);
+ } else dest_max_val;
+ const dest_range = try sema.addConstant(unsigned_operand_ty, dest_range_val);
+
+ const ok = if (is_vector) ok: {
+ const is_in_range = try block.addCmpVector(diff_unsigned, dest_range, .lte, try sema.addType(operand_ty));
+ const all_in_range = try block.addInst(.{
+ .tag = .reduce,
+ .data = .{ .reduce = .{
+ .operand = is_in_range,
+ .operation = .And,
+ } },
+ });
+ break :ok all_in_range;
+ } else ok: {
+ const is_in_range = try block.addBinOp(.cmp_lte, diff_unsigned, dest_range);
+ break :ok is_in_range;
+ };
+ try sema.addSafetyCheck(block, ok, .cast_truncated_data);
+ } else {
+ const ok = if (is_vector) ok: {
+ const is_in_range = try block.addCmpVector(diff, dest_max, .lte, try sema.addType(operand_ty));
+ const all_in_range = try block.addInst(.{
+ .tag = .reduce,
+ .data = .{ .reduce = .{
+ .operand = is_in_range,
+ .operation = .And,
+ } },
+ });
+ break :ok all_in_range;
+ } else ok: {
+ const is_in_range = try block.addBinOp(.cmp_lte, diff, dest_max);
+ break :ok is_in_range;
+ };
+ try sema.addSafetyCheck(block, ok, .cast_truncated_data);
+ }
+ } else if (actual_info.signedness == .signed and wanted_info.signedness == .unsigned) {
+ // no shrinkage, yes sign loss
+ // requirement: signed to unsigned >= 0
+ const ok = if (is_vector) ok: {
+ const zero_val = try Value.Tag.repeated.create(sema.arena, Value.zero);
+ const zero_inst = try sema.addConstant(operand_ty, zero_val);
+ const is_in_range = try block.addCmpVector(operand, zero_inst, .lte, try sema.addType(operand_ty));
+ const all_in_range = try block.addInst(.{
+ .tag = .reduce,
+ .data = .{ .reduce = .{
+ .operand = is_in_range,
+ .operation = .And,
+ } },
+ });
+ break :ok all_in_range;
+ } else ok: {
+ const zero_inst = try sema.addConstant(operand_ty, Value.zero);
+ const is_in_range = try block.addBinOp(.cmp_gte, operand, zero_inst);
+ break :ok is_in_range;
+ };
+ try sema.addSafetyCheck(block, ok, .cast_truncated_data);
+ }
+ }
return block.addTyOp(.intcast, dest_ty, operand);
}
@@ -5923,7 +7687,61 @@ fn zirBitcast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air
const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
const dest_ty = try sema.resolveType(block, dest_ty_src, extra.lhs);
- const operand = sema.resolveInst(extra.rhs);
+ const operand = try sema.resolveInst(extra.rhs);
+ switch (dest_ty.zigTypeTag()) {
+ .AnyFrame,
+ .ComptimeFloat,
+ .ComptimeInt,
+ .EnumLiteral,
+ .ErrorSet,
+ .ErrorUnion,
+ .Fn,
+ .Frame,
+ .NoReturn,
+ .Null,
+ .Opaque,
+ .Optional,
+ .Type,
+ .Undefined,
+ .Void,
+ => return sema.fail(block, dest_ty_src, "cannot @bitCast to '{}'", .{dest_ty.fmt(sema.mod)}),
+
+ .Enum => {
+ const msg = msg: {
+ const msg = try sema.errMsg(block, dest_ty_src, "cannot @bitCast to '{}'", .{dest_ty.fmt(sema.mod)});
+ errdefer msg.destroy(sema.gpa);
+ switch (sema.typeOf(operand).zigTypeTag()) {
+ .Int, .ComptimeInt => try sema.errNote(block, dest_ty_src, msg, "use @intToEnum for type coercion", .{}),
+ else => {},
+ }
+
+ break :msg msg;
+ };
+ return sema.failWithOwnedErrorMsg(block, msg);
+ },
+
+ .Pointer => return sema.fail(block, dest_ty_src, "cannot @bitCast to '{}', use @ptrCast to cast to a pointer", .{
+ dest_ty.fmt(sema.mod),
+ }),
+ .Struct, .Union => if (dest_ty.containerLayout() == .Auto) {
+ const container = switch (dest_ty.zigTypeTag()) {
+ .Struct => "struct",
+ .Union => "union",
+ else => unreachable,
+ };
+ return sema.fail(block, dest_ty_src, "cannot @bitCast to '{}', {s} does not have a guaranteed in-memory layout", .{
+ dest_ty.fmt(sema.mod), container,
+ });
+ },
+ .BoundFn => @panic("TODO remove this type from the language and compiler"),
+
+ .Array,
+ .Bool,
+ .Float,
+ .Int,
+ .Vector,
+ => {},
+ }
return sema.bitCast(block, dest_ty, operand, operand_src);
}
@@ -5932,14 +7750,14 @@ fn zirFloatCast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A
defer tracy.end();
const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
- const src = inst_data.src();
const dest_ty_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node };
const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
const dest_ty = try sema.resolveType(block, dest_ty_src, extra.lhs);
- const operand = sema.resolveInst(extra.rhs);
+ const operand = try sema.resolveInst(extra.rhs);
+ const target = sema.mod.getTarget();
const dest_is_comptime_float = switch (dest_ty.zigTypeTag()) {
.ComptimeFloat => true,
.Float => false,
@@ -5947,7 +7765,7 @@ fn zirFloatCast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A
block,
dest_ty_src,
"expected float type, found '{}'",
- .{dest_ty},
+ .{dest_ty.fmt(sema.mod)},
),
};
@@ -5958,17 +7776,16 @@ fn zirFloatCast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A
block,
operand_src,
"expected float type, found '{}'",
- .{operand_ty},
+ .{operand_ty.fmt(sema.mod)},
),
}
- if (try sema.isComptimeKnown(block, operand_src, operand)) {
- return sema.coerce(block, dest_ty, operand, operand_src);
+ if (try sema.resolveMaybeUndefVal(block, operand_src, operand)) |operand_val| {
+ return sema.addConstant(dest_ty, try operand_val.floatCast(sema.arena, dest_ty, target));
}
if (dest_is_comptime_float) {
- return sema.fail(block, src, "unable to cast runtime value to 'comptime_float'", .{});
+ return sema.fail(block, operand_src, "unable to cast runtime value to 'comptime_float'", .{});
}
- const target = sema.mod.getTarget();
const src_bits = operand_ty.floatBits(target);
const dst_bits = dest_ty.floatBits(target);
if (dst_bits >= src_bits) {
@@ -5982,10 +7799,12 @@ fn zirElemVal(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air
const tracy = trace(@src());
defer tracy.end();
- const bin_inst = sema.code.instructions.items(.data)[inst].bin;
- const array = sema.resolveInst(bin_inst.lhs);
- const elem_index = sema.resolveInst(bin_inst.rhs);
- return sema.elemVal(block, sema.src, array, elem_index, sema.src);
+ const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
+ const src = inst_data.src();
+ const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
+ const array = try sema.resolveInst(extra.lhs);
+ const elem_index = try sema.resolveInst(extra.rhs);
+ return sema.elemVal(block, src, array, elem_index, src);
}
fn zirElemValNode(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
@@ -5996,8 +7815,8 @@ fn zirElemValNode(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
const src = inst_data.src();
const elem_index_src: LazySrcLoc = .{ .node_offset_array_access_index = inst_data.src_node };
const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
- const array = sema.resolveInst(extra.lhs);
- const elem_index = sema.resolveInst(extra.rhs);
+ const array = try sema.resolveInst(extra.lhs);
+ const elem_index = try sema.resolveInst(extra.rhs);
return sema.elemVal(block, src, array, elem_index, elem_index_src);
}
@@ -6005,10 +7824,12 @@ fn zirElemPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air
const tracy = trace(@src());
defer tracy.end();
- const bin_inst = sema.code.instructions.items(.data)[inst].bin;
- const array_ptr = sema.resolveInst(bin_inst.lhs);
- const elem_index = sema.resolveInst(bin_inst.rhs);
- return sema.elemPtr(block, sema.src, array_ptr, elem_index, sema.src);
+ const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
+ const src = inst_data.src();
+ const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
+ const array_ptr = try sema.resolveInst(extra.lhs);
+ const elem_index = try sema.resolveInst(extra.rhs);
+ return sema.elemPtr(block, src, array_ptr, elem_index, src, false);
}
fn zirElemPtrNode(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
@@ -6019,9 +7840,9 @@ fn zirElemPtrNode(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
const src = inst_data.src();
const elem_index_src: LazySrcLoc = .{ .node_offset_array_access_index = inst_data.src_node };
const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
- const array_ptr = sema.resolveInst(extra.lhs);
- const elem_index = sema.resolveInst(extra.rhs);
- return sema.elemPtr(block, src, array_ptr, elem_index, elem_index_src);
+ const array_ptr = try sema.resolveInst(extra.lhs);
+ const elem_index = try sema.resolveInst(extra.rhs);
+ return sema.elemPtr(block, src, array_ptr, elem_index, elem_index_src, false);
}
fn zirElemPtrImm(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
@@ -6031,9 +7852,9 @@ fn zirElemPtrImm(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!
const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
const src = inst_data.src();
const extra = sema.code.extraData(Zir.Inst.ElemPtrImm, inst_data.payload_index).data;
- const array_ptr = sema.resolveInst(extra.ptr);
+ const array_ptr = try sema.resolveInst(extra.ptr);
const elem_index = try sema.addIntUnsigned(Type.usize, extra.index);
- return sema.elemPtr(block, src, array_ptr, elem_index, src);
+ return sema.elemPtr(block, src, array_ptr, elem_index, src, true);
}
fn zirSliceStart(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
@@ -6043,8 +7864,8 @@ fn zirSliceStart(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!
const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
const src = inst_data.src();
const extra = sema.code.extraData(Zir.Inst.SliceStart, inst_data.payload_index).data;
- const array_ptr = sema.resolveInst(extra.lhs);
- const start = sema.resolveInst(extra.start);
+ const array_ptr = try sema.resolveInst(extra.lhs);
+ const start = try sema.resolveInst(extra.start);
return sema.analyzeSlice(block, src, array_ptr, start, .none, .none, .unneeded);
}
@@ -6056,9 +7877,9 @@ fn zirSliceEnd(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
const src = inst_data.src();
const extra = sema.code.extraData(Zir.Inst.SliceEnd, inst_data.payload_index).data;
- const array_ptr = sema.resolveInst(extra.lhs);
- const start = sema.resolveInst(extra.start);
- const end = sema.resolveInst(extra.end);
+ const array_ptr = try sema.resolveInst(extra.lhs);
+ const start = try sema.resolveInst(extra.start);
+ const end = try sema.resolveInst(extra.end);
return sema.analyzeSlice(block, src, array_ptr, start, end, .none, .unneeded);
}
@@ -6071,10 +7892,10 @@ fn zirSliceSentinel(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileErr
const src = inst_data.src();
const sentinel_src: LazySrcLoc = .{ .node_offset_slice_sentinel = inst_data.src_node };
const extra = sema.code.extraData(Zir.Inst.SliceSentinel, inst_data.payload_index).data;
- const array_ptr = sema.resolveInst(extra.lhs);
- const start = sema.resolveInst(extra.start);
- const end = sema.resolveInst(extra.end);
- const sentinel = sema.resolveInst(extra.sentinel);
+ const array_ptr = try sema.resolveInst(extra.lhs);
+ const start = try sema.resolveInst(extra.start);
+ const end = try sema.resolveInst(extra.end);
+ const sentinel = try sema.resolveInst(extra.sentinel);
return sema.analyzeSlice(block, src, array_ptr, start, end, sentinel, sentinel_src);
}
@@ -6098,55 +7919,78 @@ fn zirSwitchCapture(
const operand_is_ref = switch_extra.data.bits.is_ref;
const cond_inst = Zir.refToIndex(switch_extra.data.operand).?;
const cond_info = sema.code.instructions.items(.data)[cond_inst].un_node;
- const operand_ptr = sema.resolveInst(cond_info.operand);
+ const operand_ptr = try sema.resolveInst(cond_info.operand);
const operand_ptr_ty = sema.typeOf(operand_ptr);
const operand_ty = if (operand_is_ref) operand_ptr_ty.childType() else operand_ptr_ty;
+ const operand = if (operand_is_ref)
+ try sema.analyzeLoad(block, operand_src, operand_ptr, operand_src)
+ else
+ operand_ptr;
+
if (capture_info.prong_index == std.math.maxInt(@TypeOf(capture_info.prong_index))) {
// It is the else/`_` prong.
- switch (operand_ty.zigTypeTag()) {
- .ErrorSet => {
- return sema.fail(block, operand_src, "TODO implement Sema for zirSwitchCaptureElse for error sets", .{});
- },
- else => {},
- }
if (is_ref) {
assert(operand_is_ref);
return operand_ptr;
}
- const operand = if (operand_is_ref)
- try sema.analyzeLoad(block, operand_src, operand_ptr, operand_src)
- else
- operand_ptr;
-
- return operand;
+ switch (operand_ty.zigTypeTag()) {
+ .ErrorSet => if (block.switch_else_err_ty) |some| {
+ return sema.bitCast(block, some, operand, operand_src);
+ } else {
+ try block.addUnreachable(operand_src, false);
+ return Air.Inst.Ref.unreachable_value;
+ },
+ else => return operand,
+ }
}
- if (is_multi) {
- return sema.fail(block, switch_src, "TODO implement Sema for switch capture multi", .{});
- }
- const scalar_prong = switch_extra.data.getScalarProng(sema.code, switch_extra.end, capture_info.prong_index);
- const item = sema.resolveInst(scalar_prong.item);
- // Previous switch validation ensured this will succeed
- const item_val = sema.resolveConstValue(block, .unneeded, item) catch unreachable;
+ const items = if (is_multi)
+ switch_extra.data.getMultiProng(sema.code, switch_extra.end, capture_info.prong_index).items
+ else
+ &[_]Zir.Inst.Ref{
+ switch_extra.data.getScalarProng(sema.code, switch_extra.end, capture_info.prong_index).item,
+ };
switch (operand_ty.zigTypeTag()) {
.Union => {
const union_obj = operand_ty.cast(Type.Payload.Union).?.data;
const enum_ty = union_obj.tag_ty;
- const field_index_usize = enum_ty.enumTagFieldIndex(item_val).?;
- const field_index = @intCast(u32, field_index_usize);
- const field = union_obj.fields.values()[field_index];
+ const first_item = try sema.resolveInst(items[0]);
+ // Previous switch validation ensured this will succeed
+ const first_item_val = sema.resolveConstValue(block, .unneeded, first_item) catch unreachable;
+
+ const first_field_index = @intCast(u32, enum_ty.enumTagFieldIndex(first_item_val, sema.mod).?);
+ const first_field = union_obj.fields.values()[first_field_index];
+
+ for (items[1..]) |item| {
+ const item_ref = try sema.resolveInst(item);
+ // Previous switch validation ensured this will succeed
+ const item_val = sema.resolveConstValue(block, .unneeded, item_ref) catch unreachable;
- // TODO handle multiple union tags which have compatible types
+ const field_index = enum_ty.enumTagFieldIndex(item_val, sema.mod).?;
+ const field = union_obj.fields.values()[field_index];
+ if (!field.ty.eql(first_field.ty, sema.mod)) {
+ const first_item_src = switch_src; // TODO better source location
+ const item_src = switch_src;
+ const msg = msg: {
+ const msg = try sema.errMsg(block, switch_src, "capture group with incompatible types", .{});
+ errdefer msg.destroy(sema.gpa);
+ try sema.errNote(block, first_item_src, msg, "type '{}' here", .{first_field.ty.fmt(sema.mod)});
+ try sema.errNote(block, item_src, msg, "type '{}' here", .{field.ty.fmt(sema.mod)});
+ break :msg msg;
+ };
+ return sema.failWithOwnedErrorMsg(block, msg);
+ }
+ }
if (is_ref) {
assert(operand_is_ref);
- const field_ty_ptr = try Type.ptr(sema.arena, .{
- .pointee_type = field.ty,
+ const field_ty_ptr = try Type.ptr(sema.arena, sema.mod, .{
+ .pointee_type = first_field.ty,
.@"addrspace" = .generic,
.mutable = operand_ptr_ty.ptrIsMutable(),
});
@@ -6156,35 +8000,60 @@ fn zirSwitchCapture(
field_ty_ptr,
try Value.Tag.field_ptr.create(sema.arena, .{
.container_ptr = op_ptr_val,
- .field_index = field_index,
+ .container_ty = operand_ty,
+ .field_index = first_field_index,
}),
);
}
try sema.requireRuntimeBlock(block, operand_src);
- return block.addStructFieldPtr(operand_ptr, field_index, field_ty_ptr);
+ return block.addStructFieldPtr(operand_ptr, first_field_index, field_ty_ptr);
}
- const operand = if (operand_is_ref)
- try sema.analyzeLoad(block, operand_src, operand_ptr, operand_src)
- else
- operand_ptr;
-
if (try sema.resolveDefinedValue(block, operand_src, operand)) |operand_val| {
return sema.addConstant(
- field.ty,
+ first_field.ty,
operand_val.castTag(.@"union").?.data.val,
);
}
try sema.requireRuntimeBlock(block, operand_src);
- return block.addStructFieldVal(operand, field_index, field.ty);
+ return block.addStructFieldVal(operand, first_field_index, first_field.ty);
},
.ErrorSet => {
- return sema.fail(block, operand_src, "TODO implement Sema for zirSwitchCapture for error sets", .{});
+ if (is_multi) {
+ var names: Module.ErrorSet.NameMap = .{};
+ try names.ensureUnusedCapacity(sema.arena, items.len);
+ for (items) |item| {
+ const item_ref = try sema.resolveInst(item);
+ // Previous switch validation ensured this will succeed
+ const item_val = sema.resolveConstValue(block, .unneeded, item_ref) catch unreachable;
+ names.putAssumeCapacityNoClobber(
+ item_val.getError().?,
+ {},
+ );
+ }
+ // names must be sorted
+ Module.ErrorSet.sortNames(&names);
+ const else_error_ty = try Type.Tag.error_set_merged.create(sema.arena, names);
+
+ return sema.bitCast(block, else_error_ty, operand, operand_src);
+ } else {
+ const item_ref = try sema.resolveInst(items[0]);
+ // Previous switch validation ensured this will succeed
+ const item_val = sema.resolveConstValue(block, .unneeded, item_ref) catch unreachable;
+
+ const item_ty = try Type.Tag.error_set_single.create(sema.arena, item_val.getError().?);
+ return sema.bitCast(block, item_ty, operand, operand_src);
+ }
},
else => {
- return sema.fail(block, operand_src, "switch on type '{}' provides no capture value", .{
- operand_ty,
- });
+ // In this case the capture value is just the passed-through value of the
+ // switch condition.
+ if (is_ref) {
+ assert(operand_is_ref);
+ return operand_ptr;
+ } else {
+ return operand;
+ }
},
}
}
@@ -6198,7 +8067,7 @@ fn zirSwitchCond(
const inst_data = sema.code.instructions.items(.data)[inst].un_node;
const src = inst_data.src();
const operand_src = src; // TODO make this point at the switch operand
- const operand_ptr = sema.resolveInst(inst_data.operand);
+ const operand_ptr = try sema.resolveInst(inst_data.operand);
const operand = if (is_ref)
try sema.analyzeLoad(block, src, operand_ptr, operand_src)
else
@@ -6234,7 +8103,7 @@ fn zirSwitchCond(
try sema.addDeclaredHereNote(msg, union_ty);
break :msg msg;
};
- return sema.failWithOwnedErrorMsg(msg);
+ return sema.failWithOwnedErrorMsg(block, msg);
};
return sema.unionToTag(block, enum_ty, operand, src);
},
@@ -6251,7 +8120,7 @@ fn zirSwitchCond(
.Vector,
.Frame,
.AnyFrame,
- => return sema.fail(block, src, "switch on type '{}'", .{operand_ty}),
+ => return sema.fail(block, src, "switch on type '{}'", .{operand_ty.fmt(sema.mod)}),
}
}
@@ -6269,7 +8138,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
const special_prong_src: LazySrcLoc = .{ .node_offset_switch_special_prong = src_node_offset };
const extra = sema.code.extraData(Zir.Inst.SwitchBlock, inst_data.payload_index);
- const operand = sema.resolveInst(extra.data.operand);
+ const operand = try sema.resolveInst(extra.data.operand);
var header_extra_index: usize = extra.end;
@@ -6295,6 +8164,8 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
const operand_ty = sema.typeOf(operand);
+ var else_error_ty: ?Type = null;
+
// Validate usage of '_' prongs.
if (special_prong == .under and !operand_ty.isNonexhaustiveEnum()) {
const msg = msg: {
@@ -6314,9 +8185,11 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
);
break :msg msg;
};
- return sema.failWithOwnedErrorMsg(msg);
+ return sema.failWithOwnedErrorMsg(block, msg);
}
+ const target = sema.mod.getTarget();
+
// Validate for duplicate items, missing else prong, and invalid range.
switch (operand_ty.zigTypeTag()) {
.Enum => {
@@ -6399,14 +8272,14 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
);
}
try sema.mod.errNoteNonLazy(
- operand_ty.declSrcLoc(),
+ operand_ty.declSrcLoc(sema.mod),
msg,
"enum '{}' declared here",
- .{operand_ty},
+ .{operand_ty.fmt(sema.mod)},
);
break :msg msg;
};
- return sema.failWithOwnedErrorMsg(msg);
+ return sema.failWithOwnedErrorMsg(block, msg);
}
},
.under => {
@@ -6476,6 +8349,8 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
}
}
+ try sema.resolveInferredErrorSetTy(block, src, operand_ty);
+
if (operand_ty.isAnyError()) {
if (special_prong != .@"else") {
return sema.fail(
@@ -6485,7 +8360,8 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
.{},
);
}
- } else {
+ else_error_ty = Type.@"anyerror";
+ } else else_validation: {
var maybe_msg: ?*Module.ErrorMsg = null;
errdefer if (maybe_msg) |msg| msg.destroy(sema.gpa);
@@ -6505,23 +8381,40 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
block,
src,
msg,
- "unhandled error value: error.{s}",
+ "unhandled error value: 'error.{s}'",
.{error_name},
);
}
}
if (maybe_msg) |msg| {
- try sema.mod.errNoteNonLazy(
- operand_ty.declSrcLoc(),
- msg,
- "error set '{}' declared here",
- .{operand_ty},
- );
- return sema.failWithOwnedErrorMsg(msg);
+ maybe_msg = null;
+ try sema.addDeclaredHereNote(msg, operand_ty);
+ return sema.failWithOwnedErrorMsg(block, msg);
}
- if (special_prong == .@"else") {
+ if (special_prong == .@"else" and seen_errors.count() == operand_ty.errorSetNames().len) {
+
+ // In order to enable common patterns for generic code allow simple else bodies
+ // else => unreachable,
+ // else => return,
+ // else => |e| return e,
+ // even if all the possible errors were already handled.
+ const tags = sema.code.instructions.items(.tag);
+ for (special.body) |else_inst| switch (tags[else_inst]) {
+ .dbg_block_begin,
+ .dbg_block_end,
+ .dbg_stmt,
+ .dbg_var_val,
+ .switch_capture,
+ .ret_type,
+ .as_node,
+ .ret_node,
+ .@"unreachable",
+ => {},
+ else => break,
+ } else break :else_validation;
+
return sema.fail(
block,
special_prong_src,
@@ -6529,11 +8422,24 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
.{},
);
}
+
+ const error_names = operand_ty.errorSetNames();
+ var names: Module.ErrorSet.NameMap = .{};
+ try names.ensureUnusedCapacity(sema.arena, error_names.len);
+ for (error_names) |error_name| {
+ if (seen_errors.contains(error_name)) continue;
+
+ names.putAssumeCapacityNoClobber(error_name, {});
+ }
+
+ // names must be sorted
+ Module.ErrorSet.sortNames(&names);
+ else_error_ty = try Type.Tag.error_set_merged.create(sema.arena, names);
}
},
.Union => return sema.fail(block, src, "TODO validate switch .Union", .{}),
.Int, .ComptimeInt => {
- var range_set = RangeSet.init(gpa);
+ var range_set = RangeSet.init(gpa, sema.mod);
defer range_set.deinit();
var extra_index: usize = special.end;
@@ -6606,7 +8512,6 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
var arena = std.heap.ArenaAllocator.init(gpa);
defer arena.deinit();
- const target = sema.mod.getTarget();
const min_int = try operand_ty.minInt(arena.allocator(), target);
const max_int = try operand_ty.maxInt(arena.allocator(), target);
if (try range_set.spans(min_int, max_int, operand_ty)) {
@@ -6710,11 +8615,14 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
block,
src,
"else prong required when switching on type '{}'",
- .{operand_ty},
+ .{operand_ty.fmt(sema.mod)},
);
}
- var seen_values = ValueSrcMap.initContext(gpa, .{ .ty = operand_ty });
+ var seen_values = ValueSrcMap.initContext(gpa, .{
+ .ty = operand_ty,
+ .mod = sema.mod,
+ });
defer seen_values.deinit();
var extra_index: usize = special.end;
@@ -6778,7 +8686,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
.ComptimeFloat,
.Float,
=> return sema.fail(block, operand_src, "invalid switch operand type '{}'", .{
- operand_ty,
+ operand_ty.fmt(sema.mod),
}),
}
@@ -6806,6 +8714,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
.label = &label,
.inlining = block.inlining,
.is_comptime = block.is_comptime,
+ .switch_else_err_ty = else_error_ty,
};
const merges = &child_block.label.?.merges;
defer child_block.instructions.deinit(gpa);
@@ -6824,10 +8733,10 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
const body = sema.code.extra[extra_index..][0..body_len];
extra_index += body_len;
- const item = sema.resolveInst(item_ref);
+ const item = try sema.resolveInst(item_ref);
// Validation above ensured these will succeed.
const item_val = sema.resolveConstValue(&child_block, .unneeded, item) catch unreachable;
- if (operand_val.eql(item_val, operand_ty)) {
+ if (operand_val.eql(item_val, operand_ty, sema.mod)) {
return sema.resolveBlockBody(block, src, &child_block, body, inst, merges);
}
}
@@ -6846,10 +8755,10 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
const body = sema.code.extra[extra_index + 2 * ranges_len ..][0..body_len];
for (items) |item_ref| {
- const item = sema.resolveInst(item_ref);
+ const item = try sema.resolveInst(item_ref);
// Validation above ensured these will succeed.
const item_val = sema.resolveConstValue(&child_block, .unneeded, item) catch unreachable;
- if (operand_val.eql(item_val, operand_ty)) {
+ if (operand_val.eql(item_val, operand_ty, sema.mod)) {
return sema.resolveBlockBody(block, src, &child_block, body, inst, merges);
}
}
@@ -6864,8 +8773,8 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
// Validation above ensured these will succeed.
const first_tv = sema.resolveInstConst(&child_block, .unneeded, item_first) catch unreachable;
const last_tv = sema.resolveInstConst(&child_block, .unneeded, item_last) catch unreachable;
- if (Value.compare(operand_val, .gte, first_tv.val, operand_ty) and
- Value.compare(operand_val, .lte, last_tv.val, operand_ty))
+ if ((try sema.compare(block, src, operand_val, .gte, first_tv.val, operand_ty)) and
+ (try sema.compare(block, src, operand_val, .lte, last_tv.val, operand_ty)))
{
return sema.resolveBlockBody(block, src, &child_block, body, inst, merges);
}
@@ -6878,6 +8787,9 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
}
if (scalar_cases_len + multi_cases_len == 0) {
+ if (special_prong == .none) {
+ return sema.fail(block, src, "switch must handle all possibilities", .{});
+ }
return sema.resolveBlockBody(block, src, &child_block, special.body, inst, merges);
}
@@ -6891,7 +8803,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
var case_block = child_block.makeSubBlock();
case_block.runtime_loop = null;
case_block.runtime_cond = operand_src;
- case_block.runtime_index += 1;
+ case_block.runtime_index.increment();
defer case_block.instructions.deinit(gpa);
var extra_index: usize = special.end;
@@ -6911,10 +8823,21 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
case_block.instructions.shrinkRetainingCapacity(0);
case_block.wip_capture_scope = wip_captures.scope;
- const item = sema.resolveInst(item_ref);
+ const item = try sema.resolveInst(item_ref);
// `item` is already guaranteed to be constant known.
- _ = try sema.analyzeBody(&case_block, body);
+ _ = sema.analyzeBodyInner(&case_block, body) catch |err| switch (err) {
+ error.ComptimeBreak => {
+ const zir_datas = sema.code.instructions.items(.data);
+ const break_data = zir_datas[sema.comptime_break_inst].@"break";
+ try sema.addRuntimeBreak(&case_block, .{
+ .block_inst = break_data.block_inst,
+ .operand = break_data.operand,
+ .inst = sema.comptime_break_inst,
+ });
+ },
+ else => |e| return e,
+ };
try wip_captures.finalize();
@@ -6957,7 +8880,18 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
const body = sema.code.extra[extra_index..][0..body_len];
extra_index += body_len;
- _ = try sema.analyzeBody(&case_block, body);
+ _ = sema.analyzeBodyInner(&case_block, body) catch |err| switch (err) {
+ error.ComptimeBreak => {
+ const zir_datas = sema.code.instructions.items(.data);
+ const break_data = zir_datas[sema.comptime_break_inst].@"break";
+ try sema.addRuntimeBreak(&case_block, .{
+ .block_inst = break_data.block_inst,
+ .operand = break_data.operand,
+ .inst = sema.comptime_break_inst,
+ });
+ },
+ else => |e| return e,
+ };
try cases_extra.ensureUnusedCapacity(gpa, 2 + items.len +
case_block.instructions.items.len);
@@ -6966,14 +8900,14 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
cases_extra.appendAssumeCapacity(@intCast(u32, case_block.instructions.items.len));
for (items) |item_ref| {
- const item = sema.resolveInst(item_ref);
+ const item = try sema.resolveInst(item_ref);
cases_extra.appendAssumeCapacity(@enumToInt(item));
}
cases_extra.appendSliceAssumeCapacity(case_block.instructions.items);
} else {
for (items) |item_ref| {
- const item = sema.resolveInst(item_ref);
+ const item = try sema.resolveInst(item_ref);
const cmp_ok = try case_block.addBinOp(.cmp_eq, operand, item);
if (any_ok != .none) {
any_ok = try case_block.addBinOp(.bool_or, any_ok, cmp_ok);
@@ -6989,8 +8923,8 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
const last_ref = @intToEnum(Zir.Inst.Ref, sema.code.extra[extra_index]);
extra_index += 1;
- const item_first = sema.resolveInst(first_ref);
- const item_last = sema.resolveInst(last_ref);
+ const item_first = try sema.resolveInst(first_ref);
+ const item_last = try sema.resolveInst(last_ref);
// operand >= first and operand <= last
const range_first_ok = try case_block.addBinOp(
@@ -7032,7 +8966,18 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
const body = sema.code.extra[extra_index..][0..body_len];
extra_index += body_len;
- _ = try sema.analyzeBody(&case_block, body);
+ _ = sema.analyzeBodyInner(&case_block, body) catch |err| switch (err) {
+ error.ComptimeBreak => {
+ const zir_datas = sema.code.instructions.items(.data);
+ const break_data = zir_datas[sema.comptime_break_inst].@"break";
+ try sema.addRuntimeBreak(&case_block, .{
+ .block_inst = break_data.block_inst,
+ .operand = break_data.operand,
+ .inst = sema.comptime_break_inst,
+ });
+ },
+ else => |e| return e,
+ };
try wip_captures.finalize();
@@ -7054,6 +8999,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
sema.air_extra.appendSliceAssumeCapacity(prev_then_body);
sema.air_extra.appendSliceAssumeCapacity(cond_body);
}
+ gpa.free(prev_then_body);
prev_then_body = case_block.instructions.toOwnedSlice(gpa);
prev_cond_br = new_cond_br;
}
@@ -7068,7 +9014,18 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
case_block.wip_capture_scope = wip_captures.scope;
if (special.body.len != 0) {
- _ = try sema.analyzeBody(&case_block, special.body);
+ _ = sema.analyzeBodyInner(&case_block, special.body) catch |err| switch (err) {
+ error.ComptimeBreak => {
+ const zir_datas = sema.code.instructions.items(.data);
+ const break_data = zir_datas[sema.comptime_break_inst].@"break";
+ try sema.addRuntimeBreak(&case_block, .{
+ .block_inst = break_data.block_inst,
+ .operand = break_data.operand,
+ .inst = sema.comptime_break_inst,
+ });
+ },
+ else => |e| return e,
+ };
} else {
// We still need a terminator in this block, but we have proven
// that it is unreachable.
@@ -7120,7 +9077,7 @@ fn resolveSwitchItemVal(
switch_prong_src: Module.SwitchProngSrc,
range_expand: Module.SwitchProngSrc.RangeExpand,
) CompileError!TypedValue {
- const item = sema.resolveInst(item_ref);
+ const item = try sema.resolveInst(item_ref);
const item_ty = sema.typeOf(item);
// Constructing a LazySrcLoc is costly because we only have the switch AST node.
// Only if we know for sure we need to report a compile error do we resolve the
@@ -7129,7 +9086,7 @@ fn resolveSwitchItemVal(
return TypedValue{ .ty = item_ty, .val = val };
} else |err| switch (err) {
error.NeededSourceLocation => {
- const src = switch_prong_src.resolve(sema.gpa, block.src_decl, switch_node_offset, range_expand);
+ const src = switch_prong_src.resolve(sema.gpa, sema.mod.declPtr(block.src_decl), switch_node_offset, range_expand);
return TypedValue{
.ty = item_ty,
.val = try sema.resolveConstValue(block, src, item),
@@ -7151,6 +9108,10 @@ fn validateSwitchRange(
) CompileError!void {
const first_val = (try sema.resolveSwitchItemVal(block, first_ref, src_node_offset, switch_prong_src, .first)).val;
const last_val = (try sema.resolveSwitchItemVal(block, last_ref, src_node_offset, switch_prong_src, .last)).val;
+ if (first_val.compare(.gt, last_val, operand_ty, sema.mod)) {
+ const src = switch_prong_src.resolve(sema.gpa, sema.mod.declPtr(block.src_decl), src_node_offset, .first);
+ return sema.fail(block, src, "range start value is greater than the end value", .{});
+ }
const maybe_prev_src = try range_set.add(first_val, last_val, operand_ty, switch_prong_src);
return sema.validateSwitchDupe(block, maybe_prev_src, switch_prong_src, src_node_offset);
}
@@ -7178,25 +9139,25 @@ fn validateSwitchItemEnum(
switch_prong_src: Module.SwitchProngSrc,
) CompileError!void {
const item_tv = try sema.resolveSwitchItemVal(block, item_ref, src_node_offset, switch_prong_src, .none);
- const field_index = item_tv.ty.enumTagFieldIndex(item_tv.val) orelse {
+ const field_index = item_tv.ty.enumTagFieldIndex(item_tv.val, sema.mod) orelse {
const msg = msg: {
- const src = switch_prong_src.resolve(sema.gpa, block.src_decl, src_node_offset, .none);
+ const src = switch_prong_src.resolve(sema.gpa, sema.mod.declPtr(block.src_decl), src_node_offset, .none);
const msg = try sema.errMsg(
block,
src,
"enum '{}' has no tag with value '{}'",
- .{ item_tv.ty, item_tv.val },
+ .{ item_tv.ty.fmt(sema.mod), item_tv.val.fmtValue(item_tv.ty, sema.mod) },
);
errdefer msg.destroy(sema.gpa);
try sema.mod.errNoteNonLazy(
- item_tv.ty.declSrcLoc(),
+ item_tv.ty.declSrcLoc(sema.mod),
msg,
"enum declared here",
.{},
);
break :msg msg;
};
- return sema.failWithOwnedErrorMsg(msg);
+ return sema.failWithOwnedErrorMsg(block, msg);
};
const maybe_prev_src = seen_fields[field_index];
seen_fields[field_index] = switch_prong_src;
@@ -7230,8 +9191,9 @@ fn validateSwitchDupe(
) CompileError!void {
const prev_prong_src = maybe_prev_src orelse return;
const gpa = sema.gpa;
- const src = switch_prong_src.resolve(gpa, block.src_decl, src_node_offset, .none);
- const prev_src = prev_prong_src.resolve(gpa, block.src_decl, src_node_offset, .none);
+ const block_src_decl = sema.mod.declPtr(block.src_decl);
+ const src = switch_prong_src.resolve(gpa, block_src_decl, src_node_offset, .none);
+ const prev_src = prev_prong_src.resolve(gpa, block_src_decl, src_node_offset, .none);
const msg = msg: {
const msg = try sema.errMsg(
block,
@@ -7249,7 +9211,7 @@ fn validateSwitchDupe(
);
break :msg msg;
};
- return sema.failWithOwnedErrorMsg(msg);
+ return sema.failWithOwnedErrorMsg(block, msg);
}
fn validateSwitchItemBool(
@@ -7268,7 +9230,8 @@ fn validateSwitchItemBool(
false_count.* += 1;
}
if (true_count.* + false_count.* > 2) {
- const src = switch_prong_src.resolve(sema.gpa, block.src_decl, src_node_offset, .none);
+ const block_src_decl = sema.mod.declPtr(block.src_decl);
+ const src = switch_prong_src.resolve(sema.gpa, block_src_decl, src_node_offset, .none);
return sema.fail(block, src, "duplicate switch value", .{});
}
}
@@ -7306,7 +9269,7 @@ fn validateSwitchNoRange(
block,
operand_src,
"ranges not allowed when switching on type '{}'",
- .{operand_ty},
+ .{operand_ty.fmt(sema.mod)},
);
errdefer msg.destroy(sema.gpa);
try sema.errNote(
@@ -7318,7 +9281,7 @@ fn validateSwitchNoRange(
);
break :msg msg;
};
- return sema.failWithOwnedErrorMsg(msg);
+ return sema.failWithOwnedErrorMsg(block, msg);
}
fn zirHasField(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
@@ -7336,13 +9299,22 @@ fn zirHasField(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
if (mem.eql(u8, field_name, "len")) break :hf true;
break :hf false;
}
+ if (ty.castTag(.anon_struct)) |pl| {
+ break :hf for (pl.data.names) |name| {
+ if (mem.eql(u8, name, field_name)) break true;
+ } else false;
+ }
+ if (ty.isTuple()) {
+ const field_index = std.fmt.parseUnsigned(u32, field_name, 10) catch break :hf false;
+ break :hf field_index < ty.structFieldCount();
+ }
break :hf switch (ty.zigTypeTag()) {
.Struct => ty.structFields().contains(field_name),
.Union => ty.unionFields().contains(field_name),
.Enum => ty.enumFields().contains(field_name),
.Array => mem.eql(u8, field_name, "len"),
else => return sema.fail(block, ty_src, "type '{}' does not support '@hasField'", .{
- ty,
+ ty.fmt(sema.mod),
}),
};
};
@@ -7362,13 +9334,11 @@ fn zirHasDecl(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air
const container_type = try sema.resolveType(block, lhs_src, extra.lhs);
const decl_name = try sema.resolveConstString(block, rhs_src, extra.rhs);
- const namespace = container_type.getNamespace() orelse return sema.fail(
- block,
- lhs_src,
- "expected struct, enum, union, or opaque, found '{}'",
- .{container_type},
- );
- if (try sema.lookupInNamespace(block, src, namespace, decl_name, true)) |decl| {
+ try checkNamespaceType(sema, block, lhs_src, container_type);
+
+ const namespace = container_type.getNamespace() orelse return Air.Inst.Ref.bool_false;
+ if (try sema.lookupInNamespace(block, src, namespace, decl_name, true)) |decl_index| {
+ const decl = sema.mod.declPtr(decl_index);
if (decl.is_pub or decl.getFileScope() == block.getFileScope()) {
return Air.Inst.Ref.bool_true;
}
@@ -7396,8 +9366,9 @@ fn zirImport(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.
},
};
try mod.semaFile(result.file);
- const file_root_decl = result.file.root_decl.?;
- try mod.declareDeclDependency(sema.owner_decl, file_root_decl);
+ const file_root_decl_index = result.file.root_decl.unwrap().?;
+ const file_root_decl = mod.declPtr(file_root_decl_index);
+ try mod.declareDeclDependency(sema.owner_decl_index, file_root_decl_index);
return sema.addConstant(file_root_decl.ty, file_root_decl.val);
}
@@ -7435,15 +9406,23 @@ fn zirEmbedFile(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A
embed_file.owner_decl = try anon_decl.finish(
try Type.Tag.array_u8_sentinel_0.create(anon_decl.arena(), embed_file.bytes.len),
try Value.Tag.bytes.create(anon_decl.arena(), bytes_including_null),
+ 0, // default alignment
);
return sema.analyzeDeclRef(embed_file.owner_decl);
}
-fn zirRetErrValueCode(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
- _ = block;
- _ = inst;
- return sema.fail(block, sema.src, "TODO implement zirRetErrValueCode", .{});
+fn zirRetErrValueCode(sema: *Sema, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
+ const inst_data = sema.code.instructions.items(.data)[inst].str_tok;
+ const err_name = inst_data.get(sema.code);
+
+ // Return the error code from the function.
+ const kv = try sema.mod.getErrorValue(err_name);
+ const result_inst = try sema.addConstant(
+ try Type.Tag.error_set_single.create(sema.arena, kv.key),
+ try Value.Tag.@"error".create(sema.arena, .{ .name = kv.key }),
+ );
+ return result_inst;
}
fn zirShl(
@@ -7456,50 +9435,85 @@ fn zirShl(
defer tracy.end();
const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
+ const src = inst_data.src();
const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node };
const rhs_src: LazySrcLoc = .{ .node_offset_bin_rhs = inst_data.src_node };
const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
- const lhs = sema.resolveInst(extra.lhs);
- const rhs = sema.resolveInst(extra.rhs);
+ const lhs = try sema.resolveInst(extra.lhs);
+ const rhs = try sema.resolveInst(extra.rhs);
+ const lhs_ty = sema.typeOf(lhs);
+ const rhs_ty = sema.typeOf(rhs);
+ const target = sema.mod.getTarget();
+ try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src);
+
+ const scalar_ty = lhs_ty.scalarType();
+ const scalar_rhs_ty = rhs_ty.scalarType();
// TODO coerce rhs if air_tag is not shl_sat
+ const rhs_is_comptime_int = try sema.checkIntType(block, rhs_src, scalar_rhs_ty);
const maybe_lhs_val = try sema.resolveMaybeUndefVal(block, lhs_src, lhs);
const maybe_rhs_val = try sema.resolveMaybeUndefVal(block, rhs_src, rhs);
- const runtime_src = if (maybe_lhs_val) |lhs_val| rs: {
- const lhs_ty = sema.typeOf(lhs);
+ if (maybe_rhs_val) |rhs_val| {
+ if (rhs_val.isUndef()) {
+ return sema.addConstUndef(sema.typeOf(lhs));
+ }
+ // If rhs is 0, return lhs without doing any calculations.
+ if (try rhs_val.compareWithZeroAdvanced(.eq, sema.kit(block, src))) {
+ return lhs;
+ }
+ if (scalar_ty.zigTypeTag() != .ComptimeInt and air_tag != .shl_sat) {
+ var bits_payload = Value.Payload.U64{
+ .base = .{ .tag = .int_u64 },
+ .data = scalar_ty.intInfo(target).bits,
+ };
+ const bit_value = Value.initPayload(&bits_payload.base);
+ if (rhs_ty.zigTypeTag() == .Vector) {
+ var i: usize = 0;
+ while (i < rhs_ty.vectorLen()) : (i += 1) {
+ if (rhs_val.indexVectorlike(i).compareHetero(.gte, bit_value, target)) {
+ return sema.fail(block, rhs_src, "shift amount '{}' at index '{d}' is too large for operand type '{}'", .{
+ rhs_val.indexVectorlike(i).fmtValue(scalar_ty, sema.mod),
+ i,
+ scalar_ty.fmt(sema.mod),
+ });
+ }
+ }
+ } else if (rhs_val.compareHetero(.gte, bit_value, target)) {
+ return sema.fail(block, rhs_src, "shift amount '{}' is too large for operand type '{}'", .{
+ rhs_val.fmtValue(scalar_ty, sema.mod),
+ scalar_ty.fmt(sema.mod),
+ });
+ }
+ }
+ }
+ const runtime_src = if (maybe_lhs_val) |lhs_val| rs: {
if (lhs_val.isUndef()) return sema.addConstUndef(lhs_ty);
const rhs_val = maybe_rhs_val orelse break :rs rhs_src;
- if (rhs_val.isUndef()) return sema.addConstUndef(lhs_ty);
- // If rhs is 0, return lhs without doing any calculations.
- if (rhs_val.compareWithZero(.eq)) {
- return sema.addConstant(lhs_ty, lhs_val);
- }
- const target = sema.mod.getTarget();
const val = switch (air_tag) {
.shl_exact => val: {
- const shifted = try lhs_val.shl(rhs_val, sema.arena);
- if (lhs_ty.zigTypeTag() == .ComptimeInt) {
+ const shifted = try lhs_val.shl(rhs_val, lhs_ty, sema.arena, target);
+ if (scalar_ty.zigTypeTag() == .ComptimeInt) {
break :val shifted;
}
- const int_info = lhs_ty.intInfo(target);
- const truncated = try shifted.intTrunc(sema.arena, int_info.signedness, int_info.bits);
- if (truncated.compareHetero(.eq, shifted)) {
+ const int_info = scalar_ty.intInfo(target);
+ const truncated = try shifted.intTrunc(lhs_ty, sema.arena, int_info.signedness, int_info.bits, target);
+ if (try sema.compare(block, src, truncated, .eq, shifted, lhs_ty)) {
break :val shifted;
}
return sema.addConstUndef(lhs_ty);
},
- .shl_sat => if (lhs_ty.zigTypeTag() == .ComptimeInt)
- try lhs_val.shl(rhs_val, sema.arena)
+ .shl_sat => if (scalar_ty.zigTypeTag() == .ComptimeInt)
+ try lhs_val.shl(rhs_val, lhs_ty, sema.arena, target)
else
try lhs_val.shlSat(rhs_val, lhs_ty, sema.arena, target),
- .shl => if (lhs_ty.zigTypeTag() == .ComptimeInt)
- try lhs_val.shl(rhs_val, sema.arena)
+ .shl => if (scalar_ty.zigTypeTag() == .ComptimeInt)
+ try lhs_val.shl(rhs_val, lhs_ty, sema.arena, target)
else
try lhs_val.shlTrunc(rhs_val, lhs_ty, sema.arena, target),
@@ -7507,17 +9521,61 @@ fn zirShl(
};
return sema.addConstant(lhs_ty, val);
- } else rs: {
- if (maybe_rhs_val) |rhs_val| {
- if (rhs_val.isUndef()) return sema.addConstUndef(sema.typeOf(lhs));
- }
- break :rs lhs_src;
- };
+ } else lhs_src;
- // TODO: insert runtime safety check for shl_exact
+ const new_rhs = if (air_tag == .shl_sat) rhs: {
+ // Limit the RHS type for saturating shl to be an integer as small as the LHS.
+ if (rhs_is_comptime_int or
+ scalar_rhs_ty.intInfo(target).bits > scalar_ty.intInfo(target).bits)
+ {
+ const max_int = try sema.addConstant(
+ lhs_ty,
+ try lhs_ty.maxInt(sema.arena, target),
+ );
+ const rhs_limited = try sema.analyzeMinMax(block, rhs_src, rhs, max_int, .min, rhs_src, rhs_src);
+ break :rhs try sema.intCast(block, lhs_ty, rhs_src, rhs_limited, rhs_src, false);
+ } else {
+ break :rhs rhs;
+ }
+ } else rhs;
try sema.requireRuntimeBlock(block, runtime_src);
- return block.addBinOp(air_tag, lhs, rhs);
+ if (block.wantSafety()) {
+ const maybe_op_ov: ?Air.Inst.Tag = switch (air_tag) {
+ .shl_exact => .shl_with_overflow,
+ else => null,
+ };
+ if (maybe_op_ov) |op_ov_tag| {
+ const op_ov_tuple_ty = try sema.overflowArithmeticTupleType(lhs_ty);
+ const op_ov = try block.addInst(.{
+ .tag = op_ov_tag,
+ .data = .{ .ty_pl = .{
+ .ty = try sema.addType(op_ov_tuple_ty),
+ .payload = try sema.addExtra(Air.Bin{
+ .lhs = lhs,
+ .rhs = rhs,
+ }),
+ } },
+ });
+ const ov_bit = try sema.tupleFieldValByIndex(block, src, op_ov, 1, op_ov_tuple_ty);
+ const any_ov_bit = if (lhs_ty.zigTypeTag() == .Vector)
+ try block.addInst(.{
+ .tag = .reduce,
+ .data = .{ .reduce = .{
+ .operand = ov_bit,
+ .operation = .Or,
+ } },
+ })
+ else
+ ov_bit;
+ const zero_ov = try sema.addConstant(Type.@"u1", Value.zero);
+ const no_ov = try block.addBinOp(.cmp_eq, any_ov_bit, zero_ov);
+
+ try sema.addSafetyCheck(block, no_ov, .shl_overflow);
+ return sema.tupleFieldValByIndex(block, src, op_ov, 0, op_ov_tuple_ty);
+ }
+ }
+ return block.addBinOp(air_tag, lhs, new_rhs);
}
fn zirShr(
@@ -7530,39 +9588,64 @@ fn zirShr(
defer tracy.end();
const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
+ const src = inst_data.src();
const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node };
const rhs_src: LazySrcLoc = .{ .node_offset_bin_rhs = inst_data.src_node };
const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
- const lhs = sema.resolveInst(extra.lhs);
- const rhs = sema.resolveInst(extra.rhs);
+ const lhs = try sema.resolveInst(extra.lhs);
+ const rhs = try sema.resolveInst(extra.rhs);
+ const lhs_ty = sema.typeOf(lhs);
+ const rhs_ty = sema.typeOf(rhs);
+ try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src);
+ const target = sema.mod.getTarget();
+ const scalar_ty = lhs_ty.scalarType();
const runtime_src = if (try sema.resolveMaybeUndefVal(block, rhs_src, rhs)) |rhs_val| rs: {
+ if (rhs_val.isUndef()) {
+ return sema.addConstUndef(lhs_ty);
+ }
+ // If rhs is 0, return lhs without doing any calculations.
+ if (try rhs_val.compareWithZeroAdvanced(.eq, sema.kit(block, src))) {
+ return lhs;
+ }
+ if (scalar_ty.zigTypeTag() != .ComptimeInt) {
+ var bits_payload = Value.Payload.U64{
+ .base = .{ .tag = .int_u64 },
+ .data = scalar_ty.intInfo(target).bits,
+ };
+ const bit_value = Value.initPayload(&bits_payload.base);
+ if (rhs_ty.zigTypeTag() == .Vector) {
+ var i: usize = 0;
+ while (i < rhs_ty.vectorLen()) : (i += 1) {
+ if (rhs_val.indexVectorlike(i).compareHetero(.gte, bit_value, target)) {
+ return sema.fail(block, rhs_src, "shift amount '{}' at index '{d}' is too large for operand type '{}'", .{
+ rhs_val.indexVectorlike(i).fmtValue(scalar_ty, sema.mod),
+ i,
+ scalar_ty.fmt(sema.mod),
+ });
+ }
+ }
+ } else if (rhs_val.compareHetero(.gte, bit_value, target)) {
+ return sema.fail(block, rhs_src, "shift amount '{}' is too large for operand type '{}'", .{
+ rhs_val.fmtValue(scalar_ty, sema.mod),
+ scalar_ty.fmt(sema.mod),
+ });
+ }
+ }
if (try sema.resolveMaybeUndefVal(block, lhs_src, lhs)) |lhs_val| {
- const lhs_ty = sema.typeOf(lhs);
- if (lhs_val.isUndef() or rhs_val.isUndef()) {
+ if (lhs_val.isUndef()) {
return sema.addConstUndef(lhs_ty);
}
- // If rhs is 0, return lhs without doing any calculations.
- if (rhs_val.compareWithZero(.eq)) {
- return sema.addConstant(lhs_ty, lhs_val);
- }
if (air_tag == .shr_exact) {
// Detect if any ones would be shifted out.
- const bits = @intCast(u16, rhs_val.toUnsignedInt());
- const truncated = try lhs_val.intTrunc(sema.arena, .unsigned, bits);
- if (!truncated.compareWithZero(.eq)) {
+ const truncated = try lhs_val.intTruncBitsAsValue(lhs_ty, sema.arena, .unsigned, rhs_val, target);
+ if (!(try truncated.compareWithZeroAdvanced(.eq, sema.kit(block, src)))) {
return sema.addConstUndef(lhs_ty);
}
}
- const val = try lhs_val.shr(rhs_val, sema.arena);
+ const val = try lhs_val.shr(rhs_val, lhs_ty, sema.arena, target);
return sema.addConstant(lhs_ty, val);
} else {
- // Even if lhs is not comptime known, we can still deduce certain things based
- // on rhs.
- // If rhs is 0, return lhs without doing any calculations.
- if (rhs_val.compareWithZero(.eq)) {
- return lhs;
- }
break :rs lhs_src;
}
} else rhs_src;
@@ -7585,57 +9668,48 @@ fn zirBitwise(
const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node };
const rhs_src: LazySrcLoc = .{ .node_offset_bin_rhs = inst_data.src_node };
const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
- const lhs = sema.resolveInst(extra.lhs);
- const rhs = sema.resolveInst(extra.rhs);
+ const lhs = try sema.resolveInst(extra.lhs);
+ const rhs = try sema.resolveInst(extra.rhs);
const lhs_ty = sema.typeOf(lhs);
const rhs_ty = sema.typeOf(rhs);
+ try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src);
const instructions = &[_]Air.Inst.Ref{ lhs, rhs };
const resolved_type = try sema.resolvePeerTypes(block, src, instructions, .{ .override = &[_]LazySrcLoc{ lhs_src, rhs_src } });
- const casted_lhs = try sema.coerce(block, resolved_type, lhs, lhs_src);
- const casted_rhs = try sema.coerce(block, resolved_type, rhs, rhs_src);
-
- const scalar_type = if (resolved_type.zigTypeTag() == .Vector)
- resolved_type.elemType()
- else
- resolved_type;
-
+ const scalar_type = resolved_type.scalarType();
const scalar_tag = scalar_type.zigTypeTag();
- if (lhs_ty.zigTypeTag() == .Vector and rhs_ty.zigTypeTag() == .Vector) {
- if (lhs_ty.arrayLen() != rhs_ty.arrayLen()) {
- return sema.fail(block, src, "vector length mismatch: {d} and {d}", .{
- lhs_ty.arrayLen(),
- rhs_ty.arrayLen(),
- });
- }
- return sema.fail(block, src, "TODO implement support for vectors in zirBitwise", .{});
- } else if (lhs_ty.zigTypeTag() == .Vector or rhs_ty.zigTypeTag() == .Vector) {
- return sema.fail(block, src, "mixed scalar and vector operands to binary expression: '{}' and '{}'", .{
- lhs_ty,
- rhs_ty,
- });
- }
+ const casted_lhs = try sema.coerce(block, resolved_type, lhs, lhs_src);
+ const casted_rhs = try sema.coerce(block, resolved_type, rhs, rhs_src);
const is_int = scalar_tag == .Int or scalar_tag == .ComptimeInt;
+ const target = sema.mod.getTarget();
if (!is_int) {
return sema.fail(block, src, "invalid operands to binary bitwise expression: '{s}' and '{s}'", .{ @tagName(lhs_ty.zigTypeTag()), @tagName(rhs_ty.zigTypeTag()) });
}
- if (try sema.resolveMaybeUndefVal(block, lhs_src, casted_lhs)) |lhs_val| {
- if (try sema.resolveMaybeUndefVal(block, rhs_src, casted_rhs)) |rhs_val| {
- const result_val = switch (air_tag) {
- .bit_and => try lhs_val.bitwiseAnd(rhs_val, sema.arena),
- .bit_or => try lhs_val.bitwiseOr(rhs_val, sema.arena),
- .xor => try lhs_val.bitwiseXor(rhs_val, sema.arena),
- else => unreachable,
- };
- return sema.addConstant(scalar_type, result_val);
+ const runtime_src = runtime: {
+ // TODO: ask the linker what kind of relocations are available, and
+ // in some cases emit a Value that means "this decl's address AND'd with this operand".
+ if (try sema.resolveMaybeUndefValIntable(block, lhs_src, casted_lhs)) |lhs_val| {
+ if (try sema.resolveMaybeUndefValIntable(block, rhs_src, casted_rhs)) |rhs_val| {
+ const result_val = switch (air_tag) {
+ .bit_and => try lhs_val.bitwiseAnd(rhs_val, resolved_type, sema.arena, target),
+ .bit_or => try lhs_val.bitwiseOr(rhs_val, resolved_type, sema.arena, target),
+ .xor => try lhs_val.bitwiseXor(rhs_val, resolved_type, sema.arena, target),
+ else => unreachable,
+ };
+ return sema.addConstant(resolved_type, result_val);
+ } else {
+ break :runtime rhs_src;
+ }
+ } else {
+ break :runtime lhs_src;
}
- }
+ };
- try sema.requireRuntimeBlock(block, src);
+ try sema.requireRuntimeBlock(block, runtime_src);
return block.addBinOp(air_tag, casted_lhs, casted_rhs);
}
@@ -7647,33 +9721,35 @@ fn zirBitNot(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.
const src = inst_data.src();
const operand_src = src; // TODO put this on the operand, not the '~'
- const operand = sema.resolveInst(inst_data.operand);
+ const operand = try sema.resolveInst(inst_data.operand);
const operand_type = sema.typeOf(operand);
const scalar_type = operand_type.scalarType();
+ const target = sema.mod.getTarget();
if (scalar_type.zigTypeTag() != .Int) {
- return sema.fail(block, src, "unable to perform binary not operation on type '{}'", .{operand_type});
+ return sema.fail(block, src, "unable to perform binary not operation on type '{}'", .{
+ operand_type.fmt(sema.mod),
+ });
}
if (try sema.resolveMaybeUndefVal(block, operand_src, operand)) |val| {
- const target = sema.mod.getTarget();
if (val.isUndef()) {
- return sema.addConstUndef(scalar_type);
+ return sema.addConstUndef(operand_type);
} else if (operand_type.zigTypeTag() == .Vector) {
- const vec_len = try sema.usizeCast(block, operand_src, operand_type.arrayLen());
+ const vec_len = try sema.usizeCast(block, operand_src, operand_type.vectorLen());
var elem_val_buf: Value.ElemValueBuffer = undefined;
const elems = try sema.arena.alloc(Value, vec_len);
for (elems) |*elem, i| {
- const elem_val = val.elemValueBuffer(i, &elem_val_buf);
+ const elem_val = val.elemValueBuffer(sema.mod, i, &elem_val_buf);
elem.* = try elem_val.bitwiseNot(scalar_type, sema.arena, target);
}
return sema.addConstant(
operand_type,
- try Value.Tag.array.create(sema.arena, elems),
+ try Value.Tag.aggregate.create(sema.arena, elems),
);
} else {
- const result_val = try val.bitwiseNot(scalar_type, sema.arena, target);
- return sema.addConstant(scalar_type, result_val);
+ const result_val = try val.bitwiseNot(operand_type, sema.arena, target);
+ return sema.addConstant(operand_type, result_val);
}
}
@@ -7681,100 +9757,347 @@ fn zirBitNot(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.
return block.addTyOp(.not, operand_type, operand);
}
+fn analyzeTupleCat(
+ sema: *Sema,
+ block: *Block,
+ src_node: i32,
+ lhs: Air.Inst.Ref,
+ rhs: Air.Inst.Ref,
+) CompileError!Air.Inst.Ref {
+ const lhs_ty = sema.typeOf(lhs);
+ const rhs_ty = sema.typeOf(rhs);
+ const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = src_node };
+ const rhs_src: LazySrcLoc = .{ .node_offset_bin_rhs = src_node };
+
+ const lhs_tuple = lhs_ty.tupleFields();
+ const rhs_tuple = rhs_ty.tupleFields();
+ const dest_fields = lhs_tuple.types.len + rhs_tuple.types.len;
+
+ if (dest_fields == 0) {
+ return sema.addConstant(Type.initTag(.empty_struct_literal), Value.initTag(.empty_struct_value));
+ }
+ const final_len = try sema.usizeCast(block, rhs_src, dest_fields);
+
+ const types = try sema.arena.alloc(Type, final_len);
+ const values = try sema.arena.alloc(Value, final_len);
+
+ const opt_runtime_src = rs: {
+ var runtime_src: ?LazySrcLoc = null;
+ for (lhs_tuple.types) |ty, i| {
+ types[i] = ty;
+ values[i] = lhs_tuple.values[i];
+ const operand_src = lhs_src; // TODO better source location
+ if (values[i].tag() == .unreachable_value) {
+ runtime_src = operand_src;
+ }
+ }
+ const offset = lhs_tuple.types.len;
+ for (rhs_tuple.types) |ty, i| {
+ types[i + offset] = ty;
+ values[i + offset] = rhs_tuple.values[i];
+ const operand_src = rhs_src; // TODO better source location
+ if (rhs_tuple.values[i].tag() == .unreachable_value) {
+ runtime_src = operand_src;
+ }
+ }
+ break :rs runtime_src;
+ };
+
+ const tuple_ty = try Type.Tag.tuple.create(sema.arena, .{
+ .types = types,
+ .values = values,
+ });
+
+ const runtime_src = opt_runtime_src orelse {
+ const tuple_val = try Value.Tag.aggregate.create(sema.arena, values);
+ return sema.addConstant(tuple_ty, tuple_val);
+ };
+
+ try sema.requireRuntimeBlock(block, runtime_src);
+
+ const element_refs = try sema.arena.alloc(Air.Inst.Ref, final_len);
+ for (lhs_tuple.types) |_, i| {
+ const operand_src = lhs_src; // TODO better source location
+ element_refs[i] = try sema.tupleFieldValByIndex(block, operand_src, lhs, @intCast(u32, i), lhs_ty);
+ }
+ const offset = lhs_tuple.types.len;
+ for (rhs_tuple.types) |_, i| {
+ const operand_src = rhs_src; // TODO better source location
+ element_refs[i + offset] =
+ try sema.tupleFieldValByIndex(block, operand_src, rhs, @intCast(u32, i), rhs_ty);
+ }
+
+ return block.addAggregateInit(tuple_ty, element_refs);
+}
+
fn zirArrayCat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
const tracy = trace(@src());
defer tracy.end();
const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
- const lhs = sema.resolveInst(extra.lhs);
- const rhs = sema.resolveInst(extra.rhs);
+ const lhs = try sema.resolveInst(extra.lhs);
+ const rhs = try sema.resolveInst(extra.rhs);
const lhs_ty = sema.typeOf(lhs);
const rhs_ty = sema.typeOf(rhs);
+ const src = inst_data.src();
+
+ if (lhs_ty.isTuple() and rhs_ty.isTuple()) {
+ return sema.analyzeTupleCat(block, inst_data.src_node, lhs, rhs);
+ }
+
const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node };
const rhs_src: LazySrcLoc = .{ .node_offset_bin_rhs = inst_data.src_node };
- const lhs_info = getArrayCatInfo(lhs_ty) orelse
- return sema.fail(block, lhs_src, "expected array, found '{}'", .{lhs_ty});
- const rhs_info = getArrayCatInfo(rhs_ty) orelse
- return sema.fail(block, rhs_src, "expected array, found '{}'", .{rhs_ty});
- if (!lhs_info.elem_type.eql(rhs_info.elem_type)) {
- return sema.fail(block, rhs_src, "expected array of type '{}', found '{}'", .{ lhs_info.elem_type, rhs_ty });
- }
+ const lhs_info = try sema.getArrayCatInfo(block, lhs_src, lhs);
+ const rhs_info = try sema.getArrayCatInfo(block, rhs_src, rhs);
+
+ const resolved_elem_ty = t: {
+ var trash_block = block.makeSubBlock();
+ trash_block.is_comptime = false;
+ defer trash_block.instructions.deinit(sema.gpa);
+
+ const instructions = [_]Air.Inst.Ref{
+ try trash_block.addBitCast(lhs_info.elem_type, .void_value),
+ try trash_block.addBitCast(rhs_info.elem_type, .void_value),
+ };
+ break :t try sema.resolvePeerTypes(block, src, &instructions, .{
+ .override = &[_]LazySrcLoc{ lhs_src, rhs_src },
+ });
+ };
- // When there is a sentinel mismatch, no sentinel on the result. The type system
- // will catch this if it is a problem.
- var res_sent: ?Value = null;
- if (rhs_info.sentinel != null and lhs_info.sentinel != null) {
- if (rhs_info.sentinel.?.eql(lhs_info.sentinel.?, lhs_info.elem_type)) {
- res_sent = lhs_info.sentinel.?;
+ // When there is a sentinel mismatch, no sentinel on the result.
+ // Otherwise, use the sentinel value provided by either operand,
+ // coercing it to the peer-resolved element type.
+ const res_sent_val: ?Value = s: {
+ if (lhs_info.sentinel) |lhs_sent_val| {
+ const lhs_sent = try sema.addConstant(lhs_info.elem_type, lhs_sent_val);
+ if (rhs_info.sentinel) |rhs_sent_val| {
+ const rhs_sent = try sema.addConstant(rhs_info.elem_type, rhs_sent_val);
+ const lhs_sent_casted = try sema.coerce(block, resolved_elem_ty, lhs_sent, lhs_src);
+ const rhs_sent_casted = try sema.coerce(block, resolved_elem_ty, rhs_sent, rhs_src);
+ const lhs_sent_casted_val = try sema.resolveConstValue(block, lhs_src, lhs_sent_casted);
+ const rhs_sent_casted_val = try sema.resolveConstValue(block, rhs_src, rhs_sent_casted);
+ if (try sema.valuesEqual(block, src, lhs_sent_casted_val, rhs_sent_casted_val, resolved_elem_ty)) {
+ break :s lhs_sent_casted_val;
+ } else {
+ break :s null;
+ }
+ } else {
+ const lhs_sent_casted = try sema.coerce(block, resolved_elem_ty, lhs_sent, lhs_src);
+ const lhs_sent_casted_val = try sema.resolveConstValue(block, lhs_src, lhs_sent_casted);
+ break :s lhs_sent_casted_val;
+ }
+ } else {
+ if (rhs_info.sentinel) |rhs_sent_val| {
+ const rhs_sent = try sema.addConstant(rhs_info.elem_type, rhs_sent_val);
+ const rhs_sent_casted = try sema.coerce(block, resolved_elem_ty, rhs_sent, rhs_src);
+ const rhs_sent_casted_val = try sema.resolveConstValue(block, rhs_src, rhs_sent_casted);
+ break :s rhs_sent_casted_val;
+ } else {
+ break :s null;
+ }
}
- }
+ };
- if (try sema.resolveDefinedValue(block, lhs_src, lhs)) |lhs_val| {
+ const lhs_len = try sema.usizeCast(block, lhs_src, lhs_info.len);
+ const rhs_len = try sema.usizeCast(block, lhs_src, rhs_info.len);
+ const result_len = std.math.add(usize, lhs_len, rhs_len) catch |err| switch (err) {
+ error.Overflow => return sema.fail(
+ block,
+ src,
+ "concatenating arrays of length {d} and {d} produces an array too large for this compiler implementation to handle",
+ .{ lhs_len, rhs_len },
+ ),
+ };
+
+ const result_ty = try Type.array(sema.arena, result_len, res_sent_val, resolved_elem_ty, sema.mod);
+ const ptr_addrspace = p: {
+ if (lhs_ty.zigTypeTag() == .Pointer) break :p lhs_ty.ptrAddressSpace();
+ if (rhs_ty.zigTypeTag() == .Pointer) break :p rhs_ty.ptrAddressSpace();
+ break :p null;
+ };
+
+ const runtime_src = if (try sema.resolveDefinedValue(block, lhs_src, lhs)) |lhs_val| rs: {
if (try sema.resolveDefinedValue(block, rhs_src, rhs)) |rhs_val| {
- const lhs_len = try sema.usizeCast(block, lhs_src, lhs_info.len);
- const rhs_len = try sema.usizeCast(block, lhs_src, rhs_info.len);
- const final_len = lhs_len + rhs_len;
- const final_len_including_sent = final_len + @boolToInt(res_sent != null);
- const is_pointer = lhs_ty.zigTypeTag() == .Pointer;
- const lhs_sub_val = if (is_pointer) (try sema.pointerDeref(block, lhs_src, lhs_val, lhs_ty)).? else lhs_val;
- const rhs_sub_val = if (is_pointer) (try sema.pointerDeref(block, rhs_src, rhs_val, rhs_ty)).? else rhs_val;
- var anon_decl = try block.startAnonDecl(LazySrcLoc.unneeded);
- defer anon_decl.deinit();
+ const lhs_sub_val = if (lhs_ty.isSinglePointer())
+ (try sema.pointerDeref(block, lhs_src, lhs_val, lhs_ty)).?
+ else
+ lhs_val;
- const buf = try anon_decl.arena().alloc(Value, final_len_including_sent);
- {
- var i: usize = 0;
- while (i < lhs_len) : (i += 1) {
- const val = try lhs_sub_val.elemValue(sema.arena, i);
- buf[i] = try val.copy(anon_decl.arena());
- }
+ const rhs_sub_val = if (rhs_ty.isSinglePointer())
+ (try sema.pointerDeref(block, rhs_src, rhs_val, rhs_ty)).?
+ else
+ rhs_val;
+
+ const final_len_including_sent = result_len + @boolToInt(res_sent_val != null);
+ const element_vals = try sema.arena.alloc(Value, final_len_including_sent);
+ var elem_i: usize = 0;
+ while (elem_i < lhs_len) : (elem_i += 1) {
+ element_vals[elem_i] = try lhs_sub_val.elemValue(sema.mod, sema.arena, elem_i);
}
- {
- var i: usize = 0;
- while (i < rhs_len) : (i += 1) {
- const val = try rhs_sub_val.elemValue(sema.arena, i);
- buf[lhs_len + i] = try val.copy(anon_decl.arena());
- }
+ while (elem_i < result_len) : (elem_i += 1) {
+ element_vals[elem_i] = try rhs_sub_val.elemValue(sema.mod, sema.arena, elem_i - lhs_len);
}
- const ty = if (res_sent) |rs| ty: {
- buf[final_len] = try rs.copy(anon_decl.arena());
- break :ty try Type.Tag.array_sentinel.create(anon_decl.arena(), .{
- .len = final_len,
- .elem_type = try lhs_info.elem_type.copy(anon_decl.arena()),
- .sentinel = try rs.copy(anon_decl.arena()),
- });
- } else try Type.Tag.array.create(anon_decl.arena(), .{
- .len = final_len,
- .elem_type = try lhs_info.elem_type.copy(anon_decl.arena()),
- });
- const val = try Value.Tag.array.create(anon_decl.arena(), buf);
- const decl = try anon_decl.finish(ty, val);
- if (is_pointer) {
- return sema.analyzeDeclRef(decl);
- } else {
- return sema.analyzeDeclVal(block, .unneeded, decl);
+ if (res_sent_val) |sent_val| {
+ element_vals[result_len] = sent_val;
}
- } else {
- return sema.fail(block, lhs_src, "TODO runtime array_cat", .{});
+ const val = try Value.Tag.aggregate.create(sema.arena, element_vals);
+ return sema.addConstantMaybeRef(block, src, result_ty, val, ptr_addrspace != null);
+ } else break :rs rhs_src;
+ } else lhs_src;
+
+ try sema.requireRuntimeBlock(block, runtime_src);
+
+ if (ptr_addrspace) |ptr_as| {
+ const alloc_ty = try Type.ptr(sema.arena, sema.mod, .{
+ .pointee_type = result_ty,
+ .@"addrspace" = ptr_as,
+ });
+ const alloc = try block.addTy(.alloc, alloc_ty);
+ const elem_ptr_ty = try Type.ptr(sema.arena, sema.mod, .{
+ .pointee_type = resolved_elem_ty,
+ .@"addrspace" = ptr_as,
+ });
+
+ var elem_i: usize = 0;
+ while (elem_i < lhs_len) : (elem_i += 1) {
+ const elem_index = try sema.addIntUnsigned(Type.usize, elem_i);
+ const elem_ptr = try block.addPtrElemPtr(alloc, elem_index, elem_ptr_ty);
+ const init = try sema.elemVal(block, lhs_src, lhs, elem_index, src);
+ try sema.storePtr2(block, src, elem_ptr, src, init, lhs_src, .store);
+ }
+ while (elem_i < result_len) : (elem_i += 1) {
+ const elem_index = try sema.addIntUnsigned(Type.usize, elem_i);
+ const rhs_index = try sema.addIntUnsigned(Type.usize, elem_i - lhs_len);
+ const elem_ptr = try block.addPtrElemPtr(alloc, elem_index, elem_ptr_ty);
+ const init = try sema.elemVal(block, rhs_src, rhs, rhs_index, src);
+ try sema.storePtr2(block, src, elem_ptr, src, init, rhs_src, .store);
+ }
+ if (res_sent_val) |sent_val| {
+ const elem_index = try sema.addIntUnsigned(Type.usize, result_len);
+ const elem_ptr = try block.addPtrElemPtr(alloc, elem_index, elem_ptr_ty);
+ const init = try sema.addConstant(lhs_info.elem_type, sent_val);
+ try sema.storePtr2(block, src, elem_ptr, src, init, lhs_src, .store);
+ }
+
+ return alloc;
+ }
+
+ const element_refs = try sema.arena.alloc(Air.Inst.Ref, result_len);
+ {
+ var elem_i: usize = 0;
+ while (elem_i < lhs_len) : (elem_i += 1) {
+ const index = try sema.addIntUnsigned(Type.usize, elem_i);
+ const init = try sema.elemVal(block, lhs_src, lhs, index, src);
+ element_refs[elem_i] = try sema.coerce(block, resolved_elem_ty, init, lhs_src);
+ }
+ while (elem_i < result_len) : (elem_i += 1) {
+ const index = try sema.addIntUnsigned(Type.usize, elem_i - lhs_len);
+ const init = try sema.elemVal(block, rhs_src, rhs, index, src);
+ element_refs[elem_i] = try sema.coerce(block, resolved_elem_ty, init, rhs_src);
}
- } else {
- return sema.fail(block, lhs_src, "TODO runtime array_cat", .{});
}
+
+ return block.addAggregateInit(result_ty, element_refs);
}
-fn getArrayCatInfo(t: Type) ?Type.ArrayInfo {
- return switch (t.zigTypeTag()) {
- .Array => t.arrayInfo(),
- .Pointer => blk: {
- const ptrinfo = t.ptrInfo().data;
- if (ptrinfo.pointee_type.zigTypeTag() != .Array) return null;
- if (ptrinfo.size != .One) return null;
- break :blk ptrinfo.pointee_type.arrayInfo();
+fn getArrayCatInfo(sema: *Sema, block: *Block, src: LazySrcLoc, operand: Air.Inst.Ref) !Type.ArrayInfo {
+ const operand_ty = sema.typeOf(operand);
+ switch (operand_ty.zigTypeTag()) {
+ .Array => return operand_ty.arrayInfo(),
+ .Pointer => {
+ const ptr_info = operand_ty.ptrInfo().data;
+ switch (ptr_info.size) {
+ // TODO: in the Many case here this should only work if the type
+ // has a sentinel, and this code should compute the length based
+ // on the sentinel value.
+ .Slice, .Many => {
+ const val = try sema.resolveConstValue(block, src, operand);
+ return Type.ArrayInfo{
+ .elem_type = ptr_info.pointee_type,
+ .sentinel = ptr_info.sentinel,
+ .len = val.sliceLen(sema.mod),
+ };
+ },
+ .One => {
+ if (ptr_info.pointee_type.zigTypeTag() == .Array) {
+ return ptr_info.pointee_type.arrayInfo();
+ }
+ },
+ .C => {},
+ }
},
- else => null,
+ else => {},
+ }
+ return sema.fail(block, src, "expected indexable; found '{}'", .{operand_ty.fmt(sema.mod)});
+}
+
+fn analyzeTupleMul(
+ sema: *Sema,
+ block: *Block,
+ src_node: i32,
+ operand: Air.Inst.Ref,
+ factor: u64,
+) CompileError!Air.Inst.Ref {
+ const operand_ty = sema.typeOf(operand);
+ const operand_tuple = operand_ty.tupleFields();
+ const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = src_node };
+ const rhs_src: LazySrcLoc = .{ .node_offset_bin_rhs = src_node };
+
+ const tuple_len = operand_tuple.types.len;
+ const final_len_u64 = std.math.mul(u64, tuple_len, factor) catch
+ return sema.fail(block, rhs_src, "operation results in overflow", .{});
+
+ if (final_len_u64 == 0) {
+ return sema.addConstant(Type.initTag(.empty_struct_literal), Value.initTag(.empty_struct_value));
+ }
+ const final_len = try sema.usizeCast(block, rhs_src, final_len_u64);
+
+ const types = try sema.arena.alloc(Type, final_len);
+ const values = try sema.arena.alloc(Value, final_len);
+
+ const opt_runtime_src = rs: {
+ var runtime_src: ?LazySrcLoc = null;
+ for (operand_tuple.types) |ty, i| {
+ types[i] = ty;
+ values[i] = operand_tuple.values[i];
+ const operand_src = lhs_src; // TODO better source location
+ if (values[i].tag() == .unreachable_value) {
+ runtime_src = operand_src;
+ }
+ }
+ var i: usize = 1;
+ while (i < factor) : (i += 1) {
+ mem.copy(Type, types[tuple_len * i ..], operand_tuple.types);
+ mem.copy(Value, values[tuple_len * i ..], operand_tuple.values);
+ }
+ break :rs runtime_src;
+ };
+
+ const tuple_ty = try Type.Tag.tuple.create(sema.arena, .{
+ .types = types,
+ .values = values,
+ });
+
+ const runtime_src = opt_runtime_src orelse {
+ const tuple_val = try Value.Tag.aggregate.create(sema.arena, values);
+ return sema.addConstant(tuple_ty, tuple_val);
};
+
+ try sema.requireRuntimeBlock(block, runtime_src);
+
+ const element_refs = try sema.arena.alloc(Air.Inst.Ref, final_len);
+ for (operand_tuple.types) |_, i| {
+ const operand_src = lhs_src; // TODO better source location
+ element_refs[i] = try sema.tupleFieldValByIndex(block, operand_src, operand, @intCast(u32, i), operand_ty);
+ }
+ var i: usize = 1;
+ while (i < factor) : (i += 1) {
+ mem.copy(Air.Inst.Ref, element_refs[tuple_len * i ..], element_refs[0..tuple_len]);
+ }
+
+ return block.addAggregateInit(tuple_ty, element_refs);
}
fn zirArrayMul(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
@@ -7783,91 +10106,171 @@ fn zirArrayMul(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
- const lhs = sema.resolveInst(extra.lhs);
+ const lhs = try sema.resolveInst(extra.lhs);
const lhs_ty = sema.typeOf(lhs);
const src: LazySrcLoc = inst_data.src();
const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node };
const rhs_src: LazySrcLoc = .{ .node_offset_bin_rhs = inst_data.src_node };
- // In `**` rhs has to be comptime-known, but lhs can be runtime-known
+ // In `**` rhs must be comptime-known, but lhs can be runtime-known
const factor = try sema.resolveInt(block, rhs_src, extra.rhs, Type.usize);
- const mulinfo = getArrayCatInfo(lhs_ty) orelse
- return sema.fail(block, lhs_src, "expected array, found '{}'", .{lhs_ty});
- const final_len_u64 = std.math.mul(u64, mulinfo.len, factor) catch
+ if (lhs_ty.isTuple()) {
+ return sema.analyzeTupleMul(block, inst_data.src_node, lhs, factor);
+ }
+
+ const lhs_info = try sema.getArrayCatInfo(block, lhs_src, lhs);
+
+ const result_len_u64 = std.math.mul(u64, lhs_info.len, factor) catch
return sema.fail(block, rhs_src, "operation results in overflow", .{});
+ const result_len = try sema.usizeCast(block, src, result_len_u64);
- if (try sema.resolveDefinedValue(block, lhs_src, lhs)) |lhs_val| {
- const final_len = try sema.usizeCast(block, src, final_len_u64);
- const final_len_including_sent = final_len + @boolToInt(mulinfo.sentinel != null);
- const lhs_len = try sema.usizeCast(block, lhs_src, mulinfo.len);
+ const result_ty = try Type.array(sema.arena, result_len, lhs_info.sentinel, lhs_info.elem_type, sema.mod);
- const lhs_sub_val = if (lhs_ty.zigTypeTag() == .Pointer) (try sema.pointerDeref(block, lhs_src, lhs_val, lhs_ty)).? else lhs_val;
+ const ptr_addrspace = if (lhs_ty.zigTypeTag() == .Pointer) lhs_ty.ptrAddressSpace() else null;
+ const lhs_len = try sema.usizeCast(block, lhs_src, lhs_info.len);
- var anon_decl = try block.startAnonDecl(src);
- defer anon_decl.deinit();
+ if (try sema.resolveDefinedValue(block, lhs_src, lhs)) |lhs_val| {
+ const final_len_including_sent = result_len + @boolToInt(lhs_info.sentinel != null);
- const final_ty = if (mulinfo.sentinel) |sent|
- try Type.Tag.array_sentinel.create(anon_decl.arena(), .{
- .len = final_len,
- .elem_type = try mulinfo.elem_type.copy(anon_decl.arena()),
- .sentinel = try sent.copy(anon_decl.arena()),
- })
+ const lhs_sub_val = if (lhs_ty.isSinglePointer())
+ (try sema.pointerDeref(block, lhs_src, lhs_val, lhs_ty)).?
else
- try Type.Tag.array.create(anon_decl.arena(), .{
- .len = final_len,
- .elem_type = try mulinfo.elem_type.copy(anon_decl.arena()),
- });
- const buf = try anon_decl.arena().alloc(Value, final_len_including_sent);
-
- // Optimization for the common pattern of a single element repeated N times, such
- // as zero-filling a byte array.
- const val = if (lhs_len == 1) blk: {
- const elem_val = try lhs_sub_val.elemValue(sema.arena, 0);
- const copied_val = try elem_val.copy(anon_decl.arena());
- break :blk try Value.Tag.repeated.create(anon_decl.arena(), copied_val);
- } else blk: {
- // the actual loop
- var i: usize = 0;
- while (i < factor) : (i += 1) {
- var j: usize = 0;
- while (j < lhs_len) : (j += 1) {
- const val = try lhs_sub_val.elemValue(sema.arena, j);
- buf[lhs_len * i + j] = try val.copy(anon_decl.arena());
+ lhs_val;
+
+ const val = v: {
+ // Optimization for the common pattern of a single element repeated N times, such
+ // as zero-filling a byte array.
+ if (lhs_len == 1) {
+ const elem_val = try lhs_sub_val.elemValue(sema.mod, sema.arena, 0);
+ break :v try Value.Tag.repeated.create(sema.arena, elem_val);
+ }
+
+ const element_vals = try sema.arena.alloc(Value, final_len_including_sent);
+ var elem_i: usize = 0;
+ while (elem_i < result_len) {
+ var lhs_i: usize = 0;
+ while (lhs_i < lhs_len) : (lhs_i += 1) {
+ const elem_val = try lhs_sub_val.elemValue(sema.mod, sema.arena, lhs_i);
+ element_vals[elem_i] = elem_val;
+ elem_i += 1;
}
}
- if (mulinfo.sentinel) |sent| {
- buf[final_len] = try sent.copy(anon_decl.arena());
+ if (lhs_info.sentinel) |sent_val| {
+ element_vals[result_len] = sent_val;
}
- break :blk try Value.Tag.array.create(anon_decl.arena(), buf);
+ break :v try Value.Tag.aggregate.create(sema.arena, element_vals);
};
- const decl = try anon_decl.finish(final_ty, val);
- if (lhs_ty.zigTypeTag() == .Pointer) {
- return sema.analyzeDeclRef(decl);
- } else {
- return sema.analyzeDeclVal(block, .unneeded, decl);
+ return sema.addConstantMaybeRef(block, src, result_ty, val, ptr_addrspace != null);
+ }
+
+ try sema.requireRuntimeBlock(block, lhs_src);
+
+ if (ptr_addrspace) |ptr_as| {
+ const alloc_ty = try Type.ptr(sema.arena, sema.mod, .{
+ .pointee_type = result_ty,
+ .@"addrspace" = ptr_as,
+ });
+ const alloc = try block.addTy(.alloc, alloc_ty);
+ const elem_ptr_ty = try Type.ptr(sema.arena, sema.mod, .{
+ .pointee_type = lhs_info.elem_type,
+ .@"addrspace" = ptr_as,
+ });
+
+ var elem_i: usize = 0;
+ while (elem_i < result_len) {
+ var lhs_i: usize = 0;
+ while (lhs_i < lhs_len) : (lhs_i += 1) {
+ const elem_index = try sema.addIntUnsigned(Type.usize, elem_i);
+ elem_i += 1;
+ const lhs_index = try sema.addIntUnsigned(Type.usize, lhs_i);
+ const elem_ptr = try block.addPtrElemPtr(alloc, elem_index, elem_ptr_ty);
+ const init = try sema.elemVal(block, lhs_src, lhs, lhs_index, src);
+ try sema.storePtr2(block, src, elem_ptr, src, init, lhs_src, .store);
+ }
+ }
+ if (lhs_info.sentinel) |sent_val| {
+ const elem_index = try sema.addIntUnsigned(Type.usize, result_len);
+ const elem_ptr = try block.addPtrElemPtr(alloc, elem_index, elem_ptr_ty);
+ const init = try sema.addConstant(lhs_info.elem_type, sent_val);
+ try sema.storePtr2(block, src, elem_ptr, src, init, lhs_src, .store);
+ }
+
+ return alloc;
+ }
+
+ const element_refs = try sema.arena.alloc(Air.Inst.Ref, result_len);
+ var elem_i: usize = 0;
+ while (elem_i < result_len) {
+ var lhs_i: usize = 0;
+ while (lhs_i < lhs_len) : (lhs_i += 1) {
+ const lhs_index = try sema.addIntUnsigned(Type.usize, lhs_i);
+ const init = try sema.elemVal(block, lhs_src, lhs, lhs_index, src);
+ element_refs[elem_i] = init;
+ elem_i += 1;
}
}
- return sema.fail(block, lhs_src, "TODO runtime array_mul", .{});
+
+ return block.addAggregateInit(result_ty, element_refs);
}
-fn zirNegate(
- sema: *Sema,
- block: *Block,
- inst: Zir.Inst.Index,
- tag_override: Zir.Inst.Tag,
-) CompileError!Air.Inst.Ref {
- const tracy = trace(@src());
- defer tracy.end();
+fn zirNegate(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
+ const inst_data = sema.code.instructions.items(.data)[inst].un_node;
+ const src = inst_data.src();
+ const lhs_src = src;
+ const rhs_src = src; // TODO better source location
+
+ const rhs = try sema.resolveInst(inst_data.operand);
+ const rhs_ty = sema.typeOf(rhs);
+ const rhs_scalar_ty = rhs_ty.scalarType();
+
+ if (rhs_scalar_ty.isUnsignedInt() or switch (rhs_scalar_ty.zigTypeTag()) {
+ .Int, .ComptimeInt, .Float, .ComptimeFloat => false,
+ else => true,
+ }) {
+ return sema.fail(block, src, "negation of type '{}'", .{rhs_ty.fmt(sema.mod)});
+ }
+
+ if (rhs_scalar_ty.isAnyFloat()) {
+ // We handle float negation here to ensure negative zero is represented in the bits.
+ if (try sema.resolveMaybeUndefVal(block, rhs_src, rhs)) |rhs_val| {
+ if (rhs_val.isUndef()) return sema.addConstUndef(rhs_ty);
+ const target = sema.mod.getTarget();
+ return sema.addConstant(rhs_ty, try rhs_val.floatNeg(rhs_ty, sema.arena, target));
+ }
+ try sema.requireRuntimeBlock(block, rhs_src);
+ return block.addUnOp(.neg, rhs);
+ }
+ const lhs = if (rhs_ty.zigTypeTag() == .Vector)
+ try sema.addConstant(rhs_ty, try Value.Tag.repeated.create(sema.arena, Value.zero))
+ else
+ try sema.resolveInst(.zero);
+
+ return sema.analyzeArithmetic(block, .sub, lhs, rhs, src, lhs_src, rhs_src);
+}
+
+fn zirNegateWrap(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
const inst_data = sema.code.instructions.items(.data)[inst].un_node;
const src = inst_data.src();
const lhs_src = src;
const rhs_src = src; // TODO better source location
- const lhs = sema.resolveInst(.zero);
- const rhs = sema.resolveInst(inst_data.operand);
- return sema.analyzeArithmetic(block, tag_override, lhs, rhs, src, lhs_src, rhs_src);
+ const rhs = try sema.resolveInst(inst_data.operand);
+ const rhs_ty = sema.typeOf(rhs);
+ const rhs_scalar_ty = rhs_ty.scalarType();
+
+ switch (rhs_scalar_ty.zigTypeTag()) {
+ .Int, .ComptimeInt, .Float, .ComptimeFloat => {},
+ else => return sema.fail(block, src, "negation of type '{}'", .{rhs_ty.fmt(sema.mod)}),
+ }
+
+ const lhs = if (rhs_ty.zigTypeTag() == .Vector)
+ try sema.addConstant(rhs_ty, try Value.Tag.repeated.create(sema.arena, Value.zero))
+ else
+ try sema.resolveInst(.zero);
+
+ return sema.analyzeArithmetic(block, .subwrap, lhs, rhs, src, lhs_src, rhs_src);
}
fn zirArithmetic(
@@ -7884,8 +10287,8 @@ fn zirArithmetic(
const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node };
const rhs_src: LazySrcLoc = .{ .node_offset_bin_rhs = inst_data.src_node };
const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
- const lhs = sema.resolveInst(extra.lhs);
- const rhs = sema.resolveInst(extra.rhs);
+ const lhs = try sema.resolveInst(extra.lhs);
+ const rhs = try sema.resolveInst(extra.rhs);
return sema.analyzeArithmetic(block, zir_tag, lhs, rhs, sema.src, lhs_src, rhs_src);
}
@@ -7900,31 +10303,40 @@ fn zirOverflowArithmetic(
defer tracy.end();
const extra = sema.code.extraData(Zir.Inst.OverflowArithmetic, extended.operand).data;
- const src: LazySrcLoc = .{ .node_offset = extra.node };
+ const src = LazySrcLoc.nodeOffset(extra.node);
const lhs_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = extra.node };
const rhs_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = extra.node };
const ptr_src: LazySrcLoc = .{ .node_offset_builtin_call_arg2 = extra.node };
- const lhs = sema.resolveInst(extra.lhs);
- const rhs = sema.resolveInst(extra.rhs);
- const ptr = sema.resolveInst(extra.ptr);
+ const lhs = try sema.resolveInst(extra.lhs);
+ const rhs = try sema.resolveInst(extra.rhs);
+ const ptr = try sema.resolveInst(extra.ptr);
const lhs_ty = sema.typeOf(lhs);
+ const rhs_ty = sema.typeOf(rhs);
+ const mod = sema.mod;
+ const target = mod.getTarget();
// Note, the types of lhs/rhs (also for shifting)/ptr are already correct as ensured by astgen.
+ try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src);
const dest_ty = lhs_ty;
- if (dest_ty.zigTypeTag() != .Int) {
- return sema.fail(block, src, "expected integer type, found '{}'", .{dest_ty});
+ if (dest_ty.scalarType().zigTypeTag() != .Int) {
+ return sema.fail(block, src, "expected vector of integers or integer type, found '{}'", .{dest_ty.fmt(mod)});
}
- const target = sema.mod.getTarget();
-
const maybe_lhs_val = try sema.resolveMaybeUndefVal(block, lhs_src, lhs);
const maybe_rhs_val = try sema.resolveMaybeUndefVal(block, rhs_src, rhs);
+ const tuple_ty = try sema.overflowArithmeticTupleType(dest_ty);
+ const ov_ty = tuple_ty.tupleFields().types[1];
+ // TODO: Remove and use `ov_ty` instead.
+ // This is a temporary type used until overflow arithmetic properly returns `u1` instead of `bool`.
+ const overflowed_ty = if (dest_ty.zigTypeTag() == .Vector) try Type.vector(sema.arena, dest_ty.vectorLen(), Type.@"bool") else Type.@"bool";
+
const result: struct {
- overflowed: enum { yes, no, undef },
+ /// TODO: Rename to `overflow_bit` and make of type `u1`.
+ overflowed: Air.Inst.Ref,
wrapped: Air.Inst.Ref,
} = result: {
switch (zir_tag) {
@@ -7933,24 +10345,25 @@ fn zirOverflowArithmetic(
// to the result, even if it is undefined..
// Otherwise, if either of the argument is undefined, undefined is returned.
if (maybe_lhs_val) |lhs_val| {
- if (!lhs_val.isUndef() and lhs_val.compareWithZero(.eq)) {
- break :result .{ .overflowed = .no, .wrapped = rhs };
+ if (!lhs_val.isUndef() and (try lhs_val.compareWithZeroAdvanced(.eq, sema.kit(block, src)))) {
+ break :result .{ .overflowed = try sema.addBool(overflowed_ty, false), .wrapped = rhs };
}
}
if (maybe_rhs_val) |rhs_val| {
- if (!rhs_val.isUndef() and rhs_val.compareWithZero(.eq)) {
- break :result .{ .overflowed = .no, .wrapped = lhs };
+ if (!rhs_val.isUndef() and (try rhs_val.compareWithZeroAdvanced(.eq, sema.kit(block, src)))) {
+ break :result .{ .overflowed = try sema.addBool(overflowed_ty, false), .wrapped = lhs };
}
}
if (maybe_lhs_val) |lhs_val| {
if (maybe_rhs_val) |rhs_val| {
if (lhs_val.isUndef() or rhs_val.isUndef()) {
- break :result .{ .overflowed = .undef, .wrapped = try sema.addConstUndef(dest_ty) };
+ break :result .{ .overflowed = try sema.addConstUndef(overflowed_ty), .wrapped = try sema.addConstUndef(dest_ty) };
}
- const result = try lhs_val.intAddWithOverflow(rhs_val, dest_ty, sema.arena, target);
- const inst = try sema.addConstant(dest_ty, result.wrapped_result);
- break :result .{ .overflowed = if (result.overflowed) .yes else .no, .wrapped = inst };
+ const result = try sema.intAddWithOverflow(block, src, lhs_val, rhs_val, dest_ty);
+ const overflowed = try sema.addConstant(overflowed_ty, result.overflowed);
+ const wrapped = try sema.addConstant(dest_ty, result.wrapped_result);
+ break :result .{ .overflowed = overflowed, .wrapped = wrapped };
}
}
},
@@ -7959,17 +10372,18 @@ fn zirOverflowArithmetic(
// Otherwise, if either result is undefined, both results are undefined.
if (maybe_rhs_val) |rhs_val| {
if (rhs_val.isUndef()) {
- break :result .{ .overflowed = .undef, .wrapped = try sema.addConstUndef(dest_ty) };
- } else if (rhs_val.compareWithZero(.eq)) {
- break :result .{ .overflowed = .no, .wrapped = lhs };
+ break :result .{ .overflowed = try sema.addConstUndef(overflowed_ty), .wrapped = try sema.addConstUndef(dest_ty) };
+ } else if (try rhs_val.compareWithZeroAdvanced(.eq, sema.kit(block, src))) {
+ break :result .{ .overflowed = try sema.addBool(overflowed_ty, false), .wrapped = lhs };
} else if (maybe_lhs_val) |lhs_val| {
if (lhs_val.isUndef()) {
- break :result .{ .overflowed = .undef, .wrapped = try sema.addConstUndef(dest_ty) };
+ break :result .{ .overflowed = try sema.addConstUndef(overflowed_ty), .wrapped = try sema.addConstUndef(dest_ty) };
}
- const result = try lhs_val.intSubWithOverflow(rhs_val, dest_ty, sema.arena, target);
- const inst = try sema.addConstant(dest_ty, result.wrapped_result);
- break :result .{ .overflowed = if (result.overflowed) .yes else .no, .wrapped = inst };
+ const result = try sema.intSubWithOverflow(block, src, lhs_val, rhs_val, dest_ty);
+ const overflowed = try sema.addConstant(overflowed_ty, result.overflowed);
+ const wrapped = try sema.addConstant(dest_ty, result.wrapped_result);
+ break :result .{ .overflowed = overflowed, .wrapped = wrapped };
}
}
},
@@ -7979,20 +10393,20 @@ fn zirOverflowArithmetic(
// Otherwise, if either of the arguments is undefined, both results are undefined.
if (maybe_lhs_val) |lhs_val| {
if (!lhs_val.isUndef()) {
- if (lhs_val.compareWithZero(.eq)) {
- break :result .{ .overflowed = .no, .wrapped = lhs };
- } else if (lhs_val.compare(.eq, Value.one, dest_ty)) {
- break :result .{ .overflowed = .no, .wrapped = rhs };
+ if (try lhs_val.compareWithZeroAdvanced(.eq, sema.kit(block, src))) {
+ break :result .{ .overflowed = try sema.addBool(overflowed_ty, false), .wrapped = lhs };
+ } else if (try sema.compare(block, src, lhs_val, .eq, Value.one, dest_ty)) {
+ break :result .{ .overflowed = try sema.addBool(overflowed_ty, false), .wrapped = rhs };
}
}
}
if (maybe_rhs_val) |rhs_val| {
if (!rhs_val.isUndef()) {
- if (rhs_val.compareWithZero(.eq)) {
- break :result .{ .overflowed = .no, .wrapped = rhs };
- } else if (rhs_val.compare(.eq, Value.one, dest_ty)) {
- break :result .{ .overflowed = .no, .wrapped = lhs };
+ if (try rhs_val.compareWithZeroAdvanced(.eq, sema.kit(block, src))) {
+ break :result .{ .overflowed = try sema.addBool(overflowed_ty, false), .wrapped = rhs };
+ } else if (try sema.compare(block, src, rhs_val, .eq, Value.one, dest_ty)) {
+ break :result .{ .overflowed = try sema.addBool(overflowed_ty, false), .wrapped = lhs };
}
}
}
@@ -8000,12 +10414,13 @@ fn zirOverflowArithmetic(
if (maybe_lhs_val) |lhs_val| {
if (maybe_rhs_val) |rhs_val| {
if (lhs_val.isUndef() or rhs_val.isUndef()) {
- break :result .{ .overflowed = .undef, .wrapped = try sema.addConstUndef(dest_ty) };
+ break :result .{ .overflowed = try sema.addConstUndef(overflowed_ty), .wrapped = try sema.addConstUndef(dest_ty) };
}
const result = try lhs_val.intMulWithOverflow(rhs_val, dest_ty, sema.arena, target);
- const inst = try sema.addConstant(dest_ty, result.wrapped_result);
- break :result .{ .overflowed = if (result.overflowed) .yes else .no, .wrapped = inst };
+ const overflowed = try sema.addConstant(overflowed_ty, result.overflowed);
+ const wrapped = try sema.addConstant(dest_ty, result.wrapped_result);
+ break :result .{ .overflowed = overflowed, .wrapped = wrapped };
}
}
},
@@ -8014,24 +10429,25 @@ fn zirOverflowArithmetic(
// If rhs is zero, the result is lhs (even if undefined) and no overflow occurred.
// Oterhwise if either of the arguments is undefined, both results are undefined.
if (maybe_lhs_val) |lhs_val| {
- if (!lhs_val.isUndef() and lhs_val.compareWithZero(.eq)) {
- break :result .{ .overflowed = .no, .wrapped = lhs };
+ if (!lhs_val.isUndef() and (try lhs_val.compareWithZeroAdvanced(.eq, sema.kit(block, src)))) {
+ break :result .{ .overflowed = try sema.addBool(overflowed_ty, false), .wrapped = lhs };
}
}
if (maybe_rhs_val) |rhs_val| {
- if (!rhs_val.isUndef() and rhs_val.compareWithZero(.eq)) {
- break :result .{ .overflowed = .no, .wrapped = lhs };
+ if (!rhs_val.isUndef() and (try rhs_val.compareWithZeroAdvanced(.eq, sema.kit(block, src)))) {
+ break :result .{ .overflowed = try sema.addBool(overflowed_ty, false), .wrapped = lhs };
}
}
if (maybe_lhs_val) |lhs_val| {
if (maybe_rhs_val) |rhs_val| {
if (lhs_val.isUndef() or rhs_val.isUndef()) {
- break :result .{ .overflowed = .undef, .wrapped = try sema.addConstUndef(dest_ty) };
+ break :result .{ .overflowed = try sema.addConstUndef(overflowed_ty), .wrapped = try sema.addConstUndef(dest_ty) };
}
const result = try lhs_val.shlWithOverflow(rhs_val, dest_ty, sema.arena, target);
- const inst = try sema.addConstant(dest_ty, result.wrapped_result);
- break :result .{ .overflowed = if (result.overflowed) .yes else .no, .wrapped = inst };
+ const overflowed = try sema.addConstant(overflowed_ty, result.overflowed);
+ const wrapped = try sema.addConstant(dest_ty, result.wrapped_result);
+ break :result .{ .overflowed = overflowed, .wrapped = wrapped };
}
}
},
@@ -8047,25 +10463,52 @@ fn zirOverflowArithmetic(
};
try sema.requireRuntimeBlock(block, src);
- return block.addInst(.{
+
+ const tuple = try block.addInst(.{
.tag = air_tag,
- .data = .{ .pl_op = .{
- .operand = ptr,
- .payload = try sema.addExtra(Air.Bin{
+ .data = .{ .ty_pl = .{
+ .ty = try block.sema.addType(tuple_ty),
+ .payload = try block.sema.addExtra(Air.Bin{
.lhs = lhs,
.rhs = rhs,
}),
} },
});
+
+ const wrapped = try sema.tupleFieldValByIndex(block, src, tuple, 0, tuple_ty);
+ try sema.storePtr2(block, src, ptr, ptr_src, wrapped, src, .store);
+
+ const overflow_bit = try sema.tupleFieldValByIndex(block, src, tuple, 1, tuple_ty);
+ const zero_ov_val = if (dest_ty.zigTypeTag() == .Vector) try Value.Tag.repeated.create(sema.arena, Value.zero) else Value.zero;
+ const zero_ov = try sema.addConstant(ov_ty, zero_ov_val);
+
+ const overflowed_inst = if (dest_ty.zigTypeTag() == .Vector)
+ block.addCmpVector(overflow_bit, .zero, .neq, try sema.addType(ov_ty))
+ else
+ block.addBinOp(.cmp_neq, overflow_bit, zero_ov);
+ return overflowed_inst;
};
try sema.storePtr2(block, src, ptr, ptr_src, result.wrapped, src, .store);
+ return result.overflowed;
+}
- return switch (result.overflowed) {
- .yes => Air.Inst.Ref.bool_true,
- .no => Air.Inst.Ref.bool_false,
- .undef => try sema.addConstUndef(Type.bool),
- };
+fn overflowArithmeticTupleType(sema: *Sema, ty: Type) !Type {
+ const ov_ty = if (ty.zigTypeTag() == .Vector) try Type.vector(sema.arena, ty.vectorLen(), Type.@"u1") else Type.@"u1";
+
+ const types = try sema.arena.alloc(Type, 2);
+ const values = try sema.arena.alloc(Value, 2);
+ const tuple_ty = try Type.Tag.tuple.create(sema.arena, .{
+ .types = types,
+ .values = values,
+ });
+
+ types[0] = ty;
+ types[1] = ov_ty;
+ values[0] = Value.initTag(.unreachable_value);
+ values[1] = Value.initTag(.unreachable_value);
+
+ return tuple_ty;
}
fn analyzeArithmetic(
@@ -8083,18 +10526,8 @@ fn analyzeArithmetic(
const rhs_ty = sema.typeOf(rhs);
const lhs_zig_ty_tag = try lhs_ty.zigTypeTagOrPoison();
const rhs_zig_ty_tag = try rhs_ty.zigTypeTagOrPoison();
- if (lhs_zig_ty_tag == .Vector and rhs_zig_ty_tag == .Vector) {
- if (lhs_ty.arrayLen() != rhs_ty.arrayLen()) {
- return sema.fail(block, src, "vector length mismatch: {d} and {d}", .{
- lhs_ty.arrayLen(), rhs_ty.arrayLen(),
- });
- }
- return sema.fail(block, src, "TODO implement support for vectors in Sema.analyzeArithmetic", .{});
- } else if (lhs_zig_ty_tag == .Vector or rhs_zig_ty_tag == .Vector) {
- return sema.fail(block, src, "mixed scalar and vector operands to binary expression: '{}' and '{}'", .{
- lhs_ty, rhs_ty,
- });
- }
+ try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src);
+
if (lhs_zig_ty_tag == .Pointer) switch (lhs_ty.ptrSize()) {
.One, .Slice => {},
.Many, .C => {
@@ -8117,15 +10550,13 @@ fn analyzeArithmetic(
const resolved_type = try sema.resolvePeerTypes(block, src, instructions, .{
.override = &[_]LazySrcLoc{ lhs_src, rhs_src },
});
+
const casted_lhs = try sema.coerce(block, resolved_type, lhs, lhs_src);
const casted_rhs = try sema.coerce(block, resolved_type, rhs, rhs_src);
- const scalar_type = if (resolved_type.zigTypeTag() == .Vector)
- resolved_type.elemType()
- else
- resolved_type;
-
- const scalar_tag = scalar_type.zigTypeTag();
+ const lhs_scalar_ty = lhs_ty.scalarType();
+ const rhs_scalar_ty = rhs_ty.scalarType();
+ const scalar_tag = resolved_type.scalarType().zigTypeTag();
const is_int = scalar_tag == .Int or scalar_tag == .ComptimeInt;
const is_float = scalar_tag == .Float or scalar_tag == .ComptimeFloat;
@@ -8136,9 +10567,10 @@ fn analyzeArithmetic(
});
}
- const target = sema.mod.getTarget();
- const maybe_lhs_val = try sema.resolveMaybeUndefVal(block, lhs_src, casted_lhs);
- const maybe_rhs_val = try sema.resolveMaybeUndefVal(block, rhs_src, casted_rhs);
+ const mod = sema.mod;
+ const target = mod.getTarget();
+ const maybe_lhs_val = try sema.resolveMaybeUndefValIntable(block, lhs_src, casted_lhs);
+ const maybe_rhs_val = try sema.resolveMaybeUndefValIntable(block, rhs_src, casted_rhs);
const rs: struct { src: LazySrcLoc, air_tag: Air.Inst.Tag } = rs: {
switch (zir_tag) {
.add => {
@@ -8150,7 +10582,7 @@ fn analyzeArithmetic(
// overflow (max_int), causing illegal behavior.
// For floats: either operand being undef makes the result undef.
if (maybe_lhs_val) |lhs_val| {
- if (!lhs_val.isUndef() and lhs_val.compareWithZero(.eq)) {
+ if (!lhs_val.isUndef() and (try lhs_val.compareWithZeroAdvanced(.eq, sema.kit(block, src)))) {
return casted_rhs;
}
}
@@ -8159,10 +10591,10 @@ fn analyzeArithmetic(
if (is_int) {
return sema.failWithUseOfUndef(block, rhs_src);
} else {
- return sema.addConstUndef(scalar_type);
+ return sema.addConstUndef(resolved_type);
}
}
- if (rhs_val.compareWithZero(.eq)) {
+ if (try rhs_val.compareWithZeroAdvanced(.eq, sema.kit(block, src))) {
return casted_lhs;
}
}
@@ -8171,19 +10603,21 @@ fn analyzeArithmetic(
if (is_int) {
return sema.failWithUseOfUndef(block, lhs_src);
} else {
- return sema.addConstUndef(scalar_type);
+ return sema.addConstUndef(resolved_type);
}
}
if (maybe_rhs_val) |rhs_val| {
if (is_int) {
- return sema.addConstant(
- scalar_type,
- try lhs_val.intAdd(rhs_val, sema.arena),
- );
+ const sum = try sema.intAdd(block, src, lhs_val, rhs_val, resolved_type);
+ var vector_index: usize = undefined;
+ if (!(try sema.intFitsInType(block, src, sum, resolved_type, &vector_index))) {
+ return sema.failWithIntegerOverflow(block, src, resolved_type, sum, vector_index);
+ }
+ return sema.addConstant(resolved_type, sum);
} else {
return sema.addConstant(
- scalar_type,
- try lhs_val.floatAdd(rhs_val, scalar_type, sema.arena),
+ resolved_type,
+ try sema.floatAdd(lhs_val, rhs_val, resolved_type),
);
}
} else break :rs .{ .src = rhs_src, .air_tag = .add };
@@ -8194,21 +10628,21 @@ fn analyzeArithmetic(
// If either of the operands are zero, the other operand is returned.
// If either of the operands are undefined, the result is undefined.
if (maybe_lhs_val) |lhs_val| {
- if (!lhs_val.isUndef() and lhs_val.compareWithZero(.eq)) {
+ if (!lhs_val.isUndef() and (try lhs_val.compareWithZeroAdvanced(.eq, sema.kit(block, src)))) {
return casted_rhs;
}
}
if (maybe_rhs_val) |rhs_val| {
if (rhs_val.isUndef()) {
- return sema.addConstUndef(scalar_type);
+ return sema.addConstUndef(resolved_type);
}
- if (rhs_val.compareWithZero(.eq)) {
+ if (try rhs_val.compareWithZeroAdvanced(.eq, sema.kit(block, src))) {
return casted_lhs;
}
if (maybe_lhs_val) |lhs_val| {
return sema.addConstant(
- scalar_type,
- try lhs_val.numberAddWrap(rhs_val, scalar_type, sema.arena, target),
+ resolved_type,
+ try sema.numberAddWrap(block, src, lhs_val, rhs_val, resolved_type),
);
} else break :rs .{ .src = lhs_src, .air_tag = .addwrap };
} else break :rs .{ .src = rhs_src, .air_tag = .addwrap };
@@ -8218,24 +10652,24 @@ fn analyzeArithmetic(
// If either of the operands are zero, then the other operand is returned.
// If either of the operands are undefined, the result is undefined.
if (maybe_lhs_val) |lhs_val| {
- if (!lhs_val.isUndef() and lhs_val.compareWithZero(.eq)) {
+ if (!lhs_val.isUndef() and (try lhs_val.compareWithZeroAdvanced(.eq, sema.kit(block, src)))) {
return casted_rhs;
}
}
if (maybe_rhs_val) |rhs_val| {
if (rhs_val.isUndef()) {
- return sema.addConstUndef(scalar_type);
+ return sema.addConstUndef(resolved_type);
}
- if (rhs_val.compareWithZero(.eq)) {
+ if (try rhs_val.compareWithZeroAdvanced(.eq, sema.kit(block, src))) {
return casted_lhs;
}
if (maybe_lhs_val) |lhs_val| {
const val = if (scalar_tag == .ComptimeInt)
- try lhs_val.intAdd(rhs_val, sema.arena)
+ try sema.intAdd(block, src, lhs_val, rhs_val, resolved_type)
else
- try lhs_val.intAddSat(rhs_val, scalar_type, sema.arena, target);
+ try lhs_val.intAddSat(rhs_val, resolved_type, sema.arena, target);
- return sema.addConstant(scalar_type, val);
+ return sema.addConstant(resolved_type, val);
} else break :rs .{ .src = lhs_src, .air_tag = .add_sat };
} else break :rs .{ .src = rhs_src, .air_tag = .add_sat };
},
@@ -8252,10 +10686,10 @@ fn analyzeArithmetic(
if (is_int) {
return sema.failWithUseOfUndef(block, rhs_src);
} else {
- return sema.addConstUndef(scalar_type);
+ return sema.addConstUndef(resolved_type);
}
}
- if (rhs_val.compareWithZero(.eq)) {
+ if (try rhs_val.compareWithZeroAdvanced(.eq, sema.kit(block, src))) {
return casted_lhs;
}
}
@@ -8264,19 +10698,21 @@ fn analyzeArithmetic(
if (is_int) {
return sema.failWithUseOfUndef(block, lhs_src);
} else {
- return sema.addConstUndef(scalar_type);
+ return sema.addConstUndef(resolved_type);
}
}
if (maybe_rhs_val) |rhs_val| {
if (is_int) {
- return sema.addConstant(
- scalar_type,
- try lhs_val.intSub(rhs_val, sema.arena),
- );
+ const diff = try sema.intSub(block, src, lhs_val, rhs_val, resolved_type);
+ var vector_index: usize = undefined;
+ if (!(try sema.intFitsInType(block, src, diff, resolved_type, &vector_index))) {
+ return sema.failWithIntegerOverflow(block, src, resolved_type, diff, vector_index);
+ }
+ return sema.addConstant(resolved_type, diff);
} else {
return sema.addConstant(
- scalar_type,
- try lhs_val.floatSub(rhs_val, scalar_type, sema.arena),
+ resolved_type,
+ try sema.floatSub(lhs_val, rhs_val, resolved_type),
);
}
} else break :rs .{ .src = rhs_src, .air_tag = .sub };
@@ -8288,20 +10724,20 @@ fn analyzeArithmetic(
// If either of the operands are undefined, the result is undefined.
if (maybe_rhs_val) |rhs_val| {
if (rhs_val.isUndef()) {
- return sema.addConstUndef(scalar_type);
+ return sema.addConstUndef(resolved_type);
}
- if (rhs_val.compareWithZero(.eq)) {
+ if (try rhs_val.compareWithZeroAdvanced(.eq, sema.kit(block, src))) {
return casted_lhs;
}
}
if (maybe_lhs_val) |lhs_val| {
if (lhs_val.isUndef()) {
- return sema.addConstUndef(scalar_type);
+ return sema.addConstUndef(resolved_type);
}
if (maybe_rhs_val) |rhs_val| {
return sema.addConstant(
- scalar_type,
- try lhs_val.numberSubWrap(rhs_val, scalar_type, sema.arena, target),
+ resolved_type,
+ try sema.numberSubWrap(block, src, lhs_val, rhs_val, resolved_type),
);
} else break :rs .{ .src = rhs_src, .air_tag = .subwrap };
} else break :rs .{ .src = lhs_src, .air_tag = .subwrap };
@@ -8312,23 +10748,23 @@ fn analyzeArithmetic(
// If either of the operands are undefined, result is undefined.
if (maybe_rhs_val) |rhs_val| {
if (rhs_val.isUndef()) {
- return sema.addConstUndef(scalar_type);
+ return sema.addConstUndef(resolved_type);
}
- if (rhs_val.compareWithZero(.eq)) {
+ if (try rhs_val.compareWithZeroAdvanced(.eq, sema.kit(block, src))) {
return casted_lhs;
}
}
if (maybe_lhs_val) |lhs_val| {
if (lhs_val.isUndef()) {
- return sema.addConstUndef(scalar_type);
+ return sema.addConstUndef(resolved_type);
}
if (maybe_rhs_val) |rhs_val| {
const val = if (scalar_tag == .ComptimeInt)
- try lhs_val.intSub(rhs_val, sema.arena)
+ try sema.intSub(block, src, lhs_val, rhs_val, resolved_type)
else
- try lhs_val.intSubSat(rhs_val, scalar_type, sema.arena, target);
+ try lhs_val.intSubSat(rhs_val, resolved_type, sema.arena, target);
- return sema.addConstant(scalar_type, val);
+ return sema.addConstant(resolved_type, val);
} else break :rs .{ .src = rhs_src, .air_tag = .sub_sat };
} else break :rs .{ .src = lhs_src, .air_tag = .sub_sat };
},
@@ -8351,48 +10787,60 @@ fn analyzeArithmetic(
// TODO: emit runtime safety for division by zero
//
// For floats:
- // If the rhs is zero, compile error for division by zero.
- // If the rhs is undefined, compile error because there is a possible
- // value (zero) for which the division would be illegal behavior.
+ // If the rhs is zero:
+ // * comptime_float: compile error for division by zero.
+ // * other float type:
+ // * if the lhs is zero: QNaN
+ // * otherwise: +Inf or -Inf depending on lhs sign
+ // If the rhs is undefined:
+ // * comptime_float: compile error because there is a possible
+ // value (zero) for which the division would be illegal behavior.
+ // * other float type: result is undefined
// If the lhs is undefined, result is undefined.
- if (maybe_lhs_val) |lhs_val| {
- if (!lhs_val.isUndef()) {
- if (lhs_val.compareWithZero(.eq)) {
- return sema.addConstant(scalar_type, Value.zero);
+ switch (scalar_tag) {
+ .Int, .ComptimeInt, .ComptimeFloat => {
+ if (maybe_lhs_val) |lhs_val| {
+ if (!lhs_val.isUndef()) {
+ if (try lhs_val.compareWithZeroAdvanced(.eq, sema.kit(block, src))) {
+ return sema.addConstant(resolved_type, Value.zero);
+ }
+ }
}
- }
- }
- if (maybe_rhs_val) |rhs_val| {
- if (rhs_val.isUndef()) {
- return sema.failWithUseOfUndef(block, rhs_src);
- }
- if (rhs_val.compareWithZero(.eq)) {
- return sema.failWithDivideByZero(block, rhs_src);
- }
+ if (maybe_rhs_val) |rhs_val| {
+ if (rhs_val.isUndef()) {
+ return sema.failWithUseOfUndef(block, rhs_src);
+ }
+ if (try rhs_val.compareWithZeroAdvanced(.eq, sema.kit(block, src))) {
+ return sema.failWithDivideByZero(block, rhs_src);
+ }
+ }
+ },
+ else => {},
}
+
if (maybe_lhs_val) |lhs_val| {
if (lhs_val.isUndef()) {
- if (lhs_ty.isSignedInt() and rhs_ty.isSignedInt()) {
+ if (lhs_scalar_ty.isSignedInt() and rhs_scalar_ty.isSignedInt()) {
if (maybe_rhs_val) |rhs_val| {
- if (rhs_val.compare(.neq, Value.negative_one, scalar_type)) {
- return sema.addConstUndef(scalar_type);
+ if (try sema.compare(block, src, rhs_val, .neq, Value.negative_one, resolved_type)) {
+ return sema.addConstUndef(resolved_type);
}
}
return sema.failWithUseOfUndef(block, rhs_src);
}
- return sema.addConstUndef(scalar_type);
+ return sema.addConstUndef(resolved_type);
}
if (maybe_rhs_val) |rhs_val| {
if (is_int) {
return sema.addConstant(
- scalar_type,
- try lhs_val.intDiv(rhs_val, sema.arena),
+ resolved_type,
+ try lhs_val.intDiv(rhs_val, resolved_type, sema.arena, target),
);
} else {
return sema.addConstant(
- scalar_type,
- try lhs_val.floatDiv(rhs_val, scalar_type, sema.arena),
+ resolved_type,
+ try lhs_val.floatDiv(rhs_val, resolved_type, sema.arena, target),
);
}
} else {
@@ -8432,8 +10880,8 @@ fn analyzeArithmetic(
// If the lhs is undefined, result is undefined.
if (maybe_lhs_val) |lhs_val| {
if (!lhs_val.isUndef()) {
- if (lhs_val.compareWithZero(.eq)) {
- return sema.addConstant(scalar_type, Value.zero);
+ if (try lhs_val.compareWithZeroAdvanced(.eq, sema.kit(block, src))) {
+ return sema.addConstant(resolved_type, Value.zero);
}
}
}
@@ -8441,33 +10889,33 @@ fn analyzeArithmetic(
if (rhs_val.isUndef()) {
return sema.failWithUseOfUndef(block, rhs_src);
}
- if (rhs_val.compareWithZero(.eq)) {
+ if (try rhs_val.compareWithZeroAdvanced(.eq, sema.kit(block, src))) {
return sema.failWithDivideByZero(block, rhs_src);
}
}
if (maybe_lhs_val) |lhs_val| {
if (lhs_val.isUndef()) {
- if (lhs_ty.isSignedInt() and rhs_ty.isSignedInt()) {
+ if (lhs_scalar_ty.isSignedInt() and rhs_scalar_ty.isSignedInt()) {
if (maybe_rhs_val) |rhs_val| {
- if (rhs_val.compare(.neq, Value.negative_one, scalar_type)) {
- return sema.addConstUndef(scalar_type);
+ if (try sema.compare(block, src, rhs_val, .neq, Value.negative_one, resolved_type)) {
+ return sema.addConstUndef(resolved_type);
}
}
return sema.failWithUseOfUndef(block, rhs_src);
}
- return sema.addConstUndef(scalar_type);
+ return sema.addConstUndef(resolved_type);
}
if (maybe_rhs_val) |rhs_val| {
if (is_int) {
return sema.addConstant(
- scalar_type,
- try lhs_val.intDiv(rhs_val, sema.arena),
+ resolved_type,
+ try lhs_val.intDiv(rhs_val, resolved_type, sema.arena, target),
);
} else {
return sema.addConstant(
- scalar_type,
- try lhs_val.floatDivTrunc(rhs_val, scalar_type, sema.arena),
+ resolved_type,
+ try lhs_val.floatDivTrunc(rhs_val, resolved_type, sema.arena, target),
);
}
} else break :rs .{ .src = rhs_src, .air_tag = .div_trunc };
@@ -8495,8 +10943,8 @@ fn analyzeArithmetic(
// If the lhs is undefined, result is undefined.
if (maybe_lhs_val) |lhs_val| {
if (!lhs_val.isUndef()) {
- if (lhs_val.compareWithZero(.eq)) {
- return sema.addConstant(scalar_type, Value.zero);
+ if (try lhs_val.compareWithZeroAdvanced(.eq, sema.kit(block, src))) {
+ return sema.addConstant(resolved_type, Value.zero);
}
}
}
@@ -8504,33 +10952,33 @@ fn analyzeArithmetic(
if (rhs_val.isUndef()) {
return sema.failWithUseOfUndef(block, rhs_src);
}
- if (rhs_val.compareWithZero(.eq)) {
+ if (try rhs_val.compareWithZeroAdvanced(.eq, sema.kit(block, src))) {
return sema.failWithDivideByZero(block, rhs_src);
}
}
if (maybe_lhs_val) |lhs_val| {
if (lhs_val.isUndef()) {
- if (lhs_ty.isSignedInt() and rhs_ty.isSignedInt()) {
+ if (lhs_scalar_ty.isSignedInt() and rhs_scalar_ty.isSignedInt()) {
if (maybe_rhs_val) |rhs_val| {
- if (rhs_val.compare(.neq, Value.negative_one, scalar_type)) {
- return sema.addConstUndef(scalar_type);
+ if (try sema.compare(block, src, rhs_val, .neq, Value.negative_one, resolved_type)) {
+ return sema.addConstUndef(resolved_type);
}
}
return sema.failWithUseOfUndef(block, rhs_src);
}
- return sema.addConstUndef(scalar_type);
+ return sema.addConstUndef(resolved_type);
}
if (maybe_rhs_val) |rhs_val| {
if (is_int) {
return sema.addConstant(
- scalar_type,
- try lhs_val.intDivFloor(rhs_val, sema.arena),
+ resolved_type,
+ try lhs_val.intDivFloor(rhs_val, resolved_type, sema.arena, target),
);
} else {
return sema.addConstant(
- scalar_type,
- try lhs_val.floatDivFloor(rhs_val, scalar_type, sema.arena),
+ resolved_type,
+ try lhs_val.floatDivFloor(rhs_val, resolved_type, sema.arena, target),
);
}
} else break :rs .{ .src = rhs_src, .air_tag = .div_floor };
@@ -8557,8 +11005,8 @@ fn analyzeArithmetic(
if (lhs_val.isUndef()) {
return sema.failWithUseOfUndef(block, rhs_src);
} else {
- if (lhs_val.compareWithZero(.eq)) {
- return sema.addConstant(scalar_type, Value.zero);
+ if (try lhs_val.compareWithZeroAdvanced(.eq, sema.kit(block, src))) {
+ return sema.addConstant(resolved_type, Value.zero);
}
}
}
@@ -8566,7 +11014,7 @@ fn analyzeArithmetic(
if (rhs_val.isUndef()) {
return sema.failWithUseOfUndef(block, rhs_src);
}
- if (rhs_val.compareWithZero(.eq)) {
+ if (try rhs_val.compareWithZeroAdvanced(.eq, sema.kit(block, src))) {
return sema.failWithDivideByZero(block, rhs_src);
}
}
@@ -8575,14 +11023,14 @@ fn analyzeArithmetic(
if (is_int) {
// TODO: emit compile error if there is a remainder
return sema.addConstant(
- scalar_type,
- try lhs_val.intDiv(rhs_val, sema.arena),
+ resolved_type,
+ try lhs_val.intDiv(rhs_val, resolved_type, sema.arena, target),
);
} else {
// TODO: emit compile error if there is a remainder
return sema.addConstant(
- scalar_type,
- try lhs_val.floatDiv(rhs_val, scalar_type, sema.arena),
+ resolved_type,
+ try lhs_val.floatDiv(rhs_val, resolved_type, sema.arena, target),
);
}
} else break :rs .{ .src = rhs_src, .air_tag = .div_exact };
@@ -8599,10 +11047,10 @@ fn analyzeArithmetic(
// For floats: either operand being undef makes the result undef.
if (maybe_lhs_val) |lhs_val| {
if (!lhs_val.isUndef()) {
- if (lhs_val.compareWithZero(.eq)) {
- return sema.addConstant(scalar_type, Value.zero);
+ if (try lhs_val.compareWithZeroAdvanced(.eq, sema.kit(block, src))) {
+ return sema.addConstant(resolved_type, Value.zero);
}
- if (lhs_val.compare(.eq, Value.one, scalar_type)) {
+ if (try sema.compare(block, src, lhs_val, .eq, Value.one, resolved_type)) {
return casted_rhs;
}
}
@@ -8612,13 +11060,13 @@ fn analyzeArithmetic(
if (is_int) {
return sema.failWithUseOfUndef(block, rhs_src);
} else {
- return sema.addConstUndef(scalar_type);
+ return sema.addConstUndef(resolved_type);
}
}
- if (rhs_val.compareWithZero(.eq)) {
- return sema.addConstant(scalar_type, Value.zero);
+ if (try rhs_val.compareWithZeroAdvanced(.eq, sema.kit(block, src))) {
+ return sema.addConstant(resolved_type, Value.zero);
}
- if (rhs_val.compare(.eq, Value.one, scalar_type)) {
+ if (try sema.compare(block, src, rhs_val, .eq, Value.one, resolved_type)) {
return casted_lhs;
}
if (maybe_lhs_val) |lhs_val| {
@@ -8626,18 +11074,20 @@ fn analyzeArithmetic(
if (is_int) {
return sema.failWithUseOfUndef(block, lhs_src);
} else {
- return sema.addConstUndef(scalar_type);
+ return sema.addConstUndef(resolved_type);
}
}
if (is_int) {
- return sema.addConstant(
- scalar_type,
- try lhs_val.intMul(rhs_val, sema.arena),
- );
+ const product = try lhs_val.intMul(rhs_val, resolved_type, sema.arena, target);
+ var vector_index: usize = undefined;
+ if (!(try sema.intFitsInType(block, src, product, resolved_type, &vector_index))) {
+ return sema.failWithIntegerOverflow(block, src, resolved_type, product, vector_index);
+ }
+ return sema.addConstant(resolved_type, product);
} else {
return sema.addConstant(
- scalar_type,
- try lhs_val.floatMul(rhs_val, scalar_type, sema.arena),
+ resolved_type,
+ try lhs_val.floatMul(rhs_val, resolved_type, sema.arena, target),
);
}
} else break :rs .{ .src = lhs_src, .air_tag = .mul };
@@ -8650,31 +11100,31 @@ fn analyzeArithmetic(
// If either of the operands are undefined, result is undefined.
if (maybe_lhs_val) |lhs_val| {
if (!lhs_val.isUndef()) {
- if (lhs_val.compareWithZero(.eq)) {
- return sema.addConstant(scalar_type, Value.zero);
+ if (try lhs_val.compareWithZeroAdvanced(.eq, sema.kit(block, src))) {
+ return sema.addConstant(resolved_type, Value.zero);
}
- if (lhs_val.compare(.eq, Value.one, scalar_type)) {
+ if (try sema.compare(block, src, lhs_val, .eq, Value.one, resolved_type)) {
return casted_rhs;
}
}
}
if (maybe_rhs_val) |rhs_val| {
if (rhs_val.isUndef()) {
- return sema.addConstUndef(scalar_type);
+ return sema.addConstUndef(resolved_type);
}
- if (rhs_val.compareWithZero(.eq)) {
- return sema.addConstant(scalar_type, Value.zero);
+ if (try rhs_val.compareWithZeroAdvanced(.eq, sema.kit(block, src))) {
+ return sema.addConstant(resolved_type, Value.zero);
}
- if (rhs_val.compare(.eq, Value.one, scalar_type)) {
+ if (try sema.compare(block, src, rhs_val, .eq, Value.one, resolved_type)) {
return casted_lhs;
}
if (maybe_lhs_val) |lhs_val| {
if (lhs_val.isUndef()) {
- return sema.addConstUndef(scalar_type);
+ return sema.addConstUndef(resolved_type);
}
return sema.addConstant(
- scalar_type,
- try lhs_val.numberMulWrap(rhs_val, scalar_type, sema.arena, target),
+ resolved_type,
+ try lhs_val.numberMulWrap(rhs_val, resolved_type, sema.arena, target),
);
} else break :rs .{ .src = lhs_src, .air_tag = .mulwrap };
} else break :rs .{ .src = rhs_src, .air_tag = .mulwrap };
@@ -8686,35 +11136,35 @@ fn analyzeArithmetic(
// If either of the operands are undefined, result is undefined.
if (maybe_lhs_val) |lhs_val| {
if (!lhs_val.isUndef()) {
- if (lhs_val.compareWithZero(.eq)) {
- return sema.addConstant(scalar_type, Value.zero);
+ if (try lhs_val.compareWithZeroAdvanced(.eq, sema.kit(block, src))) {
+ return sema.addConstant(resolved_type, Value.zero);
}
- if (lhs_val.compare(.eq, Value.one, scalar_type)) {
+ if (try sema.compare(block, src, lhs_val, .eq, Value.one, resolved_type)) {
return casted_rhs;
}
}
}
if (maybe_rhs_val) |rhs_val| {
if (rhs_val.isUndef()) {
- return sema.addConstUndef(scalar_type);
+ return sema.addConstUndef(resolved_type);
}
- if (rhs_val.compareWithZero(.eq)) {
- return sema.addConstant(scalar_type, Value.zero);
+ if (try rhs_val.compareWithZeroAdvanced(.eq, sema.kit(block, src))) {
+ return sema.addConstant(resolved_type, Value.zero);
}
- if (rhs_val.compare(.eq, Value.one, scalar_type)) {
+ if (try sema.compare(block, src, rhs_val, .eq, Value.one, resolved_type)) {
return casted_lhs;
}
if (maybe_lhs_val) |lhs_val| {
if (lhs_val.isUndef()) {
- return sema.addConstUndef(scalar_type);
+ return sema.addConstUndef(resolved_type);
}
const val = if (scalar_tag == .ComptimeInt)
- try lhs_val.intMul(rhs_val, sema.arena)
+ try lhs_val.intMul(rhs_val, resolved_type, sema.arena, target)
else
- try lhs_val.intMulSat(rhs_val, scalar_type, sema.arena, target);
+ try lhs_val.intMulSat(rhs_val, resolved_type, sema.arena, target);
- return sema.addConstant(scalar_type, val);
+ return sema.addConstant(resolved_type, val);
} else break :rs .{ .src = lhs_src, .air_tag = .mul_sat };
} else break :rs .{ .src = rhs_src, .air_tag = .mul_sat };
},
@@ -8737,40 +11187,40 @@ fn analyzeArithmetic(
if (lhs_val.isUndef()) {
return sema.failWithUseOfUndef(block, lhs_src);
}
- if (lhs_val.compareWithZero(.eq)) {
- return sema.addConstant(scalar_type, Value.zero);
+ if (try lhs_val.compareWithZeroAdvanced(.eq, sema.kit(block, src))) {
+ return sema.addConstant(resolved_type, Value.zero);
}
- } else if (lhs_ty.isSignedInt()) {
+ } else if (lhs_scalar_ty.isSignedInt()) {
return sema.failWithModRemNegative(block, lhs_src, lhs_ty, rhs_ty);
}
if (maybe_rhs_val) |rhs_val| {
if (rhs_val.isUndef()) {
return sema.failWithUseOfUndef(block, rhs_src);
}
- if (rhs_val.compareWithZero(.eq)) {
+ if (try rhs_val.compareWithZeroAdvanced(.eq, sema.kit(block, src))) {
return sema.failWithDivideByZero(block, rhs_src);
}
if (maybe_lhs_val) |lhs_val| {
- const rem_result = try lhs_val.intRem(rhs_val, sema.arena);
+ const rem_result = try lhs_val.intRem(rhs_val, resolved_type, sema.arena, target);
// If this answer could possibly be different by doing `intMod`,
// we must emit a compile error. Otherwise, it's OK.
- if (rhs_val.compareWithZero(.lt) != lhs_val.compareWithZero(.lt) and
- !rem_result.compareWithZero(.eq))
+ if ((try rhs_val.compareWithZeroAdvanced(.lt, sema.kit(block, src))) != (try lhs_val.compareWithZeroAdvanced(.lt, sema.kit(block, src))) and
+ !(try rem_result.compareWithZeroAdvanced(.eq, sema.kit(block, src))))
{
- const bad_src = if (lhs_val.compareWithZero(.lt))
+ const bad_src = if (try lhs_val.compareWithZeroAdvanced(.lt, sema.kit(block, src)))
lhs_src
else
rhs_src;
return sema.failWithModRemNegative(block, bad_src, lhs_ty, rhs_ty);
}
- if (lhs_val.compareWithZero(.lt)) {
+ if (try lhs_val.compareWithZeroAdvanced(.lt, sema.kit(block, src))) {
// Negative
- return sema.addConstant(scalar_type, Value.zero);
+ return sema.addConstant(resolved_type, Value.zero);
}
- return sema.addConstant(scalar_type, rem_result);
+ return sema.addConstant(resolved_type, rem_result);
}
break :rs .{ .src = lhs_src, .air_tag = .rem };
- } else if (rhs_ty.isSignedInt()) {
+ } else if (rhs_scalar_ty.isSignedInt()) {
return sema.failWithModRemNegative(block, rhs_src, lhs_ty, rhs_ty);
} else {
break :rs .{ .src = rhs_src, .air_tag = .rem };
@@ -8781,19 +11231,19 @@ fn analyzeArithmetic(
if (rhs_val.isUndef()) {
return sema.failWithUseOfUndef(block, rhs_src);
}
- if (rhs_val.compareWithZero(.eq)) {
+ if (try rhs_val.compareWithZeroAdvanced(.eq, sema.kit(block, src))) {
return sema.failWithDivideByZero(block, rhs_src);
}
- if (rhs_val.compareWithZero(.lt)) {
+ if (try rhs_val.compareWithZeroAdvanced(.lt, sema.kit(block, src))) {
return sema.failWithModRemNegative(block, rhs_src, lhs_ty, rhs_ty);
}
if (maybe_lhs_val) |lhs_val| {
- if (lhs_val.isUndef() or lhs_val.compareWithZero(.lt)) {
+ if (lhs_val.isUndef() or (try lhs_val.compareWithZeroAdvanced(.lt, sema.kit(block, src)))) {
return sema.failWithModRemNegative(block, lhs_src, lhs_ty, rhs_ty);
}
return sema.addConstant(
- scalar_type,
- try lhs_val.floatRem(rhs_val, sema.arena),
+ resolved_type,
+ try lhs_val.floatRem(rhs_val, resolved_type, sema.arena, target),
);
} else {
return sema.failWithModRemNegative(block, lhs_src, lhs_ty, rhs_ty);
@@ -8824,13 +11274,13 @@ fn analyzeArithmetic(
if (rhs_val.isUndef()) {
return sema.failWithUseOfUndef(block, rhs_src);
}
- if (rhs_val.compareWithZero(.eq)) {
+ if (try rhs_val.compareWithZeroAdvanced(.eq, sema.kit(block, src))) {
return sema.failWithDivideByZero(block, rhs_src);
}
if (maybe_lhs_val) |lhs_val| {
return sema.addConstant(
- scalar_type,
- try lhs_val.intRem(rhs_val, sema.arena),
+ resolved_type,
+ try lhs_val.intRem(rhs_val, resolved_type, sema.arena, target),
);
}
break :rs .{ .src = lhs_src, .air_tag = .rem };
@@ -8843,18 +11293,18 @@ fn analyzeArithmetic(
if (rhs_val.isUndef()) {
return sema.failWithUseOfUndef(block, rhs_src);
}
- if (rhs_val.compareWithZero(.eq)) {
+ if (try rhs_val.compareWithZeroAdvanced(.eq, sema.kit(block, src))) {
return sema.failWithDivideByZero(block, rhs_src);
}
}
if (maybe_lhs_val) |lhs_val| {
if (lhs_val.isUndef()) {
- return sema.addConstUndef(scalar_type);
+ return sema.addConstUndef(resolved_type);
}
if (maybe_rhs_val) |rhs_val| {
return sema.addConstant(
- scalar_type,
- try lhs_val.floatRem(rhs_val, sema.arena),
+ resolved_type,
+ try lhs_val.floatRem(rhs_val, resolved_type, sema.arena, target),
);
} else break :rs .{ .src = rhs_src, .air_tag = .rem };
} else break :rs .{ .src = lhs_src, .air_tag = .rem };
@@ -8881,13 +11331,13 @@ fn analyzeArithmetic(
if (rhs_val.isUndef()) {
return sema.failWithUseOfUndef(block, rhs_src);
}
- if (rhs_val.compareWithZero(.eq)) {
+ if (try rhs_val.compareWithZeroAdvanced(.eq, sema.kit(block, src))) {
return sema.failWithDivideByZero(block, rhs_src);
}
if (maybe_lhs_val) |lhs_val| {
return sema.addConstant(
- scalar_type,
- try lhs_val.intMod(rhs_val, sema.arena),
+ resolved_type,
+ try lhs_val.intMod(rhs_val, resolved_type, sema.arena, target),
);
}
break :rs .{ .src = lhs_src, .air_tag = .mod };
@@ -8900,18 +11350,18 @@ fn analyzeArithmetic(
if (rhs_val.isUndef()) {
return sema.failWithUseOfUndef(block, rhs_src);
}
- if (rhs_val.compareWithZero(.eq)) {
+ if (try rhs_val.compareWithZeroAdvanced(.eq, sema.kit(block, src))) {
return sema.failWithDivideByZero(block, rhs_src);
}
}
if (maybe_lhs_val) |lhs_val| {
if (lhs_val.isUndef()) {
- return sema.addConstUndef(scalar_type);
+ return sema.addConstUndef(resolved_type);
}
if (maybe_rhs_val) |rhs_val| {
return sema.addConstant(
- scalar_type,
- try lhs_val.floatMod(rhs_val, sema.arena),
+ resolved_type,
+ try lhs_val.floatMod(rhs_val, resolved_type, sema.arena, target),
);
} else break :rs .{ .src = rhs_src, .air_tag = .mod };
} else break :rs .{ .src = lhs_src, .air_tag = .mod };
@@ -8921,6 +11371,45 @@ fn analyzeArithmetic(
};
try sema.requireRuntimeBlock(block, rs.src);
+ if (block.wantSafety()) {
+ if (scalar_tag == .Int) {
+ const maybe_op_ov: ?Air.Inst.Tag = switch (rs.air_tag) {
+ .add => .add_with_overflow,
+ .sub => .sub_with_overflow,
+ .mul => .mul_with_overflow,
+ else => null,
+ };
+ if (maybe_op_ov) |op_ov_tag| {
+ const op_ov_tuple_ty = try sema.overflowArithmeticTupleType(resolved_type);
+ const op_ov = try block.addInst(.{
+ .tag = op_ov_tag,
+ .data = .{ .ty_pl = .{
+ .ty = try sema.addType(op_ov_tuple_ty),
+ .payload = try sema.addExtra(Air.Bin{
+ .lhs = casted_lhs,
+ .rhs = casted_rhs,
+ }),
+ } },
+ });
+ const ov_bit = try sema.tupleFieldValByIndex(block, src, op_ov, 1, op_ov_tuple_ty);
+ const any_ov_bit = if (resolved_type.zigTypeTag() == .Vector)
+ try block.addInst(.{
+ .tag = .reduce,
+ .data = .{ .reduce = .{
+ .operand = ov_bit,
+ .operation = .Or,
+ } },
+ })
+ else
+ ov_bit;
+ const zero_ov = try sema.addConstant(Type.@"u1", Value.zero);
+ const no_ov = try block.addBinOp(.cmp_eq, any_ov_bit, zero_ov);
+
+ try sema.addSafetyCheck(block, no_ov, .integer_overflow);
+ return sema.tupleFieldValByIndex(block, src, op_ov, 0, op_ov_tuple_ty);
+ }
+ }
+ }
return block.addBinOp(rs.air_tag, casted_lhs, casted_rhs);
}
@@ -8937,26 +11426,56 @@ fn analyzePtrArithmetic(
// TODO if the operand is comptime-known to be negative, or is a negative int,
// coerce to isize instead of usize.
const offset = try sema.coerce(block, Type.usize, uncasted_offset, offset_src);
- // TODO adjust the return type according to alignment and other factors
- const runtime_src = rs: {
- if (try sema.resolveMaybeUndefVal(block, ptr_src, ptr)) |ptr_val| {
- if (try sema.resolveMaybeUndefVal(block, offset_src, offset)) |offset_val| {
- const ptr_ty = sema.typeOf(ptr);
- const new_ptr_ty = ptr_ty; // TODO modify alignment
-
- if (ptr_val.isUndef() or offset_val.isUndef()) {
- return sema.addConstUndef(new_ptr_ty);
- }
+ const target = sema.mod.getTarget();
+ const opt_ptr_val = try sema.resolveMaybeUndefVal(block, ptr_src, ptr);
+ const opt_off_val = try sema.resolveDefinedValue(block, offset_src, offset);
+ const ptr_ty = sema.typeOf(ptr);
+ const ptr_info = ptr_ty.ptrInfo().data;
+ const elem_ty = if (ptr_info.size == .One and ptr_info.pointee_type.zigTypeTag() == .Array)
+ ptr_info.pointee_type.childType()
+ else
+ ptr_info.pointee_type;
+
+ const new_ptr_ty = t: {
+ // Calculate the new pointer alignment.
+ // This code is duplicated in `elemPtrType`.
+ if (ptr_info.@"align" == 0) {
+ // ABI-aligned pointer. Any pointer arithmetic maintains the same ABI-alignedness.
+ break :t ptr_ty;
+ }
+ // If the addend is not a comptime-known value we can still count on
+ // it being a multiple of the type size.
+ const elem_size = elem_ty.abiSize(target);
+ const addend = if (opt_off_val) |off_val| a: {
+ const off_int = try sema.usizeCast(block, offset_src, off_val.toUnsignedInt(target));
+ break :a elem_size * off_int;
+ } else elem_size;
+
+ // The resulting pointer is aligned to the lcd between the offset (an
+ // arbitrary number) and the alignment factor (always a power of two,
+ // non zero).
+ const new_align = @as(u32, 1) << @intCast(u5, @ctz(u64, addend | ptr_info.@"align"));
+
+ break :t try Type.ptr(sema.arena, sema.mod, .{
+ .pointee_type = ptr_info.pointee_type,
+ .sentinel = ptr_info.sentinel,
+ .@"align" = new_align,
+ .@"addrspace" = ptr_info.@"addrspace",
+ .mutable = ptr_info.mutable,
+ .@"allowzero" = ptr_info.@"allowzero",
+ .@"volatile" = ptr_info.@"volatile",
+ .size = ptr_info.size,
+ });
+ };
- const offset_int = try sema.usizeCast(block, offset_src, offset_val.toUnsignedInt());
- if (ptr_val.getUnsignedInt()) |addr| {
- const target = sema.mod.getTarget();
- const ptr_child_ty = ptr_ty.childType();
- const elem_ty = if (ptr_ty.isSinglePointer() and ptr_child_ty.zigTypeTag() == .Array)
- ptr_child_ty.childType()
- else
- ptr_child_ty;
+ const runtime_src = rs: {
+ if (opt_ptr_val) |ptr_val| {
+ if (opt_off_val) |offset_val| {
+ if (ptr_val.isUndef()) return sema.addConstUndef(new_ptr_ty);
+ const offset_int = try sema.usizeCast(block, offset_src, offset_val.toUnsignedInt(target));
+ if (offset_int == 0) return ptr;
+ if (try ptr_val.getUnsignedIntAdvanced(target, sema.kit(block, ptr_src))) |addr| {
const elem_size = elem_ty.abiSize(target);
const new_addr = switch (air_tag) {
.ptr_add => addr + elem_size * offset_int,
@@ -8969,14 +11488,23 @@ fn analyzePtrArithmetic(
if (air_tag == .ptr_sub) {
return sema.fail(block, op_src, "TODO implement Sema comptime pointer subtraction", .{});
}
- const new_ptr_val = try ptr_val.elemPtr(sema.arena, offset_int);
+ const new_ptr_val = try ptr_val.elemPtr(ptr_ty, sema.arena, offset_int, sema.mod);
return sema.addConstant(new_ptr_ty, new_ptr_val);
} else break :rs offset_src;
} else break :rs ptr_src;
};
try sema.requireRuntimeBlock(block, runtime_src);
- return block.addBinOp(air_tag, ptr, offset);
+ return block.addInst(.{
+ .tag = air_tag,
+ .data = .{ .ty_pl = .{
+ .ty = try sema.addType(new_ptr_ty),
+ .payload = try sema.addExtra(Air.Bin{
+ .lhs = ptr,
+ .rhs = offset,
+ }),
+ } },
+ });
}
fn zirLoad(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
@@ -8986,7 +11514,7 @@ fn zirLoad(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.In
const inst_data = sema.code.instructions.items(.data)[inst].un_node;
const src = inst_data.src();
const ptr_src: LazySrcLoc = .{ .node_offset_deref_ptr = inst_data.src_node };
- const ptr = sema.resolveInst(inst_data.operand);
+ const ptr = try sema.resolveInst(inst_data.operand);
return sema.analyzeLoad(block, src, ptr, ptr_src);
}
@@ -8994,17 +11522,18 @@ fn zirAsm(
sema: *Sema,
block: *Block,
extended: Zir.Inst.Extended.InstData,
- inst: Zir.Inst.Index,
) CompileError!Air.Inst.Ref {
const tracy = trace(@src());
defer tracy.end();
const extra = sema.code.extraData(Zir.Inst.Asm, extended.operand);
- const src: LazySrcLoc = .{ .node_offset = extra.data.src_node };
+ const src = LazySrcLoc.nodeOffset(extra.data.src_node);
const ret_ty_src: LazySrcLoc = .{ .node_offset_asm_ret_ty = extra.data.src_node };
const outputs_len = @truncate(u5, extended.small);
const inputs_len = @truncate(u5, extended.small >> 5);
const clobbers_len = @truncate(u5, extended.small >> 10);
+ const is_volatile = @truncate(u1, extended.small >> 15) != 0;
+ const is_global_assembly = sema.func == null;
if (extra.data.asm_source == 0) {
// This can move to become an AstGen error after inline assembly improvements land
@@ -9012,65 +11541,139 @@ fn zirAsm(
return sema.fail(block, src, "assembly code must use string literal syntax", .{});
}
- if (outputs_len > 1) {
- return sema.fail(block, src, "TODO implement Sema for asm with more than 1 output", .{});
+ const asm_source = sema.code.nullTerminatedString(extra.data.asm_source);
+
+ if (is_global_assembly) {
+ if (outputs_len != 0) {
+ return sema.fail(block, src, "module-level assembly does not support outputs", .{});
+ }
+ if (inputs_len != 0) {
+ return sema.fail(block, src, "module-level assembly does not support inputs", .{});
+ }
+ if (clobbers_len != 0) {
+ return sema.fail(block, src, "module-level assembly does not support clobbers", .{});
+ }
+ if (is_volatile) {
+ return sema.fail(block, src, "volatile keyword is redundant on module-level assembly", .{});
+ }
+ try sema.mod.addGlobalAssembly(sema.owner_decl_index, asm_source);
+ return Air.Inst.Ref.void_value;
+ }
+
+ if (block.is_comptime) {
+ try sema.requireRuntimeBlock(block, src);
}
var extra_i = extra.end;
var output_type_bits = extra.data.output_type_bits;
+ var needed_capacity: usize = @typeInfo(Air.Asm).Struct.fields.len + outputs_len + inputs_len;
+
+ const ConstraintName = struct { c: []const u8, n: []const u8 };
+ const out_args = try sema.arena.alloc(Air.Inst.Ref, outputs_len);
+ const outputs = try sema.arena.alloc(ConstraintName, outputs_len);
+ var expr_ty = Air.Inst.Ref.void_type;
- const Output = struct { constraint: []const u8, ty: Type };
- const output: ?Output = if (outputs_len == 0) null else blk: {
+ for (out_args) |*arg, out_i| {
const output = sema.code.extraData(Zir.Inst.Asm.Output, extra_i);
extra_i = output.end;
const is_type = @truncate(u1, output_type_bits) != 0;
output_type_bits >>= 1;
- if (!is_type) {
- return sema.fail(block, src, "TODO implement Sema for asm with non `->` output", .{});
+ if (is_type) {
+ // Indicate the output is the asm instruction return value.
+ arg.* = .none;
+ const out_ty = try sema.resolveType(block, ret_ty_src, output.data.operand);
+ try sema.queueFullTypeResolution(out_ty);
+ expr_ty = try sema.addType(out_ty);
+ } else {
+ arg.* = try sema.resolveInst(output.data.operand);
}
const constraint = sema.code.nullTerminatedString(output.data.constraint);
- break :blk Output{
- .constraint = constraint,
- .ty = try sema.resolveType(block, ret_ty_src, output.data.operand),
- };
- };
+ const name = sema.code.nullTerminatedString(output.data.name);
+ needed_capacity += (constraint.len + name.len + (2 + 3)) / 4;
+
+ outputs[out_i] = .{ .c = constraint, .n = name };
+ }
const args = try sema.arena.alloc(Air.Inst.Ref, inputs_len);
- const inputs = try sema.arena.alloc([]const u8, inputs_len);
+ const inputs = try sema.arena.alloc(ConstraintName, inputs_len);
for (args) |*arg, arg_i| {
const input = sema.code.extraData(Zir.Inst.Asm.Input, extra_i);
extra_i = input.end;
- const name = sema.code.nullTerminatedString(input.data.name);
- _ = name; // TODO: use the name
+ const uncasted_arg = try sema.resolveInst(input.data.operand);
+ const uncasted_arg_ty = sema.typeOf(uncasted_arg);
+ switch (uncasted_arg_ty.zigTypeTag()) {
+ .ComptimeInt => arg.* = try sema.coerce(block, Type.initTag(.usize), uncasted_arg, src),
+ .ComptimeFloat => arg.* = try sema.coerce(block, Type.initTag(.f64), uncasted_arg, src),
+ else => {
+ arg.* = uncasted_arg;
+ try sema.queueFullTypeResolution(uncasted_arg_ty);
+ },
+ }
- arg.* = sema.resolveInst(input.data.operand);
- inputs[arg_i] = sema.code.nullTerminatedString(input.data.constraint);
+ const constraint = sema.code.nullTerminatedString(input.data.constraint);
+ const name = sema.code.nullTerminatedString(input.data.name);
+ needed_capacity += (constraint.len + name.len + (2 + 3)) / 4;
+ inputs[arg_i] = .{ .c = constraint, .n = name };
}
const clobbers = try sema.arena.alloc([]const u8, clobbers_len);
for (clobbers) |*name| {
name.* = sema.code.nullTerminatedString(sema.code.extra[extra_i]);
extra_i += 1;
+
+ needed_capacity += name.*.len / 4 + 1;
}
- try sema.requireRuntimeBlock(block, src);
+ needed_capacity += (asm_source.len + 3) / 4;
+
const gpa = sema.gpa;
- try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.Asm).Struct.fields.len + args.len);
+ try sema.air_extra.ensureUnusedCapacity(gpa, needed_capacity);
const asm_air = try block.addInst(.{
.tag = .assembly,
.data = .{ .ty_pl = .{
- .ty = if (output) |o| try sema.addType(o.ty) else Air.Inst.Ref.void_type,
+ .ty = expr_ty,
.payload = sema.addExtraAssumeCapacity(Air.Asm{
- .zir_index = inst,
+ .source_len = @intCast(u32, asm_source.len),
+ .outputs_len = outputs_len,
+ .inputs_len = @intCast(u32, args.len),
+ .flags = (@as(u32, @boolToInt(is_volatile)) << 31) | @intCast(u32, clobbers.len),
}),
} },
});
+ sema.appendRefsAssumeCapacity(out_args);
sema.appendRefsAssumeCapacity(args);
+ for (outputs) |o| {
+ const buffer = mem.sliceAsBytes(sema.air_extra.unusedCapacitySlice());
+ mem.copy(u8, buffer, o.c);
+ buffer[o.c.len] = 0;
+ mem.copy(u8, buffer[o.c.len + 1 ..], o.n);
+ buffer[o.c.len + 1 + o.n.len] = 0;
+ sema.air_extra.items.len += (o.c.len + o.n.len + (2 + 3)) / 4;
+ }
+ for (inputs) |input| {
+ const buffer = mem.sliceAsBytes(sema.air_extra.unusedCapacitySlice());
+ mem.copy(u8, buffer, input.c);
+ buffer[input.c.len] = 0;
+ mem.copy(u8, buffer[input.c.len + 1 ..], input.n);
+ buffer[input.c.len + 1 + input.n.len] = 0;
+ sema.air_extra.items.len += (input.c.len + input.n.len + (2 + 3)) / 4;
+ }
+ for (clobbers) |clobber| {
+ const buffer = mem.sliceAsBytes(sema.air_extra.unusedCapacitySlice());
+ mem.copy(u8, buffer, clobber);
+ buffer[clobber.len] = 0;
+ sema.air_extra.items.len += clobber.len / 4 + 1;
+ }
+ {
+ const buffer = mem.sliceAsBytes(sema.air_extra.unusedCapacitySlice());
+ mem.copy(u8, buffer, asm_source);
+ sema.air_extra.items.len += (asm_source.len + 3) / 4;
+ }
return asm_air;
}
@@ -9090,8 +11693,8 @@ fn zirCmpEq(
const src: LazySrcLoc = inst_data.src();
const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node };
const rhs_src: LazySrcLoc = .{ .node_offset_bin_rhs = inst_data.src_node };
- const lhs = sema.resolveInst(extra.lhs);
- const rhs = sema.resolveInst(extra.rhs);
+ const lhs = try sema.resolveInst(extra.lhs);
+ const rhs = try sema.resolveInst(extra.rhs);
const lhs_ty = sema.typeOf(lhs);
const rhs_ty = sema.typeOf(rhs);
@@ -9116,7 +11719,7 @@ fn zirCmpEq(
if (lhs_ty_tag == .Null or rhs_ty_tag == .Null) {
const non_null_type = if (lhs_ty_tag == .Null) rhs_ty else lhs_ty;
- return sema.fail(block, src, "comparison of '{}' with null", .{non_null_type});
+ return sema.fail(block, src, "comparison of '{}' with null", .{non_null_type.fmt(sema.mod)});
}
if (lhs_ty_tag == .Union and (rhs_ty_tag == .EnumLiteral or rhs_ty_tag == .Enum)) {
@@ -9156,7 +11759,7 @@ fn zirCmpEq(
if (lhs_ty_tag == .Type and rhs_ty_tag == .Type) {
const lhs_as_type = try sema.analyzeAsType(block, lhs_src, lhs);
const rhs_as_type = try sema.analyzeAsType(block, rhs_src, rhs);
- if (lhs_as_type.eql(rhs_as_type) == (op == .eq)) {
+ if (lhs_as_type.eql(rhs_as_type, sema.mod) == (op == .eq)) {
return Air.Inst.Ref.bool_true;
} else {
return Air.Inst.Ref.bool_false;
@@ -9176,8 +11779,13 @@ fn analyzeCmpUnionTag(
) CompileError!Air.Inst.Ref {
const union_ty = try sema.resolveTypeFields(block, un_src, sema.typeOf(un));
const union_tag_ty = union_ty.unionTagType() orelse {
- // TODO note at declaration site that says "union foo is not tagged"
- return sema.fail(block, un_src, "comparison of union and enum literal is only valid for tagged union types", .{});
+ const msg = msg: {
+ const msg = try sema.errMsg(block, un_src, "comparison of union and enum literal is only valid for tagged union types", .{});
+ errdefer msg.destroy(sema.gpa);
+ try sema.mod.errNoteNonLazy(union_ty.declSrcLoc(sema.mod), msg, "union '{}' is not a tagged union", .{union_ty.fmt(sema.mod)});
+ break :msg msg;
+ };
+ return sema.failWithOwnedErrorMsg(block, msg);
};
// Coerce both the union and the tag to the union's tag type, and then execute the
// enum comparison codepath.
@@ -9202,8 +11810,8 @@ fn zirCmp(
const src: LazySrcLoc = inst_data.src();
const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node };
const rhs_src: LazySrcLoc = .{ .node_offset_bin_rhs = inst_data.src_node };
- const lhs = sema.resolveInst(extra.lhs);
- const rhs = sema.resolveInst(extra.rhs);
+ const lhs = try sema.resolveInst(extra.lhs);
+ const rhs = try sema.resolveInst(extra.rhs);
return sema.analyzeCmp(block, src, lhs, rhs, op, lhs_src, rhs_src, false);
}
@@ -9220,6 +11828,11 @@ fn analyzeCmp(
) CompileError!Air.Inst.Ref {
const lhs_ty = sema.typeOf(lhs);
const rhs_ty = sema.typeOf(rhs);
+ try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src);
+
+ if (lhs_ty.zigTypeTag() == .Vector and rhs_ty.zigTypeTag() == .Vector) {
+ return sema.cmpVector(block, src, lhs, rhs, op, lhs_src, rhs_src);
+ }
if (lhs_ty.isNumeric() and rhs_ty.isNumeric()) {
// This operation allows any combination of integer and float types, regardless of the
// signed-ness, comptime-ness, and bit-width. So peer type resolution is incorrect for
@@ -9229,8 +11842,8 @@ fn analyzeCmp(
const instructions = &[_]Air.Inst.Ref{ lhs, rhs };
const resolved_type = try sema.resolvePeerTypes(block, src, instructions, .{ .override = &[_]LazySrcLoc{ lhs_src, rhs_src } });
if (!resolved_type.isSelfComparable(is_equality_cmp)) {
- return sema.fail(block, src, "{s} operator not allowed for type '{}'", .{
- @tagName(op), resolved_type,
+ return sema.fail(block, src, "operator {s} not allowed for type '{}'", .{
+ compareOperatorName(op), resolved_type.fmt(sema.mod),
});
}
const casted_lhs = try sema.coerce(block, resolved_type, lhs, lhs_src);
@@ -9238,6 +11851,17 @@ fn analyzeCmp(
return sema.cmpSelf(block, casted_lhs, casted_rhs, op, lhs_src, rhs_src);
}
+fn compareOperatorName(comp: std.math.CompareOperator) []const u8 {
+ return switch (comp) {
+ .lt => "<",
+ .lte => "<=",
+ .eq => "==",
+ .gte => ">=",
+ .gt => ">",
+ .neq => "!=",
+ };
+}
+
fn cmpSelf(
sema: *Sema,
block: *Block,
@@ -9254,7 +11878,13 @@ fn cmpSelf(
if (try sema.resolveMaybeUndefVal(block, rhs_src, casted_rhs)) |rhs_val| {
if (rhs_val.isUndef()) return sema.addConstUndef(Type.bool);
- if (lhs_val.compare(op, rhs_val, resolved_type)) {
+ if (resolved_type.zigTypeTag() == .Vector) {
+ const result_ty = try Type.vector(sema.arena, resolved_type.vectorLen(), Type.@"bool");
+ const cmp_val = try sema.compareVector(block, lhs_src, lhs_val, op, rhs_val, resolved_type);
+ return sema.addConstant(result_ty, cmp_val);
+ }
+
+ if (try sema.compare(block, lhs_src, lhs_val, op, rhs_val, resolved_type)) {
return Air.Inst.Ref.bool_true;
} else {
return Air.Inst.Ref.bool_false;
@@ -9279,16 +11909,12 @@ fn cmpSelf(
}
};
try sema.requireRuntimeBlock(block, runtime_src);
-
- const tag: Air.Inst.Tag = switch (op) {
- .lt => .cmp_lt,
- .lte => .cmp_lte,
- .eq => .cmp_eq,
- .gte => .cmp_gte,
- .gt => .cmp_gt,
- .neq => .cmp_neq,
- };
- // TODO handle vectors
+ if (resolved_type.zigTypeTag() == .Vector) {
+ const result_ty = try Type.vector(sema.arena, resolved_type.vectorLen(), Type.@"bool");
+ const result_ty_ref = try sema.addType(result_ty);
+ return block.addCmpVector(casted_lhs, casted_rhs, op, result_ty_ref);
+ }
+ const tag = Air.Inst.Tag.fromCmpOp(op);
return block.addBinOp(tag, casted_lhs, casted_rhs);
}
@@ -9316,23 +11942,22 @@ fn zirSizeOf(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.
const inst_data = sema.code.instructions.items(.data)[inst].un_node;
const src = inst_data.src();
const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
- const operand_ty = try sema.resolveType(block, operand_src, inst_data.operand);
- try sema.resolveTypeLayout(block, src, operand_ty);
- const target = sema.mod.getTarget();
- const abi_size = switch (operand_ty.zigTypeTag()) {
- .Fn => unreachable,
+ const ty = try sema.resolveType(block, operand_src, inst_data.operand);
+ switch (ty.zigTypeTag()) {
+ .Fn,
.NoReturn,
.Undefined,
.Null,
.BoundFn,
.Opaque,
- => return sema.fail(block, src, "no size available for type '{}'", .{operand_ty}),
+ => return sema.fail(block, src, "no size available for type '{}'", .{ty.fmt(sema.mod)}),
+
.Type,
.EnumLiteral,
.ComptimeFloat,
.ComptimeInt,
.Void,
- => 0,
+ => return sema.addIntUnsigned(Type.comptime_int, 0),
.Bool,
.Int,
@@ -9348,18 +11973,24 @@ fn zirSizeOf(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.
.Vector,
.Frame,
.AnyFrame,
- => operand_ty.abiSize(target),
- };
- return sema.addIntUnsigned(Type.comptime_int, abi_size);
+ => {},
+ }
+ const target = sema.mod.getTarget();
+ const val = try ty.lazyAbiSize(target, sema.arena);
+ if (val.tag() == .lazy_size) {
+ try sema.queueFullTypeResolution(ty);
+ }
+ return sema.addConstant(Type.comptime_int, val);
}
fn zirBitSizeOf(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
const inst_data = sema.code.instructions.items(.data)[inst].un_node;
+ const src = inst_data.src();
const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
const operand_ty = try sema.resolveType(block, operand_src, inst_data.operand);
const target = sema.mod.getTarget();
- const bit_size = operand_ty.bitSize(target);
- return sema.addIntUnsigned(Type.initTag(.comptime_int), bit_size);
+ const bit_size = try operand_ty.bitSizeAdvanced(target, sema.kit(block, src));
+ return sema.addIntUnsigned(Type.comptime_int, bit_size);
}
fn zirThis(
@@ -9367,9 +11998,9 @@ fn zirThis(
block: *Block,
extended: Zir.Inst.Extended.InstData,
) CompileError!Air.Inst.Ref {
- const this_decl = block.namespace.getDecl();
- const src: LazySrcLoc = .{ .node_offset = @bitCast(i32, extended.operand) };
- return sema.analyzeDeclVal(block, src, this_decl);
+ const this_decl_index = block.namespace.getDeclIndex();
+ const src = LazySrcLoc.nodeOffset(@bitCast(i32, extended.operand));
+ return sema.analyzeDeclVal(block, src, this_decl_index);
}
fn zirClosureCapture(
@@ -9379,10 +12010,19 @@ fn zirClosureCapture(
) CompileError!void {
// TODO: Compile error when closed over values are modified
const inst_data = sema.code.instructions.items(.data)[inst].un_tok;
- const tv = try sema.resolveInstConst(block, inst_data.src(), inst_data.operand);
+ const src = inst_data.src();
+ // Closures are not necessarily constant values. For example, the
+ // code might do something like this:
+ // fn foo(x: anytype) void { const S = struct {field: @TypeOf(x)}; }
+ // ...in which case the closure_capture instruction has access to a runtime
+ // value only. In such case we preserve the type and use a dummy runtime value.
+ const operand = try sema.resolveInst(inst_data.operand);
+ const val = (try sema.resolveMaybeUndefValAllowVariables(block, src, operand)) orelse
+ Value.initTag(.generic_poison);
+
try block.wip_capture_scope.captures.putNoClobber(sema.gpa, inst, .{
- .ty = try tv.ty.copy(sema.perm_arena),
- .val = try tv.val.copy(sema.perm_arena),
+ .ty = try sema.typeOf(operand).copy(sema.perm_arena),
+ .val = try val.copy(sema.perm_arena),
});
}
@@ -9393,7 +12033,7 @@ fn zirClosureGet(
) CompileError!Air.Inst.Ref {
// TODO CLOSURE: Test this with inline functions
const inst_data = sema.code.instructions.items(.data)[inst].inst_node;
- var scope: *CaptureScope = block.src_decl.src_scope.?;
+ var scope: *CaptureScope = sema.mod.declPtr(block.src_decl).src_scope.?;
// Note: The target closure must be in this scope list.
// If it's not here, the zir is invalid, or the list is broken.
const tv = while (true) {
@@ -9413,14 +12053,21 @@ fn zirRetAddr(
block: *Block,
extended: Zir.Inst.Extended.InstData,
) CompileError!Air.Inst.Ref {
- const tracy = trace(@src());
- defer tracy.end();
-
- const src: LazySrcLoc = .{ .node_offset = @bitCast(i32, extended.operand) };
+ const src = LazySrcLoc.nodeOffset(@bitCast(i32, extended.operand));
try sema.requireRuntimeBlock(block, src);
return try block.addNoOp(.ret_addr);
}
+fn zirFrameAddress(
+ sema: *Sema,
+ block: *Block,
+ extended: Zir.Inst.Extended.InstData,
+) CompileError!Air.Inst.Ref {
+ const src = LazySrcLoc.nodeOffset(@bitCast(i32, extended.operand));
+ try sema.requireRuntimeBlock(block, src);
+ return try block.addNoOp(.frame_addr);
+}
+
fn zirBuiltinSrc(
sema: *Sema,
block: *Block,
@@ -9429,18 +12076,20 @@ fn zirBuiltinSrc(
const tracy = trace(@src());
defer tracy.end();
- const src: LazySrcLoc = .{ .node_offset = @bitCast(i32, extended.operand) };
+ const src = LazySrcLoc.nodeOffset(@bitCast(i32, extended.operand));
const extra = sema.code.extraData(Zir.Inst.LineColumn, extended.operand).data;
const func = sema.func orelse return sema.fail(block, src, "@src outside function", .{});
+ const fn_owner_decl = sema.mod.declPtr(func.owner_decl);
const func_name_val = blk: {
var anon_decl = try block.startAnonDecl(src);
defer anon_decl.deinit();
- const name = std.mem.span(func.owner_decl.name);
+ const name = std.mem.span(fn_owner_decl.name);
const bytes = try anon_decl.arena().dupe(u8, name[0 .. name.len + 1]);
const new_decl = try anon_decl.finish(
try Type.Tag.array_u8_sentinel_0.create(anon_decl.arena(), bytes.len - 1),
try Value.Tag.bytes.create(anon_decl.arena(), bytes),
+ 0, // default alignment
);
break :blk try Value.Tag.decl_ref.create(sema.arena, new_decl);
};
@@ -9448,10 +12097,15 @@ fn zirBuiltinSrc(
const file_name_val = blk: {
var anon_decl = try block.startAnonDecl(src);
defer anon_decl.deinit();
- const name = try func.owner_decl.getFileScope().fullPathZ(anon_decl.arena());
+ const relative_path = try fn_owner_decl.getFileScope().fullPath(sema.arena);
+ const absolute_path = std.fs.realpathAlloc(sema.arena, relative_path) catch |err| {
+ return sema.fail(block, src, "failed to get absolute path of file '{s}': {s}", .{ relative_path, @errorName(err) });
+ };
+ const aboslute_duped = try anon_decl.arena().dupeZ(u8, absolute_path);
const new_decl = try anon_decl.finish(
- try Type.Tag.array_u8_sentinel_0.create(anon_decl.arena(), name.len),
- try Value.Tag.bytes.create(anon_decl.arena(), name[0 .. name.len + 1]),
+ try Type.Tag.array_u8_sentinel_0.create(anon_decl.arena(), aboslute_duped.len),
+ try Value.Tag.bytes.create(anon_decl.arena(), aboslute_duped[0 .. aboslute_duped.len + 1]),
+ 0, // default alignment
);
break :blk try Value.Tag.decl_ref.create(sema.arena, new_decl);
};
@@ -9461,6 +12115,7 @@ fn zirBuiltinSrc(
field_values[0] = file_name_val;
// fn_name: [:0]const u8,
field_values[1] = func_name_val;
+ // TODO these should be runtime only!
// line: u32
field_values[2] = try Value.Tag.int_u64.create(sema.arena, extra.line + 1);
// column: u32,
@@ -9468,7 +12123,7 @@ fn zirBuiltinSrc(
return sema.addConstant(
try sema.getBuiltinType(block, src, "SourceLocation"),
- try Value.Tag.@"struct".create(sema.arena, field_values),
+ try Value.Tag.aggregate.create(sema.arena, field_values),
);
}
@@ -9476,7 +12131,7 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
const inst_data = sema.code.instructions.items(.data)[inst].un_node;
const src = inst_data.src();
const ty = try sema.resolveType(block, src, inst_data.operand);
- const type_info_ty = try sema.getBuiltinType(block, src, "TypeInfo");
+ const type_info_ty = try sema.getBuiltinType(block, src, "Type");
const target = sema.mod.getTarget();
switch (ty.zigTypeTag()) {
@@ -9484,86 +12139,165 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
type_info_ty,
try Value.Tag.@"union".create(sema.arena, .{
.tag = try Value.Tag.enum_field_index.create(sema.arena, @enumToInt(std.builtin.TypeId.Type)),
- .val = Value.initTag(.unreachable_value),
+ .val = Value.@"void",
}),
),
.Void => return sema.addConstant(
type_info_ty,
try Value.Tag.@"union".create(sema.arena, .{
.tag = try Value.Tag.enum_field_index.create(sema.arena, @enumToInt(std.builtin.TypeId.Void)),
- .val = Value.initTag(.unreachable_value),
+ .val = Value.@"void",
}),
),
.Bool => return sema.addConstant(
type_info_ty,
try Value.Tag.@"union".create(sema.arena, .{
.tag = try Value.Tag.enum_field_index.create(sema.arena, @enumToInt(std.builtin.TypeId.Bool)),
- .val = Value.initTag(.unreachable_value),
+ .val = Value.@"void",
}),
),
.NoReturn => return sema.addConstant(
type_info_ty,
try Value.Tag.@"union".create(sema.arena, .{
.tag = try Value.Tag.enum_field_index.create(sema.arena, @enumToInt(std.builtin.TypeId.NoReturn)),
- .val = Value.initTag(.unreachable_value),
+ .val = Value.@"void",
}),
),
.ComptimeFloat => return sema.addConstant(
type_info_ty,
try Value.Tag.@"union".create(sema.arena, .{
.tag = try Value.Tag.enum_field_index.create(sema.arena, @enumToInt(std.builtin.TypeId.ComptimeFloat)),
- .val = Value.initTag(.unreachable_value),
+ .val = Value.@"void",
}),
),
.ComptimeInt => return sema.addConstant(
type_info_ty,
try Value.Tag.@"union".create(sema.arena, .{
.tag = try Value.Tag.enum_field_index.create(sema.arena, @enumToInt(std.builtin.TypeId.ComptimeInt)),
- .val = Value.initTag(.unreachable_value),
+ .val = Value.@"void",
}),
),
.Undefined => return sema.addConstant(
type_info_ty,
try Value.Tag.@"union".create(sema.arena, .{
.tag = try Value.Tag.enum_field_index.create(sema.arena, @enumToInt(std.builtin.TypeId.Undefined)),
- .val = Value.initTag(.unreachable_value),
+ .val = Value.@"void",
}),
),
.Null => return sema.addConstant(
type_info_ty,
try Value.Tag.@"union".create(sema.arena, .{
.tag = try Value.Tag.enum_field_index.create(sema.arena, @enumToInt(std.builtin.TypeId.Null)),
- .val = Value.initTag(.unreachable_value),
+ .val = Value.@"void",
}),
),
.EnumLiteral => return sema.addConstant(
type_info_ty,
try Value.Tag.@"union".create(sema.arena, .{
.tag = try Value.Tag.enum_field_index.create(sema.arena, @enumToInt(std.builtin.TypeId.EnumLiteral)),
- .val = Value.initTag(.unreachable_value),
+ .val = Value.@"void",
}),
),
.Fn => {
+ // TODO: look into memoizing this result.
const info = ty.fnInfo();
- const field_values = try sema.arena.alloc(Value, 6);
- // calling_convention: CallingConvention,
- field_values[0] = try Value.Tag.enum_field_index.create(sema.arena, @enumToInt(info.cc));
- // alignment: comptime_int,
- field_values[1] = try Value.Tag.int_u64.create(sema.arena, ty.abiAlignment(target));
- // is_generic: bool,
- field_values[2] = if (info.is_generic) Value.@"true" else Value.@"false";
- // is_var_args: bool,
- field_values[3] = if (info.is_var_args) Value.@"true" else Value.@"false";
- // return_type: ?type,
- field_values[4] = try Value.Tag.ty.create(sema.arena, ty.fnReturnType());
- // args: []const FnArg,
- field_values[5] = Value.@"null"; // TODO
+
+ var params_anon_decl = try block.startAnonDecl(src);
+ defer params_anon_decl.deinit();
+
+ const param_vals = try params_anon_decl.arena().alloc(Value, info.param_types.len);
+ for (param_vals) |*param_val, i| {
+ const param_ty = info.param_types[i];
+ const is_generic = param_ty.tag() == .generic_poison;
+ const param_ty_val = if (is_generic)
+ Value.@"null"
+ else
+ try Value.Tag.opt_payload.create(
+ params_anon_decl.arena(),
+ try Value.Tag.ty.create(params_anon_decl.arena(), param_ty),
+ );
+
+ const param_fields = try params_anon_decl.arena().create([3]Value);
+ param_fields.* = .{
+ // is_generic: bool,
+ Value.makeBool(is_generic),
+ // is_noalias: bool,
+ Value.@"false", // TODO
+ // arg_type: ?type,
+ param_ty_val,
+ };
+ param_val.* = try Value.Tag.aggregate.create(params_anon_decl.arena(), param_fields);
+ }
+
+ const args_val = v: {
+ const fn_info_decl_index = (try sema.namespaceLookup(
+ block,
+ src,
+ type_info_ty.getNamespace().?,
+ "Fn",
+ )).?;
+ try sema.mod.declareDeclDependency(sema.owner_decl_index, fn_info_decl_index);
+ try sema.ensureDeclAnalyzed(fn_info_decl_index);
+ const fn_info_decl = sema.mod.declPtr(fn_info_decl_index);
+ var fn_ty_buffer: Value.ToTypeBuffer = undefined;
+ const fn_ty = fn_info_decl.val.toType(&fn_ty_buffer);
+ const param_info_decl_index = (try sema.namespaceLookup(
+ block,
+ src,
+ fn_ty.getNamespace().?,
+ "Param",
+ )).?;
+ try sema.mod.declareDeclDependency(sema.owner_decl_index, param_info_decl_index);
+ try sema.ensureDeclAnalyzed(param_info_decl_index);
+ const param_info_decl = sema.mod.declPtr(param_info_decl_index);
+ var param_buffer: Value.ToTypeBuffer = undefined;
+ const param_ty = param_info_decl.val.toType(&param_buffer);
+ const new_decl = try params_anon_decl.finish(
+ try Type.Tag.array.create(params_anon_decl.arena(), .{
+ .len = param_vals.len,
+ .elem_type = try param_ty.copy(params_anon_decl.arena()),
+ }),
+ try Value.Tag.aggregate.create(
+ params_anon_decl.arena(),
+ param_vals,
+ ),
+ 0, // default alignment
+ );
+ break :v try Value.Tag.slice.create(sema.arena, .{
+ .ptr = try Value.Tag.decl_ref.create(sema.arena, new_decl),
+ .len = try Value.Tag.int_u64.create(sema.arena, param_vals.len),
+ });
+ };
+
+ const ret_ty_opt = if (info.return_type.tag() != .generic_poison)
+ try Value.Tag.opt_payload.create(
+ sema.arena,
+ try Value.Tag.ty.create(sema.arena, info.return_type),
+ )
+ else
+ Value.@"null";
+
+ const field_values = try sema.arena.create([6]Value);
+ field_values.* = .{
+ // calling_convention: CallingConvention,
+ try Value.Tag.enum_field_index.create(sema.arena, @enumToInt(info.cc)),
+ // alignment: comptime_int,
+ try Value.Tag.int_u64.create(sema.arena, ty.abiAlignment(target)),
+ // is_generic: bool,
+ Value.makeBool(info.is_generic),
+ // is_var_args: bool,
+ Value.makeBool(info.is_var_args),
+ // return_type: ?type,
+ ret_ty_opt,
+ // args: []const Fn.Param,
+ args_val,
+ };
return sema.addConstant(
type_info_ty,
try Value.Tag.@"union".create(sema.arena, .{
.tag = try Value.Tag.enum_field_index.create(sema.arena, @enumToInt(std.builtin.TypeId.Fn)),
- .val = try Value.Tag.@"struct".create(sema.arena, field_values),
+ .val = try Value.Tag.aggregate.create(sema.arena, field_values),
}),
);
},
@@ -9582,7 +12316,7 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
type_info_ty,
try Value.Tag.@"union".create(sema.arena, .{
.tag = try Value.Tag.enum_field_index.create(sema.arena, @enumToInt(std.builtin.TypeId.Int)),
- .val = try Value.Tag.@"struct".create(sema.arena, field_values),
+ .val = try Value.Tag.aggregate.create(sema.arena, field_values),
}),
);
},
@@ -9595,35 +12329,42 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
type_info_ty,
try Value.Tag.@"union".create(sema.arena, .{
.tag = try Value.Tag.enum_field_index.create(sema.arena, @enumToInt(std.builtin.TypeId.Float)),
- .val = try Value.Tag.@"struct".create(sema.arena, field_values),
+ .val = try Value.Tag.aggregate.create(sema.arena, field_values),
}),
);
},
.Pointer => {
const info = ty.ptrInfo().data;
- const field_values = try sema.arena.alloc(Value, 8);
- // size: Size,
- field_values[0] = try Value.Tag.enum_field_index.create(sema.arena, @enumToInt(info.size));
- // is_const: bool,
- field_values[1] = if (!info.mutable) Value.@"true" else Value.@"false";
- // is_volatile: bool,
- field_values[2] = if (info.@"volatile") Value.@"true" else Value.@"false";
- // alignment: comptime_int,
- field_values[3] = try Value.Tag.int_u64.create(sema.arena, info.@"align");
- // address_space: AddressSpace
- field_values[4] = try Value.Tag.enum_field_index.create(sema.arena, @enumToInt(info.@"addrspace"));
- // child: type,
- field_values[5] = try Value.Tag.ty.create(sema.arena, info.pointee_type);
- // is_allowzero: bool,
- field_values[6] = if (info.@"allowzero") Value.@"true" else Value.@"false";
- // sentinel: anytype,
- field_values[7] = if (info.sentinel) |some| try Value.Tag.opt_payload.create(sema.arena, some) else Value.@"null";
+ const alignment = if (info.@"align" != 0)
+ try Value.Tag.int_u64.create(sema.arena, info.@"align")
+ else
+ try info.pointee_type.lazyAbiAlignment(target, sema.arena);
+
+ const field_values = try sema.arena.create([8]Value);
+ field_values.* = .{
+ // size: Size,
+ try Value.Tag.enum_field_index.create(sema.arena, @enumToInt(info.size)),
+ // is_const: bool,
+ Value.makeBool(!info.mutable),
+ // is_volatile: bool,
+ Value.makeBool(info.@"volatile"),
+ // alignment: comptime_int,
+ alignment,
+ // address_space: AddressSpace
+ try Value.Tag.enum_field_index.create(sema.arena, @enumToInt(info.@"addrspace")),
+ // child: type,
+ try Value.Tag.ty.create(sema.arena, info.pointee_type),
+ // is_allowzero: bool,
+ Value.makeBool(info.@"allowzero"),
+ // sentinel: ?*const anyopaque,
+ try sema.optRefValue(block, src, info.pointee_type, info.sentinel),
+ };
return sema.addConstant(
type_info_ty,
try Value.Tag.@"union".create(sema.arena, .{
.tag = try Value.Tag.enum_field_index.create(sema.arena, @enumToInt(std.builtin.TypeId.Pointer)),
- .val = try Value.Tag.@"struct".create(sema.arena, field_values),
+ .val = try Value.Tag.aggregate.create(sema.arena, field_values),
}),
);
},
@@ -9634,14 +12375,14 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
field_values[0] = try Value.Tag.int_u64.create(sema.arena, info.len);
// child: type,
field_values[1] = try Value.Tag.ty.create(sema.arena, info.elem_type);
- // sentinel: anytype,
- field_values[2] = if (info.sentinel) |some| try Value.Tag.opt_payload.create(sema.arena, some) else Value.@"null";
+ // sentinel: ?*const anyopaque,
+ field_values[2] = try sema.optRefValue(block, src, info.elem_type, info.sentinel);
return sema.addConstant(
type_info_ty,
try Value.Tag.@"union".create(sema.arena, .{
.tag = try Value.Tag.enum_field_index.create(sema.arena, @enumToInt(std.builtin.TypeId.Array)),
- .val = try Value.Tag.@"struct".create(sema.arena, field_values),
+ .val = try Value.Tag.aggregate.create(sema.arena, field_values),
}),
);
},
@@ -9657,7 +12398,7 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
type_info_ty,
try Value.Tag.@"union".create(sema.arena, .{
.tag = try Value.Tag.enum_field_index.create(sema.arena, @enumToInt(std.builtin.TypeId.Vector)),
- .val = try Value.Tag.@"struct".create(sema.arena, field_values),
+ .val = try Value.Tag.aggregate.create(sema.arena, field_values),
}),
);
},
@@ -9670,7 +12411,97 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
type_info_ty,
try Value.Tag.@"union".create(sema.arena, .{
.tag = try Value.Tag.enum_field_index.create(sema.arena, @enumToInt(std.builtin.TypeId.Optional)),
- .val = try Value.Tag.@"struct".create(sema.arena, field_values),
+ .val = try Value.Tag.aggregate.create(sema.arena, field_values),
+ }),
+ );
+ },
+ .ErrorSet => {
+ var fields_anon_decl = try block.startAnonDecl(src);
+ defer fields_anon_decl.deinit();
+
+ // Get the Error type
+ const error_field_ty = t: {
+ const set_field_ty_decl_index = (try sema.namespaceLookup(
+ block,
+ src,
+ type_info_ty.getNamespace().?,
+ "Error",
+ )).?;
+ try sema.mod.declareDeclDependency(sema.owner_decl_index, set_field_ty_decl_index);
+ try sema.ensureDeclAnalyzed(set_field_ty_decl_index);
+ const set_field_ty_decl = sema.mod.declPtr(set_field_ty_decl_index);
+ var buffer: Value.ToTypeBuffer = undefined;
+ break :t try set_field_ty_decl.val.toType(&buffer).copy(fields_anon_decl.arena());
+ };
+
+ try sema.queueFullTypeResolution(try error_field_ty.copy(sema.arena));
+
+ // If the error set is inferred it must be resolved at this point
+ try sema.resolveInferredErrorSetTy(block, src, ty);
+
+ // Build our list of Error values
+ // Optional value is only null if anyerror
+ // Value can be zero-length slice otherwise
+ const error_field_vals: ?[]Value = if (ty.isAnyError()) null else blk: {
+ const names = ty.errorSetNames();
+ const vals = try fields_anon_decl.arena().alloc(Value, names.len);
+ for (vals) |*field_val, i| {
+ const name = names[i];
+ const name_val = v: {
+ var anon_decl = try block.startAnonDecl(src);
+ defer anon_decl.deinit();
+ const bytes = try anon_decl.arena().dupeZ(u8, name);
+ const new_decl = try anon_decl.finish(
+ try Type.Tag.array_u8_sentinel_0.create(anon_decl.arena(), bytes.len),
+ try Value.Tag.bytes.create(anon_decl.arena(), bytes[0 .. bytes.len + 1]),
+ 0, // default alignment
+ );
+ break :v try Value.Tag.decl_ref.create(fields_anon_decl.arena(), new_decl);
+ };
+
+ const error_field_fields = try fields_anon_decl.arena().create([1]Value);
+ error_field_fields.* = .{
+ // name: []const u8,
+ name_val,
+ };
+
+ field_val.* = try Value.Tag.aggregate.create(
+ fields_anon_decl.arena(),
+ error_field_fields,
+ );
+ }
+
+ break :blk vals;
+ };
+
+ // Build our ?[]const Error value
+ const errors_val = if (error_field_vals) |vals| v: {
+ const new_decl = try fields_anon_decl.finish(
+ try Type.Tag.array.create(fields_anon_decl.arena(), .{
+ .len = vals.len,
+ .elem_type = error_field_ty,
+ }),
+ try Value.Tag.aggregate.create(
+ fields_anon_decl.arena(),
+ vals,
+ ),
+ 0, // default alignment
+ );
+
+ const new_decl_val = try Value.Tag.decl_ref.create(sema.arena, new_decl);
+ const slice_val = try Value.Tag.slice.create(sema.arena, .{
+ .ptr = new_decl_val,
+ .len = try Value.Tag.int_u64.create(sema.arena, vals.len),
+ });
+ break :v try Value.Tag.opt_payload.create(sema.arena, slice_val);
+ } else Value.@"null";
+
+ // Construct Type{ .ErrorSet = errors_val }
+ return sema.addConstant(
+ type_info_ty,
+ try Value.Tag.@"union".create(sema.arena, .{
+ .tag = try Value.Tag.enum_field_index.create(sema.arena, @enumToInt(std.builtin.TypeId.ErrorSet)),
+ .val = errors_val,
}),
);
},
@@ -9685,7 +12516,7 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
type_info_ty,
try Value.Tag.@"union".create(sema.arena, .{
.tag = try Value.Tag.enum_field_index.create(sema.arena, @enumToInt(std.builtin.TypeId.ErrorUnion)),
- .val = try Value.Tag.@"struct".create(sema.arena, field_values),
+ .val = try Value.Tag.aggregate.create(sema.arena, field_values),
}),
);
},
@@ -9694,20 +12525,21 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
var int_tag_type_buffer: Type.Payload.Bits = undefined;
const int_tag_ty = try ty.intTagType(&int_tag_type_buffer).copy(sema.arena);
- const is_exhaustive = if (ty.isNonexhaustiveEnum()) Value.@"false" else Value.@"true";
+ const is_exhaustive = Value.makeBool(!ty.isNonexhaustiveEnum());
var fields_anon_decl = try block.startAnonDecl(src);
defer fields_anon_decl.deinit();
const enum_field_ty = t: {
- const enum_field_ty_decl = (try sema.namespaceLookup(
+ const enum_field_ty_decl_index = (try sema.namespaceLookup(
block,
src,
type_info_ty.getNamespace().?,
"EnumField",
)).?;
- try sema.mod.declareDeclDependency(sema.owner_decl, enum_field_ty_decl);
- try sema.ensureDeclAnalyzed(enum_field_ty_decl);
+ try sema.mod.declareDeclDependency(sema.owner_decl_index, enum_field_ty_decl_index);
+ try sema.ensureDeclAnalyzed(enum_field_ty_decl_index);
+ const enum_field_ty_decl = sema.mod.declPtr(enum_field_ty_decl_index);
var buffer: Value.ToTypeBuffer = undefined;
break :t try enum_field_ty_decl.val.toType(&buffer).copy(fields_anon_decl.arena());
};
@@ -9733,6 +12565,7 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
const new_decl = try anon_decl.finish(
try Type.Tag.array_u8_sentinel_0.create(anon_decl.arena(), bytes.len),
try Value.Tag.bytes.create(anon_decl.arena(), bytes[0 .. bytes.len + 1]),
+ 0, // default alignment
);
break :v try Value.Tag.decl_ref.create(fields_anon_decl.arena(), new_decl);
};
@@ -9744,7 +12577,7 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
// value: comptime_int,
int_val,
};
- field_val.* = try Value.Tag.@"struct".create(fields_anon_decl.arena(), enum_field_fields);
+ field_val.* = try Value.Tag.aggregate.create(fields_anon_decl.arena(), enum_field_fields);
}
const fields_val = v: {
@@ -9753,10 +12586,11 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
.len = enum_field_vals.len,
.elem_type = enum_field_ty,
}),
- try Value.Tag.array.create(
+ try Value.Tag.aggregate.create(
fields_anon_decl.arena(),
- try fields_anon_decl.arena().dupe(Value, enum_field_vals),
+ enum_field_vals,
),
+ 0, // default alignment
);
break :v try Value.Tag.decl_ref.create(sema.arena, new_decl);
};
@@ -9768,7 +12602,7 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
// layout: ContainerLayout,
try Value.Tag.enum_field_index.create(
sema.arena,
- @enumToInt(std.builtin.TypeInfo.ContainerLayout.Auto),
+ @enumToInt(std.builtin.Type.ContainerLayout.Auto),
),
// tag_type: type,
@@ -9785,7 +12619,7 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
type_info_ty,
try Value.Tag.@"union".create(sema.arena, .{
.tag = try Value.Tag.enum_field_index.create(sema.arena, @enumToInt(std.builtin.TypeId.Enum)),
- .val = try Value.Tag.@"struct".create(sema.arena, field_values),
+ .val = try Value.Tag.aggregate.create(sema.arena, field_values),
}),
);
},
@@ -9796,19 +12630,23 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
defer fields_anon_decl.deinit();
const union_field_ty = t: {
- const union_field_ty_decl = (try sema.namespaceLookup(
+ const union_field_ty_decl_index = (try sema.namespaceLookup(
block,
src,
type_info_ty.getNamespace().?,
"UnionField",
)).?;
- try sema.mod.declareDeclDependency(sema.owner_decl, union_field_ty_decl);
- try sema.ensureDeclAnalyzed(union_field_ty_decl);
+ try sema.mod.declareDeclDependency(sema.owner_decl_index, union_field_ty_decl_index);
+ try sema.ensureDeclAnalyzed(union_field_ty_decl_index);
+ const union_field_ty_decl = sema.mod.declPtr(union_field_ty_decl_index);
var buffer: Value.ToTypeBuffer = undefined;
break :t try union_field_ty_decl.val.toType(&buffer).copy(fields_anon_decl.arena());
};
const union_ty = try sema.resolveTypeFields(block, src, ty);
+ try sema.resolveTypeLayout(block, src, ty); // Getting alignment requires type layout
+ const layout = union_ty.containerLayout();
+
const union_fields = union_ty.unionFields();
const union_field_vals = try fields_anon_decl.arena().alloc(Value, union_fields.count());
@@ -9822,20 +12660,26 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
const new_decl = try anon_decl.finish(
try Type.Tag.array_u8_sentinel_0.create(anon_decl.arena(), bytes.len),
try Value.Tag.bytes.create(anon_decl.arena(), bytes[0 .. bytes.len + 1]),
+ 0, // default alignment
);
break :v try Value.Tag.decl_ref.create(fields_anon_decl.arena(), new_decl);
};
const union_field_fields = try fields_anon_decl.arena().create([3]Value);
+ const alignment = switch (layout) {
+ .Auto, .Extern => try sema.unionFieldAlignment(block, src, field),
+ .Packed => 0,
+ };
+
union_field_fields.* = .{
// name: []const u8,
name_val,
// field_type: type,
try Value.Tag.ty.create(fields_anon_decl.arena(), field.ty),
// alignment: comptime_int,
- try field.abi_align.copy(fields_anon_decl.arena()),
+ try Value.Tag.int_u64.create(fields_anon_decl.arena(), alignment),
};
- field_val.* = try Value.Tag.@"struct".create(fields_anon_decl.arena(), union_field_fields);
+ field_val.* = try Value.Tag.aggregate.create(fields_anon_decl.arena(), union_field_fields);
}
const fields_val = v: {
@@ -9844,12 +12688,16 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
.len = union_field_vals.len,
.elem_type = union_field_ty,
}),
- try Value.Tag.array.create(
+ try Value.Tag.aggregate.create(
fields_anon_decl.arena(),
try fields_anon_decl.arena().dupe(Value, union_field_vals),
),
+ 0, // default alignment
);
- break :v try Value.Tag.decl_ref.create(sema.arena, new_decl);
+ break :v try Value.Tag.slice.create(sema.arena, .{
+ .ptr = try Value.Tag.decl_ref.create(sema.arena, new_decl),
+ .len = try Value.Tag.int_u64.create(sema.arena, union_field_vals.len),
+ });
};
const decls_val = try sema.typeInfoDecls(block, src, type_info_ty, union_ty.getNamespace());
@@ -9864,7 +12712,7 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
// layout: ContainerLayout,
try Value.Tag.enum_field_index.create(
sema.arena,
- @enumToInt(std.builtin.TypeInfo.ContainerLayout.Auto),
+ @enumToInt(layout),
),
// tag_type: ?type,
@@ -9879,17 +12727,174 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
type_info_ty,
try Value.Tag.@"union".create(sema.arena, .{
.tag = try Value.Tag.enum_field_index.create(sema.arena, @enumToInt(std.builtin.TypeId.Union)),
- .val = try Value.Tag.@"struct".create(sema.arena, field_values),
+ .val = try Value.Tag.aggregate.create(sema.arena, field_values),
}),
);
},
- .Struct => return sema.fail(block, src, "TODO: implement zirTypeInfo for Struct", .{}),
- .Opaque => {
+ .Struct => {
// TODO: look into memoizing this result.
var fields_anon_decl = try block.startAnonDecl(src);
defer fields_anon_decl.deinit();
+ const struct_field_ty = t: {
+ const struct_field_ty_decl_index = (try sema.namespaceLookup(
+ block,
+ src,
+ type_info_ty.getNamespace().?,
+ "StructField",
+ )).?;
+ try sema.mod.declareDeclDependency(sema.owner_decl_index, struct_field_ty_decl_index);
+ try sema.ensureDeclAnalyzed(struct_field_ty_decl_index);
+ const struct_field_ty_decl = sema.mod.declPtr(struct_field_ty_decl_index);
+ var buffer: Value.ToTypeBuffer = undefined;
+ break :t try struct_field_ty_decl.val.toType(&buffer).copy(fields_anon_decl.arena());
+ };
+ const struct_ty = try sema.resolveTypeFields(block, src, ty);
+ try sema.resolveTypeLayout(block, src, ty); // Getting alignment requires type layout
+ const layout = struct_ty.containerLayout();
+
+ const struct_field_vals = fv: {
+ if (struct_ty.isTupleOrAnonStruct()) {
+ const tuple = struct_ty.tupleFields();
+ const field_types = tuple.types;
+ const struct_field_vals = try fields_anon_decl.arena().alloc(Value, field_types.len);
+ for (struct_field_vals) |*struct_field_val, i| {
+ const field_ty = field_types[i];
+ const name_val = v: {
+ var anon_decl = try block.startAnonDecl(src);
+ defer anon_decl.deinit();
+ const bytes = if (struct_ty.castTag(.anon_struct)) |payload|
+ try anon_decl.arena().dupeZ(u8, payload.data.names[i])
+ else
+ try std.fmt.allocPrintZ(anon_decl.arena(), "{d}", .{i});
+ const new_decl = try anon_decl.finish(
+ try Type.Tag.array_u8_sentinel_0.create(anon_decl.arena(), bytes.len),
+ try Value.Tag.bytes.create(anon_decl.arena(), bytes[0 .. bytes.len + 1]),
+ 0, // default alignment
+ );
+ break :v try Value.Tag.slice.create(fields_anon_decl.arena(), .{
+ .ptr = try Value.Tag.decl_ref.create(fields_anon_decl.arena(), new_decl),
+ .len = try Value.Tag.int_u64.create(fields_anon_decl.arena(), bytes.len),
+ });
+ };
+
+ const struct_field_fields = try fields_anon_decl.arena().create([5]Value);
+ const field_val = tuple.values[i];
+ const is_comptime = field_val.tag() != .unreachable_value;
+ const opt_default_val = if (is_comptime) field_val else null;
+ const default_val_ptr = try sema.optRefValue(block, src, field_ty, opt_default_val);
+ struct_field_fields.* = .{
+ // name: []const u8,
+ name_val,
+ // field_type: type,
+ try Value.Tag.ty.create(fields_anon_decl.arena(), field_ty),
+ // default_value: ?*const anyopaque,
+ try default_val_ptr.copy(fields_anon_decl.arena()),
+ // is_comptime: bool,
+ Value.makeBool(is_comptime),
+ // alignment: comptime_int,
+ try field_ty.lazyAbiAlignment(target, fields_anon_decl.arena()),
+ };
+ struct_field_val.* = try Value.Tag.aggregate.create(fields_anon_decl.arena(), struct_field_fields);
+ }
+ break :fv struct_field_vals;
+ }
+ const struct_fields = struct_ty.structFields();
+ const struct_field_vals = try fields_anon_decl.arena().alloc(Value, struct_fields.count());
+
+ for (struct_field_vals) |*field_val, i| {
+ const field = struct_fields.values()[i];
+ const name = struct_fields.keys()[i];
+ const name_val = v: {
+ var anon_decl = try block.startAnonDecl(src);
+ defer anon_decl.deinit();
+ const bytes = try anon_decl.arena().dupeZ(u8, name);
+ const new_decl = try anon_decl.finish(
+ try Type.Tag.array_u8_sentinel_0.create(anon_decl.arena(), bytes.len),
+ try Value.Tag.bytes.create(anon_decl.arena(), bytes[0 .. bytes.len + 1]),
+ 0, // default alignment
+ );
+ break :v try Value.Tag.slice.create(fields_anon_decl.arena(), .{
+ .ptr = try Value.Tag.decl_ref.create(fields_anon_decl.arena(), new_decl),
+ .len = try Value.Tag.int_u64.create(fields_anon_decl.arena(), bytes.len),
+ });
+ };
+
+ const struct_field_fields = try fields_anon_decl.arena().create([5]Value);
+ const opt_default_val = if (field.default_val.tag() == .unreachable_value)
+ null
+ else
+ field.default_val;
+ const default_val_ptr = try sema.optRefValue(block, src, field.ty, opt_default_val);
+ const alignment = switch (layout) {
+ .Auto, .Extern => field.normalAlignment(target),
+ .Packed => 0,
+ };
+
+ struct_field_fields.* = .{
+ // name: []const u8,
+ name_val,
+ // field_type: type,
+ try Value.Tag.ty.create(fields_anon_decl.arena(), field.ty),
+ // default_value: ?*const anyopaque,
+ try default_val_ptr.copy(fields_anon_decl.arena()),
+ // is_comptime: bool,
+ Value.makeBool(field.is_comptime),
+ // alignment: comptime_int,
+ try Value.Tag.int_u64.create(fields_anon_decl.arena(), alignment),
+ };
+ field_val.* = try Value.Tag.aggregate.create(fields_anon_decl.arena(), struct_field_fields);
+ }
+ break :fv struct_field_vals;
+ };
+
+ const fields_val = v: {
+ const new_decl = try fields_anon_decl.finish(
+ try Type.Tag.array.create(fields_anon_decl.arena(), .{
+ .len = struct_field_vals.len,
+ .elem_type = struct_field_ty,
+ }),
+ try Value.Tag.aggregate.create(
+ fields_anon_decl.arena(),
+ try fields_anon_decl.arena().dupe(Value, struct_field_vals),
+ ),
+ 0, // default alignment
+ );
+ break :v try Value.Tag.slice.create(sema.arena, .{
+ .ptr = try Value.Tag.decl_ref.create(sema.arena, new_decl),
+ .len = try Value.Tag.int_u64.create(sema.arena, struct_field_vals.len),
+ });
+ };
+
+ const decls_val = try sema.typeInfoDecls(block, src, type_info_ty, struct_ty.getNamespace());
+
+ const field_values = try sema.arena.create([4]Value);
+ field_values.* = .{
+ // layout: ContainerLayout,
+ try Value.Tag.enum_field_index.create(
+ sema.arena,
+ @enumToInt(layout),
+ ),
+ // fields: []const StructField,
+ fields_val,
+ // decls: []const Declaration,
+ decls_val,
+ // is_tuple: bool,
+ Value.makeBool(struct_ty.isTuple()),
+ };
+
+ return sema.addConstant(
+ type_info_ty,
+ try Value.Tag.@"union".create(sema.arena, .{
+ .tag = try Value.Tag.enum_field_index.create(sema.arena, @enumToInt(std.builtin.TypeId.Struct)),
+ .val = try Value.Tag.aggregate.create(sema.arena, field_values),
+ }),
+ );
+ },
+ .Opaque => {
+ // TODO: look into memoizing this result.
+
const opaque_ty = try sema.resolveTypeFields(block, src, ty);
const decls_val = try sema.typeInfoDecls(block, src, type_info_ty, opaque_ty.getNamespace());
@@ -9903,11 +12908,10 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
type_info_ty,
try Value.Tag.@"union".create(sema.arena, .{
.tag = try Value.Tag.enum_field_index.create(sema.arena, @enumToInt(std.builtin.TypeId.Opaque)),
- .val = try Value.Tag.@"struct".create(sema.arena, field_values),
+ .val = try Value.Tag.aggregate.create(sema.arena, field_values),
}),
);
},
- .ErrorSet => return sema.fail(block, src, "TODO: implement zirTypeInfo for ErrorSet", .{}),
.BoundFn => @panic("TODO remove this type from the language and compiler"),
.Frame => return sema.fail(block, src, "TODO: implement zirTypeInfo for Frame", .{}),
.AnyFrame => return sema.fail(block, src, "TODO: implement zirTypeInfo for AnyFrame", .{}),
@@ -9921,51 +12925,52 @@ fn typeInfoDecls(
type_info_ty: Type,
opt_namespace: ?*Module.Namespace,
) CompileError!Value {
- const namespace = opt_namespace orelse return Value.initTag(.empty_array);
- const decls_len = namespace.decls.count();
- if (decls_len == 0) return Value.initTag(.empty_array);
-
var decls_anon_decl = try block.startAnonDecl(src);
defer decls_anon_decl.deinit();
const declaration_ty = t: {
- const declaration_ty_decl = (try sema.namespaceLookup(
+ const declaration_ty_decl_index = (try sema.namespaceLookup(
block,
src,
type_info_ty.getNamespace().?,
- "EnumField",
+ "Declaration",
)).?;
- try sema.mod.declareDeclDependency(sema.owner_decl, declaration_ty_decl);
- try sema.ensureDeclAnalyzed(declaration_ty_decl);
+ try sema.mod.declareDeclDependency(sema.owner_decl_index, declaration_ty_decl_index);
+ try sema.ensureDeclAnalyzed(declaration_ty_decl_index);
+ const declaration_ty_decl = sema.mod.declPtr(declaration_ty_decl_index);
var buffer: Value.ToTypeBuffer = undefined;
break :t try declaration_ty_decl.val.toType(&buffer).copy(decls_anon_decl.arena());
};
+ try sema.queueFullTypeResolution(try declaration_ty.copy(sema.arena));
+ const decls_len = if (opt_namespace) |ns| ns.decls.count() else 0;
const decls_vals = try decls_anon_decl.arena().alloc(Value, decls_len);
for (decls_vals) |*decls_val, i| {
- const decl = namespace.decls.values()[i];
- const name = namespace.decls.keys()[i];
+ const decl_index = opt_namespace.?.decls.keys()[i];
+ const decl = sema.mod.declPtr(decl_index);
const name_val = v: {
var anon_decl = try block.startAnonDecl(src);
defer anon_decl.deinit();
- const bytes = try anon_decl.arena().dupeZ(u8, name);
+ const bytes = try anon_decl.arena().dupeZ(u8, mem.sliceTo(decl.name, 0));
const new_decl = try anon_decl.finish(
try Type.Tag.array_u8_sentinel_0.create(anon_decl.arena(), bytes.len),
try Value.Tag.bytes.create(anon_decl.arena(), bytes[0 .. bytes.len + 1]),
+ 0, // default alignment
);
- break :v try Value.Tag.decl_ref.create(decls_anon_decl.arena(), new_decl);
+ break :v try Value.Tag.slice.create(decls_anon_decl.arena(), .{
+ .ptr = try Value.Tag.decl_ref.create(decls_anon_decl.arena(), new_decl),
+ .len = try Value.Tag.int_u64.create(decls_anon_decl.arena(), bytes.len),
+ });
};
- const is_pub = if (decl.is_pub) Value.@"true" else Value.@"false";
-
const fields = try decls_anon_decl.arena().create([2]Value);
fields.* = .{
//name: []const u8,
name_val,
//is_pub: bool,
- is_pub,
+ Value.makeBool(decl.is_pub),
};
- decls_val.* = try Value.Tag.@"struct".create(decls_anon_decl.arena(), fields);
+ decls_val.* = try Value.Tag.aggregate.create(decls_anon_decl.arena(), fields);
}
const new_decl = try decls_anon_decl.finish(
@@ -9973,41 +12978,72 @@ fn typeInfoDecls(
.len = decls_vals.len,
.elem_type = declaration_ty,
}),
- try Value.Tag.array.create(
+ try Value.Tag.aggregate.create(
decls_anon_decl.arena(),
try decls_anon_decl.arena().dupe(Value, decls_vals),
),
+ 0, // default alignment
);
- return try Value.Tag.decl_ref.create(sema.arena, new_decl);
+ return try Value.Tag.slice.create(sema.arena, .{
+ .ptr = try Value.Tag.decl_ref.create(sema.arena, new_decl),
+ .len = try Value.Tag.int_u64.create(sema.arena, decls_vals.len),
+ });
}
fn zirTypeof(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
_ = block;
const zir_datas = sema.code.instructions.items(.data);
const inst_data = zir_datas[inst].un_node;
- const operand = sema.resolveInst(inst_data.operand);
+ const operand = try sema.resolveInst(inst_data.operand);
+ const operand_ty = sema.typeOf(operand);
+ return sema.addType(operand_ty);
+}
+
+fn zirTypeofBuiltin(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
+ const pl_node = sema.code.instructions.items(.data)[inst].pl_node;
+ const extra = sema.code.extraData(Zir.Inst.Block, pl_node.payload_index);
+ const body = sema.code.extra[extra.end..][0..extra.data.body_len];
+
+ var child_block: Block = .{
+ .parent = block,
+ .sema = sema,
+ .src_decl = block.src_decl,
+ .namespace = block.namespace,
+ .wip_capture_scope = block.wip_capture_scope,
+ .instructions = .{},
+ .inlining = block.inlining,
+ .is_comptime = false,
+ .is_typeof = true,
+ .want_safety = false,
+ };
+ defer child_block.instructions.deinit(sema.gpa);
+
+ const operand = try sema.resolveBody(&child_block, body, inst);
const operand_ty = sema.typeOf(operand);
+ if (operand_ty.tag() == .generic_poison) return error.GenericPoison;
return sema.addType(operand_ty);
}
fn zirTypeofLog2IntType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
const inst_data = sema.code.instructions.items(.data)[inst].un_node;
const src = inst_data.src();
- const operand = sema.resolveInst(inst_data.operand);
+ const operand = try sema.resolveInst(inst_data.operand);
const operand_ty = sema.typeOf(operand);
- return sema.log2IntType(block, operand_ty, src);
+ const res_ty = try sema.log2IntType(block, operand_ty, src);
+ return sema.addType(res_ty);
}
fn zirLog2IntType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
const inst_data = sema.code.instructions.items(.data)[inst].un_node;
const src = inst_data.src();
const operand = try sema.resolveType(block, src, inst_data.operand);
- return sema.log2IntType(block, operand, src);
+ const res_ty = try sema.log2IntType(block, operand, src);
+ return sema.addType(res_ty);
}
-fn log2IntType(sema: *Sema, block: *Block, operand: Type, src: LazySrcLoc) CompileError!Air.Inst.Ref {
+fn log2IntType(sema: *Sema, block: *Block, operand: Type, src: LazySrcLoc) CompileError!Type {
switch (operand.zigTypeTag()) {
- .ComptimeInt => return Air.Inst.Ref.comptime_int_type,
+ .ComptimeInt => return Type.@"comptime_int",
.Int => {
const bits = operand.bitSize(sema.mod.getTarget());
const count = if (bits == 0)
@@ -10020,16 +13056,24 @@ fn log2IntType(sema: *Sema, block: *Block, operand: Type, src: LazySrcLoc) Compi
}
break :blk count;
};
- const res = try Module.makeIntType(sema.arena, .unsigned, count);
- return sema.addType(res);
+ return Module.makeIntType(sema.arena, .unsigned, count);
},
- else => return sema.fail(
- block,
- src,
- "bit shifting operation expected integer type, found '{}'",
- .{operand},
- ),
+ .Vector => {
+ const elem_ty = operand.elemType2();
+ const log2_elem_ty = try sema.log2IntType(block, elem_ty, src);
+ return Type.Tag.vector.create(sema.arena, .{
+ .len = operand.vectorLen(),
+ .elem_type = log2_elem_ty,
+ });
+ },
+ else => {},
}
+ return sema.fail(
+ block,
+ src,
+ "bit shifting operation expected integer type, found '{}'",
+ .{operand.fmt(sema.mod)},
+ );
}
fn zirTypeofPeer(
@@ -10040,15 +13084,32 @@ fn zirTypeofPeer(
const tracy = trace(@src());
defer tracy.end();
- const extra = sema.code.extraData(Zir.Inst.NodeMultiOp, extended.operand);
- const src: LazySrcLoc = .{ .node_offset = extra.data.src_node };
+ const extra = sema.code.extraData(Zir.Inst.TypeOfPeer, extended.operand);
+ const src = LazySrcLoc.nodeOffset(extra.data.src_node);
+ const body = sema.code.extra[extra.data.body_index..][0..extra.data.body_len];
+
+ var child_block: Block = .{
+ .parent = block,
+ .sema = sema,
+ .src_decl = block.src_decl,
+ .namespace = block.namespace,
+ .wip_capture_scope = block.wip_capture_scope,
+ .instructions = .{},
+ .inlining = block.inlining,
+ .is_comptime = false,
+ .is_typeof = true,
+ };
+ defer child_block.instructions.deinit(sema.gpa);
+ // Ignore the result, we only care about the instructions in `args`.
+ _ = try sema.analyzeBodyBreak(&child_block, body);
+
const args = sema.code.refSlice(extra.end, extended.small);
const inst_list = try sema.gpa.alloc(Air.Inst.Ref, args.len);
defer sema.gpa.free(inst_list);
for (args) |arg_ref, i| {
- inst_list[i] = sema.resolveInst(arg_ref);
+ inst_list[i] = try sema.resolveInst(arg_ref);
}
const result_type = try sema.resolvePeerTypes(block, src, inst_list, .{ .typeof_builtin_call_node_offset = extra.data.src_node });
@@ -10062,7 +13123,7 @@ fn zirBoolNot(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air
const inst_data = sema.code.instructions.items(.data)[inst].un_node;
const src = inst_data.src();
const operand_src = src; // TODO put this on the operand, not the `!`
- const uncasted_operand = sema.resolveInst(inst_data.operand);
+ const uncasted_operand = try sema.resolveInst(inst_data.operand);
const operand = try sema.coerce(block, Type.bool, uncasted_operand, operand_src);
if (try sema.resolveMaybeUndefVal(block, operand_src, operand)) |val| {
@@ -10088,7 +13149,7 @@ fn zirBoolBr(
const datas = sema.code.instructions.items(.data);
const inst_data = datas[inst].bool_br;
- const lhs = sema.resolveInst(inst_data.lhs);
+ const lhs = try sema.resolveInst(inst_data.lhs);
const lhs_src = sema.src;
const extra = sema.code.extraData(Zir.Inst.Block, inst_data.payload_index);
const body = sema.code.extra[extra.end..][0..extra.data.body_len];
@@ -10120,7 +13181,7 @@ fn zirBoolBr(
var child_block = parent_block.makeSubBlock();
child_block.runtime_loop = null;
child_block.runtime_cond = lhs_src;
- child_block.runtime_index += 1;
+ child_block.runtime_index.increment();
defer child_block.instructions.deinit(gpa);
var then_block = child_block.makeSubBlock();
@@ -10173,7 +13234,7 @@ fn zirIsNonNull(
const inst_data = sema.code.instructions.items(.data)[inst].un_node;
const src = inst_data.src();
- const operand = sema.resolveInst(inst_data.operand);
+ const operand = try sema.resolveInst(inst_data.operand);
return sema.analyzeIsNull(block, src, operand, true);
}
@@ -10187,7 +13248,7 @@ fn zirIsNonNullPtr(
const inst_data = sema.code.instructions.items(.data)[inst].un_node;
const src = inst_data.src();
- const ptr = sema.resolveInst(inst_data.operand);
+ const ptr = try sema.resolveInst(inst_data.operand);
if ((try sema.resolveMaybeUndefVal(block, src, ptr)) == null) {
return block.addUnOp(.is_non_null_ptr, ptr);
}
@@ -10200,7 +13261,7 @@ fn zirIsNonErr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
defer tracy.end();
const inst_data = sema.code.instructions.items(.data)[inst].un_node;
- const operand = sema.resolveInst(inst_data.operand);
+ const operand = try sema.resolveInst(inst_data.operand);
return sema.analyzeIsNonErr(block, inst_data.src(), operand);
}
@@ -10210,7 +13271,7 @@ fn zirIsNonErrPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
const inst_data = sema.code.instructions.items(.data)[inst].un_node;
const src = inst_data.src();
- const ptr = sema.resolveInst(inst_data.operand);
+ const ptr = try sema.resolveInst(inst_data.operand);
const loaded = try sema.analyzeLoad(block, src, ptr, src);
return sema.analyzeIsNonErr(block, src, loaded);
}
@@ -10224,20 +13285,20 @@ fn zirCondbr(
defer tracy.end();
const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
- const src = inst_data.src();
const cond_src: LazySrcLoc = .{ .node_offset_if_cond = inst_data.src_node };
const extra = sema.code.extraData(Zir.Inst.CondBr, inst_data.payload_index);
const then_body = sema.code.extra[extra.end..][0..extra.data.then_body_len];
const else_body = sema.code.extra[extra.end + then_body.len ..][0..extra.data.else_body_len];
- const uncasted_cond = sema.resolveInst(extra.data.condition);
+ const uncasted_cond = try sema.resolveInst(extra.data.condition);
const cond = try sema.coerce(parent_block, Type.bool, uncasted_cond, cond_src);
- if (try sema.resolveDefinedValue(parent_block, src, cond)) |cond_val| {
+ if (try sema.resolveDefinedValue(parent_block, cond_src, cond)) |cond_val| {
const body = if (cond_val.toBool()) then_body else else_body;
- _ = try sema.analyzeBody(parent_block, body);
- return always_noreturn;
+ // We use `analyzeBodyInner` since we want to propagate any possible
+ // `error.ComptimeBreak` to the caller.
+ return sema.analyzeBodyInner(parent_block, body);
}
const gpa = sema.gpa;
@@ -10247,14 +13308,36 @@ fn zirCondbr(
var sub_block = parent_block.makeSubBlock();
sub_block.runtime_loop = null;
sub_block.runtime_cond = cond_src;
- sub_block.runtime_index += 1;
+ sub_block.runtime_index.increment();
defer sub_block.instructions.deinit(gpa);
- _ = try sema.analyzeBody(&sub_block, then_body);
+ _ = sema.analyzeBodyInner(&sub_block, then_body) catch |err| switch (err) {
+ error.ComptimeBreak => {
+ const zir_datas = sema.code.instructions.items(.data);
+ const break_data = zir_datas[sema.comptime_break_inst].@"break";
+ try sema.addRuntimeBreak(&sub_block, .{
+ .block_inst = break_data.block_inst,
+ .operand = break_data.operand,
+ .inst = sema.comptime_break_inst,
+ });
+ },
+ else => |e| return e,
+ };
const true_instructions = sub_block.instructions.toOwnedSlice(gpa);
defer gpa.free(true_instructions);
- _ = try sema.analyzeBody(&sub_block, else_body);
+ _ = sema.analyzeBodyInner(&sub_block, else_body) catch |err| switch (err) {
+ error.ComptimeBreak => {
+ const zir_datas = sema.code.instructions.items(.data);
+ const break_data = zir_datas[sema.comptime_break_inst].@"break";
+ try sema.addRuntimeBreak(&sub_block, .{
+ .block_inst = break_data.block_inst,
+ .operand = break_data.operand,
+ .inst = sema.comptime_break_inst,
+ });
+ },
+ else => |e| return e,
+ };
try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.CondBr).Struct.fields.len +
true_instructions.len + sub_block.instructions.items.len);
_ = try parent_block.addInst(.{
@@ -10272,15 +13355,173 @@ fn zirCondbr(
return always_noreturn;
}
-fn zirUnreachable(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Zir.Inst.Index {
- const tracy = trace(@src());
- defer tracy.end();
+fn zirTry(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileError!Zir.Inst.Ref {
+ const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
+ const src = inst_data.src();
+ const operand_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node };
+ const extra = sema.code.extraData(Zir.Inst.Try, inst_data.payload_index);
+ const body = sema.code.extra[extra.end..][0..extra.data.body_len];
+ const err_union = try sema.resolveInst(extra.data.operand);
+ const err_union_ty = sema.typeOf(err_union);
+ if (err_union_ty.zigTypeTag() != .ErrorUnion) {
+ return sema.fail(parent_block, operand_src, "expected error union type, found '{}'", .{
+ err_union_ty.fmt(sema.mod),
+ });
+ }
+ const is_non_err = try sema.analyzeIsNonErrComptimeOnly(parent_block, operand_src, err_union);
+ if (is_non_err != .none) {
+ const is_non_err_val = (try sema.resolveDefinedValue(parent_block, operand_src, is_non_err)).?;
+ if (is_non_err_val.toBool()) {
+ return sema.analyzeErrUnionPayload(parent_block, src, err_union_ty, err_union, operand_src, false);
+ }
+ // We can analyze the body directly in the parent block because we know there are
+ // no breaks from the body possible, and that the body is noreturn.
+ return sema.resolveBody(parent_block, body, inst);
+ }
+
+ var sub_block = parent_block.makeSubBlock();
+ defer sub_block.instructions.deinit(sema.gpa);
+
+ // This body is guaranteed to end with noreturn and has no breaks.
+ _ = try sema.analyzeBodyInner(&sub_block, body);
+
+ try sema.air_extra.ensureUnusedCapacity(sema.gpa, @typeInfo(Air.Try).Struct.fields.len +
+ sub_block.instructions.items.len);
+ const try_inst = try parent_block.addInst(.{
+ .tag = .@"try",
+ .data = .{ .pl_op = .{
+ .operand = err_union,
+ .payload = sema.addExtraAssumeCapacity(Air.Try{
+ .body_len = @intCast(u32, sub_block.instructions.items.len),
+ }),
+ } },
+ });
+ sema.air_extra.appendSliceAssumeCapacity(sub_block.instructions.items);
+ return try_inst;
+}
+
+fn zirTryPtr(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileError!Zir.Inst.Ref {
+ const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
+ const src = inst_data.src();
+ const operand_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node };
+ const extra = sema.code.extraData(Zir.Inst.Try, inst_data.payload_index);
+ const body = sema.code.extra[extra.end..][0..extra.data.body_len];
+ const operand = try sema.resolveInst(extra.data.operand);
+ const err_union = try sema.analyzeLoad(parent_block, src, operand, operand_src);
+ const err_union_ty = sema.typeOf(err_union);
+ if (err_union_ty.zigTypeTag() != .ErrorUnion) {
+ return sema.fail(parent_block, operand_src, "expected error union type, found '{}'", .{
+ err_union_ty.fmt(sema.mod),
+ });
+ }
+ const is_non_err = try sema.analyzeIsNonErrComptimeOnly(parent_block, operand_src, err_union);
+ if (is_non_err != .none) {
+ const is_non_err_val = (try sema.resolveDefinedValue(parent_block, operand_src, is_non_err)).?;
+ if (is_non_err_val.toBool()) {
+ return sema.analyzeErrUnionPayloadPtr(parent_block, src, operand, false, false);
+ }
+ // We can analyze the body directly in the parent block because we know there are
+ // no breaks from the body possible, and that the body is noreturn.
+ return sema.resolveBody(parent_block, body, inst);
+ }
+
+ var sub_block = parent_block.makeSubBlock();
+ defer sub_block.instructions.deinit(sema.gpa);
+ // This body is guaranteed to end with noreturn and has no breaks.
+ _ = try sema.analyzeBodyInner(&sub_block, body);
+
+ const operand_ty = sema.typeOf(operand);
+ const ptr_info = operand_ty.ptrInfo().data;
+ const res_ty = try Type.ptr(sema.arena, sema.mod, .{
+ .pointee_type = err_union_ty.errorUnionPayload(),
+ .@"addrspace" = ptr_info.@"addrspace",
+ .mutable = ptr_info.mutable,
+ .@"allowzero" = ptr_info.@"allowzero",
+ .@"volatile" = ptr_info.@"volatile",
+ });
+ const res_ty_ref = try sema.addType(res_ty);
+ try sema.air_extra.ensureUnusedCapacity(sema.gpa, @typeInfo(Air.TryPtr).Struct.fields.len +
+ sub_block.instructions.items.len);
+ const try_inst = try parent_block.addInst(.{
+ .tag = .try_ptr,
+ .data = .{ .ty_pl = .{
+ .ty = res_ty_ref,
+ .payload = sema.addExtraAssumeCapacity(Air.TryPtr{
+ .ptr = operand,
+ .body_len = @intCast(u32, sub_block.instructions.items.len),
+ }),
+ } },
+ });
+ sema.air_extra.appendSliceAssumeCapacity(sub_block.instructions.items);
+ return try_inst;
+}
+
+// A `break` statement is inside a runtime condition, but trying to
+// break from an inline loop. In such case we must convert it to
+// a runtime break.
+fn addRuntimeBreak(sema: *Sema, child_block: *Block, break_data: BreakData) !void {
+ const gop = try sema.inst_map.getOrPut(sema.gpa, break_data.block_inst);
+ const labeled_block = if (!gop.found_existing) blk: {
+ try sema.post_hoc_blocks.ensureUnusedCapacity(sema.gpa, 1);
+
+ const new_block_inst = @intCast(Air.Inst.Index, sema.air_instructions.len);
+ gop.value_ptr.* = Air.indexToRef(new_block_inst);
+ try sema.air_instructions.append(sema.gpa, .{
+ .tag = .block,
+ .data = undefined,
+ });
+ const labeled_block = try sema.gpa.create(LabeledBlock);
+ labeled_block.* = .{
+ .label = .{
+ .zir_block = break_data.block_inst,
+ .merges = .{
+ .results = .{},
+ .br_list = .{},
+ .block_inst = new_block_inst,
+ },
+ },
+ .block = .{
+ .parent = child_block,
+ .sema = sema,
+ .src_decl = child_block.src_decl,
+ .namespace = child_block.namespace,
+ .wip_capture_scope = child_block.wip_capture_scope,
+ .instructions = .{},
+ .label = &labeled_block.label,
+ .inlining = child_block.inlining,
+ .is_comptime = child_block.is_comptime,
+ },
+ };
+ sema.post_hoc_blocks.putAssumeCapacityNoClobber(new_block_inst, labeled_block);
+ break :blk labeled_block;
+ } else blk: {
+ const new_block_inst = Air.refToIndex(gop.value_ptr.*).?;
+ const labeled_block = sema.post_hoc_blocks.get(new_block_inst).?;
+ break :blk labeled_block;
+ };
+
+ const operand = try sema.resolveInst(break_data.operand);
+ const br_ref = try child_block.addBr(labeled_block.label.merges.block_inst, operand);
+ try labeled_block.label.merges.results.append(sema.gpa, operand);
+ try labeled_block.label.merges.br_list.append(sema.gpa, Air.refToIndex(br_ref).?);
+ labeled_block.block.runtime_index.increment();
+ if (labeled_block.block.runtime_cond == null and labeled_block.block.runtime_loop == null) {
+ labeled_block.block.runtime_cond = child_block.runtime_cond orelse child_block.runtime_loop;
+ labeled_block.block.runtime_loop = child_block.runtime_loop;
+ }
+}
+
+fn zirUnreachable(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Zir.Inst.Index {
const inst_data = sema.code.instructions.items(.data)[inst].@"unreachable";
const src = inst_data.src();
+
+ if (block.is_comptime or inst_data.force_comptime) {
+ return sema.fail(block, src, "reached unreachable code", .{});
+ }
try sema.requireRuntimeBlock(block, src);
// TODO Add compile error for @optimizeFor occurring too late in a scope.
- try block.addUnreachable(src, inst_data.safety);
+ try block.addUnreachable(src, true);
return always_noreturn;
}
@@ -10302,7 +13543,7 @@ fn zirRetErrValue(
return sema.analyzeRet(block, result_inst, src);
}
-fn zirRetCoerce(
+fn zirRetTok(
sema: *Sema,
block: *Block,
inst: Zir.Inst.Index,
@@ -10311,7 +13552,7 @@ fn zirRetCoerce(
defer tracy.end();
const inst_data = sema.code.instructions.items(.data)[inst].un_tok;
- const operand = sema.resolveInst(inst_data.operand);
+ const operand = try sema.resolveInst(inst_data.operand);
const src = inst_data.src();
return sema.analyzeRet(block, operand, src);
@@ -10322,7 +13563,7 @@ fn zirRetNode(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Zir
defer tracy.end();
const inst_data = sema.code.instructions.items(.data)[inst].un_node;
- const operand = sema.resolveInst(inst_data.operand);
+ const operand = try sema.resolveInst(inst_data.operand);
const src = inst_data.src();
return sema.analyzeRet(block, operand, src);
@@ -10334,7 +13575,7 @@ fn zirRetLoad(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Zir
const inst_data = sema.code.instructions.items(.data)[inst].un_node;
const src = inst_data.src();
- const ret_ptr = sema.resolveInst(inst_data.operand);
+ const ret_ptr = try sema.resolveInst(inst_data.operand);
if (block.is_comptime or block.inlining != null) {
const operand = try sema.analyzeLoad(block, src, ret_ptr, src);
@@ -10345,6 +13586,23 @@ fn zirRetLoad(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Zir
return always_noreturn;
}
+fn addToInferredErrorSet(sema: *Sema, uncasted_operand: Air.Inst.Ref) !void {
+ assert(sema.fn_ret_ty.zigTypeTag() == .ErrorUnion);
+
+ if (sema.fn_ret_ty.errorUnionSet().castTag(.error_set_inferred)) |payload| {
+ const op_ty = sema.typeOf(uncasted_operand);
+ switch (op_ty.zigTypeTag()) {
+ .ErrorSet => {
+ try payload.data.addErrorSet(sema.gpa, op_ty);
+ },
+ .ErrorUnion => {
+ try payload.data.addErrorSet(sema.gpa, op_ty.errorUnionSet());
+ },
+ else => {},
+ }
+ }
+}
+
fn analyzeRet(
sema: *Sema,
block: *Block,
@@ -10355,18 +13613,7 @@ fn analyzeRet(
// add the error tag to the inferred error set of the in-scope function, so
// that the coercion below works correctly.
if (sema.fn_ret_ty.zigTypeTag() == .ErrorUnion) {
- if (sema.fn_ret_ty.errorUnionSet().castTag(.error_set_inferred)) |payload| {
- const op_ty = sema.typeOf(uncasted_operand);
- switch (op_ty.zigTypeTag()) {
- .ErrorSet => {
- try payload.data.addErrorSet(sema.gpa, op_ty);
- },
- .ErrorUnion => {
- try payload.data.addErrorSet(sema.gpa, op_ty.errorUnionSet());
- },
- else => {},
- }
- }
+ try sema.addToInferredErrorSet(uncasted_operand);
}
const operand = try sema.coerce(block, sema.fn_ret_ty, uncasted_operand, src);
@@ -10381,6 +13628,26 @@ fn analyzeRet(
return always_noreturn;
}
+ // TODO implement this feature in all the backends and then delete this check.
+ const backend_supports_error_return_tracing =
+ sema.mod.comp.bin_file.options.use_llvm;
+
+ if (sema.fn_ret_ty.isError() and
+ sema.mod.comp.bin_file.options.error_return_tracing and
+ backend_supports_error_return_tracing)
+ ret_err: {
+ if (try sema.resolveMaybeUndefVal(block, src, operand)) |ret_val| {
+ if (ret_val.tag() != .@"error") break :ret_err;
+ }
+ const return_err_fn = try sema.getBuiltin(block, src, "returnError");
+ const unresolved_stack_trace_ty = try sema.getBuiltinType(block, src, "StackTrace");
+ const stack_trace_ty = try sema.resolveTypeFields(block, src, unresolved_stack_trace_ty);
+ const ptr_stack_trace_ty = try Type.Tag.optional_single_mut_pointer.create(sema.arena, stack_trace_ty);
+ const err_return_trace = try block.addTy(.err_return_trace, ptr_stack_trace_ty);
+ const args: [1]Air.Inst.Ref = .{err_return_trace};
+ _ = try sema.analyzeCall(block, return_err_fn, src, src, .never_inline, false, &args);
+ }
+
try sema.resolveTypeLayout(block, src, sema.fn_ret_ty);
_ = try block.addUnOp(.ret, operand);
return always_noreturn;
@@ -10400,7 +13667,7 @@ fn zirPtrTypeSimple(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileErr
const inst_data = sema.code.instructions.items(.data)[inst].ptr_type_simple;
const elem_type = try sema.resolveType(block, .unneeded, inst_data.elem_type);
- const ty = try Type.ptr(sema.arena, .{
+ const ty = try Type.ptr(sema.arena, sema.mod, .{
.pointee_type = elem_type,
.@"addrspace" = .generic,
.mutable = inst_data.is_mutable,
@@ -10415,9 +13682,13 @@ fn zirPtrType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air
const tracy = trace(@src());
defer tracy.end();
- const src: LazySrcLoc = .unneeded;
+ // TODO better source location
+ const src: LazySrcLoc = sema.src;
+ const elem_ty_src: LazySrcLoc = .unneeded;
const inst_data = sema.code.instructions.items(.data)[inst].ptr_type;
const extra = sema.code.extraData(Zir.Inst.PtrType, inst_data.payload_index);
+ const unresolved_elem_ty = try sema.resolveType(block, elem_ty_src, extra.data.elem_type);
+ const target = sema.mod.getTarget();
var extra_i = extra.end;
@@ -10427,10 +13698,20 @@ fn zirPtrType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air
break :blk (try sema.resolveInstConst(block, .unneeded, ref)).val;
} else null;
- const abi_align = if (inst_data.flags.has_align) blk: {
+ const abi_align: u32 = if (inst_data.flags.has_align) blk: {
const ref = @intToEnum(Zir.Inst.Ref, sema.code.extra[extra_i]);
extra_i += 1;
- break :blk try sema.resolveAlreadyCoercedInt(block, .unneeded, ref, u32);
+ const coerced = try sema.coerce(block, Type.u32, try sema.resolveInst(ref), src);
+ const val = try sema.resolveConstValue(block, src, coerced);
+ // Check if this happens to be the lazy alignment of our element type, in
+ // which case we can make this 0 without resolving it.
+ if (val.castTag(.lazy_align)) |payload| {
+ if (payload.data.eql(unresolved_elem_ty, sema.mod)) {
+ break :blk 0;
+ }
+ }
+ const abi_align = (try val.getUnsignedIntAdvanced(target, sema.kit(block, src))).?;
+ break :blk @intCast(u32, abi_align);
} else 0;
const address_space = if (inst_data.flags.has_addrspace) blk: {
@@ -10439,32 +13720,40 @@ fn zirPtrType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air
break :blk try sema.analyzeAddrspace(block, .unneeded, ref, .pointer);
} else .generic;
- const bit_start = if (inst_data.flags.has_bit_range) blk: {
+ const bit_offset = if (inst_data.flags.has_bit_range) blk: {
const ref = @intToEnum(Zir.Inst.Ref, sema.code.extra[extra_i]);
extra_i += 1;
- break :blk try sema.resolveAlreadyCoercedInt(block, .unneeded, ref, u16);
+ const bit_offset = try sema.resolveInt(block, .unneeded, ref, Type.u16);
+ break :blk @intCast(u16, bit_offset);
} else 0;
- const bit_end = if (inst_data.flags.has_bit_range) blk: {
+ const host_size: u16 = if (inst_data.flags.has_bit_range) blk: {
const ref = @intToEnum(Zir.Inst.Ref, sema.code.extra[extra_i]);
extra_i += 1;
- break :blk try sema.resolveAlreadyCoercedInt(block, .unneeded, ref, u16);
+ const host_size = try sema.resolveInt(block, .unneeded, ref, Type.u16);
+ break :blk @intCast(u16, host_size);
} else 0;
- if (bit_end != 0 and bit_start >= bit_end * 8)
+ if (host_size != 0 and bit_offset >= host_size * 8) {
return sema.fail(block, src, "bit offset starts after end of host integer", .{});
+ }
- const elem_type = try sema.resolveType(block, .unneeded, extra.data.elem_type);
-
- const ty = try Type.ptr(sema.arena, .{
- .pointee_type = elem_type,
+ const elem_ty = if (abi_align == 0)
+ unresolved_elem_ty
+ else t: {
+ const elem_ty = try sema.resolveTypeFields(block, elem_ty_src, unresolved_elem_ty);
+ try sema.resolveTypeLayout(block, elem_ty_src, elem_ty);
+ break :t elem_ty;
+ };
+ const ty = try Type.ptr(sema.arena, sema.mod, .{
+ .pointee_type = elem_ty,
.sentinel = sentinel,
.@"align" = abi_align,
.@"addrspace" = address_space,
- .bit_offset = bit_start,
- .host_size = bit_end,
+ .bit_offset = bit_offset,
+ .host_size = host_size,
.mutable = inst_data.flags.is_mutable,
- .@"allowzero" = inst_data.flags.is_allowzero or inst_data.size == .C,
+ .@"allowzero" = inst_data.flags.is_allowzero,
.@"volatile" = inst_data.flags.is_volatile,
.size = inst_data.size,
});
@@ -10483,7 +13772,7 @@ fn zirStructInitEmpty(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileE
.Struct => return structInitEmpty(sema, block, obj_ty, src, src),
.Array => return arrayInitEmpty(sema, obj_ty),
.Void => return sema.addConstant(obj_ty, Value.void),
- else => unreachable,
+ else => return sema.failWithArrayInitNotSupported(block, src, obj_ty),
}
}
@@ -10531,10 +13820,44 @@ fn arrayInitEmpty(sema: *Sema, obj_ty: Type) CompileError!Air.Inst.Ref {
}
}
-fn zirUnionInitPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
+fn zirUnionInit(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
- const src = inst_data.src();
- return sema.fail(block, src, "TODO: Sema.zirUnionInitPtr", .{});
+ const ty_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
+ const field_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node };
+ const init_src: LazySrcLoc = .{ .node_offset_builtin_call_arg2 = inst_data.src_node };
+ const extra = sema.code.extraData(Zir.Inst.UnionInit, inst_data.payload_index).data;
+ const union_ty = try sema.resolveType(block, ty_src, extra.union_type);
+ const field_name = try sema.resolveConstString(block, field_src, extra.field_name);
+ const init = try sema.resolveInst(extra.init);
+ return sema.unionInit(block, init, init_src, union_ty, ty_src, field_name, field_src);
+}
+
+fn unionInit(
+ sema: *Sema,
+ block: *Block,
+ uncasted_init: Air.Inst.Ref,
+ init_src: LazySrcLoc,
+ union_ty: Type,
+ union_ty_src: LazySrcLoc,
+ field_name: []const u8,
+ field_src: LazySrcLoc,
+) CompileError!Air.Inst.Ref {
+ const field_index = try sema.unionFieldIndex(block, union_ty, field_name, field_src);
+ const field = union_ty.unionFields().values()[field_index];
+ const init = try sema.coerce(block, field.ty, uncasted_init, init_src);
+
+ if (try sema.resolveMaybeUndefVal(block, init_src, init)) |init_val| {
+ const tag_val = try Value.Tag.enum_field_index.create(sema.arena, field_index);
+ return sema.addConstant(union_ty, try Value.Tag.@"union".create(sema.arena, .{
+ .tag = tag_val,
+ .val = init_val,
+ }));
+ }
+
+ try sema.requireRuntimeBlock(block, init_src);
+ _ = union_ty_src;
+ try sema.queueFullTypeResolution(union_ty);
+ return block.addUnionInit(union_ty, field_index, init);
}
fn zirStructInit(
@@ -10592,10 +13915,10 @@ fn zirStructInit(
try sema.errNote(block, other_field_src, msg, "other field here", .{});
break :msg msg;
};
- return sema.failWithOwnedErrorMsg(msg);
+ return sema.failWithOwnedErrorMsg(block, msg);
}
found_fields[field_index] = item.data.field_type;
- field_inits[field_index] = sema.resolveInst(item.data.init);
+ field_inits[field_index] = try sema.resolveInst(item.data.init);
}
var root_msg: ?*Module.ErrorMsg = null;
@@ -10619,9 +13942,7 @@ fn zirStructInit(
}
}
return sema.finishStructInit(block, src, field_inits, root_msg, struct_obj, resolved_ty, is_ref);
- } else if (resolved_ty.cast(Type.Payload.Union)) |union_payload| {
- const union_obj = union_payload.data;
-
+ } else if (resolved_ty.zigTypeTag() == .Union) {
if (extra.data.fields_len != 1) {
return sema.fail(block, src, "union initialization expects exactly one field", .{});
}
@@ -10632,23 +13953,39 @@ fn zirStructInit(
const field_src: LazySrcLoc = .{ .node_offset_back2tok = field_type_data.src_node };
const field_type_extra = sema.code.extraData(Zir.Inst.FieldType, field_type_data.payload_index).data;
const field_name = sema.code.nullTerminatedString(field_type_extra.name_start);
- const field_index_usize = union_obj.fields.getIndex(field_name) orelse
- return sema.failWithBadUnionFieldAccess(block, union_obj, field_src, field_name);
- const field_index = @intCast(u32, field_index_usize);
+ const field_index = try sema.unionFieldIndex(block, resolved_ty, field_name, field_src);
+ const tag_val = try Value.Tag.enum_field_index.create(sema.arena, field_index);
- if (is_ref) {
- return sema.fail(block, src, "TODO: Sema.zirStructInit is_ref=true union", .{});
- }
-
- const init_inst = sema.resolveInst(item.data.init);
+ const init_inst = try sema.resolveInst(item.data.init);
if (try sema.resolveMaybeUndefVal(block, field_src, init_inst)) |val| {
- const tag_val = try Value.Tag.enum_field_index.create(sema.arena, field_index);
- return sema.addConstant(
+ return sema.addConstantMaybeRef(
+ block,
+ src,
resolved_ty,
try Value.Tag.@"union".create(sema.arena, .{ .tag = tag_val, .val = val }),
+ is_ref,
);
}
- return sema.fail(block, src, "TODO: Sema.zirStructInit for runtime-known union values", .{});
+
+ if (is_ref) {
+ const target = sema.mod.getTarget();
+ const alloc_ty = try Type.ptr(sema.arena, sema.mod, .{
+ .pointee_type = resolved_ty,
+ .@"addrspace" = target_util.defaultAddressSpace(target, .local),
+ });
+ const alloc = try block.addTy(.alloc, alloc_ty);
+ const field_ptr = try sema.unionFieldPtr(block, field_src, alloc, field_name, field_src, resolved_ty);
+ try sema.storePtr(block, src, field_ptr, init_inst);
+ const new_tag = try sema.addConstant(resolved_ty.unionTagTypeHypothetical(), tag_val);
+ _ = try block.addBinOp(.set_union_tag, alloc, new_tag);
+ return alloc;
+ }
+
+ try sema.requireRuntimeBlock(block, src);
+ try sema.queueFullTypeResolution(resolved_ty);
+ return block.addUnionInit(resolved_ty, field_index, init_inst);
+ } else if (resolved_ty.isAnonStruct()) {
+ return sema.fail(block, src, "TODO anon struct init validation", .{});
}
unreachable;
}
@@ -10666,15 +14003,15 @@ fn finishStructInit(
const gpa = sema.gpa;
if (root_msg) |msg| {
- const fqn = try struct_obj.getFullyQualifiedName(gpa);
+ const fqn = try struct_obj.getFullyQualifiedName(sema.mod);
defer gpa.free(fqn);
try sema.mod.errNoteNonLazy(
- struct_obj.srcLoc(),
+ struct_obj.srcLoc(sema.mod),
msg,
"struct '{s}' declared here",
.{fqn},
);
- return sema.failWithOwnedErrorMsg(msg);
+ return sema.failWithOwnedErrorMsg(block, msg);
}
const is_comptime = for (field_inits) |field_init| {
@@ -10688,12 +14025,17 @@ fn finishStructInit(
for (field_inits) |field_init, i| {
values[i] = (sema.resolveMaybeUndefVal(block, src, field_init) catch unreachable).?;
}
- const struct_val = try Value.Tag.@"struct".create(sema.arena, values);
+ const struct_val = try Value.Tag.aggregate.create(sema.arena, values);
return sema.addConstantMaybeRef(block, src, struct_ty, struct_val, is_ref);
}
if (is_ref) {
- const alloc = try block.addTy(.alloc, struct_ty);
+ const target = sema.mod.getTarget();
+ const alloc_ty = try Type.ptr(sema.arena, sema.mod, .{
+ .pointee_type = struct_ty,
+ .@"addrspace" = target_util.defaultAddressSpace(target, .local),
+ });
+ const alloc = try block.addTy(.alloc, alloc_ty);
for (field_inits) |field_init, i_usize| {
const i = @intCast(u32, i_usize);
const field_src = src;
@@ -10705,15 +14047,94 @@ fn finishStructInit(
}
try sema.requireRuntimeBlock(block, src);
- return block.addVectorInit(struct_ty, field_inits);
+ try sema.queueFullTypeResolution(struct_ty);
+ return block.addAggregateInit(struct_ty, field_inits);
}
-fn zirStructInitAnon(sema: *Sema, block: *Block, inst: Zir.Inst.Index, is_ref: bool) CompileError!Air.Inst.Ref {
+fn zirStructInitAnon(
+ sema: *Sema,
+ block: *Block,
+ inst: Zir.Inst.Index,
+ is_ref: bool,
+) CompileError!Air.Inst.Ref {
const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
const src = inst_data.src();
+ const extra = sema.code.extraData(Zir.Inst.StructInitAnon, inst_data.payload_index);
+ const types = try sema.arena.alloc(Type, extra.data.fields_len);
+ const values = try sema.arena.alloc(Value, types.len);
+ const names = try sema.arena.alloc([]const u8, types.len);
+
+ const opt_runtime_src = rs: {
+ var runtime_src: ?LazySrcLoc = null;
+ var extra_index = extra.end;
+ for (types) |*field_ty, i| {
+ const item = sema.code.extraData(Zir.Inst.StructInitAnon.Item, extra_index);
+ extra_index = item.end;
+
+ names[i] = sema.code.nullTerminatedString(item.data.field_name);
+ const init = try sema.resolveInst(item.data.init);
+ field_ty.* = sema.typeOf(init);
+ const init_src = src; // TODO better source location
+ if (try sema.resolveMaybeUndefVal(block, init_src, init)) |init_val| {
+ values[i] = init_val;
+ } else {
+ values[i] = Value.initTag(.unreachable_value);
+ runtime_src = init_src;
+ }
+ }
+ break :rs runtime_src;
+ };
+
+ const tuple_ty = try Type.Tag.anon_struct.create(sema.arena, .{
+ .names = names,
+ .types = types,
+ .values = values,
+ });
+
+ const runtime_src = opt_runtime_src orelse {
+ const tuple_val = try Value.Tag.aggregate.create(sema.arena, values);
+ return sema.addConstantMaybeRef(block, src, tuple_ty, tuple_val, is_ref);
+ };
+
+ try sema.requireRuntimeBlock(block, runtime_src);
- _ = is_ref;
- return sema.fail(block, src, "TODO: Sema.zirStructInitAnon", .{});
+ if (is_ref) {
+ const target = sema.mod.getTarget();
+ const alloc_ty = try Type.ptr(sema.arena, sema.mod, .{
+ .pointee_type = tuple_ty,
+ .@"addrspace" = target_util.defaultAddressSpace(target, .local),
+ });
+ const alloc = try block.addTy(.alloc, alloc_ty);
+ var extra_index = extra.end;
+ for (types) |field_ty, i_usize| {
+ const i = @intCast(u32, i_usize);
+ const item = sema.code.extraData(Zir.Inst.StructInitAnon.Item, extra_index);
+ extra_index = item.end;
+
+ const field_ptr_ty = try Type.ptr(sema.arena, sema.mod, .{
+ .mutable = true,
+ .@"addrspace" = target_util.defaultAddressSpace(target, .local),
+ .pointee_type = field_ty,
+ });
+ if (values[i].tag() == .unreachable_value) {
+ const init = try sema.resolveInst(item.data.init);
+ const field_ptr = try block.addStructFieldPtr(alloc, i, field_ptr_ty);
+ _ = try block.addBinOp(.store, field_ptr, init);
+ }
+ }
+
+ return alloc;
+ }
+
+ const element_refs = try sema.arena.alloc(Air.Inst.Ref, types.len);
+ var extra_index = extra.end;
+ for (types) |_, i| {
+ const item = sema.code.extraData(Zir.Inst.StructInitAnon.Item, extra_index);
+ extra_index = item.end;
+ element_refs[i] = try sema.resolveInst(item.data.init);
+ }
+
+ return block.addAggregateInit(tuple_ty, element_refs);
}
fn zirArrayInit(
@@ -10728,19 +14149,26 @@ fn zirArrayInit(
const extra = sema.code.extraData(Zir.Inst.MultiOp, inst_data.payload_index);
const args = sema.code.refSlice(extra.end, extra.data.operands_len);
- assert(args.len != 0);
+ assert(args.len >= 2); // array_ty + at least one element
- const resolved_args = try gpa.alloc(Air.Inst.Ref, args.len);
- defer gpa.free(resolved_args);
-
- for (args) |arg, i| resolved_args[i] = sema.resolveInst(arg);
+ const array_ty = try sema.resolveType(block, src, args[0]);
+ const sentinel_val = array_ty.sentinel();
- const elem_ty = sema.typeOf(resolved_args[0]);
+ const resolved_args = try gpa.alloc(Air.Inst.Ref, args.len - 1 + @boolToInt(sentinel_val != null));
+ defer gpa.free(resolved_args);
+ for (args[1..]) |arg, i| {
+ const resolved_arg = try sema.resolveInst(arg);
+ const arg_src = src; // TODO better source location
+ const elem_ty = if (array_ty.zigTypeTag() == .Struct)
+ array_ty.tupleFields().types[i]
+ else
+ array_ty.elemType2();
+ resolved_args[i] = try sema.coerce(block, elem_ty, resolved_arg, arg_src);
+ }
- const array_ty = try Type.Tag.array.create(sema.arena, .{
- .len = resolved_args.len,
- .elem_type = elem_ty,
- });
+ if (sentinel_val) |some| {
+ resolved_args[resolved_args.len - 1] = try sema.addConstant(array_ty.elemType2(), some);
+ }
const opt_runtime_src: ?LazySrcLoc = for (resolved_args) |arg| {
const arg_src = src; // TODO better source location
@@ -10756,24 +14184,42 @@ fn zirArrayInit(
elem_vals[i] = (sema.resolveMaybeUndefVal(block, src, arg) catch unreachable).?;
}
- const array_val = try Value.Tag.array.create(sema.arena, elem_vals);
+ const array_val = try Value.Tag.aggregate.create(sema.arena, elem_vals);
return sema.addConstantMaybeRef(block, src, array_ty, array_val, is_ref);
};
try sema.requireRuntimeBlock(block, runtime_src);
- try sema.resolveTypeLayout(block, src, elem_ty);
+ try sema.queueFullTypeResolution(array_ty);
if (is_ref) {
- const alloc_ty = try Type.ptr(sema.arena, .{
+ const target = sema.mod.getTarget();
+ const alloc_ty = try Type.ptr(sema.arena, sema.mod, .{
.pointee_type = array_ty,
- .@"addrspace" = target_util.defaultAddressSpace(sema.mod.getTarget(), .local),
+ .@"addrspace" = target_util.defaultAddressSpace(target, .local),
});
const alloc = try block.addTy(.alloc, alloc_ty);
- const elem_ptr_ty = try Type.ptr(sema.arena, .{
+ if (array_ty.isTuple()) {
+ const types = array_ty.tupleFields().types;
+ for (resolved_args) |arg, i| {
+ const elem_ptr_ty = try Type.ptr(sema.arena, sema.mod, .{
+ .mutable = true,
+ .@"addrspace" = target_util.defaultAddressSpace(target, .local),
+ .pointee_type = types[i],
+ });
+ const elem_ptr_ty_ref = try sema.addType(elem_ptr_ty);
+
+ const index = try sema.addIntUnsigned(Type.usize, i);
+ const elem_ptr = try block.addPtrElemPtrTypeRef(alloc, index, elem_ptr_ty_ref);
+ _ = try block.addBinOp(.store, elem_ptr, arg);
+ }
+ return alloc;
+ }
+
+ const elem_ptr_ty = try Type.ptr(sema.arena, sema.mod, .{
.mutable = true,
- .@"addrspace" = target_util.defaultAddressSpace(sema.mod.getTarget(), .local),
- .pointee_type = elem_ty,
+ .@"addrspace" = target_util.defaultAddressSpace(target, .local),
+ .pointee_type = array_ty.elemType2(),
});
const elem_ptr_ty_ref = try sema.addType(elem_ptr_ty);
@@ -10785,7 +14231,7 @@ fn zirArrayInit(
return alloc;
}
- return block.addVectorInit(array_ty, resolved_args);
+ return block.addAggregateInit(array_ty, resolved_args);
}
fn zirArrayInitAnon(
@@ -10805,7 +14251,7 @@ fn zirArrayInitAnon(
const opt_runtime_src = rs: {
var runtime_src: ?LazySrcLoc = null;
for (operands) |operand, i| {
- const elem = sema.resolveInst(operand);
+ const elem = try sema.resolveInst(operand);
types[i] = sema.typeOf(elem);
const operand_src = src; // TODO better source location
if (try sema.resolveMaybeUndefVal(block, operand_src, elem)) |val| {
@@ -10824,23 +14270,30 @@ fn zirArrayInitAnon(
});
const runtime_src = opt_runtime_src orelse {
- const tuple_val = try Value.Tag.@"struct".create(sema.arena, values);
+ const tuple_val = try Value.Tag.aggregate.create(sema.arena, values);
return sema.addConstantMaybeRef(block, src, tuple_ty, tuple_val, is_ref);
};
try sema.requireRuntimeBlock(block, runtime_src);
if (is_ref) {
- const alloc = try block.addTy(.alloc, tuple_ty);
+ const target = sema.mod.getTarget();
+ const alloc_ty = try Type.ptr(sema.arena, sema.mod, .{
+ .pointee_type = tuple_ty,
+ .@"addrspace" = target_util.defaultAddressSpace(target, .local),
+ });
+ const alloc = try block.addTy(.alloc, alloc_ty);
for (operands) |operand, i_usize| {
const i = @intCast(u32, i_usize);
- const field_ptr_ty = try Type.ptr(sema.arena, .{
+ const field_ptr_ty = try Type.ptr(sema.arena, sema.mod, .{
.mutable = true,
- .@"addrspace" = target_util.defaultAddressSpace(sema.mod.getTarget(), .local),
+ .@"addrspace" = target_util.defaultAddressSpace(target, .local),
.pointee_type = types[i],
});
- const field_ptr = try block.addStructFieldPtr(alloc, i, field_ptr_ty);
- _ = try block.addBinOp(.store, field_ptr, sema.resolveInst(operand));
+ if (values[i].tag() == .unreachable_value) {
+ const field_ptr = try block.addStructFieldPtr(alloc, i, field_ptr_ty);
+ _ = try block.addBinOp(.store, field_ptr, try sema.resolveInst(operand));
+ }
}
return alloc;
@@ -10848,10 +14301,10 @@ fn zirArrayInitAnon(
const element_refs = try sema.arena.alloc(Air.Inst.Ref, operands.len);
for (operands) |operand, i| {
- element_refs[i] = sema.resolveInst(operand);
+ element_refs[i] = try sema.resolveInst(operand);
}
- return block.addVectorInit(tuple_ty, element_refs);
+ return block.addAggregateInit(tuple_ty, element_refs);
}
fn addConstantMaybeRef(
@@ -10869,84 +14322,133 @@ fn addConstantMaybeRef(
const decl = try anon_decl.finish(
try ty.copy(anon_decl.arena()),
try val.copy(anon_decl.arena()),
+ 0, // default alignment
);
return sema.analyzeDeclRef(decl);
}
fn zirFieldTypeRef(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
- const src = inst_data.src();
- return sema.fail(block, src, "TODO: Sema.zirFieldTypeRef", .{});
+ const extra = sema.code.extraData(Zir.Inst.FieldTypeRef, inst_data.payload_index).data;
+ const ty_src = inst_data.src();
+ const field_src = inst_data.src();
+ const aggregate_ty = try sema.resolveType(block, ty_src, extra.container_type);
+ const field_name = try sema.resolveConstString(block, field_src, extra.field_name);
+ return sema.fieldType(block, aggregate_ty, field_name, field_src, ty_src);
}
fn zirFieldType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
const extra = sema.code.extraData(Zir.Inst.FieldType, inst_data.payload_index).data;
- const src = inst_data.src();
+ const ty_src = inst_data.src();
+ const field_src = inst_data.src();
+ const aggregate_ty = try sema.resolveType(block, ty_src, extra.container_type);
+ if (aggregate_ty.tag() == .var_args_param) return sema.addType(aggregate_ty);
const field_name = sema.code.nullTerminatedString(extra.name_start);
- const unresolved_ty = try sema.resolveType(block, src, extra.container_type);
- const resolved_ty = try sema.resolveTypeFields(block, src, unresolved_ty);
- switch (resolved_ty.zigTypeTag()) {
- .Struct => {
- const struct_obj = resolved_ty.castTag(.@"struct").?.data;
- const field = struct_obj.fields.get(field_name) orelse
- return sema.failWithBadStructFieldAccess(block, struct_obj, src, field_name);
- return sema.addType(field.ty);
- },
- .Union => {
- const union_obj = resolved_ty.cast(Type.Payload.Union).?.data;
- const field = union_obj.fields.get(field_name) orelse
- return sema.failWithBadUnionFieldAccess(block, union_obj, src, field_name);
- return sema.addType(field.ty);
- },
- else => return sema.fail(block, src, "expected struct or union; found '{}'", .{
- resolved_ty,
- }),
- }
+ return sema.fieldType(block, aggregate_ty, field_name, field_src, ty_src);
}
-fn zirErrorReturnTrace(
+fn fieldType(
sema: *Sema,
block: *Block,
- extended: Zir.Inst.Extended.InstData,
+ aggregate_ty: Type,
+ field_name: []const u8,
+ field_src: LazySrcLoc,
+ ty_src: LazySrcLoc,
) CompileError!Air.Inst.Ref {
- const src: LazySrcLoc = .{ .node_offset = @bitCast(i32, extended.operand) };
- return sema.fail(block, src, "TODO: Sema.zirErrorReturnTrace", .{});
+ const resolved_ty = try sema.resolveTypeFields(block, ty_src, aggregate_ty);
+ var cur_ty = resolved_ty;
+ while (true) {
+ switch (cur_ty.zigTypeTag()) {
+ .Struct => {
+ if (cur_ty.isAnonStruct()) {
+ const field_index = try sema.anonStructFieldIndex(block, cur_ty, field_name, field_src);
+ return sema.addType(cur_ty.tupleFields().types[field_index]);
+ }
+ const struct_obj = cur_ty.castTag(.@"struct").?.data;
+ const field = struct_obj.fields.get(field_name) orelse
+ return sema.failWithBadStructFieldAccess(block, struct_obj, field_src, field_name);
+ return sema.addType(field.ty);
+ },
+ .Union => {
+ const union_obj = cur_ty.cast(Type.Payload.Union).?.data;
+ const field = union_obj.fields.get(field_name) orelse
+ return sema.failWithBadUnionFieldAccess(block, union_obj, field_src, field_name);
+ return sema.addType(field.ty);
+ },
+ .Optional => {
+ if (cur_ty.castTag(.optional)) |some| {
+ // Struct/array init through optional requires the child type to not be a pointer.
+ // If the child of .optional is a pointer it'll error on the next loop.
+ cur_ty = some.data;
+ continue;
+ }
+ },
+ .ErrorUnion => {
+ cur_ty = cur_ty.errorUnionPayload();
+ continue;
+ },
+ else => {},
+ }
+ return sema.fail(block, ty_src, "expected struct or union; found '{}'", .{
+ resolved_ty.fmt(sema.mod),
+ });
+ }
}
-fn zirFrame(
+fn zirErrorReturnTrace(
sema: *Sema,
block: *Block,
extended: Zir.Inst.Extended.InstData,
) CompileError!Air.Inst.Ref {
- const src: LazySrcLoc = .{ .node_offset = @bitCast(i32, extended.operand) };
- return sema.fail(block, src, "TODO: Sema.zirFrame", .{});
+ const src = LazySrcLoc.nodeOffset(@bitCast(i32, extended.operand));
+ return sema.getErrorReturnTrace(block, src);
}
-fn zirFrameAddress(
+fn getErrorReturnTrace(sema: *Sema, block: *Block, src: LazySrcLoc) CompileError!Air.Inst.Ref {
+ const unresolved_stack_trace_ty = try sema.getBuiltinType(block, src, "StackTrace");
+ const stack_trace_ty = try sema.resolveTypeFields(block, src, unresolved_stack_trace_ty);
+ const opt_ptr_stack_trace_ty = try Type.Tag.optional_single_mut_pointer.create(sema.arena, stack_trace_ty);
+
+ // TODO implement this feature in all the backends and then delete this check.
+ const backend_supports_error_return_tracing =
+ sema.mod.comp.bin_file.options.use_llvm;
+
+ if (sema.owner_func != null and
+ sema.owner_func.?.calls_or_awaits_errorable_fn and
+ sema.mod.comp.bin_file.options.error_return_tracing and
+ backend_supports_error_return_tracing)
+ {
+ return block.addTy(.err_return_trace, opt_ptr_stack_trace_ty);
+ }
+ return sema.addConstant(opt_ptr_stack_trace_ty, Value.@"null");
+}
+
+fn zirFrame(
sema: *Sema,
block: *Block,
extended: Zir.Inst.Extended.InstData,
) CompileError!Air.Inst.Ref {
- const src: LazySrcLoc = .{ .node_offset = @bitCast(i32, extended.operand) };
- return sema.fail(block, src, "TODO: Sema.zirFrameAddress", .{});
+ const src = LazySrcLoc.nodeOffset(@bitCast(i32, extended.operand));
+ return sema.fail(block, src, "TODO: Sema.zirFrame", .{});
}
fn zirAlignOf(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
const inst_data = sema.code.instructions.items(.data)[inst].un_node;
const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
const ty = try sema.resolveType(block, operand_src, inst_data.operand);
- const resolved_ty = try sema.resolveTypeFields(block, operand_src, ty);
- try sema.resolveTypeLayout(block, operand_src, resolved_ty);
const target = sema.mod.getTarget();
- const abi_align = resolved_ty.abiAlignment(target);
- return sema.addIntUnsigned(Type.comptime_int, abi_align);
+ const val = try ty.lazyAbiAlignment(target, sema.arena);
+ if (val.tag() == .lazy_align) {
+ try sema.queueFullTypeResolution(ty);
+ }
+ return sema.addConstant(Type.comptime_int, val);
}
fn zirBoolToInt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
const inst_data = sema.code.instructions.items(.data)[inst].un_node;
const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
- const operand = sema.resolveInst(inst_data.operand);
+ const operand = try sema.resolveInst(inst_data.operand);
if (try sema.resolveMaybeUndefVal(block, operand_src, operand)) |val| {
if (val.isUndef()) return sema.addConstUndef(Type.initTag(.u1));
const bool_ints = [2]Air.Inst.Ref{ .zero, .one };
@@ -10959,7 +14461,7 @@ fn zirErrorName(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A
const inst_data = sema.code.instructions.items(.data)[inst].un_node;
const src = inst_data.src();
_ = src;
- const operand = sema.resolveInst(inst_data.operand);
+ const operand = try sema.resolveInst(inst_data.operand);
const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
if (try sema.resolveDefinedValue(block, operand_src, operand)) |val| {
@@ -10972,19 +14474,82 @@ fn zirErrorName(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A
return block.addUnOp(.error_name, operand);
}
-fn zirUnaryMath(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
+fn zirUnaryMath(
+ sema: *Sema,
+ block: *Block,
+ inst: Zir.Inst.Index,
+ air_tag: Air.Inst.Tag,
+ eval: fn (Value, Type, Allocator, std.Target) Allocator.Error!Value,
+) CompileError!Air.Inst.Ref {
+ const tracy = trace(@src());
+ defer tracy.end();
+
const inst_data = sema.code.instructions.items(.data)[inst].un_node;
- const src = inst_data.src();
- return sema.fail(block, src, "TODO: Sema.zirUnaryMath", .{});
+ const operand = try sema.resolveInst(inst_data.operand);
+ const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
+ const operand_ty = sema.typeOf(operand);
+ const target = sema.mod.getTarget();
+
+ switch (operand_ty.zigTypeTag()) {
+ .ComptimeFloat, .Float => {},
+ .Vector => {
+ const scalar_ty = operand_ty.scalarType();
+ switch (scalar_ty.zigTypeTag()) {
+ .ComptimeFloat, .Float => {},
+ else => return sema.fail(block, operand_src, "expected vector of floats or float type, found '{}'", .{scalar_ty.fmt(sema.mod)}),
+ }
+ },
+ else => return sema.fail(block, operand_src, "expected vector of floats or float type, found '{}'", .{operand_ty.fmt(sema.mod)}),
+ }
+
+ switch (operand_ty.zigTypeTag()) {
+ .Vector => {
+ const scalar_ty = operand_ty.scalarType();
+ const vec_len = operand_ty.vectorLen();
+ const result_ty = try Type.vector(sema.arena, vec_len, scalar_ty);
+ if (try sema.resolveMaybeUndefVal(block, operand_src, operand)) |val| {
+ if (val.isUndef())
+ return sema.addConstUndef(result_ty);
+
+ var elem_buf: Value.ElemValueBuffer = undefined;
+ const elems = try sema.arena.alloc(Value, vec_len);
+ for (elems) |*elem, i| {
+ const elem_val = val.elemValueBuffer(sema.mod, i, &elem_buf);
+ elem.* = try eval(elem_val, scalar_ty, sema.arena, target);
+ }
+ return sema.addConstant(
+ result_ty,
+ try Value.Tag.aggregate.create(sema.arena, elems),
+ );
+ }
+
+ try sema.requireRuntimeBlock(block, operand_src);
+ return block.addUnOp(air_tag, operand);
+ },
+ .ComptimeFloat, .Float => {
+ if (try sema.resolveMaybeUndefVal(block, operand_src, operand)) |operand_val| {
+ if (operand_val.isUndef())
+ return sema.addConstUndef(operand_ty);
+ const result_val = try eval(operand_val, operand_ty, sema.arena, target);
+ return sema.addConstant(operand_ty, result_val);
+ }
+
+ try sema.requireRuntimeBlock(block, operand_src);
+ return block.addUnOp(air_tag, operand);
+ },
+ else => unreachable,
+ }
}
fn zirTagName(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
const inst_data = sema.code.instructions.items(.data)[inst].un_node;
const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
const src = inst_data.src();
- const operand = sema.resolveInst(inst_data.operand);
+ const operand = try sema.resolveInst(inst_data.operand);
const operand_ty = sema.typeOf(operand);
+ const mod = sema.mod;
+ try sema.resolveTypeLayout(block, operand_src, operand_ty);
const enum_ty = switch (operand_ty.zigTypeTag()) {
.EnumLiteral => {
const val = try sema.resolveConstValue(block, operand_src, operand);
@@ -10993,34 +14558,36 @@ fn zirTagName(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air
},
.Enum => operand_ty,
.Union => operand_ty.unionTagType() orelse {
- const decl = operand_ty.getOwnerDecl();
+ const decl_index = operand_ty.getOwnerDecl();
+ const decl = mod.declPtr(decl_index);
const msg = msg: {
const msg = try sema.errMsg(block, src, "union '{s}' is untagged", .{
decl.name,
});
errdefer msg.destroy(sema.gpa);
- try sema.mod.errNoteNonLazy(decl.srcLoc(), msg, "declared here", .{});
+ try mod.errNoteNonLazy(decl.srcLoc(), msg, "declared here", .{});
break :msg msg;
};
- return sema.failWithOwnedErrorMsg(msg);
+ return sema.failWithOwnedErrorMsg(block, msg);
},
- else => return sema.fail(block, operand_src, "expected enum or union; found {}", .{
- operand_ty,
+ else => return sema.fail(block, operand_src, "expected enum or union; found '{}'", .{
+ operand_ty.fmt(mod),
}),
};
- const enum_decl = enum_ty.getOwnerDecl();
+ const enum_decl_index = enum_ty.getOwnerDecl();
const casted_operand = try sema.coerce(block, enum_ty, operand, operand_src);
if (try sema.resolveDefinedValue(block, operand_src, casted_operand)) |val| {
- const field_index = enum_ty.enumTagFieldIndex(val) orelse {
+ const field_index = enum_ty.enumTagFieldIndex(val, mod) orelse {
+ const enum_decl = mod.declPtr(enum_decl_index);
const msg = msg: {
- const msg = try sema.errMsg(block, src, "no field with value {} in enum '{s}'", .{
- casted_operand, enum_decl.name,
+ const msg = try sema.errMsg(block, src, "no field with value '{}' in enum '{s}'", .{
+ val.fmtValue(enum_ty, sema.mod), enum_decl.name,
});
errdefer msg.destroy(sema.gpa);
- try sema.mod.errNoteNonLazy(enum_decl.srcLoc(), msg, "declared here", .{});
+ try mod.errNoteNonLazy(enum_decl.srcLoc(), msg, "declared here", .{});
break :msg msg;
};
- return sema.failWithOwnedErrorMsg(msg);
+ return sema.failWithOwnedErrorMsg(block, msg);
};
const field_name = enum_ty.enumFieldName(field_index);
return sema.addStrLit(block, field_name);
@@ -11032,16 +14599,18 @@ fn zirTagName(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air
}
fn zirReify(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
+ const mod = sema.mod;
const inst_data = sema.code.instructions.items(.data)[inst].un_node;
const src = inst_data.src();
- const type_info_ty = try sema.resolveBuiltinTypeFields(block, src, "TypeInfo");
- const uncasted_operand = sema.resolveInst(inst_data.operand);
+ const type_info_ty = try sema.resolveBuiltinTypeFields(block, src, "Type");
+ const uncasted_operand = try sema.resolveInst(inst_data.operand);
const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
const type_info = try sema.coerce(block, type_info_ty, uncasted_operand, operand_src);
const val = try sema.resolveConstValue(block, operand_src, type_info);
const union_val = val.cast(Value.Payload.Union).?.data;
const tag_ty = type_info_ty.unionTagType().?;
- const tag_index = tag_ty.enumTagFieldIndex(union_val.tag).?;
+ const target = mod.getTarget();
+ const tag_index = tag_ty.enumTagFieldIndex(union_val.tag, mod).?;
switch (@intToEnum(std.builtin.TypeId, tag_index)) {
.Type => return Air.Inst.Ref.type_type,
.Void => return Air.Inst.Ref.void_type,
@@ -11054,13 +14623,13 @@ fn zirReify(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I
.AnyFrame => return Air.Inst.Ref.anyframe_type,
.EnumLiteral => return Air.Inst.Ref.enum_literal_type,
.Int => {
- const struct_val = union_val.val.castTag(.@"struct").?.data;
+ const struct_val = union_val.val.castTag(.aggregate).?.data;
// TODO use reflection instead of magic numbers here
const signedness_val = struct_val[0];
const bits_val = struct_val[1];
const signedness = signedness_val.toEnum(std.builtin.Signedness);
- const bits = @intCast(u16, bits_val.toUnsignedInt());
+ const bits = @intCast(u16, bits_val.toUnsignedInt(target));
const ty = switch (signedness) {
.signed => try Type.Tag.int_signed.create(sema.arena, bits),
.unsigned => try Type.Tag.int_unsigned.create(sema.arena, bits),
@@ -11068,21 +14637,39 @@ fn zirReify(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I
return sema.addType(ty);
},
.Vector => {
- const struct_val = union_val.val.castTag(.@"struct").?.data;
+ const struct_val = union_val.val.castTag(.aggregate).?.data;
// TODO use reflection instead of magic numbers here
const len_val = struct_val[0];
const child_val = struct_val[1];
- const len = len_val.toUnsignedInt();
+ const len = len_val.toUnsignedInt(target);
var buffer: Value.ToTypeBuffer = undefined;
const child_ty = child_val.toType(&buffer);
+ try sema.checkVectorElemType(block, src, child_ty);
+
const ty = try Type.vector(sema.arena, len, try child_ty.copy(sema.arena));
return sema.addType(ty);
},
- .Float => return sema.fail(block, src, "TODO: Sema.zirReify for Float", .{}),
+ .Float => {
+ const struct_val = union_val.val.castTag(.aggregate).?.data;
+ // TODO use reflection instead of magic numbers here
+ // bits: comptime_int,
+ const bits_val = struct_val[0];
+
+ const bits = @intCast(u16, bits_val.toUnsignedInt(target));
+ const ty = switch (bits) {
+ 16 => Type.@"f16",
+ 32 => Type.@"f32",
+ 64 => Type.@"f64",
+ 80 => Type.@"f80",
+ 128 => Type.@"f128",
+ else => return sema.fail(block, src, "{}-bit float unsupported", .{bits}),
+ };
+ return sema.addType(ty);
+ },
.Pointer => {
- const struct_val = union_val.val.castTag(.@"struct").?.data;
+ const struct_val = union_val.val.castTag(.aggregate).?.data;
// TODO use reflection instead of magic numbers here
const size_val = struct_val[0];
const is_const_val = struct_val[1];
@@ -11096,36 +14683,566 @@ fn zirReify(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I
var buffer: Value.ToTypeBuffer = undefined;
const child_ty = child_val.toType(&buffer);
+ const ptr_size = size_val.toEnum(std.builtin.Type.Pointer.Size);
+
+ var actual_sentinel: ?Value = null;
if (!sentinel_val.isNull()) {
- return sema.fail(block, src, "TODO: implement zirReify for pointer with non-null sentinel", .{});
+ if (ptr_size == .One or ptr_size == .C) {
+ return sema.fail(block, src, "sentinels are only allowed on slices and unknown-length pointers", .{});
+ }
+ const sentinel_ptr_val = sentinel_val.castTag(.opt_payload).?.data;
+ const ptr_ty = try Type.ptr(sema.arena, mod, .{
+ .@"addrspace" = .generic,
+ .pointee_type = child_ty,
+ });
+ actual_sentinel = (try sema.pointerDeref(block, src, sentinel_ptr_val, ptr_ty)).?;
}
- const ty = try Type.ptr(sema.arena, .{
- .size = size_val.toEnum(std.builtin.TypeInfo.Pointer.Size),
+ const ty = try Type.ptr(sema.arena, mod, .{
+ .size = ptr_size,
.mutable = !is_const_val.toBool(),
.@"volatile" = is_volatile_val.toBool(),
- .@"align" = @intCast(u8, alignment_val.toUnsignedInt()), // TODO: Validate this value.
+ .@"align" = @intCast(u29, alignment_val.toUnsignedInt(target)), // TODO: Validate this value.
.@"addrspace" = address_space_val.toEnum(std.builtin.AddressSpace),
.pointee_type = try child_ty.copy(sema.arena),
.@"allowzero" = is_allowzero_val.toBool(),
- .sentinel = null,
+ .sentinel = actual_sentinel,
+ });
+ return sema.addType(ty);
+ },
+ .Array => {
+ const struct_val = union_val.val.castTag(.aggregate).?.data;
+ // TODO use reflection instead of magic numbers here
+ // len: comptime_int,
+ const len_val = struct_val[0];
+ // child: type,
+ const child_val = struct_val[1];
+ // sentinel: ?*const anyopaque,
+ const sentinel_val = struct_val[2];
+
+ const len = len_val.toUnsignedInt(target);
+ var buffer: Value.ToTypeBuffer = undefined;
+ const child_ty = try child_val.toType(&buffer).copy(sema.arena);
+ const sentinel = if (sentinel_val.castTag(.opt_payload)) |p| blk: {
+ const ptr_ty = try Type.ptr(sema.arena, mod, .{
+ .@"addrspace" = .generic,
+ .pointee_type = child_ty,
+ });
+ break :blk (try sema.pointerDeref(block, src, p.data, ptr_ty)).?;
+ } else null;
+
+ const ty = try Type.array(sema.arena, len, sentinel, child_ty, sema.mod);
+ return sema.addType(ty);
+ },
+ .Optional => {
+ const struct_val = union_val.val.castTag(.aggregate).?.data;
+ // TODO use reflection instead of magic numbers here
+ // child: type,
+ const child_val = struct_val[0];
+
+ var buffer: Value.ToTypeBuffer = undefined;
+ const child_ty = try child_val.toType(&buffer).copy(sema.arena);
+
+ const ty = try Type.optional(sema.arena, child_ty);
+ return sema.addType(ty);
+ },
+ .ErrorUnion => {
+ const struct_val = union_val.val.castTag(.aggregate).?.data;
+ // TODO use reflection instead of magic numbers here
+ // error_set: type,
+ const error_set_val = struct_val[0];
+ // payload: type,
+ const payload_val = struct_val[1];
+
+ var buffer: Value.ToTypeBuffer = undefined;
+ const error_set_ty = try error_set_val.toType(&buffer).copy(sema.arena);
+ const payload_ty = try payload_val.toType(&buffer).copy(sema.arena);
+
+ const ty = try Type.Tag.error_union.create(sema.arena, .{
+ .error_set = error_set_ty,
+ .payload = payload_ty,
});
return sema.addType(ty);
},
- .Array => return sema.fail(block, src, "TODO: Sema.zirReify for Array", .{}),
- .Struct => return sema.fail(block, src, "TODO: Sema.zirReify for Struct", .{}),
- .Optional => return sema.fail(block, src, "TODO: Sema.zirReify for Optional", .{}),
- .ErrorUnion => return sema.fail(block, src, "TODO: Sema.zirReify for ErrorUnion", .{}),
- .ErrorSet => return sema.fail(block, src, "TODO: Sema.zirReify for ErrorSet", .{}),
- .Enum => return sema.fail(block, src, "TODO: Sema.zirReify for Enum", .{}),
- .Union => return sema.fail(block, src, "TODO: Sema.zirReify for Union", .{}),
+ .ErrorSet => {
+ const payload_val = union_val.val.optionalValue() orelse
+ return sema.addType(Type.initTag(.anyerror));
+ const slice_val = payload_val.castTag(.slice).?.data;
+ const decl_index = slice_val.ptr.pointerDecl().?;
+ try sema.ensureDeclAnalyzed(decl_index);
+ const decl = mod.declPtr(decl_index);
+ const array_val = decl.val.castTag(.aggregate).?.data;
+
+ var names: Module.ErrorSet.NameMap = .{};
+ try names.ensureUnusedCapacity(sema.arena, array_val.len);
+ for (array_val) |elem_val| {
+ const struct_val = elem_val.castTag(.aggregate).?.data;
+ // TODO use reflection instead of magic numbers here
+ // error_set: type,
+ const name_val = struct_val[0];
+ const name_str = try name_val.toAllocatedBytes(Type.initTag(.const_slice_u8), sema.arena, sema.mod);
+
+ const kv = try mod.getErrorValue(name_str);
+ names.putAssumeCapacityNoClobber(kv.key, {});
+ }
+
+ // names must be sorted
+ Module.ErrorSet.sortNames(&names);
+ const ty = try Type.Tag.error_set_merged.create(sema.arena, names);
+ return sema.addType(ty);
+ },
+ .Struct => {
+ // TODO use reflection instead of magic numbers here
+ const struct_val = union_val.val.castTag(.aggregate).?.data;
+ // layout: containerlayout,
+ const layout_val = struct_val[0];
+ // fields: []const enumfield,
+ const fields_val = struct_val[1];
+ // decls: []const declaration,
+ const decls_val = struct_val[2];
+ // is_tuple: bool,
+ const is_tuple_val = struct_val[3];
+
+ // Decls
+ if (decls_val.sliceLen(mod) > 0) {
+ return sema.fail(block, src, "reified structs must have no decls", .{});
+ }
+
+ return if (is_tuple_val.toBool())
+ try sema.reifyTuple(block, src, fields_val)
+ else
+ try sema.reifyStruct(block, inst, src, layout_val, fields_val);
+ },
+ .Enum => {
+ const struct_val = union_val.val.castTag(.aggregate).?.data;
+ // TODO use reflection instead of magic numbers here
+ // layout: ContainerLayout,
+ const layout_val = struct_val[0];
+ // tag_type: type,
+ const tag_type_val = struct_val[1];
+ // fields: []const EnumField,
+ const fields_val = struct_val[2];
+ // decls: []const Declaration,
+ const decls_val = struct_val[3];
+ // is_exhaustive: bool,
+ const is_exhaustive_val = struct_val[4];
+
+ // enum layout is always auto
+ const layout = layout_val.toEnum(std.builtin.Type.ContainerLayout);
+ if (layout != .Auto) {
+ return sema.fail(block, src, "reified enums must have a layout .Auto", .{});
+ }
+
+ // Decls
+ if (decls_val.sliceLen(mod) > 0) {
+ return sema.fail(block, src, "reified enums must have no decls", .{});
+ }
+
+ const gpa = sema.gpa;
+ var new_decl_arena = std.heap.ArenaAllocator.init(gpa);
+ errdefer new_decl_arena.deinit();
+ const new_decl_arena_allocator = new_decl_arena.allocator();
+
+ // Define our empty enum decl
+ const enum_obj = try new_decl_arena_allocator.create(Module.EnumFull);
+ const enum_ty_payload = try new_decl_arena_allocator.create(Type.Payload.EnumFull);
+ enum_ty_payload.* = .{
+ .base = .{
+ .tag = if (!is_exhaustive_val.toBool())
+ .enum_nonexhaustive
+ else
+ .enum_full,
+ },
+ .data = enum_obj,
+ };
+ const enum_ty = Type.initPayload(&enum_ty_payload.base);
+ const enum_val = try Value.Tag.ty.create(new_decl_arena_allocator, enum_ty);
+ const new_decl_index = try sema.createAnonymousDeclTypeNamed(block, .{
+ .ty = Type.type,
+ .val = enum_val,
+ }, .anon, "enum", null);
+ const new_decl = mod.declPtr(new_decl_index);
+ new_decl.owns_tv = true;
+ errdefer mod.abortAnonDecl(new_decl_index);
+
+ // Enum tag type
+ var buffer: Value.ToTypeBuffer = undefined;
+ const int_tag_ty = try tag_type_val.toType(&buffer).copy(new_decl_arena_allocator);
+
+ enum_obj.* = .{
+ .owner_decl = new_decl_index,
+ .tag_ty = int_tag_ty,
+ .tag_ty_inferred = false,
+ .fields = .{},
+ .values = .{},
+ .node_offset = src.node_offset.x,
+ .namespace = .{
+ .parent = block.namespace,
+ .ty = enum_ty,
+ .file_scope = block.getFileScope(),
+ },
+ };
+
+ // Fields
+ const fields_len = try sema.usizeCast(block, src, fields_val.sliceLen(mod));
+ if (fields_len > 0) {
+ try enum_obj.fields.ensureTotalCapacity(new_decl_arena_allocator, fields_len);
+ try enum_obj.values.ensureTotalCapacityContext(new_decl_arena_allocator, fields_len, .{
+ .ty = enum_obj.tag_ty,
+ .mod = mod,
+ });
+
+ var i: usize = 0;
+ while (i < fields_len) : (i += 1) {
+ const elem_val = try fields_val.elemValue(sema.mod, sema.arena, i);
+ const field_struct_val = elem_val.castTag(.aggregate).?.data;
+ // TODO use reflection instead of magic numbers here
+ // name: []const u8
+ const name_val = field_struct_val[0];
+ // value: comptime_int
+ const value_val = field_struct_val[1];
+
+ const field_name = try name_val.toAllocatedBytes(
+ Type.initTag(.const_slice_u8),
+ new_decl_arena_allocator,
+ sema.mod,
+ );
+
+ const gop = enum_obj.fields.getOrPutAssumeCapacity(field_name);
+ if (gop.found_existing) {
+ // TODO: better source location
+ return sema.fail(block, src, "duplicate enum tag {s}", .{field_name});
+ }
+
+ const copied_tag_val = try value_val.copy(new_decl_arena_allocator);
+ enum_obj.values.putAssumeCapacityNoClobberContext(copied_tag_val, {}, .{
+ .ty = enum_obj.tag_ty,
+ .mod = mod,
+ });
+ }
+ }
+
+ try new_decl.finalizeNewArena(&new_decl_arena);
+ return sema.analyzeDeclVal(block, src, new_decl_index);
+ },
+ .Opaque => {
+ const struct_val = union_val.val.castTag(.aggregate).?.data;
+ // decls: []const Declaration,
+ const decls_val = struct_val[0];
+
+ // Decls
+ if (decls_val.sliceLen(mod) > 0) {
+ return sema.fail(block, src, "reified opaque must have no decls", .{});
+ }
+
+ var new_decl_arena = std.heap.ArenaAllocator.init(sema.gpa);
+ errdefer new_decl_arena.deinit();
+ const new_decl_arena_allocator = new_decl_arena.allocator();
+
+ const opaque_obj = try new_decl_arena_allocator.create(Module.Opaque);
+ const opaque_ty_payload = try new_decl_arena_allocator.create(Type.Payload.Opaque);
+ opaque_ty_payload.* = .{
+ .base = .{ .tag = .@"opaque" },
+ .data = opaque_obj,
+ };
+ const opaque_ty = Type.initPayload(&opaque_ty_payload.base);
+ const opaque_val = try Value.Tag.ty.create(new_decl_arena_allocator, opaque_ty);
+ const new_decl_index = try sema.createAnonymousDeclTypeNamed(block, .{
+ .ty = Type.type,
+ .val = opaque_val,
+ }, .anon, "opaque", null);
+ const new_decl = mod.declPtr(new_decl_index);
+ new_decl.owns_tv = true;
+ errdefer mod.abortAnonDecl(new_decl_index);
+
+ opaque_obj.* = .{
+ .owner_decl = new_decl_index,
+ .node_offset = src.node_offset.x,
+ .namespace = .{
+ .parent = block.namespace,
+ .ty = opaque_ty,
+ .file_scope = block.getFileScope(),
+ },
+ };
+
+ try new_decl.finalizeNewArena(&new_decl_arena);
+ return sema.analyzeDeclVal(block, src, new_decl_index);
+ },
+ .Union => {
+ // TODO use reflection instead of magic numbers here
+ const struct_val = union_val.val.castTag(.aggregate).?.data;
+ // layout: containerlayout,
+ const layout_val = struct_val[0];
+ // tag_type: ?type,
+ const tag_type_val = struct_val[1];
+ // fields: []const enumfield,
+ const fields_val = struct_val[2];
+ // decls: []const declaration,
+ const decls_val = struct_val[3];
+
+ // Decls
+ if (decls_val.sliceLen(mod) > 0) {
+ return sema.fail(block, src, "reified unions must have no decls", .{});
+ }
+
+ var new_decl_arena = std.heap.ArenaAllocator.init(sema.gpa);
+ errdefer new_decl_arena.deinit();
+ const new_decl_arena_allocator = new_decl_arena.allocator();
+
+ const union_obj = try new_decl_arena_allocator.create(Module.Union);
+ const type_tag: Type.Tag = if (!tag_type_val.isNull()) .union_tagged else .@"union";
+ const union_payload = try new_decl_arena_allocator.create(Type.Payload.Union);
+ union_payload.* = .{
+ .base = .{ .tag = type_tag },
+ .data = union_obj,
+ };
+ const union_ty = Type.initPayload(&union_payload.base);
+ const new_union_val = try Value.Tag.ty.create(new_decl_arena_allocator, union_ty);
+ const new_decl_index = try sema.createAnonymousDeclTypeNamed(block, .{
+ .ty = Type.type,
+ .val = new_union_val,
+ }, .anon, "union", null);
+ const new_decl = mod.declPtr(new_decl_index);
+ new_decl.owns_tv = true;
+ errdefer mod.abortAnonDecl(new_decl_index);
+ union_obj.* = .{
+ .owner_decl = new_decl_index,
+ .tag_ty = Type.initTag(.@"null"),
+ .fields = .{},
+ .node_offset = src.node_offset.x,
+ .zir_index = inst,
+ .layout = layout_val.toEnum(std.builtin.Type.ContainerLayout),
+ .status = .have_field_types,
+ .namespace = .{
+ .parent = block.namespace,
+ .ty = union_ty,
+ .file_scope = block.getFileScope(),
+ },
+ };
+
+ // Tag type
+ const fields_len = try sema.usizeCast(block, src, fields_val.sliceLen(mod));
+ union_obj.tag_ty = if (tag_type_val.optionalValue()) |payload_val| blk: {
+ var buffer: Value.ToTypeBuffer = undefined;
+ break :blk try payload_val.toType(&buffer).copy(new_decl_arena_allocator);
+ } else try sema.generateUnionTagTypeSimple(block, fields_len, null);
+
+ // Fields
+ if (fields_len > 0) {
+ try union_obj.fields.ensureTotalCapacity(new_decl_arena_allocator, fields_len);
+
+ var i: usize = 0;
+ while (i < fields_len) : (i += 1) {
+ const elem_val = try fields_val.elemValue(sema.mod, sema.arena, i);
+ const field_struct_val = elem_val.castTag(.aggregate).?.data;
+ // TODO use reflection instead of magic numbers here
+ // name: []const u8
+ const name_val = field_struct_val[0];
+ // field_type: type,
+ const field_type_val = field_struct_val[1];
+ // alignment: comptime_int,
+ const alignment_val = field_struct_val[2];
+
+ const field_name = try name_val.toAllocatedBytes(
+ Type.initTag(.const_slice_u8),
+ new_decl_arena_allocator,
+ sema.mod,
+ );
+
+ const gop = union_obj.fields.getOrPutAssumeCapacity(field_name);
+ if (gop.found_existing) {
+ // TODO: better source location
+ return sema.fail(block, src, "duplicate union field {s}", .{field_name});
+ }
+
+ var buffer: Value.ToTypeBuffer = undefined;
+ gop.value_ptr.* = .{
+ .ty = try field_type_val.toType(&buffer).copy(new_decl_arena_allocator),
+ .abi_align = @intCast(u32, alignment_val.toUnsignedInt(target)),
+ };
+ }
+ }
+
+ try new_decl.finalizeNewArena(&new_decl_arena);
+ return sema.analyzeDeclVal(block, src, new_decl_index);
+ },
.Fn => return sema.fail(block, src, "TODO: Sema.zirReify for Fn", .{}),
.BoundFn => @panic("TODO delete BoundFn from the language"),
- .Opaque => return sema.fail(block, src, "TODO: Sema.zirReify for Opaque", .{}),
- .Frame => return sema.fail(block, src, "TODO: Sema.zirReify for Frame", .{}),
+ .Frame => @panic("TODO implement https://github.com/ziglang/zig/issues/10710"),
}
}
+fn reifyTuple(
+ sema: *Sema,
+ block: *Block,
+ src: LazySrcLoc,
+ fields_val: Value,
+) CompileError!Air.Inst.Ref {
+ const fields_len = try sema.usizeCast(block, src, fields_val.sliceLen(sema.mod));
+ if (fields_len == 0) return sema.addType(Type.initTag(.empty_struct_literal));
+
+ const types = try sema.arena.alloc(Type, fields_len);
+ const values = try sema.arena.alloc(Value, fields_len);
+
+ var used_fields: std.AutoArrayHashMapUnmanaged(u32, void) = .{};
+ defer used_fields.deinit(sema.gpa);
+ try used_fields.ensureTotalCapacity(sema.gpa, fields_len);
+
+ var i: usize = 0;
+ while (i < fields_len) : (i += 1) {
+ const elem_val = try fields_val.elemValue(sema.mod, sema.arena, i);
+ const field_struct_val = elem_val.castTag(.aggregate).?.data;
+ // TODO use reflection instead of magic numbers here
+ // name: []const u8
+ const name_val = field_struct_val[0];
+ // field_type: type,
+ const field_type_val = field_struct_val[1];
+ //default_value: ?*const anyopaque,
+ const default_value_val = field_struct_val[2];
+
+ const field_name = try name_val.toAllocatedBytes(
+ Type.initTag(.const_slice_u8),
+ sema.arena,
+ sema.mod,
+ );
+
+ const field_index = std.fmt.parseUnsigned(u32, field_name, 10) catch |err| {
+ return sema.fail(
+ block,
+ src,
+ "tuple cannot have non-numeric field '{s}': {}",
+ .{ field_name, err },
+ );
+ };
+
+ if (field_index >= fields_len) {
+ return sema.fail(
+ block,
+ src,
+ "tuple field {} exceeds tuple field count",
+ .{field_index},
+ );
+ }
+
+ const gop = used_fields.getOrPutAssumeCapacity(field_index);
+ if (gop.found_existing) {
+ // TODO: better source location
+ return sema.fail(block, src, "duplicate tuple field {}", .{field_index});
+ }
+
+ const default_val = if (default_value_val.optionalValue()) |opt_val| blk: {
+ const payload_val = if (opt_val.pointerDecl()) |opt_decl|
+ sema.mod.declPtr(opt_decl).val
+ else
+ opt_val;
+ break :blk try payload_val.copy(sema.arena);
+ } else Value.initTag(.unreachable_value);
+
+ var buffer: Value.ToTypeBuffer = undefined;
+ types[field_index] = try field_type_val.toType(&buffer).copy(sema.arena);
+ values[field_index] = default_val;
+ }
+
+ const ty = try Type.Tag.tuple.create(sema.arena, .{
+ .types = types,
+ .values = values,
+ });
+ return sema.addType(ty);
+}
+
+fn reifyStruct(
+ sema: *Sema,
+ block: *Block,
+ inst: Zir.Inst.Index,
+ src: LazySrcLoc,
+ layout_val: Value,
+ fields_val: Value,
+) CompileError!Air.Inst.Ref {
+ var new_decl_arena = std.heap.ArenaAllocator.init(sema.gpa);
+ errdefer new_decl_arena.deinit();
+ const new_decl_arena_allocator = new_decl_arena.allocator();
+
+ const struct_obj = try new_decl_arena_allocator.create(Module.Struct);
+ const struct_ty = try Type.Tag.@"struct".create(new_decl_arena_allocator, struct_obj);
+ const new_struct_val = try Value.Tag.ty.create(new_decl_arena_allocator, struct_ty);
+ const mod = sema.mod;
+ const new_decl_index = try sema.createAnonymousDeclTypeNamed(block, .{
+ .ty = Type.type,
+ .val = new_struct_val,
+ }, .anon, "struct", null);
+ const new_decl = mod.declPtr(new_decl_index);
+ new_decl.owns_tv = true;
+ errdefer mod.abortAnonDecl(new_decl_index);
+ struct_obj.* = .{
+ .owner_decl = new_decl_index,
+ .fields = .{},
+ .node_offset = src.node_offset.x,
+ .zir_index = inst,
+ .layout = layout_val.toEnum(std.builtin.Type.ContainerLayout),
+ .status = .have_field_types,
+ .known_non_opv = false,
+ .namespace = .{
+ .parent = block.namespace,
+ .ty = struct_ty,
+ .file_scope = block.getFileScope(),
+ },
+ };
+
+ const target = mod.getTarget();
+
+ // Fields
+ const fields_len = try sema.usizeCast(block, src, fields_val.sliceLen(mod));
+ try struct_obj.fields.ensureTotalCapacity(new_decl_arena_allocator, fields_len);
+ var i: usize = 0;
+ while (i < fields_len) : (i += 1) {
+ const elem_val = try fields_val.elemValue(sema.mod, sema.arena, i);
+ const field_struct_val = elem_val.castTag(.aggregate).?.data;
+ // TODO use reflection instead of magic numbers here
+ // name: []const u8
+ const name_val = field_struct_val[0];
+ // field_type: type,
+ const field_type_val = field_struct_val[1];
+ //default_value: ?*const anyopaque,
+ const default_value_val = field_struct_val[2];
+ // is_comptime: bool,
+ const is_comptime_val = field_struct_val[3];
+ // alignment: comptime_int,
+ const alignment_val = field_struct_val[4];
+
+ const field_name = try name_val.toAllocatedBytes(
+ Type.initTag(.const_slice_u8),
+ new_decl_arena_allocator,
+ mod,
+ );
+
+ const gop = struct_obj.fields.getOrPutAssumeCapacity(field_name);
+ if (gop.found_existing) {
+ // TODO: better source location
+ return sema.fail(block, src, "duplicate struct field {s}", .{field_name});
+ }
+
+ const default_val = if (default_value_val.optionalValue()) |opt_val| blk: {
+ const payload_val = if (opt_val.pointerDecl()) |opt_decl|
+ mod.declPtr(opt_decl).val
+ else
+ opt_val;
+ break :blk try payload_val.copy(new_decl_arena_allocator);
+ } else Value.initTag(.unreachable_value);
+
+ var buffer: Value.ToTypeBuffer = undefined;
+ gop.value_ptr.* = .{
+ .ty = try field_type_val.toType(&buffer).copy(new_decl_arena_allocator),
+ .abi_align = @intCast(u32, alignment_val.toUnsignedInt(target)),
+ .default_val = default_val,
+ .is_comptime = is_comptime_val.toBool(),
+ .offset = undefined,
+ };
+ }
+
+ try new_decl.finalizeNewArena(&new_decl_arena);
+ return sema.analyzeDeclVal(block, src, new_decl_index);
+}
+
fn zirTypeName(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
const inst_data = sema.code.instructions.items(.data)[inst].un_node;
const ty_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
@@ -11134,11 +15251,12 @@ fn zirTypeName(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
var anon_decl = try block.startAnonDecl(LazySrcLoc.unneeded);
defer anon_decl.deinit();
- const bytes = try ty.nameAlloc(anon_decl.arena());
+ const bytes = try ty.nameAllocArena(anon_decl.arena(), sema.mod);
const new_decl = try anon_decl.finish(
try Type.Tag.array_u8_sentinel_0.create(anon_decl.arena(), bytes.len),
try Value.Tag.bytes.create(anon_decl.arena(), bytes[0 .. bytes.len + 1]),
+ 0, // default alignment
);
return sema.analyzeDeclRef(new_decl);
@@ -11162,21 +15280,17 @@ fn zirFloatToInt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!
const ty_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node };
const dest_ty = try sema.resolveType(block, ty_src, extra.lhs);
- const operand = sema.resolveInst(extra.rhs);
+ const operand = try sema.resolveInst(extra.rhs);
const operand_ty = sema.typeOf(operand);
_ = try sema.checkIntType(block, ty_src, dest_ty);
try sema.checkFloatType(block, operand_src, operand_ty);
if (try sema.resolveMaybeUndefVal(block, operand_src, operand)) |val| {
- const target = sema.mod.getTarget();
- const result_val = val.floatToInt(sema.arena, dest_ty, target) catch |err| switch (err) {
- error.FloatCannotFit => {
- return sema.fail(block, operand_src, "integer value {d} cannot be stored in type '{}'", .{ std.math.floor(val.toFloat(f64)), dest_ty });
- },
- else => |e| return e,
- };
+ const result_val = try sema.floatToInt(block, operand_src, val, operand_ty, dest_ty);
return sema.addConstant(dest_ty, result_val);
+ } else if (dest_ty.zigTypeTag() == .ComptimeInt) {
+ return sema.failWithNeededComptime(block, operand_src);
}
try sema.requireRuntimeBlock(block, operand_src);
@@ -11189,7 +15303,7 @@ fn zirIntToFloat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!
const ty_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node };
const dest_ty = try sema.resolveType(block, ty_src, extra.lhs);
- const operand = sema.resolveInst(extra.rhs);
+ const operand = try sema.resolveInst(extra.rhs);
const operand_ty = sema.typeOf(operand);
try sema.checkFloatType(block, ty_src, dest_ty);
@@ -11197,8 +15311,10 @@ fn zirIntToFloat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!
if (try sema.resolveMaybeUndefVal(block, operand_src, operand)) |val| {
const target = sema.mod.getTarget();
- const result_val = try val.intToFloat(sema.arena, dest_ty, target);
+ const result_val = try val.intToFloat(sema.arena, operand_ty, dest_ty, target);
return sema.addConstant(dest_ty, result_val);
+ } else if (dest_ty.zigTypeTag() == .ComptimeFloat) {
+ return sema.failWithNeededComptime(block, operand_src);
}
try sema.requireRuntimeBlock(block, operand_src);
@@ -11212,20 +15328,22 @@ fn zirIntToPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node };
- const operand_res = sema.resolveInst(extra.rhs);
+ const operand_res = try sema.resolveInst(extra.rhs);
const operand_coerced = try sema.coerce(block, Type.usize, operand_res, operand_src);
const type_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
const type_res = try sema.resolveType(block, src, extra.lhs);
try sema.checkPtrType(block, type_src, type_res);
+ try sema.resolveTypeLayout(block, src, type_res.elemType2());
const ptr_align = type_res.ptrAlignment(sema.mod.getTarget());
+ const target = sema.mod.getTarget();
if (try sema.resolveDefinedValue(block, operand_src, operand_coerced)) |val| {
- const addr = val.toUnsignedInt();
+ const addr = val.toUnsignedInt(target);
if (!type_res.isAllowzeroPtr() and addr == 0)
- return sema.fail(block, operand_src, "pointer type '{}' does not allow address zero", .{type_res});
+ return sema.fail(block, operand_src, "pointer type '{}' does not allow address zero", .{type_res.fmt(sema.mod)});
if (addr != 0 and addr % ptr_align != 0)
- return sema.fail(block, operand_src, "pointer type '{}' requires aligned address", .{type_res});
+ return sema.fail(block, operand_src, "pointer type '{}' requires aligned address", .{type_res.fmt(sema.mod)});
const val_payload = try sema.arena.create(Value.Payload.U64);
val_payload.* = .{
@@ -11260,10 +15378,92 @@ fn zirIntToPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
return block.addBitCast(type_res, operand_coerced);
}
-fn zirErrSetCast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
- const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
- const src = inst_data.src();
- return sema.fail(block, src, "TODO: Sema.zirErrSetCast", .{});
+fn zirErrSetCast(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref {
+ const extra = sema.code.extraData(Zir.Inst.BinNode, extended.operand).data;
+ const src = LazySrcLoc.nodeOffset(extra.node);
+ const dest_ty_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = extra.node };
+ const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = extra.node };
+ const dest_ty = try sema.resolveType(block, dest_ty_src, extra.lhs);
+ const operand = try sema.resolveInst(extra.rhs);
+ const operand_ty = sema.typeOf(operand);
+ try sema.checkErrorSetType(block, dest_ty_src, dest_ty);
+ try sema.checkErrorSetType(block, operand_src, operand_ty);
+
+ // operand must be defined since it can be an invalid error value
+ const maybe_operand_val = try sema.resolveDefinedValue(block, operand_src, operand);
+
+ if (disjoint: {
+ // Try avoiding resolving inferred error sets if we can
+ if (!dest_ty.isAnyError() and dest_ty.errorSetNames().len == 0) break :disjoint true;
+ if (!operand_ty.isAnyError() and operand_ty.errorSetNames().len == 0) break :disjoint true;
+ if (dest_ty.isAnyError()) break :disjoint false;
+ if (operand_ty.isAnyError()) break :disjoint false;
+ for (dest_ty.errorSetNames()) |dest_err_name|
+ if (operand_ty.errorSetHasField(dest_err_name))
+ break :disjoint false;
+
+ if (dest_ty.tag() != .error_set_inferred and operand_ty.tag() != .error_set_inferred)
+ break :disjoint true;
+
+ try sema.resolveInferredErrorSetTy(block, dest_ty_src, dest_ty);
+ try sema.resolveInferredErrorSetTy(block, operand_src, operand_ty);
+ for (dest_ty.errorSetNames()) |dest_err_name|
+ if (operand_ty.errorSetHasField(dest_err_name))
+ break :disjoint false;
+
+ break :disjoint true;
+ }) {
+ const msg = msg: {
+ const msg = try sema.errMsg(
+ block,
+ src,
+ "error sets '{}' and '{}' have no common errors",
+ .{ operand_ty.fmt(sema.mod), dest_ty.fmt(sema.mod) },
+ );
+ errdefer msg.destroy(sema.gpa);
+ try sema.addDeclaredHereNote(msg, operand_ty);
+ try sema.addDeclaredHereNote(msg, dest_ty);
+ break :msg msg;
+ };
+ return sema.failWithOwnedErrorMsg(block, msg);
+ }
+
+ if (maybe_operand_val) |val| {
+ if (!dest_ty.isAnyError()) {
+ const error_name = val.castTag(.@"error").?.data.name;
+ if (!dest_ty.errorSetHasField(error_name)) {
+ const msg = msg: {
+ const msg = try sema.errMsg(
+ block,
+ src,
+ "'error.{s}' not a member of error set '{}'",
+ .{ error_name, dest_ty.fmt(sema.mod) },
+ );
+ errdefer msg.destroy(sema.gpa);
+ try sema.addDeclaredHereNote(msg, dest_ty);
+ break :msg msg;
+ };
+ return sema.failWithOwnedErrorMsg(block, msg);
+ }
+ }
+
+ return sema.addConstant(dest_ty, val);
+ }
+
+ try sema.requireRuntimeBlock(block, src);
+ if (block.wantSafety() and !dest_ty.isAnyError()) {
+ const err_int_inst = try block.addBitCast(Type.u16, operand);
+ // TODO: Output a switch instead of chained OR's.
+ var found_match: Air.Inst.Ref = undefined;
+ for (dest_ty.errorSetNames()) |dest_err_name, i| {
+ const dest_err_int = (try sema.mod.getErrorValue(dest_err_name)).value;
+ const dest_err_int_inst = try sema.addIntUnsigned(Type.u16, dest_err_int);
+ const next_match = try block.addBinOp(.cmp_eq, dest_err_int_inst, err_int_inst);
+ found_match = if (i == 0) next_match else try block.addBinOp(.bool_or, found_match, next_match);
+ }
+ try sema.addSafetyCheck(block, found_match, .invalid_error_code);
+ }
+ return block.addBitCast(dest_ty, operand);
}
fn zirPtrCast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
@@ -11272,11 +15472,57 @@ fn zirPtrCast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air
const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node };
const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
const dest_ty = try sema.resolveType(block, dest_ty_src, extra.lhs);
- const operand = sema.resolveInst(extra.rhs);
+ const operand = try sema.resolveInst(extra.rhs);
const operand_ty = sema.typeOf(operand);
+ const target = sema.mod.getTarget();
+
try sema.checkPtrType(block, dest_ty_src, dest_ty);
try sema.checkPtrOperand(block, operand_src, operand_ty);
- return sema.coerceCompatiblePtrs(block, dest_ty, operand, operand_src);
+
+ const dest_is_slice = dest_ty.isSlice();
+ const operand_is_slice = operand_ty.isSlice();
+ if (dest_is_slice and !operand_is_slice) {
+ return sema.fail(block, dest_ty_src, "illegal pointer cast to slice", .{});
+ }
+ const ptr = if (operand_is_slice and !dest_is_slice)
+ try sema.analyzeSlicePtr(block, operand_src, operand, operand_ty)
+ else
+ operand;
+
+ const dest_elem_ty = dest_ty.elemType2();
+ try sema.resolveTypeLayout(block, dest_ty_src, dest_elem_ty);
+ const dest_align = dest_ty.ptrAlignment(target);
+
+ const operand_elem_ty = operand_ty.elemType2();
+ try sema.resolveTypeLayout(block, operand_src, operand_elem_ty);
+ const operand_align = operand_ty.ptrAlignment(target);
+
+ // If the destination is less aligned than the source, preserve the source alignment
+ const aligned_dest_ty = if (operand_align <= dest_align) dest_ty else blk: {
+ // Unwrap the pointer (or pointer-like optional) type, set alignment, and re-wrap into result
+ if (dest_ty.zigTypeTag() == .Optional) {
+ var buf: Type.Payload.ElemType = undefined;
+ var dest_ptr_info = dest_ty.optionalChild(&buf).ptrInfo().data;
+ dest_ptr_info.@"align" = operand_align;
+ break :blk try Type.optional(sema.arena, try Type.ptr(sema.arena, sema.mod, dest_ptr_info));
+ } else {
+ var dest_ptr_info = dest_ty.ptrInfo().data;
+ dest_ptr_info.@"align" = operand_align;
+ break :blk try Type.ptr(sema.arena, sema.mod, dest_ptr_info);
+ }
+ };
+
+ if (dest_is_slice) {
+ const operand_elem_size = operand_elem_ty.abiSize(target);
+ const dest_elem_size = dest_elem_ty.abiSize(target);
+ if (operand_elem_size != dest_elem_size) {
+ // note that this is not implemented in stage1 so we should probably wait
+ // until that codebase is replaced before implementing this in stage2.
+ return sema.fail(block, dest_ty_src, "TODO: implement @ptrCast between slices changing the length", .{});
+ }
+ }
+
+ return sema.coerceCompatiblePtrs(block, aligned_dest_ty, ptr, operand_src);
}
fn zirTruncate(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
@@ -11285,58 +15531,78 @@ fn zirTruncate(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
const dest_ty_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node };
const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
- const dest_ty = try sema.resolveType(block, dest_ty_src, extra.lhs);
- const operand = sema.resolveInst(extra.rhs);
+ const dest_scalar_ty = try sema.resolveType(block, dest_ty_src, extra.lhs);
+ const operand = try sema.resolveInst(extra.rhs);
+ const dest_is_comptime_int = try sema.checkIntType(block, dest_ty_src, dest_scalar_ty);
const operand_ty = sema.typeOf(operand);
- const dest_is_comptime_int = try sema.checkIntType(block, dest_ty_src, dest_ty);
- const src_is_comptime_int = try sema.checkIntType(block, operand_src, operand_ty);
+ const operand_scalar_ty = try sema.checkIntOrVectorAllowComptime(block, operand_ty, operand_src);
+ const is_vector = operand_ty.zigTypeTag() == .Vector;
+ const dest_ty = if (is_vector)
+ try Type.vector(sema.arena, operand_ty.vectorLen(), dest_scalar_ty)
+ else
+ dest_scalar_ty;
if (dest_is_comptime_int) {
return sema.coerce(block, dest_ty, operand, operand_src);
}
const target = sema.mod.getTarget();
- const dest_info = dest_ty.intInfo(target);
+ const dest_info = dest_scalar_ty.intInfo(target);
- if (dest_info.bits == 0) {
- return sema.addConstant(dest_ty, Value.zero);
+ if (try sema.typeHasOnePossibleValue(block, dest_ty_src, dest_ty)) |val| {
+ return sema.addConstant(dest_ty, val);
}
- if (!src_is_comptime_int) {
- const src_info = operand_ty.intInfo(target);
- if (src_info.bits == 0) {
- return sema.addConstant(dest_ty, Value.zero);
+ if (operand_scalar_ty.zigTypeTag() != .ComptimeInt) {
+ const operand_info = operand_ty.intInfo(target);
+ if (try sema.typeHasOnePossibleValue(block, operand_src, operand_ty)) |val| {
+ return sema.addConstant(operand_ty, val);
}
- if (src_info.signedness != dest_info.signedness) {
+ if (operand_info.signedness != dest_info.signedness) {
return sema.fail(block, operand_src, "expected {s} integer type, found '{}'", .{
- @tagName(dest_info.signedness), operand_ty,
+ @tagName(dest_info.signedness), operand_ty.fmt(sema.mod),
});
}
- if (src_info.bits > 0 and src_info.bits < dest_info.bits) {
+ if (operand_info.bits < dest_info.bits) {
const msg = msg: {
const msg = try sema.errMsg(
block,
src,
"destination type '{}' has more bits than source type '{}'",
- .{ dest_ty, operand_ty },
+ .{ dest_ty.fmt(sema.mod), operand_ty.fmt(sema.mod) },
);
errdefer msg.destroy(sema.gpa);
try sema.errNote(block, dest_ty_src, msg, "destination type has {d} bits", .{
dest_info.bits,
});
- try sema.errNote(block, operand_src, msg, "source type has {d} bits", .{
- src_info.bits,
+ try sema.errNote(block, operand_src, msg, "operand type has {d} bits", .{
+ operand_info.bits,
});
break :msg msg;
};
- return sema.failWithOwnedErrorMsg(msg);
+ return sema.failWithOwnedErrorMsg(block, msg);
}
}
if (try sema.resolveMaybeUndefVal(block, operand_src, operand)) |val| {
if (val.isUndef()) return sema.addConstUndef(dest_ty);
- return sema.addConstant(dest_ty, try val.intTrunc(sema.arena, dest_info.signedness, dest_info.bits));
+ if (!is_vector) {
+ return sema.addConstant(
+ dest_ty,
+ try val.intTrunc(operand_ty, sema.arena, dest_info.signedness, dest_info.bits, target),
+ );
+ }
+ var elem_buf: Value.ElemValueBuffer = undefined;
+ const elems = try sema.arena.alloc(Value, operand_ty.vectorLen());
+ for (elems) |*elem, i| {
+ const elem_val = val.elemValueBuffer(sema.mod, i, &elem_buf);
+ elem.* = try elem_val.intTrunc(operand_scalar_ty, sema.arena, dest_info.signedness, dest_info.bits, target);
+ }
+ return sema.addConstant(
+ dest_ty,
+ try Value.Tag.aggregate.create(sema.arena, elems),
+ );
}
try sema.requireRuntimeBlock(block, src);
@@ -11349,7 +15615,7 @@ fn zirAlignCast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A
const align_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
const ptr_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node };
const dest_align = try sema.resolveAlign(block, align_src, extra.lhs);
- const ptr = sema.resolveInst(extra.rhs);
+ const ptr = try sema.resolveInst(extra.rhs);
const ptr_ty = sema.typeOf(ptr);
// TODO in addition to pointers, this instruction is supposed to work for
@@ -11362,7 +15628,7 @@ fn zirAlignCast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A
// TODO insert safety check that the alignment is correct
const ptr_info = ptr_ty.ptrInfo().data;
- const dest_ty = try Type.ptr(sema.arena, .{
+ const dest_ty = try Type.ptr(sema.arena, sema.mod, .{
.pointee_type = ptr_info.pointee_type,
.@"align" = dest_align,
.@"addrspace" = ptr_info.@"addrspace",
@@ -11374,7 +15640,7 @@ fn zirAlignCast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A
return sema.coerceCompatiblePtrs(block, dest_ty, ptr, ptr_src);
}
-fn zirClzCtz(
+fn zirBitCount(
sema: *Sema,
block: *Block,
inst: Zir.Inst.Index,
@@ -11383,20 +15649,14 @@ fn zirClzCtz(
) CompileError!Air.Inst.Ref {
const inst_data = sema.code.instructions.items(.data)[inst].un_node;
const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node };
- const operand = sema.resolveInst(inst_data.operand);
+ const operand = try sema.resolveInst(inst_data.operand);
const operand_ty = sema.typeOf(operand);
- try checkIntOrVector(sema, block, operand, operand_src);
+ _ = try checkIntOrVector(sema, block, operand, operand_src);
const target = sema.mod.getTarget();
const bits = operand_ty.intInfo(target).bits;
- if (bits == 0) {
- switch (operand_ty.zigTypeTag()) {
- .Vector => return sema.addConstant(
- try Type.vector(sema.arena, operand_ty.arrayLen(), Type.comptime_int),
- try Value.Tag.repeated.create(sema.arena, Value.zero),
- ),
- .Int => return Air.Inst.Ref.zero,
- else => unreachable,
- }
+
+ if (try sema.typeHasOnePossibleValue(block, operand_src, operand_ty)) |val| {
+ return sema.addConstant(operand_ty, val);
}
const result_scalar_ty = try Type.smallestUnsignedInt(sema.arena, bits);
@@ -11411,13 +15671,13 @@ fn zirClzCtz(
const elems = try sema.arena.alloc(Value, vec_len);
const scalar_ty = operand_ty.scalarType();
for (elems) |*elem, i| {
- const elem_val = val.elemValueBuffer(i, &elem_buf);
+ const elem_val = val.elemValueBuffer(sema.mod, i, &elem_buf);
const count = comptimeOp(elem_val, scalar_ty, target);
elem.* = try Value.Tag.int_u64.create(sema.arena, count);
}
return sema.addConstant(
result_ty,
- try Value.Tag.array.create(sema.arena, elems),
+ try Value.Tag.aggregate.create(sema.arena, elems),
);
} else {
try sema.requireRuntimeBlock(block, operand_src);
@@ -11437,44 +15697,110 @@ fn zirClzCtz(
}
}
-fn zirPopCount(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
+fn zirByteSwap(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
const inst_data = sema.code.instructions.items(.data)[inst].un_node;
const ty_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node };
- const operand = sema.resolveInst(inst_data.operand);
+ const operand = try sema.resolveInst(inst_data.operand);
const operand_ty = sema.typeOf(operand);
- // TODO implement support for vectors
- if (operand_ty.zigTypeTag() != .Int) {
- return sema.fail(block, ty_src, "expected integer type, found '{}'", .{
- operand_ty,
- });
- }
+ const scalar_ty = try sema.checkIntOrVectorAllowComptime(block, operand_ty, operand_src);
const target = sema.mod.getTarget();
- const bits = operand_ty.intInfo(target).bits;
- if (bits == 0) return Air.Inst.Ref.zero;
+ const bits = scalar_ty.intInfo(target).bits;
+ if (bits % 8 != 0) {
+ return sema.fail(
+ block,
+ ty_src,
+ "@byteSwap requires the number of bits to be evenly divisible by 8, but {} has {} bits",
+ .{ scalar_ty.fmt(sema.mod), bits },
+ );
+ }
- const result_ty = try Type.smallestUnsignedInt(sema.arena, bits);
+ if (try sema.typeHasOnePossibleValue(block, operand_src, operand_ty)) |val| {
+ return sema.addConstant(operand_ty, val);
+ }
- const runtime_src = if (try sema.resolveMaybeUndefVal(block, operand_src, operand)) |val| {
- if (val.isUndef()) return sema.addConstUndef(result_ty);
- const result_val = try val.popCount(operand_ty, target, sema.arena);
- return sema.addConstant(result_ty, result_val);
- } else operand_src;
+ switch (operand_ty.zigTypeTag()) {
+ .Int, .ComptimeInt => {
+ const runtime_src = if (try sema.resolveMaybeUndefVal(block, operand_src, operand)) |val| {
+ if (val.isUndef()) return sema.addConstUndef(operand_ty);
+ const result_val = try val.byteSwap(operand_ty, target, sema.arena);
+ return sema.addConstant(operand_ty, result_val);
+ } else operand_src;
+
+ try sema.requireRuntimeBlock(block, runtime_src);
+ return block.addTyOp(.byte_swap, operand_ty, operand);
+ },
+ .Vector => {
+ const runtime_src = if (try sema.resolveMaybeUndefVal(block, operand_src, operand)) |val| {
+ if (val.isUndef())
+ return sema.addConstUndef(operand_ty);
- try sema.requireRuntimeBlock(block, runtime_src);
- return block.addTyOp(.popcount, result_ty, operand);
-}
+ const vec_len = operand_ty.vectorLen();
+ var elem_buf: Value.ElemValueBuffer = undefined;
+ const elems = try sema.arena.alloc(Value, vec_len);
+ for (elems) |*elem, i| {
+ const elem_val = val.elemValueBuffer(sema.mod, i, &elem_buf);
+ elem.* = try elem_val.byteSwap(operand_ty, target, sema.arena);
+ }
+ return sema.addConstant(
+ operand_ty,
+ try Value.Tag.aggregate.create(sema.arena, elems),
+ );
+ } else operand_src;
-fn zirByteSwap(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
- const inst_data = sema.code.instructions.items(.data)[inst].un_node;
- const src = inst_data.src();
- return sema.fail(block, src, "TODO: Sema.zirByteSwap", .{});
+ try sema.requireRuntimeBlock(block, runtime_src);
+ return block.addTyOp(.byte_swap, operand_ty, operand);
+ },
+ else => unreachable,
+ }
}
fn zirBitReverse(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
const inst_data = sema.code.instructions.items(.data)[inst].un_node;
- const src = inst_data.src();
- return sema.fail(block, src, "TODO: Sema.zirBitReverse", .{});
+ const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node };
+ const operand = try sema.resolveInst(inst_data.operand);
+ const operand_ty = sema.typeOf(operand);
+ _ = try sema.checkIntOrVectorAllowComptime(block, operand_ty, operand_src);
+
+ if (try sema.typeHasOnePossibleValue(block, operand_src, operand_ty)) |val| {
+ return sema.addConstant(operand_ty, val);
+ }
+
+ const target = sema.mod.getTarget();
+ switch (operand_ty.zigTypeTag()) {
+ .Int, .ComptimeInt => {
+ const runtime_src = if (try sema.resolveMaybeUndefVal(block, operand_src, operand)) |val| {
+ if (val.isUndef()) return sema.addConstUndef(operand_ty);
+ const result_val = try val.bitReverse(operand_ty, target, sema.arena);
+ return sema.addConstant(operand_ty, result_val);
+ } else operand_src;
+
+ try sema.requireRuntimeBlock(block, runtime_src);
+ return block.addTyOp(.bit_reverse, operand_ty, operand);
+ },
+ .Vector => {
+ const runtime_src = if (try sema.resolveMaybeUndefVal(block, operand_src, operand)) |val| {
+ if (val.isUndef())
+ return sema.addConstUndef(operand_ty);
+
+ const vec_len = operand_ty.vectorLen();
+ var elem_buf: Value.ElemValueBuffer = undefined;
+ const elems = try sema.arena.alloc(Value, vec_len);
+ for (elems) |*elem, i| {
+ const elem_val = val.elemValueBuffer(sema.mod, i, &elem_buf);
+ elem.* = try elem_val.bitReverse(operand_ty, target, sema.arena);
+ }
+ return sema.addConstant(
+ operand_ty,
+ try Value.Tag.aggregate.create(sema.arena, elems),
+ );
+ } else operand_src;
+
+ try sema.requireRuntimeBlock(block, runtime_src);
+ return block.addTyOp(.bit_reverse, operand_ty, operand);
+ },
+ else => unreachable,
+ }
}
fn zirBitOffsetOf(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
@@ -11484,6 +15810,7 @@ fn zirBitOffsetOf(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
fn zirOffsetOf(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
const offset = try bitOffsetOf(sema, block, inst);
+ // TODO reminder to make this a compile error for packed structs
return sema.addIntUnsigned(Type.comptime_int, offset / 8);
}
@@ -11496,53 +15823,69 @@ fn bitOffsetOf(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!u6
const ty = try sema.resolveType(block, lhs_src, extra.lhs);
const field_name = try sema.resolveConstString(block, rhs_src, extra.rhs);
+ const target = sema.mod.getTarget();
try sema.resolveTypeLayout(block, lhs_src, ty);
if (ty.tag() != .@"struct") {
- return sema.fail(
- block,
- lhs_src,
- "expected struct type, found '{}'",
- .{ty},
- );
+ const msg = msg: {
+ const msg = try sema.errMsg(block, lhs_src, "expected struct type, found '{}'", .{ty.fmt(sema.mod)});
+ errdefer msg.destroy(sema.gpa);
+ try sema.addDeclaredHereNote(msg, ty);
+ break :msg msg;
+ };
+ return sema.failWithOwnedErrorMsg(block, msg);
}
- const index = ty.structFields().getIndex(field_name) orelse {
- return sema.fail(
- block,
- rhs_src,
- "struct '{}' has no field '{s}'",
- .{ ty, field_name },
- );
+ const fields = ty.structFields();
+ const index = fields.getIndex(field_name) orelse {
+ const msg = msg: {
+ const msg = try sema.errMsg(
+ block,
+ rhs_src,
+ "struct '{}' has no field '{s}'",
+ .{ ty.fmt(sema.mod), field_name },
+ );
+ errdefer msg.destroy(sema.gpa);
+ try sema.addDeclaredHereNote(msg, ty);
+ break :msg msg;
+ };
+ return sema.failWithOwnedErrorMsg(block, msg);
};
- const target = sema.mod.getTarget();
- const layout = ty.containerLayout();
- if (layout == .Packed) {
- var it = ty.iteratePackedStructOffsets(target);
- while (it.next()) |field_offset| {
- if (field_offset.field == index) {
- return (field_offset.offset * 8) + field_offset.running_bits;
- }
- }
- } else {
- var it = ty.iterateStructOffsets(target);
- while (it.next()) |field_offset| {
- if (field_offset.field == index) {
- return field_offset.offset * 8;
- }
- }
+ switch (ty.containerLayout()) {
+ .Packed => {
+ var bit_sum: u64 = 0;
+ for (fields.values()) |field, i| {
+ if (i == index) {
+ return bit_sum;
+ }
+ bit_sum += field.ty.bitSize(target);
+ } else unreachable;
+ },
+ else => {
+ var it = ty.iterateStructOffsets(target);
+ while (it.next()) |field_offset| {
+ if (field_offset.field == index) {
+ return field_offset.offset * 8;
+ }
+ } else unreachable;
+ },
}
+}
- unreachable;
+fn checkNamespaceType(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) CompileError!void {
+ switch (ty.zigTypeTag()) {
+ .Struct, .Enum, .Union, .Opaque => return,
+ else => return sema.fail(block, src, "expected struct, enum, union, or opaque; found '{}'", .{ty.fmt(sema.mod)}),
+ }
}
/// Returns `true` if the type was a comptime_int.
fn checkIntType(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) CompileError!bool {
- switch (ty.zigTypeTag()) {
+ switch (try ty.zigTypeTagOrPoison()) {
.ComptimeInt => return true,
.Int => return false,
- else => return sema.fail(block, src, "expected integer type, found '{}'", .{ty}),
+ else => return sema.fail(block, src, "expected integer type, found '{}'", .{ty.fmt(sema.mod)}),
}
}
@@ -11553,14 +15896,14 @@ fn checkPtrOperand(
ty: Type,
) CompileError!void {
switch (ty.zigTypeTag()) {
- .Pointer => {},
+ .Pointer => return,
.Fn => {
const msg = msg: {
const msg = try sema.errMsg(
block,
ty_src,
- "expected pointer, found {}",
- .{ty},
+ "expected pointer, found '{}'",
+ .{ty.fmt(sema.mod)},
);
errdefer msg.destroy(sema.gpa);
@@ -11568,10 +15911,12 @@ fn checkPtrOperand(
break :msg msg;
};
- return sema.failWithOwnedErrorMsg(msg);
+ return sema.failWithOwnedErrorMsg(block, msg);
},
- else => return sema.fail(block, ty_src, "expected pointer, found '{}'", .{ty}),
+ .Optional => if (ty.isPtrLikeOptional()) return,
+ else => {},
}
+ return sema.fail(block, ty_src, "expected pointer type, found '{}'", .{ty.fmt(sema.mod)});
}
fn checkPtrType(
@@ -11581,14 +15926,14 @@ fn checkPtrType(
ty: Type,
) CompileError!void {
switch (ty.zigTypeTag()) {
- .Pointer => {},
+ .Pointer => return,
.Fn => {
const msg = msg: {
const msg = try sema.errMsg(
block,
ty_src,
"expected pointer type, found '{}'",
- .{ty},
+ .{ty.fmt(sema.mod)},
);
errdefer msg.destroy(sema.gpa);
@@ -11596,10 +15941,12 @@ fn checkPtrType(
break :msg msg;
};
- return sema.failWithOwnedErrorMsg(msg);
+ return sema.failWithOwnedErrorMsg(block, msg);
},
- else => return sema.fail(block, ty_src, "expected pointer type, found '{}'", .{ty}),
+ .Optional => if (ty.isPtrLikeOptional()) return,
+ else => {},
}
+ return sema.fail(block, ty_src, "expected pointer type, found '{}'", .{ty.fmt(sema.mod)});
}
fn checkVectorElemType(
@@ -11612,7 +15959,7 @@ fn checkVectorElemType(
.Int, .Float, .Bool => return,
else => if (ty.isPtrAtRuntime()) return,
}
- return sema.fail(block, ty_src, "expected integer, float, bool, or pointer for the vector element type; found '{}'", .{ty});
+ return sema.fail(block, ty_src, "expected integer, float, bool, or pointer for the vector element type; found '{}'", .{ty.fmt(sema.mod)});
}
fn checkFloatType(
@@ -11622,8 +15969,8 @@ fn checkFloatType(
ty: Type,
) CompileError!void {
switch (ty.zigTypeTag()) {
- .ComptimeFloat, .Float => {},
- else => return sema.fail(block, ty_src, "expected float type, found '{}'", .{ty}),
+ .ComptimeInt, .ComptimeFloat, .Float => {},
+ else => return sema.fail(block, ty_src, "expected float type, found '{}'", .{ty.fmt(sema.mod)}),
}
}
@@ -11639,55 +15986,68 @@ fn checkNumericType(
.ComptimeFloat, .Float, .ComptimeInt, .Int => {},
else => |t| return sema.fail(block, ty_src, "expected number, found '{}'", .{t}),
},
- else => return sema.fail(block, ty_src, "expected number, found '{}'", .{ty}),
+ else => return sema.fail(block, ty_src, "expected number, found '{}'", .{ty.fmt(sema.mod)}),
}
}
-fn checkAtomicOperandType(
+/// Returns the casted pointer.
+fn checkAtomicPtrOperand(
sema: *Sema,
block: *Block,
- ty_src: LazySrcLoc,
- ty: Type,
-) CompileError!void {
- var buffer: Type.Payload.Bits = undefined;
+ elem_ty: Type,
+ elem_ty_src: LazySrcLoc,
+ ptr: Air.Inst.Ref,
+ ptr_src: LazySrcLoc,
+ ptr_const: bool,
+) CompileError!Air.Inst.Ref {
const target = sema.mod.getTarget();
- const max_atomic_bits = target_util.largestAtomicBits(target);
- const int_ty = switch (ty.zigTypeTag()) {
- .Int => ty,
- .Enum => ty.intTagType(&buffer),
- .Float => {
- const bit_count = ty.floatBits(target);
- if (bit_count > max_atomic_bits) {
- return sema.fail(
- block,
- ty_src,
- "expected {d}-bit float type or smaller; found {d}-bit float type",
- .{ max_atomic_bits, bit_count },
- );
- }
- return;
- },
- .Bool => return, // Will be treated as `u8`.
- else => {
- if (ty.isPtrAtRuntime()) return;
+ var diag: target_util.AtomicPtrAlignmentDiagnostics = .{};
+ const alignment = target_util.atomicPtrAlignment(target, elem_ty, &diag) catch |err| switch (err) {
+ error.FloatTooBig => return sema.fail(
+ block,
+ elem_ty_src,
+ "expected {d}-bit float type or smaller; found {d}-bit float type",
+ .{ diag.max_bits, diag.bits },
+ ),
+ error.IntTooBig => return sema.fail(
+ block,
+ elem_ty_src,
+ "expected {d}-bit integer type or smaller; found {d}-bit integer type",
+ .{ diag.max_bits, diag.bits },
+ ),
+ error.BadType => return sema.fail(
+ block,
+ elem_ty_src,
+ "expected bool, integer, float, enum, or pointer type; found '{}'",
+ .{elem_ty.fmt(sema.mod)},
+ ),
+ };
- return sema.fail(
- block,
- ty_src,
- "expected bool, integer, float, enum, or pointer type; found {}",
- .{ty},
- );
+ var wanted_ptr_data: Type.Payload.Pointer.Data = .{
+ .pointee_type = elem_ty,
+ .@"align" = alignment,
+ .@"addrspace" = .generic,
+ .mutable = !ptr_const,
+ };
+
+ const ptr_ty = sema.typeOf(ptr);
+ const ptr_data = switch (try ptr_ty.zigTypeTagOrPoison()) {
+ .Pointer => ptr_ty.ptrInfo().data,
+ else => {
+ const wanted_ptr_ty = try Type.ptr(sema.arena, sema.mod, wanted_ptr_data);
+ _ = try sema.coerce(block, wanted_ptr_ty, ptr, ptr_src);
+ unreachable;
},
};
- const bit_count = int_ty.intInfo(target).bits;
- if (bit_count > max_atomic_bits) {
- return sema.fail(
- block,
- ty_src,
- "expected {d}-bit integer type or smaller; found {d}-bit integer type",
- .{ max_atomic_bits, bit_count },
- );
- }
+
+ wanted_ptr_data.@"addrspace" = ptr_data.@"addrspace";
+ wanted_ptr_data.@"allowzero" = ptr_data.@"allowzero";
+ wanted_ptr_data.@"volatile" = ptr_data.@"volatile";
+
+ const wanted_ptr_ty = try Type.ptr(sema.arena, sema.mod, wanted_ptr_data);
+ const casted_ptr = try sema.coerce(block, wanted_ptr_ty, ptr, ptr_src);
+
+ return casted_ptr;
}
fn checkPtrIsNotComptimeMutable(
@@ -11709,7 +16069,7 @@ fn checkComptimeVarStore(
src: LazySrcLoc,
decl_ref_mut: Value.Payload.DeclRefMut.Data,
) CompileError!void {
- if (decl_ref_mut.runtime_index < block.runtime_index) {
+ if (@enumToInt(decl_ref_mut.runtime_index) < @enumToInt(block.runtime_index)) {
if (block.runtime_cond) |cond_src| {
const msg = msg: {
const msg = try sema.errMsg(block, src, "store to comptime variable depends on runtime condition", .{});
@@ -11717,7 +16077,7 @@ fn checkComptimeVarStore(
try sema.errNote(block, cond_src, msg, "runtime condition here", .{});
break :msg msg;
};
- return sema.failWithOwnedErrorMsg(msg);
+ return sema.failWithOwnedErrorMsg(block, msg);
}
if (block.runtime_loop) |loop_src| {
const msg = msg: {
@@ -11726,7 +16086,7 @@ fn checkComptimeVarStore(
try sema.errNote(block, loop_src, msg, "non-inline loop here", .{});
break :msg msg;
};
- return sema.failWithOwnedErrorMsg(msg);
+ return sema.failWithOwnedErrorMsg(block, msg);
}
unreachable;
}
@@ -11737,17 +16097,55 @@ fn checkIntOrVector(
block: *Block,
operand: Air.Inst.Ref,
operand_src: LazySrcLoc,
-) CompileError!void {
+) CompileError!Type {
const operand_ty = sema.typeOf(operand);
- const operand_zig_ty_tag = try operand_ty.zigTypeTagOrPoison();
- switch (operand_zig_ty_tag) {
- .Vector, .Int => return,
+ switch (try operand_ty.zigTypeTagOrPoison()) {
+ .Int => return operand_ty,
+ .Vector => {
+ const elem_ty = operand_ty.childType();
+ switch (try elem_ty.zigTypeTagOrPoison()) {
+ .Int => return elem_ty,
+ else => return sema.fail(block, operand_src, "expected vector of integers; found vector of '{}'", .{
+ elem_ty.fmt(sema.mod),
+ }),
+ }
+ },
+ else => return sema.fail(block, operand_src, "expected integer or vector, found '{}'", .{
+ operand_ty.fmt(sema.mod),
+ }),
+ }
+}
+
+fn checkIntOrVectorAllowComptime(
+ sema: *Sema,
+ block: *Block,
+ operand_ty: Type,
+ operand_src: LazySrcLoc,
+) CompileError!Type {
+ switch (try operand_ty.zigTypeTagOrPoison()) {
+ .Int, .ComptimeInt => return operand_ty,
+ .Vector => {
+ const elem_ty = operand_ty.childType();
+ switch (try elem_ty.zigTypeTagOrPoison()) {
+ .Int, .ComptimeInt => return elem_ty,
+ else => return sema.fail(block, operand_src, "expected vector of integers; found vector of '{}'", .{
+ elem_ty.fmt(sema.mod),
+ }),
+ }
+ },
else => return sema.fail(block, operand_src, "expected integer or vector, found '{}'", .{
- operand_ty,
+ operand_ty.fmt(sema.mod),
}),
}
}
+fn checkErrorSetType(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) CompileError!void {
+ switch (ty.zigTypeTag()) {
+ .ErrorSet => return,
+ else => return sema.fail(block, src, "expected error set type, found '{}'", .{ty.fmt(sema.mod)}),
+ }
+}
+
const SimdBinOp = struct {
len: ?usize,
/// Coerced to `result_ty`.
@@ -11772,11 +16170,49 @@ fn checkSimdBinOp(
) CompileError!SimdBinOp {
const lhs_ty = sema.typeOf(uncasted_lhs);
const rhs_ty = sema.typeOf(uncasted_rhs);
+
+ try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src);
+ var vec_len: ?usize = if (lhs_ty.zigTypeTag() == .Vector) lhs_ty.vectorLen() else null;
+ const result_ty = try sema.resolvePeerTypes(block, src, &.{ uncasted_lhs, uncasted_rhs }, .{
+ .override = &[_]LazySrcLoc{ lhs_src, rhs_src },
+ });
+ const lhs = try sema.coerce(block, result_ty, uncasted_lhs, lhs_src);
+ const rhs = try sema.coerce(block, result_ty, uncasted_rhs, rhs_src);
+
+ return SimdBinOp{
+ .len = vec_len,
+ .lhs = lhs,
+ .rhs = rhs,
+ .lhs_val = try sema.resolveMaybeUndefVal(block, lhs_src, lhs),
+ .rhs_val = try sema.resolveMaybeUndefVal(block, rhs_src, rhs),
+ .result_ty = result_ty,
+ .scalar_ty = result_ty.scalarType(),
+ };
+}
+
+fn checkVectorizableBinaryOperands(
+ sema: *Sema,
+ block: *Block,
+ src: LazySrcLoc,
+ lhs_ty: Type,
+ rhs_ty: Type,
+ lhs_src: LazySrcLoc,
+ rhs_src: LazySrcLoc,
+) CompileError!void {
const lhs_zig_ty_tag = try lhs_ty.zigTypeTagOrPoison();
const rhs_zig_ty_tag = try rhs_ty.zigTypeTagOrPoison();
+ if (lhs_zig_ty_tag != .Vector and rhs_zig_ty_tag != .Vector) return;
+
+ const lhs_is_vector = switch (lhs_zig_ty_tag) {
+ .Vector, .Array => true,
+ else => false,
+ };
+ const rhs_is_vector = switch (rhs_zig_ty_tag) {
+ .Vector, .Array => true,
+ else => false,
+ };
- var vec_len: ?usize = null;
- if (lhs_zig_ty_tag == .Vector and rhs_zig_ty_tag == .Vector) {
+ if (lhs_is_vector and rhs_is_vector) {
const lhs_len = lhs_ty.arrayLen();
const rhs_len = rhs_ty.arrayLen();
if (lhs_len != rhs_len) {
@@ -11787,16 +16223,15 @@ fn checkSimdBinOp(
try sema.errNote(block, rhs_src, msg, "length {d} here", .{rhs_len});
break :msg msg;
};
- return sema.failWithOwnedErrorMsg(msg);
+ return sema.failWithOwnedErrorMsg(block, msg);
}
- vec_len = try sema.usizeCast(block, lhs_src, lhs_len);
- } else if (lhs_zig_ty_tag == .Vector or rhs_zig_ty_tag == .Vector) {
+ } else {
const msg = msg: {
- const msg = try sema.errMsg(block, src, "mixed scalar and vector operands: {} and {}", .{
- lhs_ty, rhs_ty,
+ const msg = try sema.errMsg(block, src, "mixed scalar and vector operands: '{}' and '{}'", .{
+ lhs_ty.fmt(sema.mod), rhs_ty.fmt(sema.mod),
});
errdefer msg.destroy(sema.gpa);
- if (lhs_zig_ty_tag == .Vector) {
+ if (lhs_is_vector) {
try sema.errNote(block, lhs_src, msg, "vector here", .{});
try sema.errNote(block, rhs_src, msg, "scalar here", .{});
} else {
@@ -11805,23 +16240,8 @@ fn checkSimdBinOp(
}
break :msg msg;
};
- return sema.failWithOwnedErrorMsg(msg);
+ return sema.failWithOwnedErrorMsg(block, msg);
}
- const result_ty = try sema.resolvePeerTypes(block, src, &.{ uncasted_lhs, uncasted_rhs }, .{
- .override = &[_]LazySrcLoc{ lhs_src, rhs_src },
- });
- const lhs = try sema.coerce(block, result_ty, uncasted_lhs, lhs_src);
- const rhs = try sema.coerce(block, result_ty, uncasted_rhs, rhs_src);
-
- return SimdBinOp{
- .len = vec_len,
- .lhs = lhs,
- .rhs = rhs,
- .lhs_val = try sema.resolveMaybeUndefVal(block, lhs_src, lhs),
- .rhs_val = try sema.resolveMaybeUndefVal(block, rhs_src, rhs),
- .result_ty = result_ty,
- .scalar_ty = result_ty.scalarType(),
- };
}
fn resolveExportOptions(
@@ -11831,36 +16251,68 @@ fn resolveExportOptions(
zir_ref: Zir.Inst.Ref,
) CompileError!std.builtin.ExportOptions {
const export_options_ty = try sema.getBuiltinType(block, src, "ExportOptions");
- const air_ref = sema.resolveInst(zir_ref);
- const coerced = try sema.coerce(block, export_options_ty, air_ref, src);
- const val = try sema.resolveConstValue(block, src, coerced);
- const fields = val.castTag(.@"struct").?.data;
- const struct_obj = export_options_ty.castTag(.@"struct").?.data;
- const name_index = struct_obj.fields.getIndex("name").?;
- const linkage_index = struct_obj.fields.getIndex("linkage").?;
- const section_index = struct_obj.fields.getIndex("section").?;
- if (!fields[section_index].isNull()) {
+ const air_ref = try sema.resolveInst(zir_ref);
+ const options = try sema.coerce(block, export_options_ty, air_ref, src);
+
+ const name_operand = try sema.fieldVal(block, src, options, "name", src);
+ const name_val = try sema.resolveConstValue(block, src, name_operand);
+ const name_ty = Type.initTag(.const_slice_u8);
+ const name = try name_val.toAllocatedBytes(name_ty, sema.arena, sema.mod);
+
+ const linkage_operand = try sema.fieldVal(block, src, options, "linkage", src);
+ const linkage_val = try sema.resolveConstValue(block, src, linkage_operand);
+ const linkage = linkage_val.toEnum(std.builtin.GlobalLinkage);
+
+ const section = try sema.fieldVal(block, src, options, "section", src);
+ const section_val = try sema.resolveConstValue(block, src, section);
+
+ const visibility_operand = try sema.fieldVal(block, src, options, "visibility", src);
+ const visibility_val = try sema.resolveConstValue(block, src, visibility_operand);
+ const visibility = visibility_val.toEnum(std.builtin.SymbolVisibility);
+
+ if (name.len < 1) {
+ return sema.fail(block, src, "exported symbol name cannot be empty", .{});
+ }
+
+ if (visibility != .default and linkage == .Internal) {
+ return sema.fail(block, src, "symbol '{s}' exported with internal linkage has non-default visibility {s}", .{
+ name, @tagName(visibility),
+ });
+ }
+
+ if (!section_val.isNull()) {
return sema.fail(block, src, "TODO: implement exporting with linksection", .{});
}
- const name_ty = Type.initTag(.const_slice_u8);
+
return std.builtin.ExportOptions{
- .name = try fields[name_index].toAllocatedBytes(name_ty, sema.arena),
- .linkage = fields[linkage_index].toEnum(std.builtin.GlobalLinkage),
+ .name = name,
+ .linkage = linkage,
.section = null, // TODO
+ .visibility = visibility,
};
}
+fn resolveBuiltinEnum(
+ sema: *Sema,
+ block: *Block,
+ src: LazySrcLoc,
+ zir_ref: Zir.Inst.Ref,
+ comptime name: []const u8,
+) CompileError!@field(std.builtin, name) {
+ const ty = try sema.getBuiltinType(block, src, name);
+ const air_ref = try sema.resolveInst(zir_ref);
+ const coerced = try sema.coerce(block, ty, air_ref, src);
+ const val = try sema.resolveConstValue(block, src, coerced);
+ return val.toEnum(@field(std.builtin, name));
+}
+
fn resolveAtomicOrder(
sema: *Sema,
block: *Block,
src: LazySrcLoc,
zir_ref: Zir.Inst.Ref,
) CompileError!std.builtin.AtomicOrder {
- const atomic_order_ty = try sema.getBuiltinType(block, src, "AtomicOrder");
- const air_ref = sema.resolveInst(zir_ref);
- const coerced = try sema.coerce(block, atomic_order_ty, air_ref, src);
- const val = try sema.resolveConstValue(block, src, coerced);
- return val.toEnum(std.builtin.AtomicOrder);
+ return resolveBuiltinEnum(sema, block, src, zir_ref, "AtomicOrder");
}
fn resolveAtomicRmwOp(
@@ -11869,11 +16321,7 @@ fn resolveAtomicRmwOp(
src: LazySrcLoc,
zir_ref: Zir.Inst.Ref,
) CompileError!std.builtin.AtomicRmwOp {
- const atomic_rmw_op_ty = try sema.getBuiltinType(block, src, "AtomicRmwOp");
- const air_ref = sema.resolveInst(zir_ref);
- const coerced = try sema.coerce(block, atomic_rmw_op_ty, air_ref, src);
- const val = try sema.resolveConstValue(block, src, coerced);
- return val.toEnum(std.builtin.AtomicRmwOp);
+ return resolveBuiltinEnum(sema, block, src, zir_ref, "AtomicRmwOp");
}
fn zirCmpxchg(
@@ -11893,20 +16341,19 @@ fn zirCmpxchg(
const success_order_src: LazySrcLoc = .{ .node_offset_builtin_call_arg4 = inst_data.src_node };
const failure_order_src: LazySrcLoc = .{ .node_offset_builtin_call_arg5 = inst_data.src_node };
// zig fmt: on
- const ptr = sema.resolveInst(extra.ptr);
- const ptr_ty = sema.typeOf(ptr);
- const elem_ty = ptr_ty.elemType();
- try sema.checkAtomicOperandType(block, elem_ty_src, elem_ty);
+ const expected_value = try sema.resolveInst(extra.expected_value);
+ const elem_ty = sema.typeOf(expected_value);
if (elem_ty.zigTypeTag() == .Float) {
return sema.fail(
block,
elem_ty_src,
"expected bool, integer, enum, or pointer type; found '{}'",
- .{elem_ty},
+ .{elem_ty.fmt(sema.mod)},
);
}
- const expected_value = try sema.coerce(block, elem_ty, sema.resolveInst(extra.expected_value), expected_src);
- const new_value = try sema.coerce(block, elem_ty, sema.resolveInst(extra.new_value), new_value_src);
+ const uncasted_ptr = try sema.resolveInst(extra.ptr);
+ const ptr = try sema.checkAtomicPtrOperand(block, elem_ty, elem_ty_src, uncasted_ptr, ptr_src, false);
+ const new_value = try sema.coerce(block, elem_ty, try sema.resolveInst(extra.new_value), new_value_src);
const success_order = try sema.resolveAtomicOrder(block, success_order_src, extra.success_order);
const failure_order = try sema.resolveAtomicOrder(block, failure_order_src, extra.failure_order);
@@ -11938,8 +16385,9 @@ fn zirCmpxchg(
// to become undef as well
return sema.addConstUndef(result_ty);
}
+ const ptr_ty = sema.typeOf(ptr);
const stored_val = (try sema.pointerDeref(block, ptr_src, ptr_val, ptr_ty)) orelse break :rs ptr_src;
- const result_val = if (stored_val.eql(expected_val, elem_ty)) blk: {
+ const result_val = if (stored_val.eql(expected_val, elem_ty, sema.mod)) blk: {
try sema.storePtr(block, src, ptr, new_value);
break :blk Value.@"null";
} else try Value.Tag.opt_payload.create(sema.arena, stored_val);
@@ -11973,7 +16421,7 @@ fn zirSplat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I
const len_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node };
const scalar_src: LazySrcLoc = .{ .node_offset_bin_rhs = inst_data.src_node };
const len = @intCast(u32, try sema.resolveInt(block, len_src, extra.lhs, Type.u32));
- const scalar = sema.resolveInst(extra.rhs);
+ const scalar = try sema.resolveInst(extra.rhs);
const scalar_ty = sema.typeOf(scalar);
try sema.checkVectorElemType(block, scalar_src, scalar_ty);
const vector_ty = try Type.Tag.vector.create(sema.arena, .{
@@ -11995,35 +16443,365 @@ fn zirSplat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I
fn zirReduce(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
- const src = inst_data.src();
- return sema.fail(block, src, "TODO: Sema.zirReduce", .{});
+ const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
+ const op_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
+ const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node };
+ const operation = try sema.resolveBuiltinEnum(block, op_src, extra.lhs, "ReduceOp");
+ const operand = try sema.resolveInst(extra.rhs);
+ const operand_ty = sema.typeOf(operand);
+ const target = sema.mod.getTarget();
+
+ if (operand_ty.zigTypeTag() != .Vector) {
+ return sema.fail(block, operand_src, "expected vector, found '{}'", .{operand_ty.fmt(sema.mod)});
+ }
+
+ const scalar_ty = operand_ty.childType();
+
+ // Type-check depending on operation.
+ switch (operation) {
+ .And, .Or, .Xor => switch (scalar_ty.zigTypeTag()) {
+ .Int, .Bool => {},
+ else => return sema.fail(block, operand_src, "@reduce operation '{s}' requires integer or boolean operand; found '{}'", .{
+ @tagName(operation), operand_ty.fmt(sema.mod),
+ }),
+ },
+ .Min, .Max, .Add, .Mul => switch (scalar_ty.zigTypeTag()) {
+ .Int, .Float => {},
+ else => return sema.fail(block, operand_src, "@reduce operation '{s}' requires integer or float operand; found '{}'", .{
+ @tagName(operation), operand_ty.fmt(sema.mod),
+ }),
+ },
+ }
+
+ const vec_len = operand_ty.vectorLen();
+ if (vec_len == 0) {
+ // TODO re-evaluate if we should introduce a "neutral value" for some operations,
+ // e.g. zero for add and one for mul.
+ return sema.fail(block, operand_src, "@reduce operation requires a vector with nonzero length", .{});
+ }
+
+ if (try sema.resolveMaybeUndefVal(block, operand_src, operand)) |operand_val| {
+ if (operand_val.isUndef()) return sema.addConstUndef(scalar_ty);
+
+ var accum: Value = try operand_val.elemValue(sema.mod, sema.arena, 0);
+ var elem_buf: Value.ElemValueBuffer = undefined;
+ var i: u32 = 1;
+ while (i < vec_len) : (i += 1) {
+ const elem_val = operand_val.elemValueBuffer(sema.mod, i, &elem_buf);
+ switch (operation) {
+ .And => accum = try accum.bitwiseAnd(elem_val, scalar_ty, sema.arena, target),
+ .Or => accum = try accum.bitwiseOr(elem_val, scalar_ty, sema.arena, target),
+ .Xor => accum = try accum.bitwiseXor(elem_val, scalar_ty, sema.arena, target),
+ .Min => accum = accum.numberMin(elem_val, target),
+ .Max => accum = accum.numberMax(elem_val, target),
+ .Add => accum = try sema.numberAddWrap(block, operand_src, accum, elem_val, scalar_ty),
+ .Mul => accum = try accum.numberMulWrap(elem_val, scalar_ty, sema.arena, target),
+ }
+ }
+ return sema.addConstant(scalar_ty, accum);
+ }
+
+ try sema.requireRuntimeBlock(block, operand_src);
+ return block.addInst(.{
+ .tag = .reduce,
+ .data = .{ .reduce = .{
+ .operand = operand,
+ .operation = operation,
+ } },
+ });
}
fn zirShuffle(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
- const src = inst_data.src();
- return sema.fail(block, src, "TODO: Sema.zirShuffle", .{});
+ const extra = sema.code.extraData(Zir.Inst.Shuffle, inst_data.payload_index).data;
+ const elem_ty_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
+ const mask_src: LazySrcLoc = .{ .node_offset_builtin_call_arg3 = inst_data.src_node };
+
+ const elem_ty = try sema.resolveType(block, elem_ty_src, extra.elem_type);
+ try sema.checkVectorElemType(block, elem_ty_src, elem_ty);
+ var a = try sema.resolveInst(extra.a);
+ var b = try sema.resolveInst(extra.b);
+ var mask = try sema.resolveInst(extra.mask);
+ var mask_ty = sema.typeOf(mask);
+
+ const mask_len = switch (sema.typeOf(mask).zigTypeTag()) {
+ .Array, .Vector => sema.typeOf(mask).arrayLen(),
+ else => return sema.fail(block, mask_src, "expected vector or array, found '{}'", .{sema.typeOf(mask).fmt(sema.mod)}),
+ };
+ mask_ty = try Type.Tag.vector.create(sema.arena, .{
+ .len = mask_len,
+ .elem_type = Type.@"i32",
+ });
+ mask = try sema.coerce(block, mask_ty, mask, mask_src);
+ const mask_val = try sema.resolveConstMaybeUndefVal(block, mask_src, mask);
+ return sema.analyzeShuffle(block, inst_data.src_node, elem_ty, a, b, mask_val, @intCast(u32, mask_len));
+}
+
+fn analyzeShuffle(
+ sema: *Sema,
+ block: *Block,
+ src_node: i32,
+ elem_ty: Type,
+ a_arg: Air.Inst.Ref,
+ b_arg: Air.Inst.Ref,
+ mask: Value,
+ mask_len: u32,
+) CompileError!Air.Inst.Ref {
+ const a_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = src_node };
+ const b_src: LazySrcLoc = .{ .node_offset_builtin_call_arg2 = src_node };
+ const mask_src: LazySrcLoc = .{ .node_offset_builtin_call_arg3 = src_node };
+ var a = a_arg;
+ var b = b_arg;
+
+ const res_ty = try Type.Tag.vector.create(sema.arena, .{
+ .len = mask_len,
+ .elem_type = elem_ty,
+ });
+
+ var maybe_a_len = switch (sema.typeOf(a).zigTypeTag()) {
+ .Array, .Vector => sema.typeOf(a).arrayLen(),
+ .Undefined => null,
+ else => return sema.fail(block, a_src, "expected vector or array with element type '{}', found '{}'", .{
+ elem_ty.fmt(sema.mod),
+ sema.typeOf(a).fmt(sema.mod),
+ }),
+ };
+ var maybe_b_len = switch (sema.typeOf(b).zigTypeTag()) {
+ .Array, .Vector => sema.typeOf(b).arrayLen(),
+ .Undefined => null,
+ else => return sema.fail(block, b_src, "expected vector or array with element type '{}', found '{}'", .{
+ elem_ty.fmt(sema.mod),
+ sema.typeOf(b).fmt(sema.mod),
+ }),
+ };
+ if (maybe_a_len == null and maybe_b_len == null) {
+ return sema.addConstUndef(res_ty);
+ }
+ const a_len = maybe_a_len orelse maybe_b_len.?;
+ const b_len = maybe_b_len orelse a_len;
+
+ const a_ty = try Type.Tag.vector.create(sema.arena, .{
+ .len = a_len,
+ .elem_type = elem_ty,
+ });
+ const b_ty = try Type.Tag.vector.create(sema.arena, .{
+ .len = b_len,
+ .elem_type = elem_ty,
+ });
+
+ if (maybe_a_len == null) a = try sema.addConstUndef(a_ty);
+ if (maybe_b_len == null) b = try sema.addConstUndef(b_ty);
+
+ const operand_info = [2]std.meta.Tuple(&.{ u64, LazySrcLoc, Type }){
+ .{ a_len, a_src, a_ty },
+ .{ b_len, b_src, b_ty },
+ };
+
+ var i: usize = 0;
+ while (i < mask_len) : (i += 1) {
+ var buf: Value.ElemValueBuffer = undefined;
+ const elem = mask.elemValueBuffer(sema.mod, i, &buf);
+ if (elem.isUndef()) continue;
+ const int = elem.toSignedInt();
+ var unsigned: u32 = undefined;
+ var chosen: u32 = undefined;
+ if (int >= 0) {
+ unsigned = @intCast(u32, int);
+ chosen = 0;
+ } else {
+ unsigned = @intCast(u32, ~int);
+ chosen = 1;
+ }
+ if (unsigned >= operand_info[chosen][0]) {
+ const msg = msg: {
+ const msg = try sema.errMsg(block, mask_src, "mask index '{d}' has out-of-bounds selection", .{i});
+ errdefer msg.destroy(sema.gpa);
+
+ try sema.errNote(block, operand_info[chosen][1], msg, "selected index '{d}' out of bounds of '{}'", .{
+ unsigned,
+ operand_info[chosen][2].fmt(sema.mod),
+ });
+
+ if (chosen == 0) {
+ try sema.errNote(block, b_src, msg, "selections from the second vector are specified with negative numbers", .{});
+ }
+
+ break :msg msg;
+ };
+ return sema.failWithOwnedErrorMsg(block, msg);
+ }
+ }
+
+ if (try sema.resolveMaybeUndefVal(block, a_src, a)) |a_val| {
+ if (try sema.resolveMaybeUndefVal(block, b_src, b)) |b_val| {
+ const values = try sema.arena.alloc(Value, mask_len);
+
+ i = 0;
+ while (i < mask_len) : (i += 1) {
+ var buf: Value.ElemValueBuffer = undefined;
+ const mask_elem_val = mask.elemValueBuffer(sema.mod, i, &buf);
+ if (mask_elem_val.isUndef()) {
+ values[i] = Value.undef;
+ continue;
+ }
+ const int = mask_elem_val.toSignedInt();
+ const unsigned = if (int >= 0) @intCast(u32, int) else @intCast(u32, ~int);
+ if (int >= 0) {
+ values[i] = try a_val.elemValue(sema.mod, sema.arena, unsigned);
+ } else {
+ values[i] = try b_val.elemValue(sema.mod, sema.arena, unsigned);
+ }
+ }
+ const res_val = try Value.Tag.aggregate.create(sema.arena, values);
+ return sema.addConstant(res_ty, res_val);
+ }
+ }
+
+ // All static analysis passed, and not comptime.
+ // For runtime codegen, vectors a and b must be the same length. Here we
+ // recursively @shuffle the smaller vector to append undefined elements
+ // to it up to the length of the longer vector. This recursion terminates
+ // in 1 call because these calls to analyzeShuffle guarantee a_len == b_len.
+ if (a_len != b_len) {
+ const min_len = std.math.min(a_len, b_len);
+ const max_src = if (a_len > b_len) a_src else b_src;
+ const max_len = try sema.usizeCast(block, max_src, std.math.max(a_len, b_len));
+
+ const expand_mask_values = try sema.arena.alloc(Value, max_len);
+ i = 0;
+ while (i < min_len) : (i += 1) {
+ expand_mask_values[i] = try Value.Tag.int_u64.create(sema.arena, i);
+ }
+ while (i < max_len) : (i += 1) {
+ expand_mask_values[i] = Value.negative_one;
+ }
+ const expand_mask = try Value.Tag.aggregate.create(sema.arena, expand_mask_values);
+
+ if (a_len < b_len) {
+ const undef = try sema.addConstUndef(a_ty);
+ a = try sema.analyzeShuffle(block, src_node, elem_ty, a, undef, expand_mask, @intCast(u32, max_len));
+ } else {
+ const undef = try sema.addConstUndef(b_ty);
+ b = try sema.analyzeShuffle(block, src_node, elem_ty, b, undef, expand_mask, @intCast(u32, max_len));
+ }
+ }
+
+ const mask_index = @intCast(u32, sema.air_values.items.len);
+ try sema.air_values.append(sema.gpa, mask);
+ return block.addInst(.{
+ .tag = .shuffle,
+ .data = .{ .ty_pl = .{
+ .ty = try sema.addType(res_ty),
+ .payload = try block.sema.addExtra(Air.Shuffle{
+ .a = a,
+ .b = b,
+ .mask = mask_index,
+ .mask_len = mask_len,
+ }),
+ } },
+ });
}
fn zirSelect(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
- const src = inst_data.src();
- return sema.fail(block, src, "TODO: Sema.zirSelect", .{});
+ const extra = sema.code.extraData(Zir.Inst.Select, inst_data.payload_index).data;
+
+ const elem_ty_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
+ const pred_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node };
+ const a_src: LazySrcLoc = .{ .node_offset_builtin_call_arg2 = inst_data.src_node };
+ const b_src: LazySrcLoc = .{ .node_offset_builtin_call_arg3 = inst_data.src_node };
+
+ const elem_ty = try sema.resolveType(block, elem_ty_src, extra.elem_type);
+ try sema.checkVectorElemType(block, elem_ty_src, elem_ty);
+ const pred_uncoerced = try sema.resolveInst(extra.pred);
+ const pred_ty = sema.typeOf(pred_uncoerced);
+
+ const vec_len_u64 = switch (try pred_ty.zigTypeTagOrPoison()) {
+ .Vector, .Array => pred_ty.arrayLen(),
+ else => return sema.fail(block, pred_src, "expected vector or array, found '{}'", .{pred_ty.fmt(sema.mod)}),
+ };
+ const vec_len = try sema.usizeCast(block, pred_src, vec_len_u64);
+
+ const bool_vec_ty = try Type.vector(sema.arena, vec_len, Type.bool);
+ const pred = try sema.coerce(block, bool_vec_ty, pred_uncoerced, pred_src);
+
+ const vec_ty = try Type.vector(sema.arena, vec_len, elem_ty);
+ const a = try sema.coerce(block, vec_ty, try sema.resolveInst(extra.a), a_src);
+ const b = try sema.coerce(block, vec_ty, try sema.resolveInst(extra.b), b_src);
+
+ const maybe_pred = try sema.resolveMaybeUndefVal(block, pred_src, pred);
+ const maybe_a = try sema.resolveMaybeUndefVal(block, a_src, a);
+ const maybe_b = try sema.resolveMaybeUndefVal(block, b_src, b);
+
+ const runtime_src = if (maybe_pred) |pred_val| rs: {
+ if (pred_val.isUndef()) return sema.addConstUndef(vec_ty);
+
+ if (maybe_a) |a_val| {
+ if (a_val.isUndef()) return sema.addConstUndef(vec_ty);
+
+ if (maybe_b) |b_val| {
+ if (b_val.isUndef()) return sema.addConstUndef(vec_ty);
+
+ var buf: Value.ElemValueBuffer = undefined;
+ const elems = try sema.gpa.alloc(Value, vec_len);
+ for (elems) |*elem, i| {
+ const pred_elem_val = pred_val.elemValueBuffer(sema.mod, i, &buf);
+ const should_choose_a = pred_elem_val.toBool();
+ if (should_choose_a) {
+ elem.* = a_val.elemValueBuffer(sema.mod, i, &buf);
+ } else {
+ elem.* = b_val.elemValueBuffer(sema.mod, i, &buf);
+ }
+ }
+
+ return sema.addConstant(
+ vec_ty,
+ try Value.Tag.aggregate.create(sema.arena, elems),
+ );
+ } else {
+ break :rs b_src;
+ }
+ } else {
+ if (maybe_b) |b_val| {
+ if (b_val.isUndef()) return sema.addConstUndef(vec_ty);
+ }
+ break :rs a_src;
+ }
+ } else rs: {
+ if (maybe_a) |a_val| {
+ if (a_val.isUndef()) return sema.addConstUndef(vec_ty);
+ }
+ if (maybe_b) |b_val| {
+ if (b_val.isUndef()) return sema.addConstUndef(vec_ty);
+ }
+ break :rs pred_src;
+ };
+
+ try sema.requireRuntimeBlock(block, runtime_src);
+ return block.addInst(.{
+ .tag = .select,
+ .data = .{ .pl_op = .{
+ .operand = pred,
+ .payload = try block.sema.addExtra(Air.Bin{
+ .lhs = a,
+ .rhs = b,
+ }),
+ } },
+ });
}
fn zirAtomicLoad(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
- const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
+ const extra = sema.code.extraData(Zir.Inst.AtomicLoad, inst_data.payload_index).data;
// zig fmt: off
const elem_ty_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
const ptr_src : LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node };
const order_src : LazySrcLoc = .{ .node_offset_builtin_call_arg2 = inst_data.src_node };
// zig fmt: on
- const ptr = sema.resolveInst(extra.lhs);
- const ptr_ty = sema.typeOf(ptr);
- const elem_ty = ptr_ty.elemType();
- try sema.checkAtomicOperandType(block, elem_ty_src, elem_ty);
- const order = try sema.resolveAtomicOrder(block, order_src, extra.rhs);
+ const elem_ty = try sema.resolveType(block, elem_ty_src, extra.elem_type);
+ const uncasted_ptr = try sema.resolveInst(extra.ptr);
+ const ptr = try sema.checkAtomicPtrOperand(block, elem_ty, elem_ty_src, uncasted_ptr, ptr_src, true);
+ const order = try sema.resolveAtomicOrder(block, order_src, extra.ordering);
switch (order) {
.Release, .AcqRel => {
@@ -12042,7 +16820,7 @@ fn zirAtomicLoad(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!
}
if (try sema.resolveDefinedValue(block, ptr_src, ptr)) |ptr_val| {
- if (try sema.pointerDeref(block, ptr_src, ptr_val, ptr_ty)) |elem_val| {
+ if (try sema.pointerDeref(block, ptr_src, ptr_val, sema.typeOf(ptr))) |elem_val| {
return sema.addConstant(elem_ty, elem_val);
}
}
@@ -12062,19 +16840,19 @@ fn zirAtomicRmw(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A
const extra = sema.code.extraData(Zir.Inst.AtomicRmw, inst_data.payload_index).data;
const src = inst_data.src();
// zig fmt: off
- const operand_ty_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
+ const elem_ty_src : LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
const ptr_src : LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node };
const op_src : LazySrcLoc = .{ .node_offset_builtin_call_arg2 = inst_data.src_node };
const operand_src : LazySrcLoc = .{ .node_offset_builtin_call_arg3 = inst_data.src_node };
const order_src : LazySrcLoc = .{ .node_offset_builtin_call_arg4 = inst_data.src_node };
// zig fmt: on
- const ptr = sema.resolveInst(extra.ptr);
- const ptr_ty = sema.typeOf(ptr);
- const operand_ty = ptr_ty.elemType();
- try sema.checkAtomicOperandType(block, operand_ty_src, operand_ty);
+ const operand = try sema.resolveInst(extra.operand);
+ const elem_ty = sema.typeOf(operand);
+ const uncasted_ptr = try sema.resolveInst(extra.ptr);
+ const ptr = try sema.checkAtomicPtrOperand(block, elem_ty, elem_ty_src, uncasted_ptr, ptr_src, false);
const op = try sema.resolveAtomicRmwOp(block, op_src, extra.operation);
- switch (operand_ty.zigTypeTag()) {
+ switch (elem_ty.zigTypeTag()) {
.Enum => if (op != .Xchg) {
return sema.fail(block, op_src, "@atomicRmw with enum only allowed with .Xchg", .{});
},
@@ -12087,7 +16865,6 @@ fn zirAtomicRmw(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A
},
else => {},
}
- const operand = try sema.coerce(block, operand_ty, sema.resolveInst(extra.operand), operand_src);
const order = try sema.resolveAtomicOrder(block, order_src, extra.ordering);
if (order == .Unordered) {
@@ -12095,8 +16872,8 @@ fn zirAtomicRmw(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A
}
// special case zero bit types
- if (try sema.typeHasOnePossibleValue(block, operand_ty_src, operand_ty)) |val| {
- return sema.addConstant(operand_ty, val);
+ if (try sema.typeHasOnePossibleValue(block, elem_ty_src, elem_ty)) |val| {
+ return sema.addConstant(elem_ty, val);
}
const runtime_src = if (try sema.resolveDefinedValue(block, ptr_src, ptr)) |ptr_val| rs: {
@@ -12107,22 +16884,23 @@ fn zirAtomicRmw(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A
};
if (ptr_val.isComptimeMutablePtr()) {
const target = sema.mod.getTarget();
+ const ptr_ty = sema.typeOf(ptr);
const stored_val = (try sema.pointerDeref(block, ptr_src, ptr_val, ptr_ty)) orelse break :rs ptr_src;
const new_val = switch (op) {
// zig fmt: off
.Xchg => operand_val,
- .Add => try stored_val.numberAddWrap(operand_val, operand_ty, sema.arena, target),
- .Sub => try stored_val.numberSubWrap(operand_val, operand_ty, sema.arena, target),
- .And => try stored_val.bitwiseAnd (operand_val, sema.arena),
- .Nand => try stored_val.bitwiseNand (operand_val, operand_ty, sema.arena, target),
- .Or => try stored_val.bitwiseOr (operand_val, sema.arena),
- .Xor => try stored_val.bitwiseXor (operand_val, sema.arena),
- .Max => try stored_val.numberMax (operand_val),
- .Min => try stored_val.numberMin (operand_val),
+ .Add => try sema.numberAddWrap(block, src, stored_val, operand_val, elem_ty),
+ .Sub => try sema.numberSubWrap(block, src, stored_val, operand_val, elem_ty),
+ .And => try stored_val.bitwiseAnd (operand_val, elem_ty, sema.arena, target),
+ .Nand => try stored_val.bitwiseNand (operand_val, elem_ty, sema.arena, target),
+ .Or => try stored_val.bitwiseOr (operand_val, elem_ty, sema.arena, target),
+ .Xor => try stored_val.bitwiseXor (operand_val, elem_ty, sema.arena, target),
+ .Max => stored_val.numberMax (operand_val, target),
+ .Min => stored_val.numberMin (operand_val, target),
// zig fmt: on
};
- try sema.storePtrVal(block, src, ptr_val, new_val, operand_ty);
- return sema.addConstant(operand_ty, stored_val);
+ try sema.storePtrVal(block, src, ptr_val, new_val, elem_ty);
+ return sema.addConstant(elem_ty, stored_val);
} else break :rs ptr_src;
} else ptr_src;
@@ -12146,15 +16924,15 @@ fn zirAtomicStore(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
const extra = sema.code.extraData(Zir.Inst.AtomicStore, inst_data.payload_index).data;
const src = inst_data.src();
// zig fmt: off
- const operand_ty_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
+ const elem_ty_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
const ptr_src : LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node };
const operand_src : LazySrcLoc = .{ .node_offset_builtin_call_arg2 = inst_data.src_node };
const order_src : LazySrcLoc = .{ .node_offset_builtin_call_arg3 = inst_data.src_node };
// zig fmt: on
- const ptr = sema.resolveInst(extra.ptr);
- const operand_ty = sema.typeOf(ptr).elemType();
- try sema.checkAtomicOperandType(block, operand_ty_src, operand_ty);
- const operand = try sema.coerce(block, operand_ty, sema.resolveInst(extra.operand), operand_src);
+ const operand = try sema.resolveInst(extra.operand);
+ const elem_ty = sema.typeOf(operand);
+ const uncasted_ptr = try sema.resolveInst(extra.ptr);
+ const ptr = try sema.checkAtomicPtrOperand(block, elem_ty, elem_ty_src, uncasted_ptr, ptr_src, false);
const order = try sema.resolveAtomicOrder(block, order_src, extra.ordering);
const air_tag: Air.Inst.Tag = switch (order) {
@@ -12177,43 +16955,282 @@ fn zirAtomicStore(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
fn zirMulAdd(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
+ const extra = sema.code.extraData(Zir.Inst.MulAdd, inst_data.payload_index).data;
const src = inst_data.src();
- return sema.fail(block, src, "TODO: Sema.zirMulAdd", .{});
+
+ const mulend1_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node };
+ const mulend2_src: LazySrcLoc = .{ .node_offset_builtin_call_arg2 = inst_data.src_node };
+ const addend_src: LazySrcLoc = .{ .node_offset_builtin_call_arg3 = inst_data.src_node };
+
+ const addend = try sema.resolveInst(extra.addend);
+ const ty = sema.typeOf(addend);
+ const mulend1 = try sema.coerce(block, ty, try sema.resolveInst(extra.mulend1), mulend1_src);
+ const mulend2 = try sema.coerce(block, ty, try sema.resolveInst(extra.mulend2), mulend2_src);
+
+ const target = sema.mod.getTarget();
+
+ const maybe_mulend1 = try sema.resolveMaybeUndefVal(block, mulend1_src, mulend1);
+ const maybe_mulend2 = try sema.resolveMaybeUndefVal(block, mulend2_src, mulend2);
+ const maybe_addend = try sema.resolveMaybeUndefVal(block, addend_src, addend);
+
+ switch (ty.zigTypeTag()) {
+ .ComptimeFloat, .Float, .Vector => {},
+ else => return sema.fail(block, src, "expected vector of floats or float type, found '{}'", .{ty.fmt(sema.mod)}),
+ }
+
+ const runtime_src = if (maybe_mulend1) |mulend1_val| rs: {
+ if (maybe_mulend2) |mulend2_val| {
+ if (mulend2_val.isUndef()) return sema.addConstUndef(ty);
+
+ if (maybe_addend) |addend_val| {
+ if (addend_val.isUndef()) return sema.addConstUndef(ty);
+ const result_val = try Value.mulAdd(ty, mulend1_val, mulend2_val, addend_val, sema.arena, target);
+ return sema.addConstant(ty, result_val);
+ } else {
+ break :rs addend_src;
+ }
+ } else {
+ if (maybe_addend) |addend_val| {
+ if (addend_val.isUndef()) return sema.addConstUndef(ty);
+ }
+ break :rs mulend2_src;
+ }
+ } else rs: {
+ if (maybe_mulend2) |mulend2_val| {
+ if (mulend2_val.isUndef()) return sema.addConstUndef(ty);
+ }
+ if (maybe_addend) |addend_val| {
+ if (addend_val.isUndef()) return sema.addConstUndef(ty);
+ }
+ break :rs mulend1_src;
+ };
+
+ try sema.requireRuntimeBlock(block, runtime_src);
+ return block.addInst(.{
+ .tag = .mul_add,
+ .data = .{ .pl_op = .{
+ .operand = addend,
+ .payload = try sema.addExtra(Air.Bin{
+ .lhs = mulend1,
+ .rhs = mulend2,
+ }),
+ } },
+ });
}
fn zirBuiltinCall(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
- const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
- const src = inst_data.src();
- return sema.fail(block, src, "TODO: Sema.zirBuiltinCall", .{});
-}
+ const tracy = trace(@src());
+ defer tracy.end();
-fn zirFieldPtrType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
- const src = inst_data.src();
- return sema.fail(block, src, "TODO: Sema.zirFieldPtrType", .{});
+ const options_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
+ const func_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node };
+ const args_src: LazySrcLoc = .{ .node_offset_builtin_call_arg2 = inst_data.src_node };
+ const call_src = inst_data.src();
+
+ const extra = sema.code.extraData(Zir.Inst.BuiltinCall, inst_data.payload_index).data;
+ var func = try sema.resolveInst(extra.callee);
+ const options = try sema.resolveInst(extra.options);
+ const args = try sema.resolveInst(extra.args);
+
+ const wanted_modifier: std.builtin.CallOptions.Modifier = modifier: {
+ const call_options_ty = try sema.getBuiltinType(block, options_src, "CallOptions");
+ const coerced_options = try sema.coerce(block, call_options_ty, options, options_src);
+
+ const modifier = try sema.fieldVal(block, options_src, coerced_options, "modifier", options_src);
+ const modifier_val = try sema.resolveConstValue(block, options_src, modifier);
+
+ const stack = try sema.fieldVal(block, options_src, coerced_options, "stack", options_src);
+ const stack_val = try sema.resolveConstValue(block, options_src, stack);
+
+ if (!stack_val.isNull()) {
+ return sema.fail(block, options_src, "TODO: implement @call with stack", .{});
+ }
+ break :modifier modifier_val.toEnum(std.builtin.CallOptions.Modifier);
+ };
+
+ const is_comptime = extra.flags.is_comptime or block.is_comptime;
+
+ const modifier: std.builtin.CallOptions.Modifier = switch (wanted_modifier) {
+ // These can be upgraded to comptime or nosuspend calls.
+ .auto, .never_tail, .no_async => m: {
+ if (is_comptime) {
+ if (wanted_modifier == .never_tail) {
+ return sema.fail(block, options_src, "unable to perform 'never_tail' call at compile-time", .{});
+ }
+ break :m .compile_time;
+ }
+ if (extra.flags.is_nosuspend) {
+ break :m .no_async;
+ }
+ break :m wanted_modifier;
+ },
+ // These can be upgraded to comptime. nosuspend bit can be safely ignored.
+ .always_tail, .always_inline, .compile_time => m: {
+ _ = (try sema.resolveDefinedValue(block, func_src, func)) orelse {
+ return sema.fail(block, func_src, "modifier '{s}' requires a comptime-known function", .{@tagName(wanted_modifier)});
+ };
+
+ if (is_comptime) {
+ break :m .compile_time;
+ }
+ break :m wanted_modifier;
+ },
+ .async_kw => m: {
+ if (extra.flags.is_nosuspend) {
+ return sema.fail(block, options_src, "modifier 'async_kw' cannot be used inside nosuspend block", .{});
+ }
+ if (is_comptime) {
+ return sema.fail(block, options_src, "modifier 'async_kw' cannot be used in combination with comptime function call", .{});
+ }
+ break :m wanted_modifier;
+ },
+ .never_inline => m: {
+ if (is_comptime) {
+ return sema.fail(block, options_src, "unable to perform 'never_inline' call at compile-time", .{});
+ }
+ break :m wanted_modifier;
+ },
+ };
+
+ const args_ty = sema.typeOf(args);
+ if (!args_ty.isTuple() and args_ty.tag() != .empty_struct_literal) {
+ return sema.fail(block, args_src, "expected a tuple, found '{}'", .{args_ty.fmt(sema.mod)});
+ }
+
+ var resolved_args: []Air.Inst.Ref = undefined;
+
+ // Desugar bound functions here
+ if (sema.typeOf(func).tag() == .bound_fn) {
+ const bound_func = try sema.resolveValue(block, func_src, func);
+ const bound_data = &bound_func.cast(Value.Payload.BoundFn).?.data;
+ func = bound_data.func_inst;
+ resolved_args = try sema.arena.alloc(Air.Inst.Ref, args_ty.structFieldCount() + 1);
+ resolved_args[0] = bound_data.arg0_inst;
+ for (resolved_args[1..]) |*resolved, i| {
+ resolved.* = try sema.tupleFieldValByIndex(block, args_src, args, @intCast(u32, i), args_ty);
+ }
+ } else {
+ resolved_args = try sema.arena.alloc(Air.Inst.Ref, args_ty.structFieldCount());
+ for (resolved_args) |*resolved, i| {
+ resolved.* = try sema.tupleFieldValByIndex(block, args_src, args, @intCast(u32, i), args_ty);
+ }
+ }
+ const ensure_result_used = extra.flags.ensure_result_used;
+ return sema.analyzeCall(block, func, func_src, call_src, modifier, ensure_result_used, resolved_args);
}
fn zirFieldParentPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
+ const extra = sema.code.extraData(Zir.Inst.FieldParentPtr, inst_data.payload_index).data;
const src = inst_data.src();
- return sema.fail(block, src, "TODO: Sema.zirFieldParentPtr", .{});
+ const ty_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
+ const name_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node };
+ const ptr_src: LazySrcLoc = .{ .node_offset_builtin_call_arg2 = inst_data.src_node };
+
+ const struct_ty = try sema.resolveType(block, ty_src, extra.parent_type);
+ const field_name = try sema.resolveConstString(block, name_src, extra.field_name);
+ const field_ptr = try sema.resolveInst(extra.field_ptr);
+ const field_ptr_ty = sema.typeOf(field_ptr);
+
+ if (struct_ty.zigTypeTag() != .Struct) {
+ return sema.fail(block, ty_src, "expected struct type, found '{}'", .{struct_ty.fmt(sema.mod)});
+ }
+ try sema.resolveTypeLayout(block, ty_src, struct_ty);
+
+ const struct_obj = struct_ty.castTag(.@"struct").?.data;
+ const field_index = struct_obj.fields.getIndex(field_name) orelse
+ return sema.failWithBadStructFieldAccess(block, struct_obj, name_src, field_name);
+
+ try sema.checkPtrOperand(block, ptr_src, field_ptr_ty);
+ const field = struct_obj.fields.values()[field_index];
+ const field_ptr_ty_info = field_ptr_ty.ptrInfo().data;
+
+ var ptr_ty_data: Type.Payload.Pointer.Data = .{
+ .pointee_type = field.ty,
+ .mutable = field_ptr_ty_info.mutable,
+ .@"addrspace" = field_ptr_ty_info.@"addrspace",
+ };
+
+ if (struct_obj.layout == .Packed) {
+ return sema.fail(block, src, "TODO handle packed structs with @fieldParentPtr", .{});
+ } else {
+ ptr_ty_data.@"align" = field.abi_align;
+ }
+
+ const actual_field_ptr_ty = try Type.ptr(sema.arena, sema.mod, ptr_ty_data);
+ const casted_field_ptr = try sema.coerce(block, actual_field_ptr_ty, field_ptr, ptr_src);
+
+ ptr_ty_data.pointee_type = struct_ty;
+ const result_ptr = try Type.ptr(sema.arena, sema.mod, ptr_ty_data);
+
+ if (try sema.resolveDefinedValue(block, src, casted_field_ptr)) |field_ptr_val| {
+ const payload = field_ptr_val.castTag(.field_ptr) orelse {
+ return sema.fail(block, ptr_src, "pointer value not based on parent struct", .{});
+ };
+ if (payload.data.field_index != field_index) {
+ const msg = msg: {
+ const msg = try sema.errMsg(
+ block,
+ src,
+ "field '{s}' has index '{d}' but pointer value is index '{d}' of struct '{}'",
+ .{
+ field_name,
+ field_index,
+ payload.data.field_index,
+ struct_ty.fmt(sema.mod),
+ },
+ );
+ errdefer msg.destroy(sema.gpa);
+ try sema.addDeclaredHereNote(msg, struct_ty);
+ break :msg msg;
+ };
+ return sema.failWithOwnedErrorMsg(block, msg);
+ }
+ return sema.addConstant(result_ptr, payload.data.container_ptr);
+ }
+
+ try sema.requireRuntimeBlock(block, src);
+ return block.addInst(.{
+ .tag = .field_parent_ptr,
+ .data = .{ .ty_pl = .{
+ .ty = try sema.addType(result_ptr),
+ .payload = try block.sema.addExtra(Air.FieldParentPtr{
+ .field_ptr = casted_field_ptr,
+ .field_index = @intCast(u32, field_index),
+ }),
+ } },
+ });
}
fn zirMinMax(
sema: *Sema,
block: *Block,
inst: Zir.Inst.Index,
- air_tag: Air.Inst.Tag,
+ comptime air_tag: Air.Inst.Tag,
) CompileError!Air.Inst.Ref {
const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
const src = inst_data.src();
const lhs_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
const rhs_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node };
- const lhs = sema.resolveInst(extra.lhs);
- const rhs = sema.resolveInst(extra.rhs);
+ const lhs = try sema.resolveInst(extra.lhs);
+ const rhs = try sema.resolveInst(extra.rhs);
try sema.checkNumericType(block, lhs_src, sema.typeOf(lhs));
try sema.checkNumericType(block, rhs_src, sema.typeOf(rhs));
+ return sema.analyzeMinMax(block, src, lhs, rhs, air_tag, lhs_src, rhs_src);
+}
+
+fn analyzeMinMax(
+ sema: *Sema,
+ block: *Block,
+ src: LazySrcLoc,
+ lhs: Air.Inst.Ref,
+ rhs: Air.Inst.Ref,
+ comptime air_tag: Air.Inst.Tag,
+ lhs_src: LazySrcLoc,
+ rhs_src: LazySrcLoc,
+) CompileError!Air.Inst.Ref {
const simd_op = try sema.checkSimdBinOp(block, src, lhs, rhs, lhs_src, rhs_src);
// TODO @maximum(max_int, undefined) should return max_int
@@ -12230,21 +17247,22 @@ fn zirMinMax(
.max => Value.numberMax,
else => unreachable,
};
+ const target = sema.mod.getTarget();
const vec_len = simd_op.len orelse {
- const result_val = try opFunc(lhs_val, rhs_val);
+ const result_val = opFunc(lhs_val, rhs_val, target);
return sema.addConstant(simd_op.result_ty, result_val);
};
var lhs_buf: Value.ElemValueBuffer = undefined;
var rhs_buf: Value.ElemValueBuffer = undefined;
const elems = try sema.arena.alloc(Value, vec_len);
for (elems) |*elem, i| {
- const lhs_elem_val = lhs_val.elemValueBuffer(i, &lhs_buf);
- const rhs_elem_val = rhs_val.elemValueBuffer(i, &rhs_buf);
- elem.* = try opFunc(lhs_elem_val, rhs_elem_val);
+ const lhs_elem_val = lhs_val.elemValueBuffer(sema.mod, i, &lhs_buf);
+ const rhs_elem_val = rhs_val.elemValueBuffer(sema.mod, i, &rhs_buf);
+ elem.* = opFunc(lhs_elem_val, rhs_elem_val, target);
}
return sema.addConstant(
simd_op.result_ty,
- try Value.Tag.array.create(sema.arena, elems),
+ try Value.Tag.aggregate.create(sema.arena, elems),
);
} else rs: {
if (simd_op.rhs_val) |rhs_val| {
@@ -12264,19 +17282,19 @@ fn zirMemcpy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void
const dest_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
const src_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node };
const len_src: LazySrcLoc = .{ .node_offset_builtin_call_arg2 = inst_data.src_node };
- const dest_ptr = sema.resolveInst(extra.dest);
+ const dest_ptr = try sema.resolveInst(extra.dest);
const dest_ptr_ty = sema.typeOf(dest_ptr);
try sema.checkPtrOperand(block, dest_src, dest_ptr_ty);
if (dest_ptr_ty.isConstPtr()) {
- return sema.fail(block, dest_src, "cannot store through const pointer '{}'", .{dest_ptr_ty});
+ return sema.fail(block, dest_src, "cannot store through const pointer '{}'", .{dest_ptr_ty.fmt(sema.mod)});
}
- const uncasted_src_ptr = sema.resolveInst(extra.source);
+ const uncasted_src_ptr = try sema.resolveInst(extra.source);
const uncasted_src_ptr_ty = sema.typeOf(uncasted_src_ptr);
try sema.checkPtrOperand(block, src_src, uncasted_src_ptr_ty);
const src_ptr_info = uncasted_src_ptr_ty.ptrInfo().data;
- const wanted_src_ptr_ty = try Type.ptr(sema.arena, .{
+ const wanted_src_ptr_ty = try Type.ptr(sema.arena, sema.mod, .{
.pointee_type = dest_ptr_ty.elemType2(),
.@"align" = src_ptr_info.@"align",
.@"addrspace" = src_ptr_info.@"addrspace",
@@ -12286,15 +17304,13 @@ fn zirMemcpy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void
.size = .Many,
});
const src_ptr = try sema.coerce(block, wanted_src_ptr_ty, uncasted_src_ptr, src_src);
- const len = try sema.coerce(block, Type.usize, sema.resolveInst(extra.byte_count), len_src);
-
- const maybe_dest_ptr_val = try sema.resolveDefinedValue(block, dest_src, dest_ptr);
- const maybe_src_ptr_val = try sema.resolveDefinedValue(block, src_src, src_ptr);
- const maybe_len_val = try sema.resolveDefinedValue(block, len_src, len);
+ const len = try sema.coerce(block, Type.usize, try sema.resolveInst(extra.byte_count), len_src);
- const runtime_src = if (maybe_dest_ptr_val) |dest_ptr_val| rs: {
- if (maybe_src_ptr_val) |src_ptr_val| {
- if (maybe_len_val) |len_val| {
+ const runtime_src = if (try sema.resolveDefinedValue(block, dest_src, dest_ptr)) |dest_ptr_val| rs: {
+ if (!dest_ptr_val.isComptimeMutablePtr()) break :rs dest_src;
+ if (try sema.resolveDefinedValue(block, src_src, src_ptr)) |src_ptr_val| {
+ if (!src_ptr_val.isComptimeMutablePtr()) break :rs src_src;
+ if (try sema.resolveDefinedValue(block, len_src, len)) |len_val| {
_ = dest_ptr_val;
_ = src_ptr_val;
_ = len_val;
@@ -12323,21 +17339,19 @@ fn zirMemset(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void
const dest_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
const value_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node };
const len_src: LazySrcLoc = .{ .node_offset_builtin_call_arg2 = inst_data.src_node };
- const dest_ptr = sema.resolveInst(extra.dest);
+ const dest_ptr = try sema.resolveInst(extra.dest);
const dest_ptr_ty = sema.typeOf(dest_ptr);
try sema.checkPtrOperand(block, dest_src, dest_ptr_ty);
if (dest_ptr_ty.isConstPtr()) {
- return sema.fail(block, dest_src, "cannot store through const pointer '{}'", .{dest_ptr_ty});
+ return sema.fail(block, dest_src, "cannot store through const pointer '{}'", .{dest_ptr_ty.fmt(sema.mod)});
}
const elem_ty = dest_ptr_ty.elemType2();
- const value = try sema.coerce(block, elem_ty, sema.resolveInst(extra.byte), value_src);
- const len = try sema.coerce(block, Type.usize, sema.resolveInst(extra.byte_count), len_src);
-
- const maybe_dest_ptr_val = try sema.resolveDefinedValue(block, dest_src, dest_ptr);
- const maybe_len_val = try sema.resolveDefinedValue(block, len_src, len);
+ const value = try sema.coerce(block, elem_ty, try sema.resolveInst(extra.byte), value_src);
+ const len = try sema.coerce(block, Type.usize, try sema.resolveInst(extra.byte_count), len_src);
- const runtime_src = if (maybe_dest_ptr_val) |ptr_val| rs: {
- if (maybe_len_val) |len_val| {
+ const runtime_src = if (try sema.resolveDefinedValue(block, dest_src, dest_ptr)) |ptr_val| rs: {
+ if (!ptr_val.isComptimeMutablePtr()) break :rs dest_src;
+ if (try sema.resolveDefinedValue(block, len_src, len)) |len_val| {
if (try sema.resolveMaybeUndefVal(block, value_src, value)) |val| {
_ = ptr_val;
_ = len_val;
@@ -12376,15 +17390,24 @@ fn zirAwait(
sema: *Sema,
block: *Block,
inst: Zir.Inst.Index,
- is_nosuspend: bool,
) CompileError!Air.Inst.Ref {
const inst_data = sema.code.instructions.items(.data)[inst].un_node;
const src = inst_data.src();
- _ = is_nosuspend;
return sema.fail(block, src, "TODO: Sema.zirAwait", .{});
}
+fn zirAwaitNosuspend(
+ sema: *Sema,
+ block: *Block,
+ extended: Zir.Inst.Extended.InstData,
+) CompileError!Air.Inst.Ref {
+ const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data;
+ const src = LazySrcLoc.nodeOffset(extra.node);
+
+ return sema.fail(block, src, "TODO: Sema.zirAwaitNosuspend", .{});
+}
+
fn zirVarExtended(
sema: *Sema,
block: *Block,
@@ -12418,7 +17441,7 @@ fn zirVarExtended(
const uncasted_init: Air.Inst.Ref = if (small.has_init) blk: {
const init_ref = @intToEnum(Zir.Inst.Ref, sema.code.extra[extra_index]);
extra_index += 1;
- break :blk sema.resolveInst(init_ref);
+ break :blk try sema.resolveInst(init_ref);
} else .none;
const have_ty = extra.data.var_type != .none;
@@ -12439,25 +17462,27 @@ fn zirVarExtended(
try sema.validateVarType(block, mut_src, var_ty, small.is_extern);
- if (lib_name != null) {
- // Look at the sema code for functions which has this logic, it just needs to
- // be extracted and shared by both var and func
- return sema.fail(block, src, "TODO: handle var with lib_name in Sema", .{});
- }
-
const new_var = try sema.gpa.create(Module.Var);
+ errdefer sema.gpa.destroy(new_var);
log.debug("created variable {*} owner_decl: {*} ({s})", .{
new_var, sema.owner_decl, sema.owner_decl.name,
});
new_var.* = .{
- .owner_decl = sema.owner_decl,
+ .owner_decl = sema.owner_decl_index,
.init = init_val,
.is_extern = small.is_extern,
.is_mutable = true, // TODO get rid of this unused field
.is_threadlocal = small.is_threadlocal,
+ .is_weak_linkage = false,
+ .lib_name = null,
};
+
+ if (lib_name) |lname| {
+ new_var.lib_name = try sema.handleExternLibName(block, ty_src, lname);
+ }
+
const result = try sema.addConstant(
var_ty,
try Value.Tag.variable.create(sema.arena, new_var),
@@ -12465,45 +17490,164 @@ fn zirVarExtended(
return result;
}
-fn zirFuncExtended(
- sema: *Sema,
- block: *Block,
- extended: Zir.Inst.Extended.InstData,
- inst: Zir.Inst.Index,
-) CompileError!Air.Inst.Ref {
+fn zirFuncFancy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
const tracy = trace(@src());
defer tracy.end();
- const extra = sema.code.extraData(Zir.Inst.ExtendedFunc, extended.operand);
- const src: LazySrcLoc = .{ .node_offset = extra.data.src_node };
- const cc_src: LazySrcLoc = .{ .node_offset_fn_type_cc = extra.data.src_node };
+ const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
+ const src = inst_data.src();
+ const extra = sema.code.extraData(Zir.Inst.FuncFancy, inst_data.payload_index);
+ const target = sema.mod.getTarget();
+
const align_src: LazySrcLoc = src; // TODO add a LazySrcLoc that points at align
- const small = @bitCast(Zir.Inst.ExtendedFunc.Small, extended.small);
+ const addrspace_src: LazySrcLoc = src; // TODO add a LazySrcLoc that points at addrspace
+ const section_src: LazySrcLoc = src; // TODO add a LazySrcLoc that points at section
+ const cc_src: LazySrcLoc = .{ .node_offset_fn_type_cc = inst_data.src_node };
+ const ret_src: LazySrcLoc = src; // TODO add a LazySrcLoc that points at the return type
var extra_index: usize = extra.end;
- const lib_name: ?[]const u8 = if (small.has_lib_name) blk: {
+ const lib_name: ?[]const u8 = if (extra.data.bits.has_lib_name) blk: {
const lib_name = sema.code.nullTerminatedString(sema.code.extra[extra_index]);
extra_index += 1;
break :blk lib_name;
} else null;
- const cc: std.builtin.CallingConvention = if (small.has_cc) blk: {
+ const @"align": ?u32 = if (extra.data.bits.has_align_body) blk: {
+ const body_len = sema.code.extra[extra_index];
+ extra_index += 1;
+ const body = sema.code.extra[extra_index..][0..body_len];
+ extra_index += body.len;
+
+ const val = try sema.resolveGenericBody(block, align_src, body, inst, Type.u29);
+ if (val.tag() == .generic_poison) {
+ break :blk null;
+ }
+ const alignment = @intCast(u32, val.toUnsignedInt(target));
+ if (alignment == target_util.defaultFunctionAlignment(target)) {
+ break :blk 0;
+ } else {
+ break :blk alignment;
+ }
+ } else if (extra.data.bits.has_align_ref) blk: {
+ const align_ref = @intToEnum(Zir.Inst.Ref, sema.code.extra[extra_index]);
+ extra_index += 1;
+ const align_tv = sema.resolveInstConst(block, align_src, align_ref) catch |err| switch (err) {
+ error.GenericPoison => {
+ break :blk null;
+ },
+ else => |e| return e,
+ };
+ const alignment = @intCast(u32, align_tv.val.toUnsignedInt(target));
+ if (alignment == target_util.defaultFunctionAlignment(target)) {
+ break :blk 0;
+ } else {
+ break :blk alignment;
+ }
+ } else 0;
+
+ const @"addrspace": ?std.builtin.AddressSpace = if (extra.data.bits.has_addrspace_body) blk: {
+ const body_len = sema.code.extra[extra_index];
+ extra_index += 1;
+ const body = sema.code.extra[extra_index..][0..body_len];
+ extra_index += body.len;
+
+ const addrspace_ty = try sema.getBuiltinType(block, addrspace_src, "AddressSpace");
+ const val = try sema.resolveGenericBody(block, addrspace_src, body, inst, addrspace_ty);
+ if (val.tag() == .generic_poison) {
+ break :blk null;
+ }
+ break :blk val.toEnum(std.builtin.AddressSpace);
+ } else if (extra.data.bits.has_addrspace_ref) blk: {
+ const addrspace_ref = @intToEnum(Zir.Inst.Ref, sema.code.extra[extra_index]);
+ extra_index += 1;
+ const addrspace_tv = sema.resolveInstConst(block, addrspace_src, addrspace_ref) catch |err| switch (err) {
+ error.GenericPoison => {
+ break :blk null;
+ },
+ else => |e| return e,
+ };
+ break :blk addrspace_tv.val.toEnum(std.builtin.AddressSpace);
+ } else target_util.defaultAddressSpace(target, .function);
+
+ const @"linksection": FuncLinkSection = if (extra.data.bits.has_section_body) blk: {
+ const body_len = sema.code.extra[extra_index];
+ extra_index += 1;
+ const body = sema.code.extra[extra_index..][0..body_len];
+ extra_index += body.len;
+
+ const val = try sema.resolveGenericBody(block, section_src, body, inst, Type.initTag(.const_slice_u8));
+ if (val.tag() == .generic_poison) {
+ break :blk FuncLinkSection{ .generic = {} };
+ }
+ _ = val;
+ return sema.fail(block, section_src, "TODO implement linksection on functions", .{});
+ } else if (extra.data.bits.has_section_ref) blk: {
+ const section_ref = @intToEnum(Zir.Inst.Ref, sema.code.extra[extra_index]);
+ extra_index += 1;
+ const section_tv = sema.resolveInstConst(block, section_src, section_ref) catch |err| switch (err) {
+ error.GenericPoison => {
+ break :blk FuncLinkSection{ .generic = {} };
+ },
+ else => |e| return e,
+ };
+ _ = section_tv;
+ return sema.fail(block, section_src, "TODO implement linksection on functions", .{});
+ } else FuncLinkSection{ .default = {} };
+
+ const cc: ?std.builtin.CallingConvention = if (extra.data.bits.has_cc_body) blk: {
+ const body_len = sema.code.extra[extra_index];
+ extra_index += 1;
+ const body = sema.code.extra[extra_index..][0..body_len];
+ extra_index += body.len;
+
+ const cc_ty = try sema.getBuiltinType(block, addrspace_src, "CallingConvention");
+ const val = try sema.resolveGenericBody(block, cc_src, body, inst, cc_ty);
+ if (val.tag() == .generic_poison) {
+ break :blk null;
+ }
+ break :blk val.toEnum(std.builtin.CallingConvention);
+ } else if (extra.data.bits.has_cc_ref) blk: {
const cc_ref = @intToEnum(Zir.Inst.Ref, sema.code.extra[extra_index]);
extra_index += 1;
- const cc_tv = try sema.resolveInstConst(block, cc_src, cc_ref);
+ const cc_tv = sema.resolveInstConst(block, cc_src, cc_ref) catch |err| switch (err) {
+ error.GenericPoison => {
+ break :blk null;
+ },
+ else => |e| return e,
+ };
break :blk cc_tv.val.toEnum(std.builtin.CallingConvention);
- } else .Unspecified;
+ } else std.builtin.CallingConvention.Unspecified;
- const align_val: Value = if (small.has_align) blk: {
- const align_ref = @intToEnum(Zir.Inst.Ref, sema.code.extra[extra_index]);
+ const ret_ty: Type = if (extra.data.bits.has_ret_ty_body) blk: {
+ const body_len = sema.code.extra[extra_index];
extra_index += 1;
- const align_tv = try sema.resolveInstConst(block, align_src, align_ref);
- break :blk align_tv.val;
- } else Value.@"null";
+ const body = sema.code.extra[extra_index..][0..body_len];
+ extra_index += body.len;
+
+ const val = try sema.resolveGenericBody(block, ret_src, body, inst, Type.type);
+ var buffer: Value.ToTypeBuffer = undefined;
+ const ty = try val.toType(&buffer).copy(sema.arena);
+ break :blk ty;
+ } else if (extra.data.bits.has_ret_ty_ref) blk: {
+ const ret_ty_ref = @intToEnum(Zir.Inst.Ref, sema.code.extra[extra_index]);
+ extra_index += 1;
+ const ret_ty_tv = sema.resolveInstConst(block, ret_src, ret_ty_ref) catch |err| switch (err) {
+ error.GenericPoison => {
+ break :blk Type.initTag(.generic_poison);
+ },
+ else => |e| return e,
+ };
+ var buffer: Value.ToTypeBuffer = undefined;
+ const ty = try ret_ty_tv.val.toType(&buffer).copy(sema.arena);
+ break :blk ty;
+ } else Type.void;
- const ret_ty_body = sema.code.extra[extra_index..][0..extra.data.ret_body_len];
- extra_index += ret_ty_body.len;
+ const noalias_bits: u32 = if (extra.data.bits.has_any_noalias) blk: {
+ const x = sema.code.extra[extra_index];
+ extra_index += 1;
+ break :blk x;
+ } else 0;
var src_locs: Zir.Inst.Func.SrcLocs = undefined;
const has_body = extra.data.body_len != 0;
@@ -12512,23 +17656,26 @@ fn zirFuncExtended(
src_locs = sema.code.extraData(Zir.Inst.Func.SrcLocs, extra_index).data;
}
- const is_var_args = small.is_var_args;
- const is_inferred_error = small.is_inferred_error;
- const is_extern = small.is_extern;
+ const is_var_args = extra.data.bits.is_var_args;
+ const is_inferred_error = extra.data.bits.is_inferred_error;
+ const is_extern = extra.data.bits.is_extern;
return sema.funcCommon(
block,
- extra.data.src_node,
+ inst_data.src_node,
inst,
- ret_ty_body,
+ @"align",
+ @"addrspace",
+ @"linksection",
cc,
- align_val,
+ ret_ty,
is_var_args,
is_inferred_error,
is_extern,
has_body,
src_locs,
lib_name,
+ noalias_bits,
);
}
@@ -12538,7 +17685,7 @@ fn zirCUndef(
extended: Zir.Inst.Extended.InstData,
) CompileError!Air.Inst.Ref {
const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data;
- const src: LazySrcLoc = .{ .node_offset = extra.node };
+ const src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = extra.node };
const name = try sema.resolveConstString(block, src, extra.operand);
try block.c_import_buf.?.writer().print("#undefine {s}\n", .{name});
@@ -12551,7 +17698,7 @@ fn zirCInclude(
extended: Zir.Inst.Extended.InstData,
) CompileError!Air.Inst.Ref {
const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data;
- const src: LazySrcLoc = .{ .node_offset = extra.node };
+ const src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = extra.node };
const name = try sema.resolveConstString(block, src, extra.operand);
try block.c_import_buf.?.writer().print("#include <{s}>\n", .{name});
@@ -12564,12 +17711,13 @@ fn zirCDefine(
extended: Zir.Inst.Extended.InstData,
) CompileError!Air.Inst.Ref {
const extra = sema.code.extraData(Zir.Inst.BinNode, extended.operand).data;
- const src: LazySrcLoc = .{ .node_offset = extra.node };
+ const name_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = extra.node };
+ const val_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = extra.node };
- const name = try sema.resolveConstString(block, src, extra.lhs);
- const rhs = sema.resolveInst(extra.rhs);
+ const name = try sema.resolveConstString(block, name_src, extra.lhs);
+ const rhs = try sema.resolveInst(extra.rhs);
if (sema.typeOf(rhs).zigTypeTag() != .Void) {
- const value = try sema.resolveConstString(block, src, extra.rhs);
+ const value = try sema.resolveConstString(block, val_src, extra.rhs);
try block.c_import_buf.?.writer().print("#define {s} {s}\n", .{ name, value });
} else {
try block.c_import_buf.?.writer().print("#define {s}\n", .{name});
@@ -12583,8 +17731,22 @@ fn zirWasmMemorySize(
extended: Zir.Inst.Extended.InstData,
) CompileError!Air.Inst.Ref {
const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data;
- const src: LazySrcLoc = .{ .node_offset = extra.node };
- return sema.fail(block, src, "TODO: implement Sema.zirWasmMemorySize", .{});
+ const index_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = extra.node };
+ const builtin_src = LazySrcLoc.nodeOffset(extra.node);
+ const target = sema.mod.getTarget();
+ if (!target.isWasm()) {
+ return sema.fail(block, builtin_src, "builtin @wasmMemorySize is available when targeting WebAssembly; targeted CPU architecture is {s}", .{@tagName(target.cpu.arch)});
+ }
+
+ const index = @intCast(u32, try sema.resolveInt(block, index_src, extra.operand, Type.u32));
+ try sema.requireRuntimeBlock(block, builtin_src);
+ return block.addInst(.{
+ .tag = .wasm_memory_size,
+ .data = .{ .pl_op = .{
+ .operand = .none,
+ .payload = index,
+ } },
+ });
}
fn zirWasmMemoryGrow(
@@ -12593,8 +17755,25 @@ fn zirWasmMemoryGrow(
extended: Zir.Inst.Extended.InstData,
) CompileError!Air.Inst.Ref {
const extra = sema.code.extraData(Zir.Inst.BinNode, extended.operand).data;
- const src: LazySrcLoc = .{ .node_offset = extra.node };
- return sema.fail(block, src, "TODO: implement Sema.zirWasmMemoryGrow", .{});
+ const builtin_src = LazySrcLoc.nodeOffset(extra.node);
+ const index_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = extra.node };
+ const delta_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = extra.node };
+ const target = sema.mod.getTarget();
+ if (!target.isWasm()) {
+ return sema.fail(block, builtin_src, "builtin @wasmMemoryGrow is available when targeting WebAssembly; targeted CPU architecture is {s}", .{@tagName(target.cpu.arch)});
+ }
+
+ const index = @intCast(u32, try sema.resolveInt(block, index_src, extra.lhs, Type.u32));
+ const delta = try sema.coerce(block, Type.u32, try sema.resolveInst(extra.rhs), delta_src);
+
+ try sema.requireRuntimeBlock(block, builtin_src);
+ return block.addInst(.{
+ .tag = .wasm_memory_grow,
+ .data = .{ .pl_op = .{
+ .operand = delta,
+ .payload = index,
+ } },
+ });
}
fn zirPrefetch(
@@ -12606,9 +17785,10 @@ fn zirPrefetch(
const ptr_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = extra.node };
const opts_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = extra.node };
const options_ty = try sema.getBuiltinType(block, opts_src, "PrefetchOptions");
- const ptr = sema.resolveInst(extra.lhs);
+ const ptr = try sema.resolveInst(extra.lhs);
try sema.checkPtrOperand(block, ptr_src, sema.typeOf(ptr));
- const options = try sema.coerce(block, options_ty, sema.resolveInst(extra.rhs), opts_src);
+ const options = try sema.coerce(block, options_ty, try sema.resolveInst(extra.rhs), opts_src);
+ const target = sema.mod.getTarget();
const rw = try sema.fieldVal(block, opts_src, options, "rw", opts_src);
const rw_val = try sema.resolveConstValue(block, opts_src, rw);
@@ -12616,7 +17796,7 @@ fn zirPrefetch(
const locality = try sema.fieldVal(block, opts_src, options, "locality", opts_src);
const locality_val = try sema.resolveConstValue(block, opts_src, locality);
- const locality_int = @intCast(u2, locality_val.toUnsignedInt());
+ const locality_int = @intCast(u2, locality_val.toUnsignedInt(target));
const cache = try sema.fieldVal(block, opts_src, options, "cache", opts_src);
const cache_val = try sema.resolveConstValue(block, opts_src, cache);
@@ -12643,12 +17823,111 @@ fn zirBuiltinExtern(
extended: Zir.Inst.Extended.InstData,
) CompileError!Air.Inst.Ref {
const extra = sema.code.extraData(Zir.Inst.BinNode, extended.operand).data;
- const src: LazySrcLoc = .{ .node_offset = extra.node };
- return sema.fail(block, src, "TODO: implement Sema.zirBuiltinExtern", .{});
+ const src = LazySrcLoc.nodeOffset(extra.node);
+ const ty_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = extra.node };
+ const options_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = extra.node };
+
+ var ty = try sema.resolveType(block, ty_src, extra.lhs);
+ const options_inst = try sema.resolveInst(extra.rhs);
+ const mod = sema.mod;
+
+ const options = options: {
+ const extern_options_ty = try sema.getBuiltinType(block, options_src, "ExternOptions");
+ const options = try sema.coerce(block, extern_options_ty, options_inst, options_src);
+
+ const name = try sema.fieldVal(block, options_src, options, "name", options_src);
+ const name_val = try sema.resolveConstValue(block, options_src, name);
+
+ const library_name_inst = try sema.fieldVal(block, options_src, options, "library_name", options_src);
+ const library_name_val = try sema.resolveConstValue(block, options_src, library_name_inst);
+
+ const linkage = try sema.fieldVal(block, options_src, options, "linkage", options_src);
+ const linkage_val = try sema.resolveConstValue(block, options_src, linkage);
+
+ const is_thread_local = try sema.fieldVal(block, options_src, options, "is_thread_local", options_src);
+ const is_thread_local_val = try sema.resolveConstValue(block, options_src, is_thread_local);
+
+ var library_name: ?[]const u8 = null;
+ if (!library_name_val.isNull()) {
+ const payload = library_name_val.castTag(.opt_payload).?.data;
+ library_name = try payload.toAllocatedBytes(Type.initTag(.const_slice_u8), sema.arena, mod);
+ }
+
+ break :options std.builtin.ExternOptions{
+ .name = try name_val.toAllocatedBytes(Type.initTag(.const_slice_u8), sema.arena, mod),
+ .library_name = library_name,
+ .linkage = linkage_val.toEnum(std.builtin.GlobalLinkage),
+ .is_thread_local = is_thread_local_val.toBool(),
+ };
+ };
+
+ if (!ty.isPtrAtRuntime()) {
+ return sema.fail(block, options_src, "expected (optional) pointer", .{});
+ }
+
+ if (options.name.len == 0) {
+ return sema.fail(block, options_src, "extern symbol name cannot be empty", .{});
+ }
+
+ if (options.linkage != .Weak and options.linkage != .Strong) {
+ return sema.fail(block, options_src, "extern symbol must use strong or weak linkage", .{});
+ }
+
+ if (options.linkage == .Weak and !ty.ptrAllowsZero()) {
+ ty = try Type.optional(sema.arena, ty);
+ }
+
+ // TODO check duplicate extern
+
+ const new_decl_index = try mod.allocateNewDecl(sema.owner_decl.src_namespace, sema.owner_decl.src_node, null);
+ errdefer mod.destroyDecl(new_decl_index);
+ const new_decl = mod.declPtr(new_decl_index);
+ new_decl.name = try sema.gpa.dupeZ(u8, options.name);
+
+ var new_decl_arena = std.heap.ArenaAllocator.init(sema.gpa);
+ errdefer new_decl_arena.deinit();
+ const new_decl_arena_allocator = new_decl_arena.allocator();
+
+ const new_var = try new_decl_arena_allocator.create(Module.Var);
+ errdefer new_decl_arena_allocator.destroy(new_var);
+
+ new_var.* = .{
+ .owner_decl = sema.owner_decl_index,
+ .init = Value.initTag(.unreachable_value),
+ .is_extern = true,
+ .is_mutable = false, // TODO get rid of this unused field
+ .is_threadlocal = options.is_thread_local,
+ .is_weak_linkage = options.linkage == .Weak,
+ .lib_name = null,
+ };
+
+ if (options.library_name) |library_name| {
+ if (library_name.len == 0) {
+ return sema.fail(block, options_src, "library name name cannot be empty", .{});
+ }
+ new_var.lib_name = try sema.handleExternLibName(block, options_src, library_name);
+ }
+
+ new_decl.src_line = sema.owner_decl.src_line;
+ new_decl.ty = try ty.copy(new_decl_arena_allocator);
+ new_decl.val = try Value.Tag.variable.create(new_decl_arena_allocator, new_var);
+ new_decl.@"align" = 0;
+ new_decl.@"linksection" = null;
+ new_decl.has_tv = true;
+ new_decl.analysis = .complete;
+ new_decl.generation = mod.generation;
+
+ const arena_state = try new_decl_arena_allocator.create(std.heap.ArenaAllocator.State);
+ arena_state.* = new_decl_arena.state;
+ new_decl.value_arena = arena_state;
+
+ const ref = try sema.analyzeDeclRef(new_decl_index);
+ try sema.requireRuntimeBlock(block, src);
+ return block.addBitCast(ty, ref);
}
fn requireFunctionBlock(sema: *Sema, block: *Block, src: LazySrcLoc) !void {
- if (sema.func == null) {
+ if (sema.func == null and !block.is_typeof and !block.is_coerce_result_ptr) {
return sema.fail(block, src, "instruction illegal outside function body", .{});
}
}
@@ -12668,6 +17947,29 @@ fn validateVarType(
var_ty: Type,
is_extern: bool,
) CompileError!void {
+ if (try sema.validateRunTimeType(block, src, var_ty, is_extern)) return;
+
+ const mod = sema.mod;
+
+ const msg = msg: {
+ const msg = try sema.errMsg(block, src, "variable of type '{}' must be const or comptime", .{var_ty.fmt(mod)});
+ errdefer msg.destroy(sema.gpa);
+
+ const src_decl = mod.declPtr(block.src_decl);
+ try sema.explainWhyTypeIsComptime(block, src, msg, src.toSrcLoc(src_decl), var_ty);
+
+ break :msg msg;
+ };
+ return sema.failWithOwnedErrorMsg(block, msg);
+}
+
+fn validateRunTimeType(
+ sema: *Sema,
+ block: *Block,
+ src: LazySrcLoc,
+ var_ty: Type,
+ is_extern: bool,
+) CompileError!bool {
var ty = var_ty;
while (true) switch (ty.zigTypeTag()) {
.Bool,
@@ -12678,7 +17980,7 @@ fn validateVarType(
.Frame,
.AnyFrame,
.Void,
- => return,
+ => return true,
.BoundFn,
.ComptimeFloat,
@@ -12689,21 +17991,22 @@ fn validateVarType(
.Undefined,
.Null,
.Fn,
- => break,
+ => return false,
.Pointer => {
const elem_ty = ty.childType();
switch (elem_ty.zigTypeTag()) {
- .Opaque, .Fn => return,
+ .Opaque => return true,
+ .Fn => return elem_ty.isFnOrHasRuntimeBits(),
else => ty = elem_ty,
}
},
- .Opaque => if (is_extern) return else break,
+ .Opaque => return is_extern,
.Optional => {
var buf: Type.Payload.ElemType = undefined;
const child_ty = ty.optionalChild(&buf);
- return validateVarType(sema, block, src, child_ty, is_extern);
+ return validateRunTimeType(sema, block, src, child_ty, is_extern);
},
.Array, .Vector => ty = ty.elemType(),
@@ -12711,23 +18014,10 @@ fn validateVarType(
.Struct, .Union => {
const resolved_ty = try sema.resolveTypeFields(block, src, ty);
- if (try sema.typeRequiresComptime(block, src, resolved_ty)) {
- break;
- } else {
- return;
- }
+ const needs_comptime = try sema.typeRequiresComptime(block, src, resolved_ty);
+ return !needs_comptime;
},
- } else unreachable; // TODO should not need else unreachable
-
- const msg = msg: {
- const msg = try sema.errMsg(block, src, "variable of type '{}' must be const or comptime", .{var_ty});
- errdefer msg.destroy(sema.gpa);
-
- try sema.explainWhyTypeIsComptime(block, src, msg, src.toSrcLoc(block.src_decl), var_ty);
-
- break :msg msg;
};
- return sema.failWithOwnedErrorMsg(msg);
}
fn explainWhyTypeIsComptime(
@@ -12752,7 +18042,7 @@ fn explainWhyTypeIsComptime(
.Fn => {
try mod.errNoteNonLazy(src_loc, msg, "use '*const {}' for a function pointer type", .{
- ty,
+ ty.fmt(sema.mod),
});
},
@@ -12771,7 +18061,25 @@ fn explainWhyTypeIsComptime(
.Optional,
=> return,
- .Pointer, .Array, .Vector => {
+ .Array, .Vector => {
+ try sema.explainWhyTypeIsComptime(block, src, msg, src_loc, ty.elemType());
+ },
+ .Pointer => {
+ const elem_ty = ty.elemType2();
+ if (elem_ty.zigTypeTag() == .Fn) {
+ const fn_info = elem_ty.fnInfo();
+ if (fn_info.is_generic) {
+ try mod.errNoteNonLazy(src_loc, msg, "function is generic", .{});
+ }
+ switch (fn_info.cc) {
+ .Inline => try mod.errNoteNonLazy(src_loc, msg, "function has inline calling convention", .{}),
+ else => {},
+ }
+ if (fn_info.return_type.comptimeOnly()) {
+ try mod.errNoteNonLazy(src_loc, msg, "function has a comptime-only return type", .{});
+ }
+ return;
+ }
try sema.explainWhyTypeIsComptime(block, src, msg, src_loc, ty.elemType());
},
@@ -12783,7 +18091,7 @@ fn explainWhyTypeIsComptime(
if (ty.castTag(.@"struct")) |payload| {
const struct_obj = payload.data;
for (struct_obj.fields.values()) |field, i| {
- const field_src_loc = struct_obj.fieldSrcLoc(sema.gpa, .{
+ const field_src_loc = struct_obj.fieldSrcLoc(sema.mod, .{
.index = i,
.range = .type,
});
@@ -12800,7 +18108,7 @@ fn explainWhyTypeIsComptime(
if (ty.cast(Type.Payload.Union)) |payload| {
const union_obj = payload.data;
for (union_obj.fields.values()) |field, i| {
- const field_src_loc = union_obj.fieldSrcLoc(sema.gpa, .{
+ const field_src_loc = union_obj.fieldSrcLoc(sema.mod, .{
.index = i,
.range = .type,
});
@@ -12817,10 +18125,12 @@ fn explainWhyTypeIsComptime(
pub const PanicId = enum {
unreach,
unwrap_null,
- unwrap_errunion,
cast_to_null,
incorrect_alignment,
invalid_error_code,
+ cast_truncated_data,
+ integer_overflow,
+ shl_overflow,
};
fn addSafetyCheck(
@@ -12846,6 +18156,17 @@ fn addSafetyCheck(
_ = try sema.safetyPanic(&fail_block, .unneeded, panic_id);
+ try sema.addSafetyCheckExtra(parent_block, ok, &fail_block);
+}
+
+fn addSafetyCheckExtra(
+ sema: *Sema,
+ parent_block: *Block,
+ ok: Air.Inst.Ref,
+ fail_block: *Block,
+) !void {
+ const gpa = sema.gpa;
+
try parent_block.instructions.ensureUnusedCapacity(gpa, 1);
try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.Block).Struct.fields.len +
@@ -12914,20 +18235,104 @@ fn panicWithMsg(
const panic_fn = try sema.getBuiltin(block, src, "panic");
const unresolved_stack_trace_ty = try sema.getBuiltinType(block, src, "StackTrace");
const stack_trace_ty = try sema.resolveTypeFields(block, src, unresolved_stack_trace_ty);
- const ptr_stack_trace_ty = try Type.ptr(arena, .{
+ const target = mod.getTarget();
+ const ptr_stack_trace_ty = try Type.ptr(arena, mod, .{
.pointee_type = stack_trace_ty,
- .@"addrspace" = target_util.defaultAddressSpace(mod.getTarget(), .global_constant), // TODO might need a place that is more dynamic
+ .@"addrspace" = target_util.defaultAddressSpace(target, .global_constant), // TODO might need a place that is more dynamic
});
const null_stack_trace = try sema.addConstant(
try Type.optional(arena, ptr_stack_trace_ty),
Value.@"null",
);
- const args = try arena.create([2]Air.Inst.Ref);
- args.* = .{ msg_inst, null_stack_trace };
- _ = try sema.analyzeCall(block, panic_fn, src, src, .auto, false, args);
+ const args: [2]Air.Inst.Ref = .{ msg_inst, null_stack_trace };
+ _ = try sema.analyzeCall(block, panic_fn, src, src, .auto, false, &args);
return always_noreturn;
}
+fn panicUnwrapError(
+ sema: *Sema,
+ parent_block: *Block,
+ src: LazySrcLoc,
+ operand: Air.Inst.Ref,
+ unwrap_err_tag: Air.Inst.Tag,
+ is_non_err_tag: Air.Inst.Tag,
+) !void {
+ const ok = try parent_block.addUnOp(is_non_err_tag, operand);
+ const gpa = sema.gpa;
+
+ var fail_block: Block = .{
+ .parent = parent_block,
+ .sema = sema,
+ .src_decl = parent_block.src_decl,
+ .namespace = parent_block.namespace,
+ .wip_capture_scope = parent_block.wip_capture_scope,
+ .instructions = .{},
+ .inlining = parent_block.inlining,
+ .is_comptime = parent_block.is_comptime,
+ };
+
+ defer fail_block.instructions.deinit(gpa);
+
+ {
+ const this_feature_is_implemented_in_the_backend =
+ sema.mod.comp.bin_file.options.use_llvm;
+
+ if (!this_feature_is_implemented_in_the_backend) {
+ // TODO implement this feature in all the backends and then delete this branch
+ _ = try fail_block.addNoOp(.breakpoint);
+ _ = try fail_block.addNoOp(.unreach);
+ } else {
+ const panic_fn = try sema.getBuiltin(&fail_block, src, "panicUnwrapError");
+ const err = try fail_block.addTyOp(unwrap_err_tag, Type.anyerror, operand);
+ const err_return_trace = try sema.getErrorReturnTrace(&fail_block, src);
+ const args: [2]Air.Inst.Ref = .{ err_return_trace, err };
+ _ = try sema.analyzeCall(&fail_block, panic_fn, src, src, .auto, false, &args);
+ }
+ }
+ try sema.addSafetyCheckExtra(parent_block, ok, &fail_block);
+}
+
+fn panicIndexOutOfBounds(
+ sema: *Sema,
+ parent_block: *Block,
+ src: LazySrcLoc,
+ index: Air.Inst.Ref,
+ len: Air.Inst.Ref,
+ cmp_op: Air.Inst.Tag,
+) !void {
+ const ok = try parent_block.addBinOp(cmp_op, index, len);
+ const gpa = sema.gpa;
+
+ var fail_block: Block = .{
+ .parent = parent_block,
+ .sema = sema,
+ .src_decl = parent_block.src_decl,
+ .namespace = parent_block.namespace,
+ .wip_capture_scope = parent_block.wip_capture_scope,
+ .instructions = .{},
+ .inlining = parent_block.inlining,
+ .is_comptime = parent_block.is_comptime,
+ };
+
+ defer fail_block.instructions.deinit(gpa);
+
+ {
+ const this_feature_is_implemented_in_the_backend =
+ sema.mod.comp.bin_file.options.use_llvm;
+
+ if (!this_feature_is_implemented_in_the_backend) {
+ // TODO implement this feature in all the backends and then delete this branch
+ _ = try fail_block.addNoOp(.breakpoint);
+ _ = try fail_block.addNoOp(.unreach);
+ } else {
+ const panic_fn = try sema.getBuiltin(&fail_block, src, "panicOutOfBounds");
+ const args: [2]Air.Inst.Ref = .{ index, len };
+ _ = try sema.analyzeCall(&fail_block, panic_fn, src, src, .auto, false, &args);
+ }
+ }
+ try sema.addSafetyCheckExtra(parent_block, ok, &fail_block);
+}
+
fn safetyPanic(
sema: *Sema,
block: *Block,
@@ -12937,10 +18342,12 @@ fn safetyPanic(
const msg = switch (panic_id) {
.unreach => "reached unreachable code",
.unwrap_null => "attempt to use null value",
- .unwrap_errunion => "unreachable error occurred",
.cast_to_null => "cast causes pointer to be null",
.incorrect_alignment => "incorrect alignment",
.invalid_error_code => "invalid error code",
+ .cast_truncated_data => "integer cast truncated bits",
+ .integer_overflow => "integer overflow",
+ .shl_overflow => "left shift overflowed bits",
};
const msg_inst = msg_inst: {
@@ -12951,6 +18358,7 @@ fn safetyPanic(
break :msg_inst try sema.analyzeDeclRef(try anon_decl.finish(
try Type.Tag.array_u8.create(anon_decl.arena(), msg.len),
try Value.Tag.bytes.create(anon_decl.arena(), msg),
+ 0, // default alignment
));
};
@@ -12961,7 +18369,6 @@ fn safetyPanic(
fn emitBackwardBranch(sema: *Sema, block: *Block, src: LazySrcLoc) !void {
sema.branch_count += 1;
if (sema.branch_count > sema.branch_quota) {
- // TODO show the "called from here" stack
return sema.fail(block, src, "evaluation exceeded {d} backwards branches", .{sema.branch_quota});
}
}
@@ -12995,7 +18402,7 @@ fn fieldVal(
.Array => {
if (mem.eql(u8, field_name, "len")) {
return sema.addConstant(
- Type.initTag(.comptime_int),
+ Type.usize,
try Value.Tag.int_u64.create(arena, inner_ty.arrayLen()),
);
} else {
@@ -13003,30 +18410,47 @@ fn fieldVal(
block,
field_name_src,
"no member named '{s}' in '{}'",
- .{ field_name, object_ty },
+ .{ field_name, object_ty.fmt(sema.mod) },
);
}
},
- .Pointer => if (inner_ty.isSlice()) {
- if (mem.eql(u8, field_name, "ptr")) {
- const slice = if (is_pointer_to)
- try sema.analyzeLoad(block, src, object, object_src)
- else
- object;
- return sema.analyzeSlicePtr(block, src, slice, inner_ty, object_src);
- } else if (mem.eql(u8, field_name, "len")) {
- const slice = if (is_pointer_to)
- try sema.analyzeLoad(block, src, object, object_src)
- else
- object;
- return sema.analyzeSliceLen(block, src, slice);
- } else {
- return sema.fail(
- block,
- field_name_src,
- "no member named '{s}' in '{}'",
- .{ field_name, object_ty },
- );
+ .Pointer => {
+ const ptr_info = inner_ty.ptrInfo().data;
+ if (ptr_info.size == .Slice) {
+ if (mem.eql(u8, field_name, "ptr")) {
+ const slice = if (is_pointer_to)
+ try sema.analyzeLoad(block, src, object, object_src)
+ else
+ object;
+ return sema.analyzeSlicePtr(block, object_src, slice, inner_ty);
+ } else if (mem.eql(u8, field_name, "len")) {
+ const slice = if (is_pointer_to)
+ try sema.analyzeLoad(block, src, object, object_src)
+ else
+ object;
+ return sema.analyzeSliceLen(block, src, slice);
+ } else {
+ return sema.fail(
+ block,
+ field_name_src,
+ "no member named '{s}' in '{}'",
+ .{ field_name, object_ty.fmt(sema.mod) },
+ );
+ }
+ } else if (ptr_info.pointee_type.zigTypeTag() == .Array) {
+ if (mem.eql(u8, field_name, "len")) {
+ return sema.addConstant(
+ Type.usize,
+ try Value.Tag.int_u64.create(arena, ptr_info.pointee_type.arrayLen()),
+ );
+ } else {
+ return sema.fail(
+ block,
+ field_name_src,
+ "no member named '{s}' in '{}'",
+ .{ field_name, ptr_info.pointee_type.fmt(sema.mod) },
+ );
+ }
}
},
.Type => {
@@ -13039,15 +18463,21 @@ fn fieldVal(
var to_type_buffer: Value.ToTypeBuffer = undefined;
const child_type = val.toType(&to_type_buffer);
- switch (child_type.zigTypeTag()) {
+ switch (try child_type.zigTypeTagOrPoison()) {
.ErrorSet => {
const name: []const u8 = if (child_type.castTag(.error_set)) |payload| blk: {
if (payload.data.names.getEntry(field_name)) |entry| {
break :blk entry.key_ptr.*;
}
- return sema.fail(block, src, "no error named '{s}' in '{}'", .{
- field_name, child_type,
- });
+ const msg = msg: {
+ const msg = try sema.errMsg(block, src, "no error named '{s}' in '{}'", .{
+ field_name, child_type.fmt(sema.mod),
+ });
+ errdefer msg.destroy(sema.gpa);
+ try sema.addDeclaredHereNote(msg, child_type);
+ break :msg msg;
+ };
+ return sema.failWithOwnedErrorMsg(block, msg);
} else (try sema.mod.getErrorValue(field_name)).key;
return sema.addConstant(
@@ -13100,10 +18530,19 @@ fn fieldVal(
else => unreachable,
};
return sema.fail(block, src, "{s} '{}' has no member named '{s}'", .{
- kw_name, child_type, field_name,
+ kw_name, child_type.fmt(sema.mod), field_name,
});
},
- else => return sema.fail(block, src, "type '{}' has no members", .{child_type}),
+ else => {
+ const msg = msg: {
+ const msg = try sema.errMsg(block, src, "type '{}' has no members", .{child_type.fmt(sema.mod)});
+ errdefer msg.destroy(sema.gpa);
+ if (child_type.isSlice()) try sema.errNote(block, src, msg, "slice values have 'len' and 'ptr' members", .{});
+ if (child_type.zigTypeTag() == .Array) try sema.errNote(block, src, msg, "array values have 'len' member", .{});
+ break :msg msg;
+ };
+ return sema.failWithOwnedErrorMsg(block, msg);
+ },
}
},
.Struct => if (is_pointer_to) {
@@ -13122,7 +18561,7 @@ fn fieldVal(
},
else => {},
}
- return sema.fail(block, src, "type '{}' does not support field access", .{object_ty});
+ return sema.fail(block, src, "type '{}' does not support field access", .{object_ty.fmt(sema.mod)});
}
fn fieldPtr(
@@ -13140,7 +18579,7 @@ fn fieldPtr(
const object_ptr_ty = sema.typeOf(object_ptr);
const object_ty = switch (object_ptr_ty.zigTypeTag()) {
.Pointer => object_ptr_ty.elemType(),
- else => return sema.fail(block, object_ptr_src, "expected pointer, found '{}'", .{object_ptr_ty}),
+ else => return sema.fail(block, object_ptr_src, "expected pointer, found '{}'", .{object_ptr_ty.fmt(sema.mod)}),
};
// Zig allows dereferencing a single pointer during field lookup. Note that
@@ -13159,15 +18598,16 @@ fn fieldPtr(
var anon_decl = try block.startAnonDecl(src);
defer anon_decl.deinit();
return sema.analyzeDeclRef(try anon_decl.finish(
- Type.initTag(.comptime_int),
+ Type.usize,
try Value.Tag.int_u64.create(anon_decl.arena(), inner_ty.arrayLen()),
+ 0, // default alignment
));
} else {
return sema.fail(
block,
field_name_src,
"no member named '{s}' in '{}'",
- .{ field_name, object_ty },
+ .{ field_name, object_ty.fmt(sema.mod) },
);
}
},
@@ -13181,49 +18621,51 @@ fn fieldPtr(
const buf = try sema.arena.create(Type.SlicePtrFieldTypeBuffer);
const slice_ptr_ty = inner_ty.slicePtrFieldType(buf);
- if (try sema.resolveDefinedValue(block, object_ptr_src, inner_ptr)) |val| {
- var anon_decl = try block.startAnonDecl(src);
- defer anon_decl.deinit();
-
- return sema.analyzeDeclRef(try anon_decl.finish(
- try slice_ptr_ty.copy(anon_decl.arena()),
- try val.slicePtr().copy(anon_decl.arena()),
- ));
- }
- try sema.requireRuntimeBlock(block, src);
-
- const result_ty = try Type.ptr(sema.arena, .{
+ const result_ty = try Type.ptr(sema.arena, sema.mod, .{
.pointee_type = slice_ptr_ty,
.mutable = object_ptr_ty.ptrIsMutable(),
.@"addrspace" = object_ptr_ty.ptrAddressSpace(),
});
- return block.addTyOp(.ptr_slice_ptr_ptr, result_ty, inner_ptr);
- } else if (mem.eql(u8, field_name, "len")) {
if (try sema.resolveDefinedValue(block, object_ptr_src, inner_ptr)) |val| {
- var anon_decl = try block.startAnonDecl(src);
- defer anon_decl.deinit();
-
- return sema.analyzeDeclRef(try anon_decl.finish(
- Type.usize,
- try Value.Tag.int_u64.create(anon_decl.arena(), val.sliceLen()),
- ));
+ return sema.addConstant(
+ result_ty,
+ try Value.Tag.field_ptr.create(sema.arena, .{
+ .container_ptr = val,
+ .container_ty = inner_ty,
+ .field_index = Value.Payload.Slice.ptr_index,
+ }),
+ );
}
try sema.requireRuntimeBlock(block, src);
- const result_ty = try Type.ptr(sema.arena, .{
+ return block.addTyOp(.ptr_slice_ptr_ptr, result_ty, inner_ptr);
+ } else if (mem.eql(u8, field_name, "len")) {
+ const result_ty = try Type.ptr(sema.arena, sema.mod, .{
.pointee_type = Type.usize,
.mutable = object_ptr_ty.ptrIsMutable(),
.@"addrspace" = object_ptr_ty.ptrAddressSpace(),
});
+ if (try sema.resolveDefinedValue(block, object_ptr_src, inner_ptr)) |val| {
+ return sema.addConstant(
+ result_ty,
+ try Value.Tag.field_ptr.create(sema.arena, .{
+ .container_ptr = val,
+ .container_ty = inner_ty,
+ .field_index = Value.Payload.Slice.len_index,
+ }),
+ );
+ }
+ try sema.requireRuntimeBlock(block, src);
+
return block.addTyOp(.ptr_slice_len_ptr, result_ty, inner_ptr);
} else {
return sema.fail(
block,
field_name_src,
"no member named '{s}' in '{}'",
- .{ field_name, object_ty },
+ .{ field_name, object_ty.fmt(sema.mod) },
);
}
},
@@ -13247,7 +18689,7 @@ fn fieldPtr(
break :blk entry.key_ptr.*;
}
return sema.fail(block, src, "no error named '{s}' in '{}'", .{
- field_name, child_type,
+ field_name, child_type.fmt(sema.mod),
});
} else (try sema.mod.getErrorValue(field_name)).key;
@@ -13256,6 +18698,7 @@ fn fieldPtr(
return sema.analyzeDeclRef(try anon_decl.finish(
try child_type.copy(anon_decl.arena()),
try Value.Tag.@"error".create(anon_decl.arena(), .{ .name = name }),
+ 0, // default alignment
));
},
.Union => {
@@ -13272,6 +18715,7 @@ fn fieldPtr(
return sema.analyzeDeclRef(try anon_decl.finish(
try enum_ty.copy(anon_decl.arena()),
try Value.Tag.enum_field_index.create(anon_decl.arena(), field_index_u32),
+ 0, // default alignment
));
}
}
@@ -13292,6 +18736,7 @@ fn fieldPtr(
return sema.analyzeDeclRef(try anon_decl.finish(
try child_type.copy(anon_decl.arena()),
try Value.Tag.enum_field_index.create(anon_decl.arena(), field_index_u32),
+ 0, // default alignment
));
},
.Struct, .Opaque => {
@@ -13302,7 +18747,7 @@ fn fieldPtr(
}
return sema.failWithBadMemberAccess(block, child_type, field_name_src, field_name);
},
- else => return sema.fail(block, src, "type '{}' has no members", .{child_type}),
+ else => return sema.fail(block, src, "type '{}' has no members", .{child_type.fmt(sema.mod)}),
}
},
.Struct => {
@@ -13321,7 +18766,7 @@ fn fieldPtr(
},
else => {},
}
- return sema.fail(block, src, "type '{}' does not support field access (fieldPtr, {}.{s})", .{ object_ty, object_ptr_ty, field_name });
+ return sema.fail(block, src, "type '{}' does not support field access", .{object_ty.fmt(sema.mod)});
}
fn fieldCallBind(
@@ -13340,7 +18785,7 @@ fn fieldCallBind(
const inner_ty = if (raw_ptr_ty.zigTypeTag() == .Pointer and raw_ptr_ty.ptrSize() == .One)
raw_ptr_ty.childType()
else
- return sema.fail(block, raw_ptr_src, "expected single pointer, found '{}'", .{raw_ptr_ty});
+ return sema.fail(block, raw_ptr_src, "expected single pointer, found '{}'", .{raw_ptr_ty.fmt(sema.mod)});
// Optionally dereference a second pointer to get the concrete type.
const is_double_ptr = inner_ty.zigTypeTag() == .Pointer and inner_ty.ptrSize() == .One;
@@ -13398,8 +18843,9 @@ fn fieldCallBind(
if (first_param_tag == .var_args_param or
first_param_tag == .generic_poison or (
first_param_type.zigTypeTag() == .Pointer and
- first_param_type.ptrSize() == .One and
- first_param_type.childType().eql(concrete_ty)))
+ (first_param_type.ptrSize() == .One or
+ first_param_type.ptrSize() == .C) and
+ first_param_type.childType().eql(concrete_ty, sema.mod)))
{
// zig fmt: on
// TODO: bound fn calls on rvalues should probably
@@ -13410,7 +18856,7 @@ fn fieldCallBind(
.arg0_inst = object_ptr,
});
return sema.addConstant(ty, value);
- } else if (first_param_type.eql(concrete_ty)) {
+ } else if (first_param_type.eql(concrete_ty, sema.mod)) {
var deref = try sema.analyzeLoad(block, src, object_ptr, src);
const ty = Type.Tag.bound_fn.init();
const value = try Value.Tag.bound_fn.create(arena, .{
@@ -13426,7 +18872,7 @@ fn fieldCallBind(
else => {},
}
- return sema.fail(block, src, "type '{}' has no field or member function named '{s}'", .{ concrete_ty, field_name });
+ return sema.fail(block, src, "type '{}' has no field or member function named '{s}'", .{ concrete_ty.fmt(sema.mod), field_name });
}
fn finishFieldCallBind(
@@ -13439,7 +18885,7 @@ fn finishFieldCallBind(
object_ptr: Air.Inst.Ref,
) CompileError!Air.Inst.Ref {
const arena = sema.arena;
- const ptr_field_ty = try Type.ptr(arena, .{
+ const ptr_field_ty = try Type.ptr(arena, sema.mod, .{
.pointee_type = field_ty,
.mutable = ptr_ty.ptrIsMutable(),
.@"addrspace" = ptr_ty.ptrAddressSpace(),
@@ -13450,6 +18896,7 @@ fn finishFieldCallBind(
ptr_field_ty,
try Value.Tag.field_ptr.create(arena, .{
.container_ptr = struct_ptr_val,
+ .container_ty = ptr_ty.childType(),
.field_index = field_index,
}),
);
@@ -13467,9 +18914,10 @@ fn namespaceLookup(
src: LazySrcLoc,
namespace: *Namespace,
decl_name: []const u8,
-) CompileError!?*Decl {
+) CompileError!?Decl.Index {
const gpa = sema.gpa;
- if (try sema.lookupInNamespace(block, src, namespace, decl_name, true)) |decl| {
+ if (try sema.lookupInNamespace(block, src, namespace, decl_name, true)) |decl_index| {
+ const decl = sema.mod.declPtr(decl_index);
if (!decl.is_pub and decl.getFileScope() != block.getFileScope()) {
const msg = msg: {
const msg = try sema.errMsg(block, src, "'{s}' is not marked 'pub'", .{
@@ -13479,9 +18927,9 @@ fn namespaceLookup(
try sema.mod.errNoteNonLazy(decl.srcLoc(), msg, "declared here", .{});
break :msg msg;
};
- return sema.failWithOwnedErrorMsg(msg);
+ return sema.failWithOwnedErrorMsg(block, msg);
}
- return decl;
+ return decl_index;
}
return null;
}
@@ -13520,6 +18968,20 @@ fn structFieldPtr(
assert(unresolved_struct_ty.zigTypeTag() == .Struct);
const struct_ty = try sema.resolveTypeFields(block, src, unresolved_struct_ty);
+ try sema.resolveStructLayout(block, src, struct_ty);
+
+ if (struct_ty.isTuple()) {
+ if (mem.eql(u8, field_name, "len")) {
+ const len_inst = try sema.addIntUnsigned(Type.usize, struct_ty.structFieldCount());
+ return sema.analyzeRef(block, src, len_inst);
+ }
+ const field_index = try sema.tupleFieldIndex(block, struct_ty, field_name, field_name_src);
+ return sema.tupleFieldPtr(block, src, struct_ptr, field_name_src, field_index);
+ } else if (struct_ty.isAnonStruct()) {
+ const field_index = try sema.anonStructFieldIndex(block, struct_ty, field_name, field_name_src);
+ return sema.tupleFieldPtr(block, src, struct_ptr, field_name_src, field_index);
+ }
+
const struct_obj = struct_ty.castTag(.@"struct").?.data;
const field_index_big = struct_obj.fields.getIndex(field_name) orelse
@@ -13539,68 +19001,85 @@ fn structFieldPtrByIndex(
field_src: LazySrcLoc,
) CompileError!Air.Inst.Ref {
const field = struct_obj.fields.values()[field_index];
-
const struct_ptr_ty = sema.typeOf(struct_ptr);
+ const struct_ptr_ty_info = struct_ptr_ty.ptrInfo().data;
+
var ptr_ty_data: Type.Payload.Pointer.Data = .{
.pointee_type = field.ty,
- .mutable = struct_ptr_ty.ptrIsMutable(),
- .@"addrspace" = struct_ptr_ty.ptrAddressSpace(),
+ .mutable = struct_ptr_ty_info.mutable,
+ .@"addrspace" = struct_ptr_ty_info.@"addrspace",
};
- // TODO handle when the struct pointer is overaligned, we should return a potentially
- // over-aligned field pointer too.
- if (struct_obj.layout == .Packed) p: {
- const target = sema.mod.getTarget();
- comptime assert(Type.packed_struct_layout_version == 1);
- var offset: u64 = 0;
+ const target = sema.mod.getTarget();
+
+ if (struct_obj.layout == .Packed) {
+ comptime assert(Type.packed_struct_layout_version == 2);
+
var running_bits: u16 = 0;
for (struct_obj.fields.values()) |f, i| {
if (!(try sema.typeHasRuntimeBits(block, field_src, f.ty))) continue;
- const field_align = f.packedAlignment();
- if (field_align == 0) {
- if (i == field_index) {
- ptr_ty_data.bit_offset = running_bits;
- }
- running_bits += @intCast(u16, f.ty.bitSize(target));
- } else {
- if (running_bits != 0) {
- var int_payload: Type.Payload.Bits = .{
- .base = .{ .tag = .int_unsigned },
- .data = running_bits,
- };
- const int_ty: Type = .{ .ptr_otherwise = &int_payload.base };
- if (i > field_index) {
- ptr_ty_data.host_size = @intCast(u16, int_ty.abiSize(target));
- break :p;
- }
- const int_align = int_ty.abiAlignment(target);
- offset = std.mem.alignForwardGeneric(u64, offset, int_align);
- offset += int_ty.abiSize(target);
- running_bits = 0;
- }
- offset = std.mem.alignForwardGeneric(u64, offset, field_align);
- if (i == field_index) {
- break :p;
- }
- offset += f.ty.abiSize(target);
+ if (i == field_index) {
+ ptr_ty_data.bit_offset = running_bits;
}
+ running_bits += @intCast(u16, f.ty.bitSize(target));
}
- assert(running_bits != 0);
- var int_payload: Type.Payload.Bits = .{
- .base = .{ .tag = .int_unsigned },
- .data = running_bits,
- };
- const int_ty: Type = .{ .ptr_otherwise = &int_payload.base };
- ptr_ty_data.host_size = @intCast(u16, int_ty.abiSize(target));
+ ptr_ty_data.host_size = (running_bits + 7) / 8;
+
+ // If this is a packed struct embedded in another one, we need to offset
+ // the bits against each other.
+ if (struct_ptr_ty_info.host_size != 0) {
+ ptr_ty_data.host_size = struct_ptr_ty_info.host_size;
+ ptr_ty_data.bit_offset += struct_ptr_ty_info.bit_offset;
+ }
+
+ const parent_align = if (struct_ptr_ty_info.@"align" != 0)
+ struct_ptr_ty_info.@"align"
+ else
+ struct_ptr_ty_info.pointee_type.abiAlignment(target);
+ ptr_ty_data.@"align" = parent_align;
+
+ // If the field happens to be byte-aligned, simplify the pointer type.
+ // The pointee type bit size must match its ABI byte size so that loads and stores
+ // do not interfere with the surrounding packed bits.
+ // We do not attempt this with big-endian targets yet because of nested
+ // structs and floats. I need to double-check the desired behavior for big endian
+ // targets before adding the necessary complications to this code. This will not
+ // cause miscompilations; it only means the field pointer uses bit masking when it
+ // might not be strictly necessary.
+ if (parent_align != 0 and ptr_ty_data.bit_offset % 8 == 0 and
+ target.cpu.arch.endian() == .Little)
+ {
+ const elem_size_bytes = ptr_ty_data.pointee_type.abiSize(target);
+ const elem_size_bits = ptr_ty_data.pointee_type.bitSize(target);
+ if (elem_size_bytes * 8 == elem_size_bits) {
+ const byte_offset = ptr_ty_data.bit_offset / 8;
+ const new_align = @as(u32, 1) << @intCast(u5, @ctz(u64, byte_offset | parent_align));
+ ptr_ty_data.bit_offset = 0;
+ ptr_ty_data.host_size = 0;
+ ptr_ty_data.@"align" = new_align;
+ }
+ }
+ } else {
+ ptr_ty_data.@"align" = field.abi_align;
+ }
+
+ const ptr_field_ty = try Type.ptr(sema.arena, sema.mod, ptr_ty_data);
+
+ if (field.is_comptime) {
+ const val = try Value.Tag.comptime_field_ptr.create(sema.arena, .{
+ .field_ty = try field.ty.copy(sema.arena),
+ .field_val = try field.default_val.copy(sema.arena),
+ });
+ return sema.addConstant(ptr_field_ty, val);
}
- const ptr_field_ty = try Type.ptr(sema.arena, ptr_ty_data);
if (try sema.resolveDefinedValue(block, src, struct_ptr)) |struct_ptr_val| {
return sema.addConstant(
ptr_field_ty,
try Value.Tag.field_ptr.create(sema.arena, .{
.container_ptr = struct_ptr_val,
+ .container_ty = struct_ptr_ty.childType(),
.field_index = field_index,
}),
);
@@ -13622,25 +19101,104 @@ fn structFieldVal(
assert(unresolved_struct_ty.zigTypeTag() == .Struct);
const struct_ty = try sema.resolveTypeFields(block, src, unresolved_struct_ty);
- const struct_obj = struct_ty.castTag(.@"struct").?.data;
+ switch (struct_ty.tag()) {
+ .tuple, .empty_struct_literal => return sema.tupleFieldVal(block, src, struct_byval, field_name, field_name_src, struct_ty),
+ .anon_struct => {
+ const field_index = try sema.anonStructFieldIndex(block, struct_ty, field_name, field_name_src);
+ return tupleFieldValByIndex(sema, block, src, struct_byval, field_index, struct_ty);
+ },
+ .@"struct" => {
+ const struct_obj = struct_ty.castTag(.@"struct").?.data;
- const field_index_usize = struct_obj.fields.getIndex(field_name) orelse
- return sema.failWithBadStructFieldAccess(block, struct_obj, field_name_src, field_name);
- const field_index = @intCast(u32, field_index_usize);
- const field = struct_obj.fields.values()[field_index];
+ const field_index_usize = struct_obj.fields.getIndex(field_name) orelse
+ return sema.failWithBadStructFieldAccess(block, struct_obj, field_name_src, field_name);
+ const field_index = @intCast(u32, field_index_usize);
+ const field = struct_obj.fields.values()[field_index];
- if (try sema.resolveMaybeUndefVal(block, src, struct_byval)) |struct_val| {
- if (struct_val.isUndef()) return sema.addConstUndef(field.ty);
- if ((try sema.typeHasOnePossibleValue(block, src, field.ty))) |opv| {
- return sema.addConstant(field.ty, opv);
- }
+ if (field.is_comptime) {
+ return sema.addConstant(field.ty, field.default_val);
+ }
- const field_values = struct_val.castTag(.@"struct").?.data;
- return sema.addConstant(field.ty, field_values[field_index]);
+ if (try sema.resolveMaybeUndefVal(block, src, struct_byval)) |struct_val| {
+ if (struct_val.isUndef()) return sema.addConstUndef(field.ty);
+ if ((try sema.typeHasOnePossibleValue(block, src, field.ty))) |opv| {
+ return sema.addConstant(field.ty, opv);
+ }
+
+ const field_values = struct_val.castTag(.aggregate).?.data;
+ return sema.addConstant(field.ty, field_values[field_index]);
+ }
+
+ try sema.requireRuntimeBlock(block, src);
+ return block.addStructFieldVal(struct_byval, field_index, field.ty);
+ },
+ else => unreachable,
+ }
+}
+
+fn tupleFieldVal(
+ sema: *Sema,
+ block: *Block,
+ src: LazySrcLoc,
+ tuple_byval: Air.Inst.Ref,
+ field_name: []const u8,
+ field_name_src: LazySrcLoc,
+ tuple_ty: Type,
+) CompileError!Air.Inst.Ref {
+ if (mem.eql(u8, field_name, "len")) {
+ return sema.addIntUnsigned(Type.usize, tuple_ty.structFieldCount());
+ }
+ const field_index = try sema.tupleFieldIndex(block, tuple_ty, field_name, field_name_src);
+ return tupleFieldValByIndex(sema, block, src, tuple_byval, field_index, tuple_ty);
+}
+
+/// Don't forget to check for "len" before calling this.
+fn tupleFieldIndex(
+ sema: *Sema,
+ block: *Block,
+ tuple_ty: Type,
+ field_name: []const u8,
+ field_name_src: LazySrcLoc,
+) CompileError!u32 {
+ const field_index = std.fmt.parseUnsigned(u32, field_name, 10) catch |err| {
+ return sema.fail(block, field_name_src, "tuple '{}' has no such field '{s}': {s}", .{
+ tuple_ty.fmt(sema.mod), field_name, @errorName(err),
+ });
+ };
+ if (field_index >= tuple_ty.structFieldCount()) {
+ return sema.fail(block, field_name_src, "tuple '{}' has no such field '{s}'", .{
+ tuple_ty.fmt(sema.mod), field_name,
+ });
+ }
+ return field_index;
+}
+
+fn tupleFieldValByIndex(
+ sema: *Sema,
+ block: *Block,
+ src: LazySrcLoc,
+ tuple_byval: Air.Inst.Ref,
+ field_index: u32,
+ tuple_ty: Type,
+) CompileError!Air.Inst.Ref {
+ const tuple = tuple_ty.tupleFields();
+ const field_ty = tuple.types[field_index];
+
+ if (tuple.values[field_index].tag() != .unreachable_value) {
+ return sema.addConstant(field_ty, tuple.values[field_index]);
+ }
+
+ if (try sema.resolveMaybeUndefVal(block, src, tuple_byval)) |tuple_val| {
+ if (tuple_val.isUndef()) return sema.addConstUndef(field_ty);
+ if ((try sema.typeHasOnePossibleValue(block, src, field_ty))) |opv| {
+ return sema.addConstant(field_ty, opv);
+ }
+ const field_values = tuple_val.castTag(.aggregate).?.data;
+ return sema.addConstant(field_ty, field_values[field_index]);
}
try sema.requireRuntimeBlock(block, src);
- return block.addStructFieldVal(struct_byval, field_index, field.ty);
+ return block.addStructFieldVal(tuple_byval, field_index, field_ty);
}
fn unionFieldPtr(
@@ -13658,24 +19216,46 @@ fn unionFieldPtr(
const union_ptr_ty = sema.typeOf(union_ptr);
const union_ty = try sema.resolveTypeFields(block, src, unresolved_union_ty);
const union_obj = union_ty.cast(Type.Payload.Union).?.data;
-
- const field_index_big = union_obj.fields.getIndex(field_name) orelse
- return sema.failWithBadUnionFieldAccess(block, union_obj, field_name_src, field_name);
- const field_index = @intCast(u32, field_index_big);
-
+ const field_index = try sema.unionFieldIndex(block, union_ty, field_name, field_name_src);
const field = union_obj.fields.values()[field_index];
- const ptr_field_ty = try Type.ptr(arena, .{
+ const ptr_field_ty = try Type.ptr(arena, sema.mod, .{
.pointee_type = field.ty,
.mutable = union_ptr_ty.ptrIsMutable(),
.@"addrspace" = union_ptr_ty.ptrAddressSpace(),
});
if (try sema.resolveDefinedValue(block, src, union_ptr)) |union_ptr_val| {
- // TODO detect inactive union field and emit compile error
+ switch (union_obj.layout) {
+ .Auto => {
+ // TODO emit the access of inactive union field error commented out below.
+ // In order to do that, we need to first solve the problem that AstGen
+ // emits field_ptr instructions in order to initialize union values.
+ // In such case we need to know that the field_ptr instruction (which is
+ // calling this unionFieldPtr function) is *initializing* the union,
+ // in which case we would skip this check, and in fact we would actually
+ // set the union tag here and the payload to undefined.
+
+ //const tag_and_val = union_val.castTag(.@"union").?.data;
+ //var field_tag_buf: Value.Payload.U32 = .{
+ // .base = .{ .tag = .enum_field_index },
+ // .data = field_index,
+ //};
+ //const field_tag = Value.initPayload(&field_tag_buf.base);
+ //const tag_matches = tag_and_val.tag.eql(field_tag, union_obj.tag_ty, mod);
+ //if (!tag_matches) {
+ // // TODO enhance this saying which one was active
+ // // and which one was accessed, and showing where the union was declared.
+ // return sema.fail(block, src, "access of inactive union field", .{});
+ //}
+ // TODO add runtime safety check for the active tag
+ },
+ .Packed, .Extern => {},
+ }
return sema.addConstant(
ptr_field_ty,
try Value.Tag.field_ptr.create(arena, .{
.container_ptr = union_ptr_val,
+ .container_ty = union_ty,
.field_index = field_index,
}),
);
@@ -13698,18 +19278,45 @@ fn unionFieldVal(
const union_ty = try sema.resolveTypeFields(block, src, unresolved_union_ty);
const union_obj = union_ty.cast(Type.Payload.Union).?.data;
-
- const field_index_usize = union_obj.fields.getIndex(field_name) orelse
- return sema.failWithBadUnionFieldAccess(block, union_obj, field_name_src, field_name);
- const field_index = @intCast(u32, field_index_usize);
+ const field_index = try sema.unionFieldIndex(block, union_ty, field_name, field_name_src);
const field = union_obj.fields.values()[field_index];
if (try sema.resolveMaybeUndefVal(block, src, union_byval)) |union_val| {
if (union_val.isUndef()) return sema.addConstUndef(field.ty);
- // TODO detect inactive union field and emit compile error
- const active_val = union_val.castTag(.@"union").?.data.val;
- return sema.addConstant(field.ty, active_val);
+ const tag_and_val = union_val.castTag(.@"union").?.data;
+ var field_tag_buf: Value.Payload.U32 = .{
+ .base = .{ .tag = .enum_field_index },
+ .data = field_index,
+ };
+ const field_tag = Value.initPayload(&field_tag_buf.base);
+ const tag_matches = tag_and_val.tag.eql(field_tag, union_obj.tag_ty, sema.mod);
+ switch (union_obj.layout) {
+ .Auto => {
+ if (tag_matches) {
+ return sema.addConstant(field.ty, tag_and_val.val);
+ } else {
+ const msg = msg: {
+ const active_index = tag_and_val.tag.castTag(.enum_field_index).?.data;
+ const active_field_name = union_obj.fields.keys()[active_index];
+ const msg = try sema.errMsg(block, src, "access of union field '{s}' while field '{s}' is active", .{ field_name, active_field_name });
+ errdefer msg.destroy(sema.gpa);
+ try sema.addDeclaredHereNote(msg, union_ty);
+ break :msg msg;
+ };
+ return sema.failWithOwnedErrorMsg(block, msg);
+ }
+ },
+ .Packed, .Extern => {
+ if (tag_matches) {
+ return sema.addConstant(field.ty, tag_and_val.val);
+ } else {
+ const old_ty = union_ty.unionFieldType(tag_and_val.tag, sema.mod);
+ const new_val = try sema.bitCastVal(block, src, tag_and_val.val, old_ty, field.ty, 0);
+ return sema.addConstant(field.ty, new_val);
+ }
+ },
+ }
}
try sema.requireRuntimeBlock(block, src);
@@ -13720,62 +19327,57 @@ fn elemPtr(
sema: *Sema,
block: *Block,
src: LazySrcLoc,
- array_ptr: Air.Inst.Ref,
+ indexable_ptr: Air.Inst.Ref,
elem_index: Air.Inst.Ref,
elem_index_src: LazySrcLoc,
+ init: bool,
) CompileError!Air.Inst.Ref {
- const array_ptr_src = src; // TODO better source location
- const array_ptr_ty = sema.typeOf(array_ptr);
- const array_ty = switch (array_ptr_ty.zigTypeTag()) {
- .Pointer => array_ptr_ty.elemType(),
- else => return sema.fail(block, array_ptr_src, "expected pointer, found '{}'", .{array_ptr_ty}),
+ const indexable_ptr_src = src; // TODO better source location
+ const indexable_ptr_ty = sema.typeOf(indexable_ptr);
+ const target = sema.mod.getTarget();
+ const indexable_ty = switch (indexable_ptr_ty.zigTypeTag()) {
+ .Pointer => indexable_ptr_ty.elemType(),
+ else => return sema.fail(block, indexable_ptr_src, "expected pointer, found '{}'", .{indexable_ptr_ty.fmt(sema.mod)}),
};
- if (!array_ty.isIndexable()) {
- return sema.fail(block, src, "array access of non-indexable type '{}'", .{array_ty});
+ if (!indexable_ty.isIndexable()) {
+ return sema.fail(block, src, "element access of non-indexable type '{}'", .{indexable_ty.fmt(sema.mod)});
}
- switch (array_ty.zigTypeTag()) {
+ switch (indexable_ty.zigTypeTag()) {
.Pointer => {
- // In all below cases, we have to deref the ptr operand to get the actual array pointer.
- const array = try sema.analyzeLoad(block, array_ptr_src, array_ptr, array_ptr_src);
- const result_ty = try array_ty.elemPtrType(sema.arena);
- switch (array_ty.ptrSize()) {
- .Slice => {
- const maybe_slice_val = try sema.resolveDefinedValue(block, array_ptr_src, array);
- const maybe_index_val = try sema.resolveDefinedValue(block, elem_index_src, elem_index);
- const runtime_src = if (maybe_slice_val) |slice_val| rs: {
- const index_val = maybe_index_val orelse break :rs elem_index_src;
- const index = @intCast(usize, index_val.toUnsignedInt());
- const elem_ptr = try slice_val.elemPtr(sema.arena, index);
- return sema.addConstant(result_ty, elem_ptr);
- } else array_ptr_src;
-
- try sema.requireRuntimeBlock(block, runtime_src);
- return block.addSliceElemPtr(array, elem_index, result_ty);
- },
+ // In all below cases, we have to deref the ptr operand to get the actual indexable pointer.
+ const indexable = try sema.analyzeLoad(block, indexable_ptr_src, indexable_ptr, indexable_ptr_src);
+ switch (indexable_ty.ptrSize()) {
+ .Slice => return sema.elemPtrSlice(block, indexable_ptr_src, indexable, elem_index_src, elem_index),
.Many, .C => {
- const maybe_ptr_val = try sema.resolveDefinedValue(block, array_ptr_src, array);
+ const maybe_ptr_val = try sema.resolveDefinedValue(block, indexable_ptr_src, indexable);
const maybe_index_val = try sema.resolveDefinedValue(block, elem_index_src, elem_index);
-
const runtime_src = rs: {
- const ptr_val = maybe_ptr_val orelse break :rs array_ptr_src;
+ const ptr_val = maybe_ptr_val orelse break :rs indexable_ptr_src;
const index_val = maybe_index_val orelse break :rs elem_index_src;
- const index = @intCast(usize, index_val.toUnsignedInt());
- const elem_ptr = try ptr_val.elemPtr(sema.arena, index);
+ const index = @intCast(usize, index_val.toUnsignedInt(target));
+ const elem_ptr = try ptr_val.elemPtr(indexable_ty, sema.arena, index, sema.mod);
+ const result_ty = try sema.elemPtrType(indexable_ty, index);
return sema.addConstant(result_ty, elem_ptr);
};
+ const result_ty = try sema.elemPtrType(indexable_ty, null);
try sema.requireRuntimeBlock(block, runtime_src);
- return block.addPtrElemPtr(array, elem_index, result_ty);
+ return block.addPtrElemPtr(indexable, elem_index, result_ty);
},
.One => {
- assert(array_ty.childType().zigTypeTag() == .Array); // Guaranteed by isIndexable
- return sema.elemPtrArray(block, array_ptr_src, array, elem_index, elem_index_src);
+ assert(indexable_ty.childType().zigTypeTag() == .Array); // Guaranteed by isIndexable
+ return sema.elemPtrArray(block, indexable_ptr_src, indexable, elem_index_src, elem_index, init);
},
}
},
- .Array => return sema.elemPtrArray(block, array_ptr_src, array_ptr, elem_index, elem_index_src),
- .Vector => return sema.fail(block, src, "TODO implement Sema for elemPtr for vector", .{}),
+ .Array, .Vector => return sema.elemPtrArray(block, indexable_ptr_src, indexable_ptr, elem_index_src, elem_index, init),
+ .Struct => {
+ // Tuple field access.
+ const index_val = try sema.resolveConstValue(block, elem_index_src, elem_index);
+ const index = @intCast(u32, index_val.toUnsignedInt(target));
+ return sema.tupleFieldPtr(block, src, indexable_ptr, elem_index_src, index);
+ },
else => unreachable,
}
}
@@ -13784,93 +19386,131 @@ fn elemVal(
sema: *Sema,
block: *Block,
src: LazySrcLoc,
- array: Air.Inst.Ref,
+ indexable: Air.Inst.Ref,
elem_index_uncasted: Air.Inst.Ref,
elem_index_src: LazySrcLoc,
) CompileError!Air.Inst.Ref {
- const array_src = src; // TODO better source location
- const array_ty = sema.typeOf(array);
+ const indexable_src = src; // TODO better source location
+ const indexable_ty = sema.typeOf(indexable);
+ const target = sema.mod.getTarget();
- if (!array_ty.isIndexable()) {
- return sema.fail(block, src, "array access of non-indexable type '{}'", .{array_ty});
+ if (!indexable_ty.isIndexable()) {
+ return sema.fail(block, src, "element access of non-indexable type '{}'", .{indexable_ty.fmt(sema.mod)});
}
// TODO in case of a vector of pointers, we need to detect whether the element
// index is a scalar or vector instead of unconditionally casting to usize.
const elem_index = try sema.coerce(block, Type.usize, elem_index_uncasted, elem_index_src);
- switch (array_ty.zigTypeTag()) {
- .Pointer => switch (array_ty.ptrSize()) {
- .Slice => {
- const maybe_slice_val = try sema.resolveDefinedValue(block, array_src, array);
- const maybe_index_val = try sema.resolveDefinedValue(block, elem_index_src, elem_index);
- const runtime_src = if (maybe_slice_val) |slice_val| rs: {
- const index_val = maybe_index_val orelse break :rs elem_index_src;
- const index = @intCast(usize, index_val.toUnsignedInt());
- const elem_val = try slice_val.elemValue(sema.arena, index);
- return sema.addConstant(array_ty.elemType2(), elem_val);
- } else array_src;
-
- try sema.requireRuntimeBlock(block, runtime_src);
- return block.addBinOp(.slice_elem_val, array, elem_index);
- },
+ switch (indexable_ty.zigTypeTag()) {
+ .Pointer => switch (indexable_ty.ptrSize()) {
+ .Slice => return sema.elemValSlice(block, indexable_src, indexable, elem_index_src, elem_index),
.Many, .C => {
- const maybe_ptr_val = try sema.resolveDefinedValue(block, array_src, array);
+ const maybe_indexable_val = try sema.resolveDefinedValue(block, indexable_src, indexable);
const maybe_index_val = try sema.resolveDefinedValue(block, elem_index_src, elem_index);
const runtime_src = rs: {
- const ptr_val = maybe_ptr_val orelse break :rs array_src;
+ const indexable_val = maybe_indexable_val orelse break :rs indexable_src;
const index_val = maybe_index_val orelse break :rs elem_index_src;
- const index = @intCast(usize, index_val.toUnsignedInt());
- const maybe_array_val = try sema.pointerDeref(block, array_src, ptr_val, array_ty);
- const array_val = maybe_array_val orelse break :rs array_src;
- const elem_val = try array_val.elemValue(sema.arena, index);
- return sema.addConstant(array_ty.elemType2(), elem_val);
+ const index = @intCast(usize, index_val.toUnsignedInt(target));
+ const elem_ptr_val = try indexable_val.elemPtr(indexable_ty, sema.arena, index, sema.mod);
+ if (try sema.pointerDeref(block, indexable_src, elem_ptr_val, indexable_ty)) |elem_val| {
+ return sema.addConstant(indexable_ty.elemType2(), elem_val);
+ }
+ break :rs indexable_src;
};
try sema.requireRuntimeBlock(block, runtime_src);
- return block.addBinOp(.ptr_elem_val, array, elem_index);
+ return block.addBinOp(.ptr_elem_val, indexable, elem_index);
},
.One => {
- assert(array_ty.childType().zigTypeTag() == .Array); // Guaranteed by isIndexable
- const elem_ptr = try sema.elemPtr(block, array_src, array, elem_index, elem_index_src);
- return sema.analyzeLoad(block, array_src, elem_ptr, elem_index_src);
+ assert(indexable_ty.childType().zigTypeTag() == .Array); // Guaranteed by isIndexable
+ const elem_ptr = try sema.elemPtr(block, indexable_src, indexable, elem_index, elem_index_src, false);
+ return sema.analyzeLoad(block, indexable_src, elem_ptr, elem_index_src);
},
},
- .Array => return elemValArray(sema, block, array, elem_index, array_src, elem_index_src),
+ .Array => return elemValArray(sema, block, indexable_src, indexable, elem_index_src, elem_index),
.Vector => {
// TODO: If the index is a vector, the result should be a vector.
- return elemValArray(sema, block, array, elem_index, array_src, elem_index_src);
+ return elemValArray(sema, block, indexable_src, indexable, elem_index_src, elem_index);
},
.Struct => {
// Tuple field access.
const index_val = try sema.resolveConstValue(block, elem_index_src, elem_index);
- const index = @intCast(u32, index_val.toUnsignedInt());
- return tupleField(sema, block, array, index, array_src, elem_index_src);
+ const index = @intCast(u32, index_val.toUnsignedInt(target));
+ return tupleField(sema, block, indexable_src, indexable, elem_index_src, index);
},
else => unreachable,
}
}
-fn tupleField(
+fn tupleFieldPtr(
sema: *Sema,
block: *Block,
- tuple: Air.Inst.Ref,
+ tuple_ptr_src: LazySrcLoc,
+ tuple_ptr: Air.Inst.Ref,
+ field_index_src: LazySrcLoc,
field_index: u32,
+) CompileError!Air.Inst.Ref {
+ const tuple_ptr_ty = sema.typeOf(tuple_ptr);
+ const tuple_ty = tuple_ptr_ty.childType();
+ const tuple_fields = tuple_ty.tupleFields();
+
+ if (tuple_fields.types.len == 0) {
+ return sema.fail(block, tuple_ptr_src, "indexing into empty tuple is not allowed", .{});
+ }
+
+ if (field_index >= tuple_fields.types.len) {
+ return sema.fail(block, field_index_src, "index {d} outside tuple of length {d}", .{
+ field_index, tuple_fields.types.len,
+ });
+ }
+
+ const field_ty = tuple_fields.types[field_index];
+ const ptr_field_ty = try Type.ptr(sema.arena, sema.mod, .{
+ .pointee_type = field_ty,
+ .mutable = tuple_ptr_ty.ptrIsMutable(),
+ .@"addrspace" = tuple_ptr_ty.ptrAddressSpace(),
+ });
+
+ if (try sema.resolveMaybeUndefVal(block, tuple_ptr_src, tuple_ptr)) |tuple_ptr_val| {
+ return sema.addConstant(
+ ptr_field_ty,
+ try Value.Tag.field_ptr.create(sema.arena, .{
+ .container_ptr = tuple_ptr_val,
+ .container_ty = tuple_ty,
+ .field_index = field_index,
+ }),
+ );
+ }
+
+ try sema.requireRuntimeBlock(block, tuple_ptr_src);
+ return block.addStructFieldPtr(tuple_ptr, field_index, ptr_field_ty);
+}
+
+fn tupleField(
+ sema: *Sema,
+ block: *Block,
tuple_src: LazySrcLoc,
+ tuple: Air.Inst.Ref,
field_index_src: LazySrcLoc,
+ field_index: u32,
) CompileError!Air.Inst.Ref {
const tuple_ty = sema.typeOf(tuple);
- const tuple_info = tuple_ty.castTag(.tuple).?.data;
+ const tuple_fields = tuple_ty.tupleFields();
- if (field_index > tuple_info.types.len) {
+ if (tuple_fields.types.len == 0) {
+ return sema.fail(block, tuple_src, "indexing into empty tuple is not allowed", .{});
+ }
+
+ if (field_index >= tuple_fields.types.len) {
return sema.fail(block, field_index_src, "index {d} outside tuple of length {d}", .{
- field_index, tuple_info.types.len,
+ field_index, tuple_fields.types.len,
});
}
- const field_ty = tuple_info.types[field_index];
- const field_val = tuple_info.values[field_index];
+ const field_ty = tuple_fields.types[field_index];
+ const field_val = tuple_fields.values[field_index];
if (field_val.tag() != .unreachable_value) {
return sema.addConstant(field_ty, field_val); // comptime field
@@ -13878,7 +19518,7 @@ fn tupleField(
if (try sema.resolveMaybeUndefVal(block, tuple_src, tuple)) |tuple_val| {
if (tuple_val.isUndef()) return sema.addConstUndef(field_ty);
- const field_values = tuple_val.castTag(.@"struct").?.data;
+ const field_values = tuple_val.castTag(.aggregate).?.data;
return sema.addConstant(field_ty, field_values[field_index]);
}
@@ -13889,56 +19529,262 @@ fn tupleField(
fn elemValArray(
sema: *Sema,
block: *Block,
- array: Air.Inst.Ref,
- elem_index: Air.Inst.Ref,
array_src: LazySrcLoc,
+ array: Air.Inst.Ref,
elem_index_src: LazySrcLoc,
+ elem_index: Air.Inst.Ref,
) CompileError!Air.Inst.Ref {
const array_ty = sema.typeOf(array);
- if (try sema.resolveMaybeUndefVal(block, array_src, array)) |array_val| {
- const elem_ty = array_ty.childType();
- if (array_val.isUndef()) return sema.addConstUndef(elem_ty);
- const maybe_index_val = try sema.resolveDefinedValue(block, elem_index_src, elem_index);
- if (maybe_index_val) |index_val| {
- const index = @intCast(usize, index_val.toUnsignedInt());
- const len = array_ty.arrayLenIncludingSentinel();
- if (index >= len) {
- return sema.fail(block, elem_index_src, "index {d} outside array of length {d}", .{
- index, len,
- });
+ const array_sent = array_ty.sentinel();
+ const array_len = array_ty.arrayLen();
+ const array_len_s = array_len + @boolToInt(array_sent != null);
+ const elem_ty = array_ty.childType();
+
+ if (array_len_s == 0) {
+ return sema.fail(block, array_src, "indexing into empty array is not allowed", .{});
+ }
+
+ const maybe_undef_array_val = try sema.resolveMaybeUndefVal(block, array_src, array);
+ // index must be defined since it can access out of bounds
+ const maybe_index_val = try sema.resolveDefinedValue(block, elem_index_src, elem_index);
+ const target = sema.mod.getTarget();
+
+ if (maybe_index_val) |index_val| {
+ const index = @intCast(usize, index_val.toUnsignedInt(target));
+ if (array_sent) |s| {
+ if (index == array_len) {
+ return sema.addConstant(elem_ty, s);
}
- const elem_val = try array_val.elemValue(sema.arena, index);
+ }
+ if (index >= array_len_s) {
+ const sentinel_label: []const u8 = if (array_sent != null) " +1 (sentinel)" else "";
+ return sema.fail(block, elem_index_src, "index {d} outside array of length {d}{s}", .{ index, array_len, sentinel_label });
+ }
+ }
+ if (maybe_undef_array_val) |array_val| {
+ if (array_val.isUndef()) {
+ return sema.addConstUndef(elem_ty);
+ }
+ if (maybe_index_val) |index_val| {
+ const index = @intCast(usize, index_val.toUnsignedInt(target));
+ const elem_val = try array_val.elemValue(sema.mod, sema.arena, index);
return sema.addConstant(elem_ty, elem_val);
}
}
- try sema.requireRuntimeBlock(block, array_src);
+
+ const valid_rt = try sema.validateRunTimeType(block, elem_index_src, elem_ty, false);
+ if (!valid_rt) {
+ const msg = msg: {
+ const msg = try sema.errMsg(
+ block,
+ elem_index_src,
+ "values of type '{}' must be comptime known, but index value is runtime known",
+ .{array_ty.fmt(sema.mod)},
+ );
+ errdefer msg.destroy(sema.gpa);
+
+ const src_decl = sema.mod.declPtr(block.src_decl);
+ try sema.explainWhyTypeIsComptime(block, elem_index_src, msg, array_src.toSrcLoc(src_decl), array_ty);
+
+ break :msg msg;
+ };
+ return sema.failWithOwnedErrorMsg(block, msg);
+ }
+
+ const runtime_src = if (maybe_undef_array_val != null) elem_index_src else array_src;
+ try sema.requireRuntimeBlock(block, runtime_src);
+ if (block.wantSafety()) {
+ // Runtime check is only needed if unable to comptime check
+ if (maybe_index_val == null) {
+ const len_inst = try sema.addIntUnsigned(Type.usize, array_len);
+ const cmp_op: Air.Inst.Tag = if (array_sent != null) .cmp_lte else .cmp_lt;
+ try sema.panicIndexOutOfBounds(block, elem_index_src, elem_index, len_inst, cmp_op);
+ }
+ }
return block.addBinOp(.array_elem_val, array, elem_index);
}
fn elemPtrArray(
sema: *Sema,
block: *Block,
- src: LazySrcLoc,
+ array_ptr_src: LazySrcLoc,
array_ptr: Air.Inst.Ref,
- elem_index: Air.Inst.Ref,
elem_index_src: LazySrcLoc,
+ elem_index: Air.Inst.Ref,
+ init: bool,
) CompileError!Air.Inst.Ref {
+ const target = sema.mod.getTarget();
const array_ptr_ty = sema.typeOf(array_ptr);
- const result_ty = try array_ptr_ty.elemPtrType(sema.arena);
+ const array_ty = array_ptr_ty.childType();
+ const array_sent = array_ty.sentinel() != null;
+ const array_len = array_ty.arrayLen();
+ const array_len_s = array_len + @boolToInt(array_sent);
+
+ if (array_len_s == 0) {
+ return sema.fail(block, array_ptr_src, "indexing into empty array is not allowed", .{});
+ }
+
+ const maybe_undef_array_ptr_val = try sema.resolveMaybeUndefVal(block, array_ptr_src, array_ptr);
+ // The index must not be undefined since it can be out of bounds.
+ const offset: ?usize = if (try sema.resolveDefinedValue(block, elem_index_src, elem_index)) |index_val| o: {
+ const index = try sema.usizeCast(block, elem_index_src, index_val.toUnsignedInt(target));
+ if (index >= array_len_s) {
+ const sentinel_label: []const u8 = if (array_sent) " +1 (sentinel)" else "";
+ return sema.fail(block, elem_index_src, "index {d} outside array of length {d}{s}", .{ index, array_len, sentinel_label });
+ }
+ break :o index;
+ } else null;
+
+ const elem_ptr_ty = try sema.elemPtrType(array_ptr_ty, offset);
- if (try sema.resolveDefinedValue(block, src, array_ptr)) |array_ptr_val| {
- if (try sema.resolveDefinedValue(block, elem_index_src, elem_index)) |index_val| {
- // Both array pointer and index are compile-time known.
- const index_u64 = index_val.toUnsignedInt();
- // @intCast here because it would have been impossible to construct a value that
- // required a larger index.
- const elem_ptr = try array_ptr_val.elemPtr(sema.arena, @intCast(usize, index_u64));
- return sema.addConstant(result_ty, elem_ptr);
+ if (maybe_undef_array_ptr_val) |array_ptr_val| {
+ if (array_ptr_val.isUndef()) {
+ return sema.addConstUndef(elem_ptr_ty);
+ }
+ if (offset) |index| {
+ const elem_ptr = try array_ptr_val.elemPtr(array_ptr_ty, sema.arena, index, sema.mod);
+ return sema.addConstant(elem_ptr_ty, elem_ptr);
}
}
- // TODO safety check for array bounds
- try sema.requireRuntimeBlock(block, src);
- return block.addPtrElemPtr(array_ptr, elem_index, result_ty);
+
+ const valid_rt = try sema.validateRunTimeType(block, elem_index_src, array_ty.elemType2(), false);
+ if (!valid_rt and !init) {
+ const msg = msg: {
+ const msg = try sema.errMsg(
+ block,
+ elem_index_src,
+ "values of type '{}' must be comptime known, but index value is runtime known",
+ .{array_ty.fmt(sema.mod)},
+ );
+ errdefer msg.destroy(sema.gpa);
+
+ const src_decl = sema.mod.declPtr(block.src_decl);
+ try sema.explainWhyTypeIsComptime(block, elem_index_src, msg, array_ptr_src.toSrcLoc(src_decl), array_ty);
+
+ break :msg msg;
+ };
+ return sema.failWithOwnedErrorMsg(block, msg);
+ }
+
+ const runtime_src = if (maybe_undef_array_ptr_val != null) elem_index_src else array_ptr_src;
+ try sema.requireRuntimeBlock(block, runtime_src);
+
+ // Runtime check is only needed if unable to comptime check.
+ if (block.wantSafety() and offset == null) {
+ const len_inst = try sema.addIntUnsigned(Type.usize, array_len);
+ const cmp_op: Air.Inst.Tag = if (array_sent) .cmp_lte else .cmp_lt;
+ try sema.panicIndexOutOfBounds(block, elem_index_src, elem_index, len_inst, cmp_op);
+ }
+
+ return block.addPtrElemPtr(array_ptr, elem_index, elem_ptr_ty);
+}
+
+fn elemValSlice(
+ sema: *Sema,
+ block: *Block,
+ slice_src: LazySrcLoc,
+ slice: Air.Inst.Ref,
+ elem_index_src: LazySrcLoc,
+ elem_index: Air.Inst.Ref,
+) CompileError!Air.Inst.Ref {
+ const slice_ty = sema.typeOf(slice);
+ const slice_sent = slice_ty.sentinel() != null;
+ const elem_ty = slice_ty.elemType2();
+ var runtime_src = slice_src;
+
+ // slice must be defined since it can dereferenced as null
+ const maybe_slice_val = try sema.resolveDefinedValue(block, slice_src, slice);
+ // index must be defined since it can index out of bounds
+ const maybe_index_val = try sema.resolveDefinedValue(block, elem_index_src, elem_index);
+ const target = sema.mod.getTarget();
+
+ if (maybe_slice_val) |slice_val| {
+ runtime_src = elem_index_src;
+ const slice_len = slice_val.sliceLen(sema.mod);
+ const slice_len_s = slice_len + @boolToInt(slice_sent);
+ if (slice_len_s == 0) {
+ return sema.fail(block, slice_src, "indexing into empty slice is not allowed", .{});
+ }
+ if (maybe_index_val) |index_val| {
+ const index = @intCast(usize, index_val.toUnsignedInt(target));
+ if (index >= slice_len_s) {
+ const sentinel_label: []const u8 = if (slice_sent) " +1 (sentinel)" else "";
+ return sema.fail(block, elem_index_src, "index {d} outside slice of length {d}{s}", .{ index, slice_len, sentinel_label });
+ }
+ const elem_ptr_val = try slice_val.elemPtr(slice_ty, sema.arena, index, sema.mod);
+ if (try sema.pointerDeref(block, slice_src, elem_ptr_val, slice_ty)) |elem_val| {
+ return sema.addConstant(elem_ty, elem_val);
+ }
+ runtime_src = slice_src;
+ }
+ }
+
+ try sema.requireRuntimeBlock(block, runtime_src);
+ if (block.wantSafety()) {
+ const len_inst = if (maybe_slice_val) |slice_val|
+ try sema.addIntUnsigned(Type.usize, slice_val.sliceLen(sema.mod))
+ else
+ try block.addTyOp(.slice_len, Type.usize, slice);
+ const cmp_op: Air.Inst.Tag = if (slice_sent) .cmp_lte else .cmp_lt;
+ try sema.panicIndexOutOfBounds(block, elem_index_src, elem_index, len_inst, cmp_op);
+ }
+ try sema.queueFullTypeResolution(sema.typeOf(slice));
+ return block.addBinOp(.slice_elem_val, slice, elem_index);
+}
+
+fn elemPtrSlice(
+ sema: *Sema,
+ block: *Block,
+ slice_src: LazySrcLoc,
+ slice: Air.Inst.Ref,
+ elem_index_src: LazySrcLoc,
+ elem_index: Air.Inst.Ref,
+) CompileError!Air.Inst.Ref {
+ const target = sema.mod.getTarget();
+ const slice_ty = sema.typeOf(slice);
+ const slice_sent = slice_ty.sentinel() != null;
+
+ const maybe_undef_slice_val = try sema.resolveMaybeUndefVal(block, slice_src, slice);
+ // The index must not be undefined since it can be out of bounds.
+ const offset: ?usize = if (try sema.resolveDefinedValue(block, elem_index_src, elem_index)) |index_val| o: {
+ const index = try sema.usizeCast(block, elem_index_src, index_val.toUnsignedInt(target));
+ break :o index;
+ } else null;
+
+ const elem_ptr_ty = try sema.elemPtrType(slice_ty, null);
+
+ if (maybe_undef_slice_val) |slice_val| {
+ if (slice_val.isUndef()) {
+ return sema.addConstUndef(elem_ptr_ty);
+ }
+ const slice_len = slice_val.sliceLen(sema.mod);
+ const slice_len_s = slice_len + @boolToInt(slice_sent);
+ if (slice_len_s == 0) {
+ return sema.fail(block, slice_src, "indexing into empty slice is not allowed", .{});
+ }
+ if (offset) |index| {
+ if (index >= slice_len_s) {
+ const sentinel_label: []const u8 = if (slice_sent) " +1 (sentinel)" else "";
+ return sema.fail(block, elem_index_src, "index {d} outside slice of length {d}{s}", .{ index, slice_len, sentinel_label });
+ }
+ const elem_ptr_val = try slice_val.elemPtr(slice_ty, sema.arena, index, sema.mod);
+ return sema.addConstant(elem_ptr_ty, elem_ptr_val);
+ }
+ }
+
+ const runtime_src = if (maybe_undef_slice_val != null) elem_index_src else slice_src;
+ try sema.requireRuntimeBlock(block, runtime_src);
+ if (block.wantSafety()) {
+ const len_inst = len: {
+ if (maybe_undef_slice_val) |slice_val|
+ if (!slice_val.isUndef())
+ break :len try sema.addIntUnsigned(Type.usize, slice_val.sliceLen(sema.mod));
+ break :len try block.addTyOp(.slice_len, Type.usize, slice);
+ };
+ const cmp_op: Air.Inst.Tag = if (slice_sent) .cmp_lte else .cmp_lt;
+ try sema.panicIndexOutOfBounds(block, elem_index_src, elem_index, len_inst, cmp_op);
+ }
+ return block.addSliceElemPtr(slice, elem_index, elem_ptr_ty);
}
fn coerce(
@@ -13956,16 +19802,17 @@ fn coerce(
const dest_ty_src = inst_src; // TODO better source location
const dest_ty = try sema.resolveTypeFields(block, dest_ty_src, dest_ty_unresolved);
const inst_ty = try sema.resolveTypeFields(block, inst_src, sema.typeOf(inst));
+ const target = sema.mod.getTarget();
// If the types are the same, we can return the operand.
- if (dest_ty.eql(inst_ty))
+ if (dest_ty.eql(inst_ty, sema.mod))
return inst;
const arena = sema.arena;
- const target = sema.mod.getTarget();
+ const maybe_inst_val = try sema.resolveMaybeUndefVal(block, inst_src, inst);
const in_memory_result = try sema.coerceInMemoryAllowed(block, dest_ty, inst_ty, false, target, dest_ty_src, inst_src);
if (in_memory_result == .ok) {
- if (try sema.resolveMaybeUndefVal(block, inst_src, inst)) |val| {
+ if (maybe_inst_val) |val| {
// Keep the comptime Value representation; take the new type.
return sema.addConstant(dest_ty, val);
}
@@ -13973,21 +19820,28 @@ fn coerce(
return block.addBitCast(dest_ty, inst);
}
- // undefined to anything
- if (try sema.resolveMaybeUndefVal(block, inst_src, inst)) |val| {
- if (val.isUndef() or inst_ty.zigTypeTag() == .Undefined) {
- return sema.addConstant(dest_ty, val);
- }
- }
- assert(inst_ty.zigTypeTag() != .Undefined);
+ const is_undef = if (maybe_inst_val) |val| val.isUndef() else false;
switch (dest_ty.zigTypeTag()) {
.Optional => {
+ // undefined sets the optional bit also to undefined.
+ if (is_undef) {
+ return sema.addConstUndef(dest_ty);
+ }
+
// null to ?T
if (inst_ty.zigTypeTag() == .Null) {
return sema.addConstant(dest_ty, Value.@"null");
}
+ // cast from ?*T and ?[*]T to ?*anyopaque
+ // but don't do it if the source type is a double pointer
+ if (dest_ty.isPtrLikeOptional() and dest_ty.elemType2().tag() == .anyopaque and
+ inst_ty.isPtrLikeOptional() and inst_ty.elemType2().zigTypeTag() != .Pointer)
+ {
+ return sema.coerceCompatiblePtrs(block, dest_ty, inst, inst_src);
+ }
+
// T to ?T
const child_type = try dest_ty.optionalChildAlloc(sema.arena);
const intermediate = try sema.coerce(block, child_type, inst, inst_src);
@@ -14029,8 +19883,7 @@ fn coerce(
const array_ty = inst_ty.childType();
if (array_ty.zigTypeTag() != .Array) break :src_array_ptr;
const len0 = array_ty.arrayLen() == 0;
- // We resolve here so that the backend has the layout of the elem type.
- const array_elem_type = try sema.resolveTypeFields(block, inst_src, array_ty.childType());
+ const array_elem_type = array_ty.childType();
const dest_is_mut = dest_info.mutable;
if (inst_ty.isConstPtr() and dest_is_mut and !len0) break :src_array_ptr;
if (inst_ty.isVolatilePtr() and !dest_info.@"volatile") break :src_array_ptr;
@@ -14057,7 +19910,7 @@ fn coerce(
// *[N:s]T to [*]T
if (dest_info.sentinel) |dst_sentinel| {
if (array_ty.sentinel()) |src_sentinel| {
- if (src_sentinel.eql(dst_sentinel, dst_elem_type)) {
+ if (src_sentinel.eql(dst_sentinel, dst_elem_type, sema.mod)) {
return sema.coerceCompatiblePtrs(block, dest_ty, inst, inst_src);
}
}
@@ -14084,9 +19937,17 @@ fn coerce(
return sema.coerceCompatiblePtrs(block, dest_ty, inst, inst_src);
}
- // coercion to C pointer
- if (dest_info.size == .C) {
- switch (inst_ty.zigTypeTag()) {
+ // cast from *T and [*]T to *anyopaque
+ // but don't do it if the source type is a double pointer
+ if (dest_info.pointee_type.tag() == .anyopaque and inst_ty.zigTypeTag() == .Pointer and
+ inst_ty.childType().zigTypeTag() != .Pointer)
+ {
+ return sema.coerceCompatiblePtrs(block, dest_ty, inst, inst_src);
+ }
+
+ switch (dest_info.size) {
+ // coercion to C pointer
+ .C => switch (inst_ty.zigTypeTag()) {
.Null => {
return sema.addConstant(dest_ty, Value.@"null");
},
@@ -14104,7 +19965,6 @@ fn coerce(
},
.Pointer => p: {
const inst_info = inst_ty.ptrInfo().data;
- if (inst_info.size == .Slice) break :p;
switch (try sema.coerceInMemoryAllowed(
block,
dest_info.pointee_type,
@@ -14117,43 +19977,117 @@ fn coerce(
.ok => {},
.no_match => break :p,
}
+ if (inst_info.size == .Slice) {
+ if (dest_info.sentinel == null or inst_info.sentinel == null or
+ !dest_info.sentinel.?.eql(inst_info.sentinel.?, dest_info.pointee_type, sema.mod))
+ break :p;
+
+ const slice_ptr = try sema.analyzeSlicePtr(block, inst_src, inst, inst_ty);
+ return sema.coerceCompatiblePtrs(block, dest_ty, slice_ptr, inst_src);
+ }
return sema.coerceCompatiblePtrs(block, dest_ty, inst, inst_src);
},
else => {},
- }
- }
+ },
+ .One => switch (dest_info.pointee_type.zigTypeTag()) {
+ .Union => {
+ // pointer to anonymous struct to pointer to union
+ if (inst_ty.isSinglePointer() and
+ inst_ty.childType().isAnonStruct() and
+ !dest_info.mutable)
+ {
+ return sema.coerceAnonStructToUnionPtrs(block, dest_ty, dest_ty_src, inst, inst_src);
+ }
+ },
+ .Struct => {
+ // pointer to anonymous struct to pointer to struct
+ if (inst_ty.isSinglePointer() and
+ inst_ty.childType().isAnonStruct() and
+ !dest_info.mutable)
+ {
+ return sema.coerceAnonStructToStructPtrs(block, dest_ty, dest_ty_src, inst, inst_src);
+ }
+ },
+ .Array => {
+ // pointer to tuple to pointer to array
+ if (inst_ty.isSinglePointer() and
+ inst_ty.childType().isTuple() and
+ !dest_info.mutable)
+ {
+ return sema.coerceTupleToArrayPtrs(block, dest_ty, dest_ty_src, inst, inst_src);
+ }
+ },
+ else => {},
+ },
+ .Slice => {
+ // pointer to tuple to slice
+ if (inst_ty.isSinglePointer() and
+ inst_ty.childType().isTuple() and
+ (!dest_info.mutable or inst_ty.ptrIsMutable() or inst_ty.childType().tupleFields().types.len == 0) and
+ dest_info.size == .Slice)
+ {
+ return sema.coerceTupleToSlicePtrs(block, dest_ty, dest_ty_src, inst, inst_src);
+ }
- // cast from *T and [*]T to *anyopaque
- // but don't do it if the source type is a double pointer
- if (dest_info.pointee_type.tag() == .anyopaque and inst_ty.zigTypeTag() == .Pointer and
- inst_ty.childType().zigTypeTag() != .Pointer)
- {
- return sema.coerceCompatiblePtrs(block, dest_ty, inst, inst_src);
- }
+ // empty tuple to zero-length slice
+ // note that this allows coercing to a mutable slice.
+ if (inst_ty.isSinglePointer() and
+ inst_ty.childType().tag() == .empty_struct_literal and
+ dest_info.size == .Slice)
+ {
+ const slice_val = try Value.Tag.slice.create(sema.arena, .{
+ .ptr = Value.undef,
+ .len = Value.zero,
+ });
+ return sema.addConstant(dest_ty, slice_val);
+ }
+ },
+ .Many => p: {
+ if (!inst_ty.isSlice()) break :p;
+ const inst_info = inst_ty.ptrInfo().data;
+
+ switch (try sema.coerceInMemoryAllowed(
+ block,
+ dest_info.pointee_type,
+ inst_info.pointee_type,
+ dest_info.mutable,
+ target,
+ dest_ty_src,
+ inst_src,
+ )) {
+ .ok => {},
+ .no_match => break :p,
+ }
+
+ if (dest_info.sentinel == null or inst_info.sentinel == null or
+ !dest_info.sentinel.?.eql(inst_info.sentinel.?, dest_info.pointee_type, sema.mod))
+ break :p;
- // This will give an extra hint on top of what the bottom of this func would provide.
- try sema.checkPtrOperand(block, dest_ty_src, inst_ty);
+ const slice_ptr = try sema.analyzeSlicePtr(block, inst_src, inst, inst_ty);
+ return sema.coerceCompatiblePtrs(block, dest_ty, slice_ptr, inst_src);
+ },
+ }
},
.Int, .ComptimeInt => switch (inst_ty.zigTypeTag()) {
.Float, .ComptimeFloat => float: {
const val = (try sema.resolveDefinedValue(block, inst_src, inst)) orelse break :float;
if (val.floatHasFraction()) {
- return sema.fail(block, inst_src, "fractional component prevents float value {} from coercion to type '{}'", .{ val, dest_ty });
+ return sema.fail(
+ block,
+ inst_src,
+ "fractional component prevents float value '{}' from coercion to type '{}'",
+ .{ val.fmtValue(inst_ty, sema.mod), dest_ty.fmt(sema.mod) },
+ );
}
- const result_val = val.floatToInt(sema.arena, dest_ty, target) catch |err| switch (err) {
- error.FloatCannotFit => {
- return sema.fail(block, inst_src, "integer value {d} cannot be stored in type '{}'", .{ std.math.floor(val.toFloat(f64)), dest_ty });
- },
- else => |e| return e,
- };
+ const result_val = try sema.floatToInt(block, inst_src, val, inst_ty, dest_ty);
return try sema.addConstant(dest_ty, result_val);
},
.Int, .ComptimeInt => {
if (try sema.resolveDefinedValue(block, inst_src, inst)) |val| {
// comptime known integer to other number
- if (!val.intFitsInType(dest_ty, target)) {
- return sema.fail(block, inst_src, "type {} cannot represent integer value {}", .{ dest_ty, val });
+ if (!(try sema.intFitsInType(block, inst_src, val, dest_ty, null))) {
+ return sema.fail(block, inst_src, "type '{}' cannot represent integer value '{}'", .{ dest_ty.fmt(sema.mod), val.fmtValue(inst_ty, sema.mod) });
}
return try sema.addConstant(dest_ty, val);
}
@@ -14169,23 +20103,26 @@ fn coerce(
return block.addTyOp(.intcast, dest_ty, inst);
}
},
+ .Undefined => {
+ return sema.addConstUndef(dest_ty);
+ },
else => {},
},
.Float, .ComptimeFloat => switch (inst_ty.zigTypeTag()) {
.ComptimeFloat => {
const val = try sema.resolveConstValue(block, inst_src, inst);
- const result_val = try val.floatCast(sema.arena, dest_ty);
+ const result_val = try val.floatCast(sema.arena, dest_ty, target);
return try sema.addConstant(dest_ty, result_val);
},
.Float => {
if (try sema.resolveDefinedValue(block, inst_src, inst)) |val| {
- const result_val = try val.floatCast(sema.arena, dest_ty);
- if (!val.eql(result_val, dest_ty)) {
+ const result_val = try val.floatCast(sema.arena, dest_ty, target);
+ if (!val.eql(result_val, dest_ty, sema.mod)) {
return sema.fail(
block,
inst_src,
- "type {} cannot represent float value {}",
- .{ dest_ty, val },
+ "type '{}' cannot represent float value '{}'",
+ .{ dest_ty.fmt(sema.mod), val.fmtValue(inst_ty, sema.mod) },
);
}
return try sema.addConstant(dest_ty, result_val);
@@ -14201,19 +20138,22 @@ fn coerce(
},
.Int, .ComptimeInt => int: {
const val = (try sema.resolveDefinedValue(block, inst_src, inst)) orelse break :int;
- const result_val = try val.intToFloat(sema.arena, dest_ty, target);
+ const result_val = try val.intToFloat(sema.arena, inst_ty, dest_ty, target);
// TODO implement this compile error
//const int_again_val = try result_val.floatToInt(sema.arena, inst_ty);
- //if (!int_again_val.eql(val, inst_ty)) {
+ //if (!int_again_val.eql(val, inst_ty, mod)) {
// return sema.fail(
// block,
// inst_src,
- // "type {} cannot represent integer value {}",
- // .{ dest_ty, val },
+ // "type '{}' cannot represent integer value '{}'",
+ // .{ dest_ty.fmt(sema.mod), val },
// );
//}
return try sema.addConstant(dest_ty, result_val);
},
+ .Undefined => {
+ return sema.addConstUndef(dest_ty);
+ },
else => {},
},
.Enum => switch (inst_ty.zigTypeTag()) {
@@ -14227,18 +20167,18 @@ fn coerce(
block,
inst_src,
"enum '{}' has no field named '{s}'",
- .{ dest_ty, bytes },
+ .{ dest_ty.fmt(sema.mod), bytes },
);
errdefer msg.destroy(sema.gpa);
try sema.mod.errNoteNonLazy(
- dest_ty.declSrcLoc(),
+ dest_ty.declSrcLoc(sema.mod),
msg,
"enum declared here",
.{},
);
break :msg msg;
};
- return sema.failWithOwnedErrorMsg(msg);
+ return sema.failWithOwnedErrorMsg(block, msg);
};
return sema.addConstant(
dest_ty,
@@ -14248,18 +20188,59 @@ fn coerce(
.Union => blk: {
// union to its own tag type
const union_tag_ty = inst_ty.unionTagType() orelse break :blk;
- if (union_tag_ty.eql(dest_ty)) {
+ if (union_tag_ty.eql(dest_ty, sema.mod)) {
return sema.unionToTag(block, dest_ty, inst, inst_src);
}
},
+ .Undefined => {
+ return sema.addConstUndef(dest_ty);
+ },
else => {},
},
- .ErrorUnion => {
- // T to E!T or E to E!T
- return sema.wrapErrorUnion(block, dest_ty, inst, inst_src);
+ .ErrorUnion => switch (inst_ty.zigTypeTag()) {
+ .ErrorUnion => {
+ if (maybe_inst_val) |inst_val| {
+ switch (inst_val.tag()) {
+ .undef => return sema.addConstUndef(dest_ty),
+ .eu_payload => {
+ const payload = try sema.addConstant(
+ inst_ty.errorUnionPayload(),
+ inst_val.castTag(.eu_payload).?.data,
+ );
+ return sema.wrapErrorUnionPayload(block, dest_ty, payload, inst_src);
+ },
+ else => {
+ const error_set = try sema.addConstant(
+ inst_ty.errorUnionSet(),
+ inst_val,
+ );
+ return sema.wrapErrorUnionSet(block, dest_ty, error_set, inst_src);
+ },
+ }
+ }
+ },
+ .ErrorSet => {
+ // E to E!T
+ return sema.wrapErrorUnionSet(block, dest_ty, inst, inst_src);
+ },
+ .Undefined => {
+ return sema.addConstUndef(dest_ty);
+ },
+ else => {
+ // T to E!T
+ return sema.wrapErrorUnionPayload(block, dest_ty, inst, inst_src);
+ },
},
.Union => switch (inst_ty.zigTypeTag()) {
.Enum, .EnumLiteral => return sema.coerceEnumToUnion(block, dest_ty, dest_ty_src, inst, inst_src),
+ .Struct => {
+ if (inst_ty.isAnonStruct()) {
+ return sema.coerceAnonStructToUnion(block, dest_ty, dest_ty_src, inst, inst_src);
+ }
+ },
+ .Undefined => {
+ return sema.addConstUndef(dest_ty);
+ },
else => {},
},
.Array => switch (inst_ty.zigTypeTag()) {
@@ -14272,21 +20253,42 @@ fn coerce(
return sema.coerceTupleToArray(block, dest_ty, dest_ty_src, inst, inst_src);
}
},
+ .Undefined => {
+ return sema.addConstUndef(dest_ty);
+ },
else => {},
},
.Vector => switch (inst_ty.zigTypeTag()) {
.Array, .Vector => return sema.coerceArrayLike(block, dest_ty, dest_ty_src, inst, inst_src),
+ .Struct => {
+ if (inst_ty.isTuple()) {
+ return sema.coerceTupleToArray(block, dest_ty, dest_ty_src, inst, inst_src);
+ }
+ },
+ .Undefined => {
+ return sema.addConstUndef(dest_ty);
+ },
else => {},
},
.Struct => {
if (inst == .empty_struct) {
return structInitEmpty(sema, block, dest_ty, dest_ty_src, inst_src);
}
+ if (inst_ty.isTupleOrAnonStruct()) {
+ return sema.coerceTupleToStruct(block, dest_ty, dest_ty_src, inst, inst_src);
+ }
},
else => {},
}
- return sema.fail(block, inst_src, "expected {}, found {}", .{ dest_ty, inst_ty });
+ // undefined to anything. We do this after the big switch above so that
+ // special logic has a chance to run first, such as `*[N]T` to `[]T` which
+ // should initialize the length field of the slice.
+ if (is_undef) {
+ return sema.addConstUndef(dest_ty);
+ }
+
+ return sema.fail(block, inst_src, "expected type '{}', found '{}'", .{ dest_ty.fmt(sema.mod), inst_ty.fmt(sema.mod) });
}
const InMemoryCoercionResult = enum {
@@ -14315,14 +20317,36 @@ fn coerceInMemoryAllowed(
dest_src: LazySrcLoc,
src_src: LazySrcLoc,
) CompileError!InMemoryCoercionResult {
- if (dest_ty.eql(src_ty))
+ if (dest_ty.eql(src_ty, sema.mod))
return .ok;
+ // Differently-named integers with the same number of bits.
+ if (dest_ty.zigTypeTag() == .Int and src_ty.zigTypeTag() == .Int) {
+ const dest_info = dest_ty.intInfo(target);
+ const src_info = src_ty.intInfo(target);
+ if (dest_info.signedness == src_info.signedness and
+ dest_info.bits == src_info.bits)
+ {
+ return .ok;
+ }
+ }
+
+ // Differently-named floats with the same number of bits.
+ if (dest_ty.zigTypeTag() == .Float and src_ty.zigTypeTag() == .Float) {
+ const dest_bits = dest_ty.floatBits(target);
+ const src_bits = src_ty.floatBits(target);
+ if (dest_bits == src_bits) {
+ return .ok;
+ }
+ }
+
// Pointers / Pointer-like Optionals
var dest_buf: Type.Payload.ElemType = undefined;
var src_buf: Type.Payload.ElemType = undefined;
- if (try sema.typePtrOrOptionalPtrTy(block, dest_ty, &dest_buf, dest_src)) |dest_ptr_ty| {
- if (try sema.typePtrOrOptionalPtrTy(block, src_ty, &src_buf, src_src)) |src_ptr_ty| {
+ const maybe_dest_ptr_ty = try sema.typePtrOrOptionalPtrTy(block, dest_ty, &dest_buf, dest_src);
+ const maybe_src_ptr_ty = try sema.typePtrOrOptionalPtrTy(block, src_ty, &src_buf, src_src);
+ if (maybe_dest_ptr_ty) |dest_ptr_ty| {
+ if (maybe_src_ptr_ty) |src_ptr_ty| {
return try sema.coerceInMemoryAllowedPtrs(block, dest_ty, src_ty, dest_ptr_ty, src_ptr_ty, dest_is_mut, target, dest_src, src_src);
}
}
@@ -14351,7 +20375,7 @@ fn coerceInMemoryAllowed(
// Error Sets
if (dest_tag == .ErrorSet and src_tag == .ErrorSet) {
- return try sema.coerceInMemoryAllowedErrorSets(dest_ty, src_ty);
+ return try sema.coerceInMemoryAllowedErrorSets(block, dest_ty, src_ty, dest_src, src_src);
}
// Arrays
@@ -14366,23 +20390,55 @@ fn coerceInMemoryAllowed(
}
const ok_sent = dest_info.sentinel == null or
(src_info.sentinel != null and
- dest_info.sentinel.?.eql(src_info.sentinel.?, dest_info.elem_type));
+ dest_info.sentinel.?.eql(src_info.sentinel.?, dest_info.elem_type, sema.mod));
if (!ok_sent) {
return .no_match;
}
return .ok;
}
- // TODO: non-pointer-like optionals
- // TODO: vectors
+ // Vectors
+ if (dest_tag == .Vector and src_tag == .Vector) vectors: {
+ const dest_len = dest_ty.vectorLen();
+ const src_len = src_ty.vectorLen();
+ if (dest_len != src_len) break :vectors;
+
+ const dest_elem_ty = dest_ty.scalarType();
+ const src_elem_ty = src_ty.scalarType();
+ const child = try sema.coerceInMemoryAllowed(block, dest_elem_ty, src_elem_ty, dest_is_mut, target, dest_src, src_src);
+ if (child == .no_match) break :vectors;
+
+ return .ok;
+ }
+
+ // Optionals
+ if (dest_tag == .Optional and src_tag == .Optional) optionals: {
+ if ((maybe_dest_ptr_ty != null) != (maybe_src_ptr_ty != null)) {
+ // TODO "optional type child '{}' cannot cast into optional type '{}'"
+ return .no_match;
+ }
+ const dest_child_type = dest_ty.optionalChild(&dest_buf);
+ const src_child_type = src_ty.optionalChild(&src_buf);
+
+ const child = try sema.coerceInMemoryAllowed(block, dest_child_type, src_child_type, dest_is_mut, target, dest_src, src_src);
+ if (child == .no_match) {
+ // TODO "optional type child '{}' cannot cast into optional type child '{}'"
+ break :optionals;
+ }
+
+ return .ok;
+ }
return .no_match;
}
fn coerceInMemoryAllowedErrorSets(
sema: *Sema,
+ block: *Block,
dest_ty: Type,
src_ty: Type,
+ dest_src: LazySrcLoc,
+ src_src: LazySrcLoc,
) !InMemoryCoercionResult {
// Coercion to `anyerror`. Note that this check can return false negatives
// in case the error sets did not get resolved.
@@ -14390,24 +20446,50 @@ fn coerceInMemoryAllowedErrorSets(
return .ok;
}
- // If both are inferred error sets of functions, and
- // the dest includes the source function, the coercion is OK.
- // This check is important because it works without forcing a full resolution
- // of inferred error sets.
- if (src_ty.castTag(.error_set_inferred)) |src_payload| {
- if (dest_ty.castTag(.error_set_inferred)) |dst_payload| {
- const src_func = src_payload.data.func;
- const dst_func = dst_payload.data.func;
+ if (dest_ty.castTag(.error_set_inferred)) |dst_payload| {
+ const dst_ies = dst_payload.data;
+ // We will make an effort to return `ok` without resolving either error set, to
+ // avoid unnecessary "unable to resolve error set" dependency loop errors.
+ switch (src_ty.tag()) {
+ .error_set_inferred => {
+ // If both are inferred error sets of functions, and
+ // the dest includes the source function, the coercion is OK.
+ // This check is important because it works without forcing a full resolution
+ // of inferred error sets.
+ const src_ies = src_ty.castTag(.error_set_inferred).?.data;
+
+ if (dst_ies.inferred_error_sets.contains(src_ies)) {
+ return .ok;
+ }
+ },
+ .error_set_single => {
+ const name = src_ty.castTag(.error_set_single).?.data;
+ if (dst_ies.errors.contains(name)) return .ok;
+ },
+ .error_set_merged => {
+ const names = src_ty.castTag(.error_set_merged).?.data.keys();
+ for (names) |name| {
+ if (!dst_ies.errors.contains(name)) break;
+ } else return .ok;
+ },
+ .error_set => {
+ const names = src_ty.castTag(.error_set).?.data.names.keys();
+ for (names) |name| {
+ if (!dst_ies.errors.contains(name)) break;
+ } else return .ok;
+ },
+ .anyerror => {},
+ else => unreachable,
+ }
- if (src_func == dst_func or dst_payload.data.inferred_error_sets.contains(src_payload.data)) {
- return .ok;
- }
- return .no_match;
+ if (dst_ies.func == sema.owner_func) {
+ // We are trying to coerce an error set to the current function's
+ // inferred error set.
+ try dst_ies.addErrorSet(sema.gpa, src_ty);
+ return .ok;
}
- }
- if (dest_ty.castTag(.error_set_inferred)) |payload| {
- try sema.resolveInferredErrorSet(payload.data);
+ try sema.resolveInferredErrorSet(block, dest_src, dst_payload.data);
// isAnyError might have changed from a false negative to a true positive after resolution.
if (dest_ty.isAnyError()) {
return .ok;
@@ -14418,16 +20500,15 @@ fn coerceInMemoryAllowedErrorSets(
.error_set_inferred => {
const src_data = src_ty.castTag(.error_set_inferred).?.data;
- try sema.resolveInferredErrorSet(src_data);
+ try sema.resolveInferredErrorSet(block, src_src, src_data);
// src anyerror status might have changed after the resolution.
if (src_ty.isAnyError()) {
// dest_ty.isAnyError() == true is already checked for at this point.
return .no_match;
}
- var it = src_data.errors.keyIterator();
- while (it.next()) |name_ptr| {
- if (!dest_ty.errorSetHasField(name_ptr.*)) {
+ for (src_data.errors.keys()) |key| {
+ if (!dest_ty.errorSetHasField(key)) {
return .no_match;
}
}
@@ -14552,7 +20633,7 @@ fn coerceInMemoryAllowedPtrs(
const ok_sent = dest_info.sentinel == null or src_info.size == .C or
(src_info.sentinel != null and
- dest_info.sentinel.?.eql(src_info.sentinel.?, dest_info.pointee_type));
+ dest_info.sentinel.?.eql(src_info.sentinel.?, dest_info.pointee_type, sema.mod));
if (!ok_sent) {
return .no_match;
}
@@ -14591,15 +20672,28 @@ fn coerceInMemoryAllowedPtrs(
// In this case, if they share the same child type, no need to resolve
// pointee type alignment. Otherwise both pointee types must have their alignment
// resolved and we compare the alignment numerically.
- if (src_info.@"align" != 0 or dest_info.@"align" != 0 or
- !dest_info.pointee_type.eql(src_info.pointee_type))
- {
- const src_align = src_info.@"align";
- const dest_align = dest_info.@"align";
+ alignment: {
+ if (src_info.@"align" == 0 and dest_info.@"align" == 0 and
+ dest_info.pointee_type.eql(src_info.pointee_type, sema.mod))
+ {
+ break :alignment;
+ }
+
+ const src_align = if (src_info.@"align" != 0)
+ src_info.@"align"
+ else
+ src_info.pointee_type.abiAlignment(target);
+
+ const dest_align = if (dest_info.@"align" != 0)
+ dest_info.@"align"
+ else
+ dest_info.pointee_type.abiAlignment(target);
if (dest_align > src_align) {
return .no_match;
}
+
+ break :alignment;
}
return .ok;
@@ -14650,26 +20744,45 @@ fn storePtr2(
// To generate better code for tuples, we detect a tuple operand here, and
// analyze field loads and stores directly. This avoids an extra allocation + memcpy
// which would occur if we used `coerce`.
+ // However, we avoid this mechanism if the destination element type is a tuple,
+ // because the regular store will be better for this case.
+ // If the destination type is a struct we don't want this mechanism to trigger, because
+ // this code does not handle tuple-to-struct coercion which requires dealing with missing
+ // fields.
const operand_ty = sema.typeOf(uncasted_operand);
- if (operand_ty.castTag(.tuple)) |payload| {
- const tuple_fields_len = payload.data.types.len;
- var i: u32 = 0;
- while (i < tuple_fields_len) : (i += 1) {
+ if (operand_ty.isTuple() and elem_ty.zigTypeTag() == .Array) {
+ const tuple = operand_ty.tupleFields();
+ for (tuple.types) |_, i_usize| {
+ const i = @intCast(u32, i_usize);
const elem_src = operand_src; // TODO better source location
- const elem = try tupleField(sema, block, uncasted_operand, i, operand_src, elem_src);
+ const elem = try tupleField(sema, block, operand_src, uncasted_operand, elem_src, i);
const elem_index = try sema.addIntUnsigned(Type.usize, i);
- const elem_ptr = try sema.elemPtr(block, ptr_src, ptr, elem_index, elem_src);
+ const elem_ptr = try sema.elemPtr(block, ptr_src, ptr, elem_index, elem_src, false);
try sema.storePtr2(block, src, elem_ptr, elem_src, elem, elem_src, .store);
}
return;
}
- const operand = try sema.coerce(block, elem_ty, uncasted_operand, operand_src);
- if ((try sema.typeHasOnePossibleValue(block, src, elem_ty)) != null)
+ // TODO do the same thing for anon structs as for tuples above.
+ // However, beware of the need to handle missing/extra fields.
+
+ // Detect if we are storing an array operand to a bitcasted vector pointer.
+ // If so, we instead reach through the bitcasted pointer to the vector pointer,
+ // bitcast the array operand to a vector, and then lower this as a store of
+ // a vector value to a vector pointer. This generally results in better code,
+ // as well as working around an LLVM bug:
+ // https://github.com/ziglang/zig/issues/11154
+ if (sema.obtainBitCastedVectorPtr(ptr)) |vector_ptr| {
+ const vector_ty = sema.typeOf(vector_ptr).childType();
+ const vector = try sema.coerce(block, vector_ty, uncasted_operand, operand_src);
+ try sema.storePtr2(block, src, vector_ptr, ptr_src, vector, operand_src, .store);
return;
+ }
+
+ const operand = try sema.coerce(block, elem_ty, uncasted_operand, operand_src);
+ const maybe_operand_val = try sema.resolveMaybeUndefVal(block, operand_src, operand);
const runtime_src = if (try sema.resolveDefinedValue(block, ptr_src, ptr)) |ptr_val| rs: {
- const maybe_operand_val = try sema.resolveMaybeUndefVal(block, operand_src, operand);
const operand_val = maybe_operand_val orelse {
try sema.checkPtrIsNotComptimeMutable(block, ptr_val, ptr_src, operand_src);
break :rs operand_src;
@@ -14680,13 +20793,58 @@ fn storePtr2(
} else break :rs ptr_src;
} else ptr_src;
+ // We do this after the possible comptime store above, for the case of field_ptr stores
+ // to unions because we want the comptime tag to be set, even if the field type is void.
+ if ((try sema.typeHasOnePossibleValue(block, src, elem_ty)) != null)
+ return;
+
// TODO handle if the element type requires comptime
+ if (air_tag == .bitcast) {
+ // `air_tag == .bitcast` is used as a special case for `zirCoerceResultPtr`
+ // to avoid calling `requireRuntimeBlock` for the dummy block.
+ _ = try block.addBinOp(.store, ptr, operand);
+ return;
+ }
+
try sema.requireRuntimeBlock(block, runtime_src);
- try sema.resolveTypeLayout(block, src, elem_ty);
+ try sema.queueFullTypeResolution(elem_ty);
_ = try block.addBinOp(air_tag, ptr, operand);
}
+/// Traverse an arbitrary number of bitcasted pointers and return the underyling vector
+/// pointer. Only if the final element type matches the vector element type, and the
+/// lengths match.
+fn obtainBitCastedVectorPtr(sema: *Sema, ptr: Air.Inst.Ref) ?Air.Inst.Ref {
+ const array_ty = sema.typeOf(ptr).childType();
+ if (array_ty.zigTypeTag() != .Array) return null;
+ var ptr_inst = Air.refToIndex(ptr) orelse return null;
+ const air_datas = sema.air_instructions.items(.data);
+ const air_tags = sema.air_instructions.items(.tag);
+ const prev_ptr = while (air_tags[ptr_inst] == .bitcast) {
+ const prev_ptr = air_datas[ptr_inst].ty_op.operand;
+ const prev_ptr_ty = sema.typeOf(prev_ptr);
+ const prev_ptr_child_ty = switch (prev_ptr_ty.tag()) {
+ .single_mut_pointer => prev_ptr_ty.castTag(.single_mut_pointer).?.data,
+ .pointer => prev_ptr_ty.castTag(.pointer).?.data.pointee_type,
+ else => return null,
+ };
+ if (prev_ptr_child_ty.zigTypeTag() == .Vector) break prev_ptr;
+ ptr_inst = Air.refToIndex(prev_ptr) orelse return null;
+ } else return null;
+
+ // We have a pointer-to-array and a pointer-to-vector. If the elements and
+ // lengths match, return the result.
+ const vector_ty = sema.typeOf(prev_ptr).childType();
+ if (array_ty.childType().eql(vector_ty.childType(), sema.mod) and
+ array_ty.arrayLen() == vector_ty.vectorLen())
+ {
+ return prev_ptr;
+ } else {
+ return null;
+ }
+}
+
/// Call when you have Value objects rather than Air instructions, and you want to
/// assert the store must be done at comptime.
fn storePtrVal(
@@ -14697,30 +20855,79 @@ fn storePtrVal(
operand_val: Value,
operand_ty: Type,
) !void {
- var kit = try beginComptimePtrMutation(sema, block, src, ptr_val);
- try sema.checkComptimeVarStore(block, src, kit.decl_ref_mut);
+ var mut_kit = try beginComptimePtrMutation(sema, block, src, ptr_val, operand_ty);
+ try sema.checkComptimeVarStore(block, src, mut_kit.decl_ref_mut);
+
+ switch (mut_kit.pointee) {
+ .direct => |val_ptr| {
+ if (mut_kit.decl_ref_mut.runtime_index == .comptime_field_ptr) {
+ if (!operand_val.eql(val_ptr.*, operand_ty, sema.mod)) {
+ // TODO add note showing where default value is provided
+ return sema.fail(block, src, "value stored in comptime field does not match the default value of the field", .{});
+ }
+ return;
+ }
+ const arena = mut_kit.beginArena(sema.mod);
+ defer mut_kit.finishArena(sema.mod);
- const bitcasted_val = try sema.bitCastVal(block, src, operand_val, operand_ty, kit.ty);
+ val_ptr.* = try operand_val.copy(arena);
+ },
+ .reinterpret => |reinterpret| {
+ const target = sema.mod.getTarget();
+ const abi_size = try sema.usizeCast(block, src, mut_kit.ty.abiSize(target));
+ const buffer = try sema.gpa.alloc(u8, abi_size);
+ defer sema.gpa.free(buffer);
+ reinterpret.val_ptr.*.writeToMemory(mut_kit.ty, sema.mod, buffer);
+ operand_val.writeToMemory(operand_ty, sema.mod, buffer[reinterpret.byte_offset..]);
- const arena = kit.beginArena(sema.gpa);
- defer kit.finishArena();
+ const arena = mut_kit.beginArena(sema.mod);
+ defer mut_kit.finishArena(sema.mod);
- kit.val.* = try bitcasted_val.copy(arena);
+ reinterpret.val_ptr.* = try Value.readFromMemory(mut_kit.ty, sema.mod, buffer, arena);
+ },
+ .bad_decl_ty, .bad_ptr_ty => {
+ // TODO show the decl declaration site in a note and explain whether the decl
+ // or the pointer is the problematic type
+ return sema.fail(block, src, "comptime mutation of a reinterpreted pointer requires type '{}' to have a well-defined memory layout", .{mut_kit.ty.fmt(sema.mod)});
+ },
+ }
}
const ComptimePtrMutationKit = struct {
decl_ref_mut: Value.Payload.DeclRefMut.Data,
- val: *Value,
+ pointee: union(enum) {
+ /// The pointer type matches the actual comptime Value so a direct
+ /// modification is possible.
+ direct: *Value,
+ /// The largest parent Value containing pointee and having a well-defined memory layout.
+ /// This is used for bitcasting, if direct dereferencing failed.
+ reinterpret: struct {
+ val_ptr: *Value,
+ byte_offset: usize,
+ },
+ /// If the root decl could not be used as parent, this means `ty` is the type that
+ /// caused that by not having a well-defined layout.
+ /// This one means the Decl that owns the value trying to be modified does not
+ /// have a well defined memory layout.
+ bad_decl_ty,
+ /// If the root decl could not be used as parent, this means `ty` is the type that
+ /// caused that by not having a well-defined layout.
+ /// This one means the pointer type that is being stored through does not
+ /// have a well defined memory layout.
+ bad_ptr_ty,
+ },
ty: Type,
decl_arena: std.heap.ArenaAllocator = undefined,
- fn beginArena(self: *ComptimePtrMutationKit, gpa: Allocator) Allocator {
- self.decl_arena = self.decl_ref_mut.decl.value_arena.?.promote(gpa);
+ fn beginArena(self: *ComptimePtrMutationKit, mod: *Module) Allocator {
+ const decl = mod.declPtr(self.decl_ref_mut.decl_index);
+ self.decl_arena = decl.value_arena.?.promote(mod.gpa);
return self.decl_arena.allocator();
}
- fn finishArena(self: *ComptimePtrMutationKit) void {
- self.decl_ref_mut.decl.value_arena.?.* = self.decl_arena.state;
+ fn finishArena(self: *ComptimePtrMutationKit, mod: *Module) void {
+ const decl = mod.declPtr(self.decl_ref_mut.decl_index);
+ decl.value_arena.?.* = self.decl_arena.state;
self.decl_arena = undefined;
}
};
@@ -14730,349 +20937,750 @@ fn beginComptimePtrMutation(
block: *Block,
src: LazySrcLoc,
ptr_val: Value,
+ ptr_elem_ty: Type,
) CompileError!ComptimePtrMutationKit {
+ const target = sema.mod.getTarget();
switch (ptr_val.tag()) {
.decl_ref_mut => {
const decl_ref_mut = ptr_val.castTag(.decl_ref_mut).?.data;
- return ComptimePtrMutationKit{
- .decl_ref_mut = decl_ref_mut,
- .val = &decl_ref_mut.decl.val,
- .ty = decl_ref_mut.decl.ty,
- };
+ const decl = sema.mod.declPtr(decl_ref_mut.decl_index);
+ return beginComptimePtrMutationInner(sema, block, src, decl.ty, &decl.val, ptr_elem_ty, decl_ref_mut);
+ },
+ .comptime_field_ptr => {
+ const payload = ptr_val.castTag(.comptime_field_ptr).?.data;
+ const duped = try sema.arena.create(Value);
+ duped.* = payload.field_val;
+ return beginComptimePtrMutationInner(sema, block, src, payload.field_ty, duped, ptr_elem_ty, .{
+ .decl_index = @intToEnum(Module.Decl.Index, 0),
+ .runtime_index = .comptime_field_ptr,
+ });
},
.elem_ptr => {
const elem_ptr = ptr_val.castTag(.elem_ptr).?.data;
- var parent = try beginComptimePtrMutation(sema, block, src, elem_ptr.array_ptr);
- switch (parent.ty.zigTypeTag()) {
- .Array, .Vector => {
- const check_len = parent.ty.arrayLenIncludingSentinel();
- if (elem_ptr.index >= check_len) {
- // TODO have the parent include the decl so we can say "declared here"
- return sema.fail(block, src, "comptime store of index {d} out of bounds of array length {d}", .{
- elem_ptr.index, check_len,
- });
- }
- const elem_ty = parent.ty.childType();
- switch (parent.val.tag()) {
- .undef => {
- // An array has been initialized to undefined at comptime and now we
- // are for the first time setting an element. We must change the representation
- // of the array from `undef` to `array`.
- const arena = parent.beginArena(sema.gpa);
- defer parent.finishArena();
-
- const array_len_including_sentinel =
- try sema.usizeCast(block, src, parent.ty.arrayLenIncludingSentinel());
- const elems = try arena.alloc(Value, array_len_including_sentinel);
- mem.set(Value, elems, Value.undef);
-
- parent.val.* = try Value.Tag.array.create(arena, elems);
+ var parent = try beginComptimePtrMutation(sema, block, src, elem_ptr.array_ptr, elem_ptr.elem_ty);
+ switch (parent.pointee) {
+ .direct => |val_ptr| switch (parent.ty.zigTypeTag()) {
+ .Array, .Vector => {
+ const check_len = parent.ty.arrayLenIncludingSentinel();
+ if (elem_ptr.index >= check_len) {
+ // TODO have the parent include the decl so we can say "declared here"
+ return sema.fail(block, src, "comptime store of index {d} out of bounds of array length {d}", .{
+ elem_ptr.index, check_len,
+ });
+ }
+ const elem_ty = parent.ty.childType();
+ switch (val_ptr.tag()) {
+ .undef => {
+ // An array has been initialized to undefined at comptime and now we
+ // are for the first time setting an element. We must change the representation
+ // of the array from `undef` to `array`.
+ const arena = parent.beginArena(sema.mod);
+ defer parent.finishArena(sema.mod);
+
+ const array_len_including_sentinel =
+ try sema.usizeCast(block, src, parent.ty.arrayLenIncludingSentinel());
+ const elems = try arena.alloc(Value, array_len_including_sentinel);
+ mem.set(Value, elems, Value.undef);
+
+ val_ptr.* = try Value.Tag.aggregate.create(arena, elems);
+
+ return beginComptimePtrMutationInner(
+ sema,
+ block,
+ src,
+ elem_ty,
+ &elems[elem_ptr.index],
+ ptr_elem_ty,
+ parent.decl_ref_mut,
+ );
+ },
+ .bytes => {
+ // An array is memory-optimized to store a slice of bytes, but we are about
+ // to modify an individual field and the representation has to change.
+ // If we wanted to avoid this, there would need to be special detection
+ // elsewhere to identify when writing a value to an array element that is stored
+ // using the `bytes` tag, and handle it without making a call to this function.
+ const arena = parent.beginArena(sema.mod);
+ defer parent.finishArena(sema.mod);
+
+ const bytes = val_ptr.castTag(.bytes).?.data;
+ const dest_len = parent.ty.arrayLenIncludingSentinel();
+ // bytes.len may be one greater than dest_len because of the case when
+ // assigning `[N:S]T` to `[N]T`. This is allowed; the sentinel is omitted.
+ assert(bytes.len >= dest_len);
+ const elems = try arena.alloc(Value, @intCast(usize, dest_len));
+ for (elems) |*elem, i| {
+ elem.* = try Value.Tag.int_u64.create(arena, bytes[i]);
+ }
- return ComptimePtrMutationKit{
- .decl_ref_mut = parent.decl_ref_mut,
- .val = &elems[elem_ptr.index],
- .ty = elem_ty,
- };
- },
- .bytes => {
- // An array is memory-optimized to store a slice of bytes, but we are about
- // to modify an individual field and the representation has to change.
- // If we wanted to avoid this, there would need to be special detection
- // elsewhere to identify when writing a value to an array element that is stored
- // using the `bytes` tag, and handle it without making a call to this function.
- const arena = parent.beginArena(sema.gpa);
- defer parent.finishArena();
-
- const bytes = parent.val.castTag(.bytes).?.data;
- const dest_len = parent.ty.arrayLenIncludingSentinel();
- // bytes.len may be one greater than dest_len because of the case when
- // assigning `[N:S]T` to `[N]T`. This is allowed; the sentinel is omitted.
- assert(bytes.len >= dest_len);
- const elems = try arena.alloc(Value, @intCast(usize, dest_len));
- for (elems) |*elem, i| {
- elem.* = try Value.Tag.int_u64.create(arena, bytes[i]);
- }
+ val_ptr.* = try Value.Tag.aggregate.create(arena, elems);
- parent.val.* = try Value.Tag.array.create(arena, elems);
+ return beginComptimePtrMutationInner(
+ sema,
+ block,
+ src,
+ elem_ty,
+ &elems[elem_ptr.index],
+ ptr_elem_ty,
+ parent.decl_ref_mut,
+ );
+ },
+ .str_lit => {
+ // An array is memory-optimized to store a slice of bytes, but we are about
+ // to modify an individual field and the representation has to change.
+ // If we wanted to avoid this, there would need to be special detection
+ // elsewhere to identify when writing a value to an array element that is stored
+ // using the `str_lit` tag, and handle it without making a call to this function.
+ const arena = parent.beginArena(sema.mod);
+ defer parent.finishArena(sema.mod);
+
+ const str_lit = val_ptr.castTag(.str_lit).?.data;
+ const dest_len = parent.ty.arrayLenIncludingSentinel();
+ const bytes = sema.mod.string_literal_bytes.items[str_lit.index..][0..str_lit.len];
+ const elems = try arena.alloc(Value, @intCast(usize, dest_len));
+ for (bytes) |byte, i| {
+ elems[i] = try Value.Tag.int_u64.create(arena, byte);
+ }
+ if (parent.ty.sentinel()) |sent_val| {
+ assert(elems.len == bytes.len + 1);
+ elems[bytes.len] = sent_val;
+ }
- return ComptimePtrMutationKit{
- .decl_ref_mut = parent.decl_ref_mut,
- .val = &elems[elem_ptr.index],
- .ty = elem_ty,
- };
- },
- .repeated => {
- // An array is memory-optimized to store only a single element value, and
- // that value is understood to be the same for the entire length of the array.
- // However, now we want to modify an individual field and so the
- // representation has to change. If we wanted to avoid this, there would
- // need to be special detection elsewhere to identify when writing a value to an
- // array element that is stored using the `repeated` tag, and handle it
- // without making a call to this function.
- const arena = parent.beginArena(sema.gpa);
- defer parent.finishArena();
-
- const repeated_val = try parent.val.castTag(.repeated).?.data.copy(arena);
- const array_len_including_sentinel =
- try sema.usizeCast(block, src, parent.ty.arrayLenIncludingSentinel());
- const elems = try arena.alloc(Value, array_len_including_sentinel);
- mem.set(Value, elems, repeated_val);
-
- parent.val.* = try Value.Tag.array.create(arena, elems);
+ val_ptr.* = try Value.Tag.aggregate.create(arena, elems);
- return ComptimePtrMutationKit{
- .decl_ref_mut = parent.decl_ref_mut,
- .val = &elems[elem_ptr.index],
- .ty = elem_ty,
- };
- },
+ return beginComptimePtrMutationInner(
+ sema,
+ block,
+ src,
+ elem_ty,
+ &elems[elem_ptr.index],
+ ptr_elem_ty,
+ parent.decl_ref_mut,
+ );
+ },
+ .repeated => {
+ // An array is memory-optimized to store only a single element value, and
+ // that value is understood to be the same for the entire length of the array.
+ // However, now we want to modify an individual field and so the
+ // representation has to change. If we wanted to avoid this, there would
+ // need to be special detection elsewhere to identify when writing a value to an
+ // array element that is stored using the `repeated` tag, and handle it
+ // without making a call to this function.
+ const arena = parent.beginArena(sema.mod);
+ defer parent.finishArena(sema.mod);
+
+ const repeated_val = try val_ptr.castTag(.repeated).?.data.copy(arena);
+ const array_len_including_sentinel =
+ try sema.usizeCast(block, src, parent.ty.arrayLenIncludingSentinel());
+ const elems = try arena.alloc(Value, array_len_including_sentinel);
+ mem.set(Value, elems, repeated_val);
+
+ val_ptr.* = try Value.Tag.aggregate.create(arena, elems);
+
+ return beginComptimePtrMutationInner(
+ sema,
+ block,
+ src,
+ elem_ty,
+ &elems[elem_ptr.index],
+ ptr_elem_ty,
+ parent.decl_ref_mut,
+ );
+ },
- .array => return ComptimePtrMutationKit{
- .decl_ref_mut = parent.decl_ref_mut,
- .val = &parent.val.castTag(.array).?.data[elem_ptr.index],
- .ty = elem_ty,
- },
+ .aggregate => return beginComptimePtrMutationInner(
+ sema,
+ block,
+ src,
+ elem_ty,
+ &val_ptr.castTag(.aggregate).?.data[elem_ptr.index],
+ ptr_elem_ty,
+ parent.decl_ref_mut,
+ ),
+
+ .the_only_possible_value => {
+ const duped = try sema.arena.create(Value);
+ duped.* = Value.initTag(.the_only_possible_value);
+ return beginComptimePtrMutationInner(
+ sema,
+ block,
+ src,
+ elem_ty,
+ duped,
+ ptr_elem_ty,
+ parent.decl_ref_mut,
+ );
+ },
- else => unreachable,
- }
+ else => unreachable,
+ }
+ },
+ else => {
+ if (elem_ptr.index != 0) {
+ // TODO include a "declared here" note for the decl
+ return sema.fail(block, src, "out of bounds comptime store of index {d}", .{
+ elem_ptr.index,
+ });
+ }
+ return beginComptimePtrMutationInner(
+ sema,
+ block,
+ src,
+ parent.ty,
+ val_ptr,
+ ptr_elem_ty,
+ parent.decl_ref_mut,
+ );
+ },
},
- else => {
- if (elem_ptr.index != 0) {
- // TODO include a "declared here" note for the decl
- return sema.fail(block, src, "out of bounds comptime store of index {d}", .{
- elem_ptr.index,
- });
+ .reinterpret => |reinterpret| {
+ if (!elem_ptr.elem_ty.hasWellDefinedLayout()) {
+ // Even though the parent value type has well-defined memory layout, our
+ // pointer type does not.
+ return ComptimePtrMutationKit{
+ .decl_ref_mut = parent.decl_ref_mut,
+ .pointee = .bad_ptr_ty,
+ .ty = elem_ptr.elem_ty,
+ };
}
+
+ const elem_abi_size_u64 = try sema.typeAbiSize(block, src, elem_ptr.elem_ty);
+ const elem_abi_size = try sema.usizeCast(block, src, elem_abi_size_u64);
return ComptimePtrMutationKit{
.decl_ref_mut = parent.decl_ref_mut,
- .val = parent.val,
+ .pointee = .{ .reinterpret = .{
+ .val_ptr = reinterpret.val_ptr,
+ .byte_offset = reinterpret.byte_offset + elem_abi_size * elem_ptr.index,
+ } },
.ty = parent.ty,
};
},
+ .bad_decl_ty, .bad_ptr_ty => return parent,
}
},
.field_ptr => {
const field_ptr = ptr_val.castTag(.field_ptr).?.data;
- var parent = try beginComptimePtrMutation(sema, block, src, field_ptr.container_ptr);
const field_index = @intCast(u32, field_ptr.field_index);
- const field_ty = parent.ty.structFieldType(field_index);
- switch (parent.val.tag()) {
- .undef => {
- // A struct or union has been initialized to undefined at comptime and now we
- // are for the first time setting a field. We must change the representation
- // of the struct/union from `undef` to `struct`/`union`.
- const arena = parent.beginArena(sema.gpa);
- defer parent.finishArena();
-
- switch (parent.ty.zigTypeTag()) {
- .Struct => {
- const fields = try arena.alloc(Value, parent.ty.structFieldCount());
- mem.set(Value, fields, Value.undef);
-
- parent.val.* = try Value.Tag.@"struct".create(arena, fields);
+
+ var parent = try beginComptimePtrMutation(sema, block, src, field_ptr.container_ptr, field_ptr.container_ty);
+ switch (parent.pointee) {
+ .direct => |val_ptr| switch (val_ptr.tag()) {
+ .undef => {
+ // A struct or union has been initialized to undefined at comptime and now we
+ // are for the first time setting a field. We must change the representation
+ // of the struct/union from `undef` to `struct`/`union`.
+ const arena = parent.beginArena(sema.mod);
+ defer parent.finishArena(sema.mod);
+
+ switch (parent.ty.zigTypeTag()) {
+ .Struct => {
+ const fields = try arena.alloc(Value, parent.ty.structFieldCount());
+ mem.set(Value, fields, Value.undef);
+
+ val_ptr.* = try Value.Tag.aggregate.create(arena, fields);
+
+ return beginComptimePtrMutationInner(
+ sema,
+ block,
+ src,
+ parent.ty.structFieldType(field_index),
+ &fields[field_index],
+ ptr_elem_ty,
+ parent.decl_ref_mut,
+ );
+ },
+ .Union => {
+ const payload = try arena.create(Value.Payload.Union);
+ payload.* = .{ .data = .{
+ .tag = try Value.Tag.enum_field_index.create(arena, field_index),
+ .val = Value.undef,
+ } };
+
+ val_ptr.* = Value.initPayload(&payload.base);
+
+ return beginComptimePtrMutationInner(
+ sema,
+ block,
+ src,
+ parent.ty.structFieldType(field_index),
+ &payload.data.val,
+ ptr_elem_ty,
+ parent.decl_ref_mut,
+ );
+ },
+ .Pointer => {
+ assert(parent.ty.isSlice());
+ val_ptr.* = try Value.Tag.slice.create(arena, .{
+ .ptr = Value.undef,
+ .len = Value.undef,
+ });
+
+ switch (field_index) {
+ Value.Payload.Slice.ptr_index => return beginComptimePtrMutationInner(
+ sema,
+ block,
+ src,
+ parent.ty.slicePtrFieldType(try sema.arena.create(Type.SlicePtrFieldTypeBuffer)),
+ &val_ptr.castTag(.slice).?.data.ptr,
+ ptr_elem_ty,
+ parent.decl_ref_mut,
+ ),
+ Value.Payload.Slice.len_index => return beginComptimePtrMutationInner(
+ sema,
+ block,
+ src,
+ Type.usize,
+ &val_ptr.castTag(.slice).?.data.len,
+ ptr_elem_ty,
+ parent.decl_ref_mut,
+ ),
+
+ else => unreachable,
+ }
+ },
+ else => unreachable,
+ }
+ },
+ .aggregate => return beginComptimePtrMutationInner(
+ sema,
+ block,
+ src,
+ parent.ty.structFieldType(field_index),
+ &val_ptr.castTag(.aggregate).?.data[field_index],
+ ptr_elem_ty,
+ parent.decl_ref_mut,
+ ),
+
+ .@"union" => {
+ // We need to set the active field of the union.
+ const arena = parent.beginArena(sema.mod);
+ defer parent.finishArena(sema.mod);
+
+ const payload = &val_ptr.castTag(.@"union").?.data;
+ payload.tag = try Value.Tag.enum_field_index.create(arena, field_index);
+
+ return beginComptimePtrMutationInner(
+ sema,
+ block,
+ src,
+ parent.ty.structFieldType(field_index),
+ &payload.val,
+ ptr_elem_ty,
+ parent.decl_ref_mut,
+ );
+ },
+ .slice => switch (field_index) {
+ Value.Payload.Slice.ptr_index => return beginComptimePtrMutationInner(
+ sema,
+ block,
+ src,
+ parent.ty.slicePtrFieldType(try sema.arena.create(Type.SlicePtrFieldTypeBuffer)),
+ &val_ptr.castTag(.slice).?.data.ptr,
+ ptr_elem_ty,
+ parent.decl_ref_mut,
+ ),
+
+ Value.Payload.Slice.len_index => return beginComptimePtrMutationInner(
+ sema,
+ block,
+ src,
+ Type.usize,
+ &val_ptr.castTag(.slice).?.data.len,
+ ptr_elem_ty,
+ parent.decl_ref_mut,
+ ),
+
+ else => unreachable,
+ },
+
+ else => unreachable,
+ },
+ .reinterpret => |reinterpret| {
+ const field_offset_u64 = field_ptr.container_ty.structFieldOffset(field_index, target);
+ const field_offset = try sema.usizeCast(block, src, field_offset_u64);
+ return ComptimePtrMutationKit{
+ .decl_ref_mut = parent.decl_ref_mut,
+ .pointee = .{ .reinterpret = .{
+ .val_ptr = reinterpret.val_ptr,
+ .byte_offset = reinterpret.byte_offset + field_offset,
+ } },
+ .ty = parent.ty,
+ };
+ },
+ .bad_decl_ty, .bad_ptr_ty => return parent,
+ }
+ },
+ .eu_payload_ptr => {
+ const eu_ptr = ptr_val.castTag(.eu_payload_ptr).?.data;
+ var parent = try beginComptimePtrMutation(sema, block, src, eu_ptr.container_ptr, eu_ptr.container_ty);
+ switch (parent.pointee) {
+ .direct => |val_ptr| {
+ const payload_ty = parent.ty.errorUnionPayload();
+ switch (val_ptr.tag()) {
+ else => {
+ // An error union has been initialized to undefined at comptime and now we
+ // are for the first time setting the payload. We must change the
+ // representation of the error union from `undef` to `opt_payload`.
+ const arena = parent.beginArena(sema.mod);
+ defer parent.finishArena(sema.mod);
+
+ const payload = try arena.create(Value.Payload.SubValue);
+ payload.* = .{
+ .base = .{ .tag = .eu_payload },
+ .data = Value.undef,
+ };
+
+ val_ptr.* = Value.initPayload(&payload.base);
return ComptimePtrMutationKit{
.decl_ref_mut = parent.decl_ref_mut,
- .val = &fields[field_index],
- .ty = field_ty,
+ .pointee = .{ .direct = &payload.data },
+ .ty = payload_ty,
};
},
- .Union => {
- const payload = try arena.create(Value.Payload.Union);
- payload.* = .{ .data = .{
- .tag = try Value.Tag.enum_field_index.create(arena, field_index),
- .val = Value.undef,
- } };
+ .eu_payload => return ComptimePtrMutationKit{
+ .decl_ref_mut = parent.decl_ref_mut,
+ .pointee = .{ .direct = &val_ptr.castTag(.eu_payload).?.data },
+ .ty = payload_ty,
+ },
+ }
+ },
+ .bad_decl_ty, .bad_ptr_ty => return parent,
+ // Even though the parent value type has well-defined memory layout, our
+ // pointer type does not.
+ .reinterpret => return ComptimePtrMutationKit{
+ .decl_ref_mut = parent.decl_ref_mut,
+ .pointee = .bad_ptr_ty,
+ .ty = eu_ptr.container_ty,
+ },
+ }
+ },
+ .opt_payload_ptr => {
+ const opt_ptr = ptr_val.castTag(.opt_payload_ptr).?.data;
+ var parent = try beginComptimePtrMutation(sema, block, src, opt_ptr.container_ptr, opt_ptr.container_ty);
+ switch (parent.pointee) {
+ .direct => |val_ptr| {
+ const payload_ty = try parent.ty.optionalChildAlloc(sema.arena);
+ switch (val_ptr.tag()) {
+ .undef, .null_value => {
+ // An optional has been initialized to undefined at comptime and now we
+ // are for the first time setting the payload. We must change the
+ // representation of the optional from `undef` to `opt_payload`.
+ const arena = parent.beginArena(sema.mod);
+ defer parent.finishArena(sema.mod);
+
+ const payload = try arena.create(Value.Payload.SubValue);
+ payload.* = .{
+ .base = .{ .tag = .opt_payload },
+ .data = Value.undef,
+ };
- parent.val.* = Value.initPayload(&payload.base);
+ val_ptr.* = Value.initPayload(&payload.base);
return ComptimePtrMutationKit{
.decl_ref_mut = parent.decl_ref_mut,
- .val = &payload.data.val,
- .ty = field_ty,
+ .pointee = .{ .direct = &payload.data },
+ .ty = payload_ty,
};
},
+ .opt_payload => return ComptimePtrMutationKit{
+ .decl_ref_mut = parent.decl_ref_mut,
+ .pointee = .{ .direct = &val_ptr.castTag(.opt_payload).?.data },
+ .ty = payload_ty,
+ },
+
else => unreachable,
}
},
- .@"struct" => return ComptimePtrMutationKit{
+ .bad_decl_ty, .bad_ptr_ty => return parent,
+ // Even though the parent value type has well-defined memory layout, our
+ // pointer type does not.
+ .reinterpret => return ComptimePtrMutationKit{
.decl_ref_mut = parent.decl_ref_mut,
- .val = &parent.val.castTag(.@"struct").?.data[field_index],
- .ty = field_ty,
- },
- .@"union" => {
- // We need to set the active field of the union.
- const arena = parent.beginArena(sema.gpa);
- defer parent.finishArena();
-
- const payload = &parent.val.castTag(.@"union").?.data;
- payload.tag = try Value.Tag.enum_field_index.create(arena, field_index);
-
- return ComptimePtrMutationKit{
- .decl_ref_mut = parent.decl_ref_mut,
- .val = &payload.val,
- .ty = field_ty,
- };
+ .pointee = .bad_ptr_ty,
+ .ty = opt_ptr.container_ty,
},
-
- else => unreachable,
}
},
- .eu_payload_ptr => return sema.fail(block, src, "TODO comptime store to eu_payload_ptr", .{}),
- .opt_payload_ptr => return sema.fail(block, src, "TODO comptime store opt_payload_ptr", .{}),
.decl_ref => unreachable, // isComptimeMutablePtr() has been checked already
else => unreachable,
}
}
+fn beginComptimePtrMutationInner(
+ sema: *Sema,
+ block: *Block,
+ src: LazySrcLoc,
+ decl_ty: Type,
+ decl_val: *Value,
+ ptr_elem_ty: Type,
+ decl_ref_mut: Value.Payload.DeclRefMut.Data,
+) CompileError!ComptimePtrMutationKit {
+ const target = sema.mod.getTarget();
+ const coerce_ok = (try sema.coerceInMemoryAllowed(block, ptr_elem_ty, decl_ty, true, target, src, src)) == .ok;
+ if (coerce_ok) {
+ return ComptimePtrMutationKit{
+ .decl_ref_mut = decl_ref_mut,
+ .pointee = .{ .direct = decl_val },
+ .ty = decl_ty,
+ };
+ }
+
+ // Handle the case that the decl is an array and we're actually trying to point to an element.
+ if (decl_ty.isArrayOrVector()) {
+ const decl_elem_ty = decl_ty.childType();
+ if ((try sema.coerceInMemoryAllowed(block, ptr_elem_ty, decl_elem_ty, true, target, src, src)) == .ok) {
+ return ComptimePtrMutationKit{
+ .decl_ref_mut = decl_ref_mut,
+ .pointee = .{ .direct = decl_val },
+ .ty = decl_ty,
+ };
+ }
+ }
+
+ if (!decl_ty.hasWellDefinedLayout()) {
+ return ComptimePtrMutationKit{
+ .decl_ref_mut = decl_ref_mut,
+ .pointee = .{ .bad_decl_ty = {} },
+ .ty = decl_ty,
+ };
+ }
+ if (!ptr_elem_ty.hasWellDefinedLayout()) {
+ return ComptimePtrMutationKit{
+ .decl_ref_mut = decl_ref_mut,
+ .pointee = .{ .bad_ptr_ty = {} },
+ .ty = ptr_elem_ty,
+ };
+ }
+ return ComptimePtrMutationKit{
+ .decl_ref_mut = decl_ref_mut,
+ .pointee = .{ .reinterpret = .{
+ .val_ptr = decl_val,
+ .byte_offset = 0,
+ } },
+ .ty = decl_ty,
+ };
+}
+
+const TypedValueAndOffset = struct {
+ tv: TypedValue,
+ byte_offset: usize,
+};
+
const ComptimePtrLoadKit = struct {
- /// The Value of the Decl that owns this memory.
- root_val: Value,
- /// Parent Value.
- val: Value,
- /// The Type of the parent Value.
- ty: Type,
- /// The starting byte offset of `val` from `root_val`.
- /// If the type does not have a well-defined memory layout, this is null.
- byte_offset: ?usize,
- /// Whether the `root_val` could be mutated by further
+ /// The Value and Type corresponding to the pointee of the provided pointer.
+ /// If a direct dereference is not possible, this is null.
+ pointee: ?TypedValue,
+ /// The largest parent Value containing `pointee` and having a well-defined memory layout.
+ /// This is used for bitcasting, if direct dereferencing failed (i.e. `pointee` is null).
+ parent: ?TypedValueAndOffset,
+ /// Whether the `pointee` could be mutated by further
/// semantic analysis and a copy must be performed.
is_mutable: bool,
+ /// If the root decl could not be used as `parent`, this is the type that
+ /// caused that by not having a well-defined layout
+ ty_without_well_defined_layout: ?Type,
};
const ComptimePtrLoadError = CompileError || error{
RuntimeLoad,
};
+/// If `maybe_array_ty` is provided, it will be used to directly dereference an
+/// .elem_ptr of type T to a value of [N]T, if necessary.
fn beginComptimePtrLoad(
sema: *Sema,
block: *Block,
src: LazySrcLoc,
ptr_val: Value,
+ maybe_array_ty: ?Type,
) ComptimePtrLoadError!ComptimePtrLoadKit {
const target = sema.mod.getTarget();
- switch (ptr_val.tag()) {
- .decl_ref => {
- const decl = ptr_val.castTag(.decl_ref).?.data;
- const decl_val = try decl.value();
- if (decl_val.tag() == .variable) return error.RuntimeLoad;
- return ComptimePtrLoadKit{
- .root_val = decl_val,
- .val = decl_val,
- .ty = decl.ty,
- .byte_offset = 0,
- .is_mutable = false,
+ var deref: ComptimePtrLoadKit = switch (ptr_val.tag()) {
+ .decl_ref,
+ .decl_ref_mut,
+ => blk: {
+ const decl_index = switch (ptr_val.tag()) {
+ .decl_ref => ptr_val.castTag(.decl_ref).?.data,
+ .decl_ref_mut => ptr_val.castTag(.decl_ref_mut).?.data.decl_index,
+ else => unreachable,
};
- },
- .decl_ref_mut => {
- const decl = ptr_val.castTag(.decl_ref_mut).?.data.decl;
- const decl_val = try decl.value();
- if (decl_val.tag() == .variable) return error.RuntimeLoad;
- return ComptimePtrLoadKit{
- .root_val = decl_val,
- .val = decl_val,
- .ty = decl.ty,
- .byte_offset = 0,
- .is_mutable = true,
+ const is_mutable = ptr_val.tag() == .decl_ref_mut;
+ const decl = sema.mod.declPtr(decl_index);
+ const decl_tv = try decl.typedValue();
+ if (decl_tv.val.tag() == .variable) return error.RuntimeLoad;
+
+ const layout_defined = decl.ty.hasWellDefinedLayout();
+ break :blk ComptimePtrLoadKit{
+ .parent = if (layout_defined) .{ .tv = decl_tv, .byte_offset = 0 } else null,
+ .pointee = decl_tv,
+ .is_mutable = is_mutable,
+ .ty_without_well_defined_layout = if (!layout_defined) decl.ty else null,
};
},
- .elem_ptr => {
+
+ .elem_ptr => blk: {
const elem_ptr = ptr_val.castTag(.elem_ptr).?.data;
- const parent = try beginComptimePtrLoad(sema, block, src, elem_ptr.array_ptr);
- switch (parent.ty.zigTypeTag()) {
- .Array, .Vector => {
- const check_len = parent.ty.arrayLenIncludingSentinel();
- if (elem_ptr.index >= check_len) {
- // TODO have the parent include the decl so we can say "declared here"
- return sema.fail(block, src, "comptime load of index {d} out of bounds of array length {d}", .{
- elem_ptr.index, check_len,
- });
+ const elem_ty = elem_ptr.elem_ty;
+ var deref = try beginComptimePtrLoad(sema, block, src, elem_ptr.array_ptr, null);
+
+ // This code assumes that elem_ptrs have been "flattened" in order for direct dereference
+ // to succeed, meaning that elem ptrs of the same elem_ty are coalesced. Here we check that
+ // our parent is not an elem_ptr with the same elem_ty, since that would be "unflattened"
+ if (elem_ptr.array_ptr.castTag(.elem_ptr)) |parent_elem_ptr| {
+ assert(!(parent_elem_ptr.data.elem_ty.eql(elem_ty, sema.mod)));
+ }
+
+ if (elem_ptr.index != 0) {
+ if (elem_ty.hasWellDefinedLayout()) {
+ if (deref.parent) |*parent| {
+ // Update the byte offset (in-place)
+ const elem_size = try sema.typeAbiSize(block, src, elem_ty);
+ const offset = parent.byte_offset + elem_size * elem_ptr.index;
+ parent.byte_offset = try sema.usizeCast(block, src, offset);
}
- const elem_ty = parent.ty.childType();
- const byte_offset: ?usize = bo: {
- if (try sema.typeRequiresComptime(block, src, elem_ty)) {
- break :bo null;
- } else {
- if (parent.byte_offset) |off| {
- try sema.resolveTypeLayout(block, src, elem_ty);
- const elem_size = elem_ty.abiSize(target);
- break :bo try sema.usizeCast(block, src, off + elem_size * elem_ptr.index);
- } else {
- break :bo null;
- }
- }
- };
- return ComptimePtrLoadKit{
- .root_val = parent.root_val,
- .val = try parent.val.elemValue(sema.arena, elem_ptr.index),
+ } else {
+ deref.parent = null;
+ deref.ty_without_well_defined_layout = elem_ty;
+ }
+ }
+
+ // If we're loading an elem_ptr that was derived from a different type
+ // than the true type of the underlying decl, we cannot deref directly
+ const ty_matches = if (deref.pointee != null and deref.pointee.?.ty.isArrayOrVector()) x: {
+ const deref_elem_ty = deref.pointee.?.ty.childType();
+ break :x (try sema.coerceInMemoryAllowed(block, deref_elem_ty, elem_ty, false, target, src, src)) == .ok or
+ (try sema.coerceInMemoryAllowed(block, elem_ty, deref_elem_ty, false, target, src, src)) == .ok;
+ } else false;
+ if (!ty_matches) {
+ deref.pointee = null;
+ break :blk deref;
+ }
+
+ var array_tv = deref.pointee.?;
+ const check_len = array_tv.ty.arrayLenIncludingSentinel();
+ if (maybe_array_ty) |load_ty| {
+ // It's possible that we're loading a [N]T, in which case we'd like to slice
+ // the pointee array directly from our parent array.
+ if (load_ty.isArrayOrVector() and load_ty.childType().eql(elem_ty, sema.mod)) {
+ const N = try sema.usizeCast(block, src, load_ty.arrayLenIncludingSentinel());
+ deref.pointee = if (elem_ptr.index + N <= check_len) TypedValue{
+ .ty = try Type.array(sema.arena, N, null, elem_ty, sema.mod),
+ .val = try array_tv.val.sliceArray(sema.mod, sema.arena, elem_ptr.index, elem_ptr.index + N),
+ } else null;
+ break :blk deref;
+ }
+ }
+
+ if (elem_ptr.index >= check_len) {
+ deref.pointee = null;
+ break :blk deref;
+ }
+ if (elem_ptr.index == check_len - 1) {
+ if (array_tv.ty.sentinel()) |sent| {
+ deref.pointee = TypedValue{
.ty = elem_ty,
- .byte_offset = byte_offset,
- .is_mutable = parent.is_mutable,
- };
- },
- else => {
- if (elem_ptr.index != 0) {
- // TODO have the parent include the decl so we can say "declared here"
- return sema.fail(block, src, "out of bounds comptime load of index {d}", .{
- elem_ptr.index,
- });
- }
- return ComptimePtrLoadKit{
- .root_val = parent.root_val,
- .val = parent.val,
- .ty = parent.ty,
- .byte_offset = parent.byte_offset,
- .is_mutable = parent.is_mutable,
+ .val = sent,
};
- },
+ break :blk deref;
+ }
}
+ deref.pointee = TypedValue{
+ .ty = elem_ty,
+ .val = try array_tv.val.elemValue(sema.mod, sema.arena, elem_ptr.index),
+ };
+ break :blk deref;
},
- .field_ptr => {
+
+ .field_ptr => blk: {
const field_ptr = ptr_val.castTag(.field_ptr).?.data;
- const parent = try beginComptimePtrLoad(sema, block, src, field_ptr.container_ptr);
const field_index = @intCast(u32, field_ptr.field_index);
- const byte_offset: ?usize = bo: {
- if (try sema.typeRequiresComptime(block, src, parent.ty)) {
- break :bo null;
- } else {
- if (parent.byte_offset) |off| {
- try sema.resolveTypeLayout(block, src, parent.ty);
- const field_offset = parent.ty.structFieldOffset(field_index, target);
- break :bo try sema.usizeCast(block, src, off + field_offset);
- } else {
- break :bo null;
- }
+ var deref = try beginComptimePtrLoad(sema, block, src, field_ptr.container_ptr, field_ptr.container_ty);
+
+ if (field_ptr.container_ty.hasWellDefinedLayout()) {
+ const struct_ty = field_ptr.container_ty.castTag(.@"struct");
+ if (struct_ty != null and struct_ty.?.data.layout == .Packed) {
+ // packed structs are not byte addressable
+ deref.parent = null;
+ } else if (deref.parent) |*parent| {
+ // Update the byte offset (in-place)
+ try sema.resolveTypeLayout(block, src, field_ptr.container_ty);
+ const field_offset = field_ptr.container_ty.structFieldOffset(field_index, target);
+ parent.byte_offset = try sema.usizeCast(block, src, parent.byte_offset + field_offset);
}
- };
- return ComptimePtrLoadKit{
- .root_val = parent.root_val,
- .val = try parent.val.fieldValue(sema.arena, field_index),
- .ty = parent.ty.structFieldType(field_index),
- .byte_offset = byte_offset,
- .is_mutable = parent.is_mutable,
- };
- },
- .eu_payload_ptr => {
- const err_union_ptr = ptr_val.castTag(.eu_payload_ptr).?.data;
- const parent = try beginComptimePtrLoad(sema, block, src, err_union_ptr);
- return ComptimePtrLoadKit{
- .root_val = parent.root_val,
- .val = parent.val.castTag(.eu_payload).?.data,
- .ty = parent.ty.errorUnionPayload(),
- .byte_offset = null,
- .is_mutable = parent.is_mutable,
- };
+ } else {
+ deref.parent = null;
+ deref.ty_without_well_defined_layout = field_ptr.container_ty;
+ }
+
+ const tv = &(deref.pointee orelse {
+ deref.pointee = null;
+ break :blk deref;
+ });
+ const coerce_in_mem_ok =
+ (try sema.coerceInMemoryAllowed(block, field_ptr.container_ty, tv.ty, false, target, src, src)) == .ok or
+ (try sema.coerceInMemoryAllowed(block, tv.ty, field_ptr.container_ty, false, target, src, src)) == .ok;
+ if (!coerce_in_mem_ok) {
+ deref.pointee = null;
+ break :blk deref;
+ }
+
+ if (field_ptr.container_ty.isSlice()) {
+ const slice_val = tv.val.castTag(.slice).?.data;
+ deref.pointee = switch (field_index) {
+ Value.Payload.Slice.ptr_index => TypedValue{
+ .ty = field_ptr.container_ty.slicePtrFieldType(try sema.arena.create(Type.SlicePtrFieldTypeBuffer)),
+ .val = slice_val.ptr,
+ },
+ Value.Payload.Slice.len_index => TypedValue{
+ .ty = Type.usize,
+ .val = slice_val.len,
+ },
+ else => unreachable,
+ };
+ } else {
+ const field_ty = field_ptr.container_ty.structFieldType(field_index);
+ deref.pointee = TypedValue{
+ .ty = field_ty,
+ .val = tv.val.fieldValue(tv.ty, field_index),
+ };
+ }
+ break :blk deref;
},
- .opt_payload_ptr => {
- const opt_ptr = ptr_val.castTag(.opt_payload_ptr).?.data;
- const parent = try beginComptimePtrLoad(sema, block, src, opt_ptr);
- return ComptimePtrLoadKit{
- .root_val = parent.root_val,
- .val = parent.val.castTag(.opt_payload).?.data,
- .ty = try parent.ty.optionalChildAlloc(sema.arena),
- .byte_offset = null,
- .is_mutable = parent.is_mutable,
+
+ .opt_payload_ptr,
+ .eu_payload_ptr,
+ => blk: {
+ const payload_ptr = ptr_val.cast(Value.Payload.PayloadPtr).?.data;
+ const payload_ty = switch (ptr_val.tag()) {
+ .eu_payload_ptr => payload_ptr.container_ty.errorUnionPayload(),
+ .opt_payload_ptr => try payload_ptr.container_ty.optionalChildAlloc(sema.arena),
+ else => unreachable,
};
+ var deref = try beginComptimePtrLoad(sema, block, src, payload_ptr.container_ptr, payload_ptr.container_ty);
+
+ // eu_payload_ptr and opt_payload_ptr never have a well-defined layout
+ if (deref.parent != null) {
+ deref.parent = null;
+ deref.ty_without_well_defined_layout = payload_ptr.container_ty;
+ }
+
+ if (deref.pointee) |*tv| {
+ const coerce_in_mem_ok =
+ (try sema.coerceInMemoryAllowed(block, payload_ptr.container_ty, tv.ty, false, target, src, src)) == .ok or
+ (try sema.coerceInMemoryAllowed(block, tv.ty, payload_ptr.container_ty, false, target, src, src)) == .ok;
+ if (coerce_in_mem_ok) {
+ const payload_val = switch (ptr_val.tag()) {
+ .eu_payload_ptr => tv.val.castTag(.eu_payload).?.data,
+ .opt_payload_ptr => tv.val.castTag(.opt_payload).?.data,
+ else => unreachable,
+ };
+ tv.* = TypedValue{ .ty = payload_ty, .val = payload_val };
+ break :blk deref;
+ }
+ }
+ deref.pointee = null;
+ break :blk deref;
},
.zero,
@@ -15087,44 +21695,69 @@ fn beginComptimePtrLoad(
=> return error.RuntimeLoad,
else => unreachable,
+ };
+
+ if (deref.pointee) |tv| {
+ if (deref.parent == null and tv.ty.hasWellDefinedLayout()) {
+ deref.parent = .{ .tv = tv, .byte_offset = 0 };
+ }
}
+ return deref;
}
fn bitCast(
sema: *Sema,
block: *Block,
- dest_ty: Type,
+ dest_ty_unresolved: Type,
inst: Air.Inst.Ref,
inst_src: LazySrcLoc,
) CompileError!Air.Inst.Ref {
- // TODO validate the type size and other compile errors
+ const dest_ty = try sema.resolveTypeFields(block, inst_src, dest_ty_unresolved);
+ try sema.resolveTypeLayout(block, inst_src, dest_ty);
+
+ const old_ty = try sema.resolveTypeFields(block, inst_src, sema.typeOf(inst));
+ try sema.resolveTypeLayout(block, inst_src, old_ty);
+
+ const target = sema.mod.getTarget();
+ const dest_bits = dest_ty.bitSize(target);
+ const old_bits = old_ty.bitSize(target);
+
+ if (old_bits != dest_bits) {
+ return sema.fail(block, inst_src, "@bitCast size mismatch: destination type '{}' has {d} bits but source type '{}' has {d} bits", .{
+ dest_ty.fmt(sema.mod),
+ dest_bits,
+ old_ty.fmt(sema.mod),
+ old_bits,
+ });
+ }
+
if (try sema.resolveMaybeUndefVal(block, inst_src, inst)) |val| {
- const old_ty = sema.typeOf(inst);
- const result_val = try sema.bitCastVal(block, inst_src, val, old_ty, dest_ty);
+ const result_val = try sema.bitCastVal(block, inst_src, val, old_ty, dest_ty, 0);
return sema.addConstant(dest_ty, result_val);
}
try sema.requireRuntimeBlock(block, inst_src);
return block.addBitCast(dest_ty, inst);
}
-pub fn bitCastVal(
+fn bitCastVal(
sema: *Sema,
block: *Block,
src: LazySrcLoc,
val: Value,
old_ty: Type,
new_ty: Type,
+ buffer_offset: usize,
) !Value {
- if (old_ty.eql(new_ty)) return val;
+ const target = sema.mod.getTarget();
+ if (old_ty.eql(new_ty, sema.mod)) return val;
// For types with well-defined memory layouts, we serialize them a byte buffer,
// then deserialize to the new type.
- const target = sema.mod.getTarget();
const abi_size = try sema.usizeCast(block, src, old_ty.abiSize(target));
const buffer = try sema.gpa.alloc(u8, abi_size);
defer sema.gpa.free(buffer);
- val.writeToMemory(old_ty, target, buffer);
- return Value.readFromMemory(new_ty, target, buffer, sema.arena);
+ val.writeToMemory(old_ty, sema.mod, buffer);
+ return Value.readFromMemory(new_ty, sema.mod, buffer[buffer_offset..], sema.arena);
}
fn coerceArrayPtrToSlice(
@@ -15134,7 +21767,7 @@ fn coerceArrayPtrToSlice(
inst: Air.Inst.Ref,
inst_src: LazySrcLoc,
) CompileError!Air.Inst.Ref {
- if (try sema.resolveDefinedValue(block, inst_src, inst)) |val| {
+ if (try sema.resolveMaybeUndefVal(block, inst_src, inst)) |val| {
const ptr_array_ty = sema.typeOf(inst);
const array_ty = ptr_array_ty.childType();
const slice_val = try Value.Tag.slice.create(sema.arena, .{
@@ -15154,6 +21787,7 @@ fn coerceCompatiblePtrs(
inst: Air.Inst.Ref,
inst_src: LazySrcLoc,
) !Air.Inst.Ref {
+ // TODO check const/volatile/alignment
if (try sema.resolveMaybeUndefVal(block, inst_src, inst)) |val| {
// The comptime Value representation is compatible with both types.
return sema.addConstant(dest_ty, val);
@@ -15174,45 +21808,46 @@ fn coerceEnumToUnion(
const tag_ty = union_ty.unionTagType() orelse {
const msg = msg: {
- const msg = try sema.errMsg(block, inst_src, "expected {}, found {}", .{
- union_ty, inst_ty,
+ const msg = try sema.errMsg(block, inst_src, "expected type '{}', found '{}'", .{
+ union_ty.fmt(sema.mod), inst_ty.fmt(sema.mod),
});
errdefer msg.destroy(sema.gpa);
try sema.errNote(block, union_ty_src, msg, "cannot coerce enum to untagged union", .{});
try sema.addDeclaredHereNote(msg, union_ty);
break :msg msg;
};
- return sema.failWithOwnedErrorMsg(msg);
+ return sema.failWithOwnedErrorMsg(block, msg);
};
const enum_tag = try sema.coerce(block, tag_ty, inst, inst_src);
if (try sema.resolveDefinedValue(block, inst_src, enum_tag)) |val| {
const union_obj = union_ty.cast(Type.Payload.Union).?.data;
- const field_index = union_obj.tag_ty.enumTagFieldIndex(val) orelse {
+ const field_index = union_obj.tag_ty.enumTagFieldIndex(val, sema.mod) orelse {
const msg = msg: {
- const msg = try sema.errMsg(block, inst_src, "union {} has no tag with value {}", .{
- union_ty, val,
+ const msg = try sema.errMsg(block, inst_src, "union '{}' has no tag with value '{}'", .{
+ union_ty.fmt(sema.mod), val.fmtValue(tag_ty, sema.mod),
});
errdefer msg.destroy(sema.gpa);
try sema.addDeclaredHereNote(msg, union_ty);
break :msg msg;
};
- return sema.failWithOwnedErrorMsg(msg);
+ return sema.failWithOwnedErrorMsg(block, msg);
};
const field = union_obj.fields.values()[field_index];
const field_ty = try sema.resolveTypeFields(block, inst_src, field.ty);
const opv = (try sema.typeHasOnePossibleValue(block, inst_src, field_ty)) orelse {
- // TODO resolve the field names and include in the error message,
- // also instead of 'union declared here' make it 'field "foo" declared here'.
const msg = msg: {
- const msg = try sema.errMsg(block, inst_src, "coercion to union {} must initialize {} field", .{
- union_ty, field_ty,
+ const field_name = union_obj.fields.keys()[field_index];
+ const msg = try sema.errMsg(block, inst_src, "coercion from enum '{}' to union '{}' must initialize '{}' field '{s}'", .{
+ inst_ty.fmt(sema.mod), union_ty.fmt(sema.mod), field_ty.fmt(sema.mod), field_name,
});
errdefer msg.destroy(sema.gpa);
+
+ try sema.addFieldErrNote(block, union_ty, field_index, msg, "field '{s}' declared here", .{field_name});
try sema.addDeclaredHereNote(msg, union_ty);
break :msg msg;
};
- return sema.failWithOwnedErrorMsg(msg);
+ return sema.failWithOwnedErrorMsg(block, msg);
};
return sema.addConstant(union_ty, try Value.Tag.@"union".create(sema.arena, .{
@@ -15225,14 +21860,14 @@ fn coerceEnumToUnion(
if (tag_ty.isNonexhaustiveEnum()) {
const msg = msg: {
- const msg = try sema.errMsg(block, inst_src, "runtime coercion to union {} from non-exhaustive enum", .{
- union_ty,
+ const msg = try sema.errMsg(block, inst_src, "runtime coercion to union '{}' from non-exhaustive enum", .{
+ union_ty.fmt(sema.mod),
});
errdefer msg.destroy(sema.gpa);
try sema.addDeclaredHereNote(msg, tag_ty);
break :msg msg;
};
- return sema.failWithOwnedErrorMsg(msg);
+ return sema.failWithOwnedErrorMsg(block, msg);
}
// If the union has all fields 0 bits, the union value is just the enum value.
@@ -15240,17 +21875,90 @@ fn coerceEnumToUnion(
return block.addBitCast(union_ty, enum_tag);
}
- // TODO resolve the field names and add a hint that says "field 'foo' has type 'bar'"
- // instead of the "union declared here" hint
const msg = msg: {
- const msg = try sema.errMsg(block, inst_src, "runtime coercion to union {} which has non-void fields", .{
- union_ty,
- });
+ const union_obj = union_ty.cast(Type.Payload.Union).?.data;
+ const msg = try sema.errMsg(
+ block,
+ inst_src,
+ "runtime coercion from enum '{}' to union '{}' which has non-void fields",
+ .{ tag_ty.fmt(sema.mod), union_ty.fmt(sema.mod) },
+ );
errdefer msg.destroy(sema.gpa);
+
+ var it = union_obj.fields.iterator();
+ var field_index: usize = 0;
+ while (it.next()) |field| {
+ const field_name = field.key_ptr.*;
+ const field_ty = field.value_ptr.ty;
+ try sema.addFieldErrNote(block, union_ty, field_index, msg, "field '{s}' has type '{}'", .{ field_name, field_ty.fmt(sema.mod) });
+ field_index += 1;
+ }
try sema.addDeclaredHereNote(msg, union_ty);
break :msg msg;
};
- return sema.failWithOwnedErrorMsg(msg);
+ return sema.failWithOwnedErrorMsg(block, msg);
+}
+
+fn coerceAnonStructToUnion(
+ sema: *Sema,
+ block: *Block,
+ union_ty: Type,
+ union_ty_src: LazySrcLoc,
+ inst: Air.Inst.Ref,
+ inst_src: LazySrcLoc,
+) !Air.Inst.Ref {
+ const inst_ty = sema.typeOf(inst);
+ const anon_struct = inst_ty.castTag(.anon_struct).?.data;
+ if (anon_struct.types.len != 1) {
+ const msg = msg: {
+ const msg = try sema.errMsg(
+ block,
+ inst_src,
+ "cannot initialize multiple union fields at once, unions can only have one active field",
+ .{},
+ );
+ errdefer msg.destroy(sema.gpa);
+
+ // TODO add notes for where the anon struct was created to point out
+ // the extra fields.
+
+ try sema.addDeclaredHereNote(msg, union_ty);
+ break :msg msg;
+ };
+ return sema.failWithOwnedErrorMsg(block, msg);
+ }
+
+ const field_name = anon_struct.names[0];
+ const init = try sema.structFieldVal(block, inst_src, inst, field_name, inst_src, inst_ty);
+ return sema.unionInit(block, init, inst_src, union_ty, union_ty_src, field_name, inst_src);
+}
+
+fn coerceAnonStructToUnionPtrs(
+ sema: *Sema,
+ block: *Block,
+ ptr_union_ty: Type,
+ union_ty_src: LazySrcLoc,
+ ptr_anon_struct: Air.Inst.Ref,
+ anon_struct_src: LazySrcLoc,
+) !Air.Inst.Ref {
+ const union_ty = ptr_union_ty.childType();
+ const anon_struct = try sema.analyzeLoad(block, anon_struct_src, ptr_anon_struct, anon_struct_src);
+ const union_inst = try sema.coerceAnonStructToUnion(block, union_ty, union_ty_src, anon_struct, anon_struct_src);
+ return sema.analyzeRef(block, union_ty_src, union_inst);
+}
+
+fn coerceAnonStructToStructPtrs(
+ sema: *Sema,
+ block: *Block,
+ ptr_struct_ty: Type,
+ struct_ty_src: LazySrcLoc,
+ ptr_anon_struct: Air.Inst.Ref,
+ anon_struct_src: LazySrcLoc,
+) !Air.Inst.Ref {
+ const struct_ty = ptr_struct_ty.childType();
+ const anon_struct = try sema.analyzeLoad(block, anon_struct_src, ptr_anon_struct, anon_struct_src);
+ const struct_inst = try sema.coerceTupleToStruct(block, struct_ty, struct_ty_src, anon_struct, anon_struct_src);
+ return sema.analyzeRef(block, struct_ty_src, struct_inst);
}
/// If the lengths match, coerces element-wise.
@@ -15265,21 +21973,21 @@ fn coerceArrayLike(
const inst_ty = sema.typeOf(inst);
const inst_len = inst_ty.arrayLen();
const dest_len = try sema.usizeCast(block, dest_ty_src, dest_ty.arrayLen());
+ const target = sema.mod.getTarget();
if (dest_len != inst_len) {
const msg = msg: {
- const msg = try sema.errMsg(block, inst_src, "expected {}, found {}", .{
- dest_ty, inst_ty,
+ const msg = try sema.errMsg(block, inst_src, "expected type '{}', found '{}'", .{
+ dest_ty.fmt(sema.mod), inst_ty.fmt(sema.mod),
});
errdefer msg.destroy(sema.gpa);
try sema.errNote(block, dest_ty_src, msg, "destination has length {d}", .{dest_len});
try sema.errNote(block, inst_src, msg, "source has length {d}", .{inst_len});
break :msg msg;
};
- return sema.failWithOwnedErrorMsg(msg);
+ return sema.failWithOwnedErrorMsg(block, msg);
}
- const target = sema.mod.getTarget();
const dest_elem_ty = dest_ty.childType();
const inst_elem_ty = inst_ty.childType();
const in_memory_result = try sema.coerceInMemoryAllowed(block, dest_elem_ty, inst_elem_ty, false, target, dest_ty_src, inst_src);
@@ -15302,7 +22010,7 @@ fn coerceArrayLike(
try Value.Tag.int_u64.create(sema.arena, i),
);
const elem_src = inst_src; // TODO better source location
- const elem_ref = try elemValArray(sema, block, inst, index_ref, inst_src, elem_src);
+ const elem_ref = try elemValArray(sema, block, inst_src, inst, elem_src, index_ref);
const coerced = try sema.coerce(block, dest_elem_ty, elem_ref, elem_src);
element_refs[i] = coerced;
if (runtime_src == null) {
@@ -15316,12 +22024,12 @@ fn coerceArrayLike(
if (runtime_src) |rs| {
try sema.requireRuntimeBlock(block, rs);
- return block.addVectorInit(dest_ty, element_refs);
+ return block.addAggregateInit(dest_ty, element_refs);
}
return sema.addConstant(
dest_ty,
- try Value.Tag.array.create(sema.arena, element_vals),
+ try Value.Tag.aggregate.create(sema.arena, element_vals),
);
}
@@ -15336,30 +22044,36 @@ fn coerceTupleToArray(
) !Air.Inst.Ref {
const inst_ty = sema.typeOf(inst);
const inst_len = inst_ty.arrayLen();
- const dest_len = try sema.usizeCast(block, dest_ty_src, dest_ty.arrayLen());
+ const dest_len = dest_ty.arrayLen();
if (dest_len != inst_len) {
const msg = msg: {
- const msg = try sema.errMsg(block, inst_src, "expected {}, found {}", .{
- dest_ty, inst_ty,
+ const msg = try sema.errMsg(block, inst_src, "expected type '{}', found '{}'", .{
+ dest_ty.fmt(sema.mod), inst_ty.fmt(sema.mod),
});
errdefer msg.destroy(sema.gpa);
try sema.errNote(block, dest_ty_src, msg, "destination has length {d}", .{dest_len});
try sema.errNote(block, inst_src, msg, "source has length {d}", .{inst_len});
break :msg msg;
};
- return sema.failWithOwnedErrorMsg(msg);
+ return sema.failWithOwnedErrorMsg(block, msg);
}
- const element_vals = try sema.arena.alloc(Value, dest_len);
- const element_refs = try sema.arena.alloc(Air.Inst.Ref, dest_len);
+ const dest_elems = try sema.usizeCast(block, dest_ty_src, dest_ty.arrayLenIncludingSentinel());
+ const element_vals = try sema.arena.alloc(Value, dest_elems);
+ const element_refs = try sema.arena.alloc(Air.Inst.Ref, dest_elems);
const dest_elem_ty = dest_ty.childType();
var runtime_src: ?LazySrcLoc = null;
for (element_vals) |*elem, i_usize| {
const i = @intCast(u32, i_usize);
+ if (i_usize == inst_len) {
+ elem.* = dest_ty.sentinel().?;
+ element_refs[i] = try sema.addConstant(dest_elem_ty, elem.*);
+ break;
+ }
const elem_src = inst_src; // TODO better source location
- const elem_ref = try tupleField(sema, block, inst, i, inst_src, elem_src);
+ const elem_ref = try tupleField(sema, block, inst_src, inst, elem_src, i);
const coerced = try sema.coerce(block, dest_elem_ty, elem_ref, elem_src);
element_refs[i] = coerced;
if (runtime_src == null) {
@@ -15373,12 +22087,143 @@ fn coerceTupleToArray(
if (runtime_src) |rs| {
try sema.requireRuntimeBlock(block, rs);
- return block.addVectorInit(dest_ty, element_refs);
+ return block.addAggregateInit(dest_ty, element_refs);
}
return sema.addConstant(
dest_ty,
- try Value.Tag.array.create(sema.arena, element_vals),
+ try Value.Tag.aggregate.create(sema.arena, element_vals),
+ );
+}
+
+/// If the lengths match, coerces element-wise.
+fn coerceTupleToSlicePtrs(
+ sema: *Sema,
+ block: *Block,
+ slice_ty: Type,
+ slice_ty_src: LazySrcLoc,
+ ptr_tuple: Air.Inst.Ref,
+ tuple_src: LazySrcLoc,
+) !Air.Inst.Ref {
+ const tuple_ty = sema.typeOf(ptr_tuple).childType();
+ const tuple = try sema.analyzeLoad(block, tuple_src, ptr_tuple, tuple_src);
+ const slice_info = slice_ty.ptrInfo().data;
+ const array_ty = try Type.array(sema.arena, tuple_ty.structFieldCount(), slice_info.sentinel, slice_info.pointee_type, sema.mod);
+ const array_inst = try sema.coerceTupleToArray(block, array_ty, slice_ty_src, tuple, tuple_src);
+ if (slice_info.@"align" != 0) {
+ return sema.fail(block, slice_ty_src, "TODO: override the alignment of the array decl we create here", .{});
+ }
+ const ptr_array = try sema.analyzeRef(block, slice_ty_src, array_inst);
+ return sema.coerceArrayPtrToSlice(block, slice_ty, ptr_array, slice_ty_src);
+}
+
+/// If the lengths match, coerces element-wise.
+fn coerceTupleToArrayPtrs(
+ sema: *Sema,
+ block: *Block,
+ ptr_array_ty: Type,
+ array_ty_src: LazySrcLoc,
+ ptr_tuple: Air.Inst.Ref,
+ tuple_src: LazySrcLoc,
+) !Air.Inst.Ref {
+ const tuple = try sema.analyzeLoad(block, tuple_src, ptr_tuple, tuple_src);
+ const ptr_info = ptr_array_ty.ptrInfo().data;
+ const array_ty = ptr_info.pointee_type;
+ const array_inst = try sema.coerceTupleToArray(block, array_ty, array_ty_src, tuple, tuple_src);
+ if (ptr_info.@"align" != 0) {
+ return sema.fail(block, array_ty_src, "TODO: override the alignment of the array decl we create here", .{});
+ }
+ const ptr_array = try sema.analyzeRef(block, array_ty_src, array_inst);
+ return ptr_array;
+}
+
+/// Handles both tuples and anon struct literals. Coerces field-wise. Reports
+/// errors for both extra fields and missing fields.
+fn coerceTupleToStruct(
+ sema: *Sema,
+ block: *Block,
+ dest_ty: Type,
+ dest_ty_src: LazySrcLoc,
+ inst: Air.Inst.Ref,
+ inst_src: LazySrcLoc,
+) !Air.Inst.Ref {
+ const struct_ty = try sema.resolveTypeFields(block, dest_ty_src, dest_ty);
+
+ if (struct_ty.isTupleOrAnonStruct()) {
+ return sema.fail(block, dest_ty_src, "TODO: implement coercion from tuples to tuples", .{});
+ }
+
+ const fields = struct_ty.structFields();
+ const field_vals = try sema.arena.alloc(Value, fields.count());
+ const field_refs = try sema.arena.alloc(Air.Inst.Ref, field_vals.len);
+ mem.set(Air.Inst.Ref, field_refs, .none);
+
+ const inst_ty = sema.typeOf(inst);
+ const tuple = inst_ty.tupleFields();
+ var runtime_src: ?LazySrcLoc = null;
+ for (tuple.types) |_, i_usize| {
+ const i = @intCast(u32, i_usize);
+ const field_src = inst_src; // TODO better source location
+ const field_name = if (inst_ty.castTag(.anon_struct)) |payload|
+ payload.data.names[i]
+ else
+ try std.fmt.allocPrint(sema.arena, "{d}", .{i});
+ const field_index = try sema.structFieldIndex(block, struct_ty, field_name, field_src);
+ const field = fields.values()[field_index];
+ if (field.is_comptime) {
+ return sema.fail(block, dest_ty_src, "TODO: implement coercion from tuples to structs when one of the destination struct fields is comptime", .{});
+ }
+ const elem_ref = try tupleField(sema, block, inst_src, inst, field_src, i);
+ const coerced = try sema.coerce(block, field.ty, elem_ref, field_src);
+ field_refs[field_index] = coerced;
+ if (runtime_src == null) {
+ if (try sema.resolveMaybeUndefVal(block, field_src, coerced)) |field_val| {
+ field_vals[field_index] = field_val;
+ } else {
+ runtime_src = field_src;
+ }
+ }
+ }
+
+ // Populate default field values and report errors for missing fields.
+ var root_msg: ?*Module.ErrorMsg = null;
+
+ for (field_refs) |*field_ref, i| {
+ if (field_ref.* != .none) continue;
+
+ const field_name = fields.keys()[i];
+ const field = fields.values()[i];
+ const field_src = inst_src; // TODO better source location
+ if (field.default_val.tag() == .unreachable_value) {
+ const template = "missing struct field: {s}";
+ const args = .{field_name};
+ if (root_msg) |msg| {
+ try sema.errNote(block, field_src, msg, template, args);
+ } else {
+ root_msg = try sema.errMsg(block, field_src, template, args);
+ }
+ continue;
+ }
+ if (runtime_src == null) {
+ field_vals[i] = field.default_val;
+ } else {
+ field_ref.* = try sema.addConstant(field.ty, field.default_val);
+ }
+ }
+
+ if (root_msg) |msg| {
+ try sema.addDeclaredHereNote(msg, struct_ty);
+ return sema.failWithOwnedErrorMsg(block, msg);
+ }
+
+ if (runtime_src) |rs| {
+ try sema.requireRuntimeBlock(block, rs);
+ return block.addAggregateInit(struct_ty, field_refs);
+ }
+
+ return sema.addConstant(
+ struct_ty,
+ try Value.Tag.aggregate.create(sema.arena, field_vals),
);
}
@@ -15386,23 +22231,34 @@ fn analyzeDeclVal(
sema: *Sema,
block: *Block,
src: LazySrcLoc,
- decl: *Decl,
+ decl_index: Decl.Index,
) CompileError!Air.Inst.Ref {
- if (sema.decl_val_table.get(decl)) |result| {
+ if (sema.decl_val_table.get(decl_index)) |result| {
return result;
}
- const decl_ref = try sema.analyzeDeclRef(decl);
+ const decl_ref = try sema.analyzeDeclRef(decl_index);
const result = try sema.analyzeLoad(block, src, decl_ref, src);
if (Air.refToIndex(result)) |index| {
- if (sema.air_instructions.items(.tag)[index] == .constant) {
- try sema.decl_val_table.put(sema.gpa, decl, result);
+ if (sema.air_instructions.items(.tag)[index] == .constant and !block.is_typeof) {
+ try sema.decl_val_table.put(sema.gpa, decl_index, result);
}
}
return result;
}
-fn ensureDeclAnalyzed(sema: *Sema, decl: *Decl) CompileError!void {
- sema.mod.ensureDeclAnalyzed(decl) catch |err| {
+fn ensureDeclAnalyzed(sema: *Sema, decl_index: Decl.Index) CompileError!void {
+ sema.mod.ensureDeclAnalyzed(decl_index) catch |err| {
+ if (sema.owner_func) |owner_func| {
+ owner_func.state = .dependency_failure;
+ } else {
+ sema.owner_decl.analysis = .dependency_failure;
+ }
+ return err;
+ };
+}
+
+fn ensureFuncBodyAnalyzed(sema: *Sema, func: *Module.Fn) CompileError!void {
+ sema.mod.ensureFuncBodyAnalyzed(func) catch |err| {
if (sema.owner_func) |owner_func| {
owner_func.state = .dependency_failure;
} else {
@@ -15412,32 +22268,48 @@ fn ensureDeclAnalyzed(sema: *Sema, decl: *Decl) CompileError!void {
};
}
-fn analyzeDeclRef(sema: *Sema, decl: *Decl) CompileError!Air.Inst.Ref {
- try sema.mod.declareDeclDependency(sema.owner_decl, decl);
- try sema.ensureDeclAnalyzed(decl);
+fn refValue(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type, val: Value) !Value {
+ var anon_decl = try block.startAnonDecl(src);
+ defer anon_decl.deinit();
+ const decl = try anon_decl.finish(
+ try ty.copy(anon_decl.arena()),
+ try val.copy(anon_decl.arena()),
+ 0, // default alignment
+ );
+ try sema.mod.declareDeclDependency(sema.owner_decl_index, decl);
+ return try Value.Tag.decl_ref.create(sema.arena, decl);
+}
+
+fn optRefValue(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type, opt_val: ?Value) !Value {
+ const val = opt_val orelse return Value.@"null";
+ const ptr_val = try refValue(sema, block, src, ty, val);
+ const result = try Value.Tag.opt_payload.create(sema.arena, ptr_val);
+ return result;
+}
+fn analyzeDeclRef(sema: *Sema, decl_index: Decl.Index) CompileError!Air.Inst.Ref {
+ try sema.mod.declareDeclDependency(sema.owner_decl_index, decl_index);
+ try sema.ensureDeclAnalyzed(decl_index);
+
+ const decl = sema.mod.declPtr(decl_index);
const decl_tv = try decl.typedValue();
if (decl_tv.val.castTag(.variable)) |payload| {
const variable = payload.data;
- const alignment: u32 = if (decl.align_val.tag() == .null_value)
- 0
- else
- @intCast(u32, decl.align_val.toUnsignedInt());
- const ty = try Type.ptr(sema.arena, .{
+ const ty = try Type.ptr(sema.arena, sema.mod, .{
.pointee_type = decl_tv.ty,
.mutable = variable.is_mutable,
.@"addrspace" = decl.@"addrspace",
- .@"align" = alignment,
+ .@"align" = decl.@"align",
});
- return sema.addConstant(ty, try Value.Tag.decl_ref.create(sema.arena, decl));
+ return sema.addConstant(ty, try Value.Tag.decl_ref.create(sema.arena, decl_index));
}
return sema.addConstant(
- try Type.ptr(sema.arena, .{
+ try Type.ptr(sema.arena, sema.mod, .{
.pointee_type = decl_tv.ty,
.mutable = false,
.@"addrspace" = decl.@"addrspace",
}),
- try Value.Tag.decl_ref.create(sema.arena, decl),
+ try Value.Tag.decl_ref.create(sema.arena, decl_index),
);
}
@@ -15455,17 +22327,18 @@ fn analyzeRef(
return sema.analyzeDeclRef(try anon_decl.finish(
try operand_ty.copy(anon_decl.arena()),
try val.copy(anon_decl.arena()),
+ 0, // default alignment
));
}
try sema.requireRuntimeBlock(block, src);
const address_space = target_util.defaultAddressSpace(sema.mod.getTarget(), .local);
- const ptr_type = try Type.ptr(sema.arena, .{
+ const ptr_type = try Type.ptr(sema.arena, sema.mod, .{
.pointee_type = operand_ty,
.mutable = false,
.@"addrspace" = address_space,
});
- const mut_ptr_type = try Type.ptr(sema.arena, .{
+ const mut_ptr_type = try Type.ptr(sema.arena, sema.mod, .{
.pointee_type = operand_ty,
.@"addrspace" = address_space,
});
@@ -15486,14 +22359,25 @@ fn analyzeLoad(
const ptr_ty = sema.typeOf(ptr);
const elem_ty = switch (ptr_ty.zigTypeTag()) {
.Pointer => ptr_ty.childType(),
- else => return sema.fail(block, ptr_src, "expected pointer, found '{}'", .{ptr_ty}),
+ else => return sema.fail(block, ptr_src, "expected pointer, found '{}'", .{ptr_ty.fmt(sema.mod)}),
};
+
+ if (try sema.typeHasOnePossibleValue(block, src, elem_ty)) |opv| {
+ return sema.addConstant(elem_ty, opv);
+ }
+
if (try sema.resolveDefinedValue(block, ptr_src, ptr)) |ptr_val| {
if (try sema.pointerDeref(block, ptr_src, ptr_val, ptr_ty)) |elem_val| {
return sema.addConstant(elem_ty, elem_val);
}
+ if (block.is_typeof) {
+ return sema.addConstUndef(elem_ty);
+ }
}
+ const valid_rt = try sema.validateRunTimeType(block, src, elem_ty, false);
+ if (!valid_rt) return sema.failWithNeededComptime(block, src);
+
try sema.requireRuntimeBlock(block, src);
return block.addTyOp(.load, elem_ty, ptr);
}
@@ -15501,19 +22385,17 @@ fn analyzeLoad(
fn analyzeSlicePtr(
sema: *Sema,
block: *Block,
- src: LazySrcLoc,
+ slice_src: LazySrcLoc,
slice: Air.Inst.Ref,
slice_ty: Type,
- slice_src: LazySrcLoc,
) CompileError!Air.Inst.Ref {
const buf = try sema.arena.create(Type.SlicePtrFieldTypeBuffer);
const result_ty = slice_ty.slicePtrFieldType(buf);
-
if (try sema.resolveMaybeUndefVal(block, slice_src, slice)) |val| {
if (val.isUndef()) return sema.addConstUndef(result_ty);
return sema.addConstant(result_ty, val.slicePtr());
}
- try sema.requireRuntimeBlock(block, src);
+ try sema.requireRuntimeBlock(block, slice_src);
return block.addTyOp(.slice_ptr, result_ty, slice);
}
@@ -15527,7 +22409,7 @@ fn analyzeSliceLen(
if (slice_val.isUndef()) {
return sema.addConstUndef(Type.usize);
}
- return sema.addIntUnsigned(Type.usize, slice_val.sliceLen());
+ return sema.addIntUnsigned(Type.usize, slice_val.sliceLen(sema.mod));
}
try sema.requireRuntimeBlock(block, src);
return block.addTyOp(.slice_len, Type.usize, slice_inst);
@@ -15558,7 +22440,7 @@ fn analyzeIsNull(
return block.addUnOp(air_tag, operand);
}
-fn analyzeIsNonErr(
+fn analyzeIsNonErrComptimeOnly(
sema: *Sema,
block: *Block,
src: LazySrcLoc,
@@ -15569,10 +22451,62 @@ fn analyzeIsNonErr(
if (ot != .ErrorSet and ot != .ErrorUnion) return Air.Inst.Ref.bool_true;
if (ot == .ErrorSet) return Air.Inst.Ref.bool_false;
assert(ot == .ErrorUnion);
- const result_ty = Type.bool;
- if (try sema.resolveMaybeUndefVal(block, src, operand)) |err_union| {
+
+ if (Air.refToIndex(operand)) |operand_inst| {
+ const air_tags = sema.air_instructions.items(.tag);
+ if (air_tags[operand_inst] == .wrap_errunion_payload) {
+ return Air.Inst.Ref.bool_true;
+ }
+ }
+
+ const maybe_operand_val = try sema.resolveMaybeUndefVal(block, src, operand);
+
+ // exception if the error union error set is known to be empty,
+ // we allow the comparison but always make it comptime known.
+ const set_ty = operand_ty.errorUnionSet();
+ switch (set_ty.tag()) {
+ .anyerror => {},
+ .error_set_inferred => blk: {
+ // If the error set is empty, we must return a comptime true or false.
+ // However we want to avoid unnecessarily resolving an inferred error set
+ // in case it is already non-empty.
+ const ies = set_ty.castTag(.error_set_inferred).?.data;
+ if (ies.is_anyerror) break :blk;
+ if (ies.errors.count() != 0) break :blk;
+ if (maybe_operand_val == null) {
+ // Try to avoid resolving inferred error set if possible.
+ if (ies.errors.count() != 0) break :blk;
+ if (ies.is_anyerror) break :blk;
+ var it = ies.inferred_error_sets.keyIterator();
+ while (it.next()) |other_error_set_ptr| {
+ const other_ies: *Module.Fn.InferredErrorSet = other_error_set_ptr.*;
+ if (ies == other_ies) continue;
+ try sema.resolveInferredErrorSet(block, src, other_ies);
+ if (other_ies.is_anyerror) {
+ ies.is_anyerror = true;
+ ies.is_resolved = true;
+ break :blk;
+ }
+
+ if (other_ies.errors.count() != 0) break :blk;
+ }
+ if (ies.func == sema.owner_func) {
+ // We're checking the inferred errorset of the current function and none of
+ // its child inferred error sets contained any errors meaning that any value
+ // so far with this type can't contain errors either.
+ return Air.Inst.Ref.bool_true;
+ }
+ try sema.resolveInferredErrorSet(block, src, ies);
+ if (ies.is_anyerror) break :blk;
+ if (ies.errors.count() == 0) return Air.Inst.Ref.bool_true;
+ }
+ },
+ else => if (set_ty.errorSetNames().len == 0) return Air.Inst.Ref.bool_true,
+ }
+
+ if (maybe_operand_val) |err_union| {
if (err_union.isUndef()) {
- return sema.addConstUndef(result_ty);
+ return sema.addConstUndef(Type.bool);
}
if (err_union.getError() == null) {
return Air.Inst.Ref.bool_true;
@@ -15580,8 +22514,22 @@ fn analyzeIsNonErr(
return Air.Inst.Ref.bool_false;
}
}
- try sema.requireRuntimeBlock(block, src);
- return block.addUnOp(.is_non_err, operand);
+ return Air.Inst.Ref.none;
+}
+
+fn analyzeIsNonErr(
+ sema: *Sema,
+ block: *Block,
+ src: LazySrcLoc,
+ operand: Air.Inst.Ref,
+) CompileError!Air.Inst.Ref {
+ const result = try sema.analyzeIsNonErrComptimeOnly(block, src, operand);
+ if (result == .none) {
+ try sema.requireRuntimeBlock(block, src);
+ return block.addUnOp(.is_non_err, operand);
+ } else {
+ return result;
+ }
}
fn analyzeSlice(
@@ -15600,21 +22548,27 @@ fn analyzeSlice(
// Slice expressions can operate on a variable whose type is an array. This requires
// the slice operand to be a pointer. In the case of a non-array, it will be a double pointer.
const ptr_ptr_ty = sema.typeOf(ptr_ptr);
+ const target = sema.mod.getTarget();
const ptr_ptr_child_ty = switch (ptr_ptr_ty.zigTypeTag()) {
.Pointer => ptr_ptr_ty.elemType(),
- else => return sema.fail(block, ptr_src, "expected pointer, found '{}'", .{ptr_ptr_ty}),
+ else => return sema.fail(block, ptr_src, "expected pointer, found '{}'", .{ptr_ptr_ty.fmt(sema.mod)}),
};
+ const mod = sema.mod;
var array_ty = ptr_ptr_child_ty;
var slice_ty = ptr_ptr_ty;
var ptr_or_slice = ptr_ptr;
var elem_ty = ptr_ptr_child_ty.childType();
+ var ptr_sentinel: ?Value = null;
switch (ptr_ptr_child_ty.zigTypeTag()) {
- .Array => {},
+ .Array => {
+ ptr_sentinel = ptr_ptr_child_ty.sentinel();
+ },
.Pointer => switch (ptr_ptr_child_ty.ptrSize()) {
.One => {
const double_child_ty = ptr_ptr_child_ty.childType();
if (double_child_ty.zigTypeTag() == .Array) {
+ ptr_sentinel = double_child_ty.sentinel();
ptr_or_slice = try sema.analyzeLoad(block, src, ptr_ptr, ptr_src);
slice_ty = ptr_ptr_child_ty;
array_ty = double_child_ty;
@@ -15624,67 +22578,178 @@ fn analyzeSlice(
}
},
.Many, .C => {
+ ptr_sentinel = ptr_ptr_child_ty.sentinel();
ptr_or_slice = try sema.analyzeLoad(block, src, ptr_ptr, ptr_src);
slice_ty = ptr_ptr_child_ty;
array_ty = ptr_ptr_child_ty;
elem_ty = ptr_ptr_child_ty.childType();
+
+ if (ptr_ptr_child_ty.ptrSize() == .C) {
+ if (try sema.resolveDefinedValue(block, ptr_src, ptr_or_slice)) |ptr_val| {
+ if (ptr_val.isNull()) {
+ return sema.fail(block, ptr_src, "slice of null pointer", .{});
+ }
+ }
+ }
},
.Slice => {
+ ptr_sentinel = ptr_ptr_child_ty.sentinel();
ptr_or_slice = try sema.analyzeLoad(block, src, ptr_ptr, ptr_src);
slice_ty = ptr_ptr_child_ty;
array_ty = ptr_ptr_child_ty;
elem_ty = ptr_ptr_child_ty.childType();
},
},
- else => return sema.fail(block, ptr_src, "slice of non-array type '{}'", .{ptr_ptr_child_ty}),
+ else => return sema.fail(block, ptr_src, "slice of non-array type '{}'", .{ptr_ptr_child_ty.fmt(mod)}),
}
const ptr = if (slice_ty.isSlice())
- try sema.analyzeSlicePtr(block, src, ptr_or_slice, slice_ty, ptr_src)
+ try sema.analyzeSlicePtr(block, ptr_src, ptr_or_slice, slice_ty)
else
ptr_or_slice;
const start = try sema.coerce(block, Type.usize, uncasted_start, start_src);
const new_ptr = try analyzePtrArithmetic(sema, block, src, ptr, start, .ptr_add, ptr_src, start_src);
+ // true if and only if the end index of the slice, implicitly or explicitly, equals
+ // the length of the underlying object being sliced. we might learn the length of the
+ // underlying object because it is an array (which has the length in the type), or
+ // we might learn of the length because it is a comptime-known slice value.
+ var end_is_len = uncasted_end_opt == .none;
const end = e: {
- if (uncasted_end_opt != .none) {
- break :e try sema.coerce(block, Type.usize, uncasted_end_opt, end_src);
- }
-
if (array_ty.zigTypeTag() == .Array) {
- break :e try sema.addConstant(
- Type.usize,
- try Value.Tag.int_u64.create(sema.arena, array_ty.arrayLen()),
- );
+ const len_val = try Value.Tag.int_u64.create(sema.arena, array_ty.arrayLen());
+
+ if (!end_is_len) {
+ const end = try sema.coerce(block, Type.usize, uncasted_end_opt, end_src);
+ if (try sema.resolveMaybeUndefVal(block, end_src, end)) |end_val| {
+ const len_s_val = try Value.Tag.int_u64.create(
+ sema.arena,
+ array_ty.arrayLenIncludingSentinel(),
+ );
+ if (try sema.compare(block, src, end_val, .gt, len_s_val, Type.usize)) {
+ const sentinel_label: []const u8 = if (array_ty.sentinel() != null)
+ " +1 (sentinel)"
+ else
+ "";
+
+ return sema.fail(
+ block,
+ end_src,
+ "end index {} out of bounds for array of length {}{s}",
+ .{
+ end_val.fmtValue(Type.usize, mod),
+ len_val.fmtValue(Type.usize, mod),
+ sentinel_label,
+ },
+ );
+ }
+
+ // end_is_len is only true if we are NOT using the sentinel
+ // length. For sentinel-length, we don't want the type to
+ // contain the sentinel.
+ if (end_val.eql(len_val, Type.usize, mod)) {
+ end_is_len = true;
+ }
+ }
+ break :e end;
+ }
+
+ break :e try sema.addConstant(Type.usize, len_val);
} else if (slice_ty.isSlice()) {
+ if (!end_is_len) {
+ const end = try sema.coerce(block, Type.usize, uncasted_end_opt, end_src);
+ if (try sema.resolveDefinedValue(block, end_src, end)) |end_val| {
+ if (try sema.resolveDefinedValue(block, src, ptr_or_slice)) |slice_val| {
+ const has_sentinel = slice_ty.sentinel() != null;
+ var int_payload: Value.Payload.U64 = .{
+ .base = .{ .tag = .int_u64 },
+ .data = slice_val.sliceLen(mod) + @boolToInt(has_sentinel),
+ };
+ const slice_len_val = Value.initPayload(&int_payload.base);
+ if (try sema.compare(block, src, end_val, .gt, slice_len_val, Type.usize)) {
+ const sentinel_label: []const u8 = if (has_sentinel)
+ " +1 (sentinel)"
+ else
+ "";
+
+ return sema.fail(
+ block,
+ end_src,
+ "end index {} out of bounds for slice of length {d}{s}",
+ .{
+ end_val.fmtValue(Type.usize, mod),
+ slice_val.sliceLen(mod),
+ sentinel_label,
+ },
+ );
+ }
+
+ // If the slice has a sentinel, we subtract one so that
+ // end_is_len is only true if it equals the length WITHOUT
+ // the sentinel, so we don't add a sentinel type.
+ if (has_sentinel) {
+ int_payload.data -= 1;
+ }
+
+ if (end_val.eql(slice_len_val, Type.usize, mod)) {
+ end_is_len = true;
+ }
+ }
+ }
+ break :e end;
+ }
break :e try sema.analyzeSliceLen(block, src, ptr_or_slice);
}
+ if (!end_is_len) {
+ break :e try sema.coerce(block, Type.usize, uncasted_end_opt, end_src);
+ }
return sema.fail(block, end_src, "slice of pointer must include end value", .{});
};
- const slice_sentinel = if (sentinel_opt != .none) blk: {
- const casted = try sema.coerce(block, elem_ty, sentinel_opt, sentinel_src);
- break :blk try sema.resolveConstValue(block, sentinel_src, casted);
- } else null;
+ const sentinel = s: {
+ if (sentinel_opt != .none) {
+ const casted = try sema.coerce(block, elem_ty, sentinel_opt, sentinel_src);
+ break :s try sema.resolveConstValue(block, sentinel_src, casted);
+ }
+ // If we are slicing to the end of something that is sentinel-terminated
+ // then the resulting slice type is also sentinel-terminated.
+ if (end_is_len) {
+ if (ptr_sentinel) |sent| {
+ break :s sent;
+ }
+ }
+ break :s null;
+ };
- const new_len = try sema.analyzeArithmetic(block, .sub, end, start, src, end_src, start_src);
+ // requirement: start <= end
+ if (try sema.resolveDefinedValue(block, src, end)) |end_val| {
+ if (try sema.resolveDefinedValue(block, src, start)) |start_val| {
+ if (try sema.compare(block, src, start_val, .gt, end_val, Type.usize)) {
+ return sema.fail(
+ block,
+ start_src,
+ "start index {} is larger than end index {}",
+ .{
+ start_val.fmtValue(Type.usize, mod),
+ end_val.fmtValue(Type.usize, mod),
+ },
+ );
+ }
+ }
+ }
+ const new_len = try sema.analyzeArithmetic(block, .sub, end, start, src, end_src, start_src);
const opt_new_len_val = try sema.resolveDefinedValue(block, src, new_len);
const new_ptr_ty_info = sema.typeOf(new_ptr).ptrInfo().data;
const new_allowzero = new_ptr_ty_info.@"allowzero" and sema.typeOf(ptr).ptrSize() != .C;
if (opt_new_len_val) |new_len_val| {
- const new_len_int = new_len_val.toUnsignedInt();
+ const new_len_int = new_len_val.toUnsignedInt(target);
- const sentinel = if (array_ty.zigTypeTag() == .Array and new_len_int == array_ty.arrayLen())
- array_ty.sentinel()
- else
- slice_sentinel;
-
- const return_ty = try Type.ptr(sema.arena, .{
- .pointee_type = try Type.array(sema.arena, new_len_int, sentinel, elem_ty),
+ const return_ty = try Type.ptr(sema.arena, mod, .{
+ .pointee_type = try Type.array(sema.arena, new_len_int, sentinel, elem_ty, mod),
.sentinel = null,
.@"align" = new_ptr_ty_info.@"align",
.@"addrspace" = new_ptr_ty_info.@"addrspace",
@@ -15711,9 +22776,9 @@ fn analyzeSlice(
return sema.fail(block, ptr_src, "non-zero length slice of undefined pointer", .{});
}
- const return_ty = try Type.ptr(sema.arena, .{
+ const return_ty = try Type.ptr(sema.arena, mod, .{
.pointee_type = elem_ty,
- .sentinel = slice_sentinel,
+ .sentinel = sentinel,
.@"align" = new_ptr_ty_info.@"align",
.@"addrspace" = new_ptr_ty_info.@"addrspace",
.mutable = new_ptr_ty_info.mutable,
@@ -15723,6 +22788,36 @@ fn analyzeSlice(
});
try sema.requireRuntimeBlock(block, src);
+ if (block.wantSafety()) {
+ // requirement: slicing C ptr is non-null
+ if (ptr_ptr_child_ty.isCPtr()) {
+ const is_non_null = try sema.analyzeIsNull(block, ptr_src, ptr, true);
+ try sema.addSafetyCheck(block, is_non_null, .unwrap_null);
+ }
+
+ // requirement: end <= len
+ const opt_len_inst = if (array_ty.zigTypeTag() == .Array)
+ try sema.addIntUnsigned(Type.usize, array_ty.arrayLenIncludingSentinel())
+ else if (slice_ty.isSlice()) blk: {
+ if (try sema.resolveDefinedValue(block, src, ptr_or_slice)) |slice_val| {
+ // we don't need to add one for sentinels because the
+ // underlying value data includes the sentinel
+ break :blk try sema.addIntUnsigned(Type.usize, slice_val.sliceLen(mod));
+ }
+
+ const slice_len_inst = try block.addTyOp(.slice_len, Type.usize, ptr_or_slice);
+ if (slice_ty.sentinel() == null) break :blk slice_len_inst;
+
+ // we have to add one because slice lengths don't include the sentinel
+ break :blk try sema.analyzeArithmetic(block, .add, slice_len_inst, .one, src, end_src, end_src);
+ } else null;
+ if (opt_len_inst) |len_inst| {
+ try sema.panicIndexOutOfBounds(block, src, end, len_inst, .cmp_lte);
+ }
+
+ // requirement: start <= end
+ try sema.panicIndexOutOfBounds(block, src, start, end, .cmp_lte);
+ }
return block.addInst(.{
.tag = .slice,
.data = .{ .ty_pl = .{
@@ -15740,33 +22835,34 @@ fn cmpNumeric(
sema: *Sema,
block: *Block,
src: LazySrcLoc,
- lhs: Air.Inst.Ref,
- rhs: Air.Inst.Ref,
+ uncasted_lhs: Air.Inst.Ref,
+ uncasted_rhs: Air.Inst.Ref,
op: std.math.CompareOperator,
lhs_src: LazySrcLoc,
rhs_src: LazySrcLoc,
) CompileError!Air.Inst.Ref {
- const lhs_ty = sema.typeOf(lhs);
- const rhs_ty = sema.typeOf(rhs);
+ const lhs_ty = sema.typeOf(uncasted_lhs);
+ const rhs_ty = sema.typeOf(uncasted_rhs);
assert(lhs_ty.isNumeric());
assert(rhs_ty.isNumeric());
const lhs_ty_tag = lhs_ty.zigTypeTag();
const rhs_ty_tag = rhs_ty.zigTypeTag();
+ const target = sema.mod.getTarget();
- if (lhs_ty_tag == .Vector and rhs_ty_tag == .Vector) {
- if (lhs_ty.arrayLen() != rhs_ty.arrayLen()) {
- return sema.fail(block, src, "vector length mismatch: {d} and {d}", .{
- lhs_ty.arrayLen(), rhs_ty.arrayLen(),
- });
- }
- return sema.fail(block, src, "TODO implement support for vectors in cmpNumeric", .{});
- } else if (lhs_ty_tag == .Vector or rhs_ty_tag == .Vector) {
- return sema.fail(block, src, "mixed scalar and vector operands to comparison operator: '{}' and '{}'", .{
- lhs_ty, rhs_ty,
- });
- }
+ // One exception to heterogeneous comparison: comptime_float needs to
+ // coerce to fixed-width float.
+
+ const lhs = if (lhs_ty_tag == .ComptimeFloat and rhs_ty_tag == .Float)
+ try sema.coerce(block, rhs_ty, uncasted_lhs, lhs_src)
+ else
+ uncasted_lhs;
+
+ const rhs = if (lhs_ty_tag == .Float and rhs_ty_tag == .ComptimeFloat)
+ try sema.coerce(block, lhs_ty, uncasted_rhs, rhs_src)
+ else
+ uncasted_rhs;
const runtime_src: LazySrcLoc = src: {
if (try sema.resolveMaybeUndefVal(block, lhs_src, lhs)) |lhs_val| {
@@ -15774,7 +22870,14 @@ fn cmpNumeric(
if (lhs_val.isUndef() or rhs_val.isUndef()) {
return sema.addConstUndef(Type.bool);
}
- if (Value.compareHetero(lhs_val, op, rhs_val)) {
+ if (lhs_val.isNan() or rhs_val.isNan()) {
+ if (op == std.math.CompareOperator.neq) {
+ return Air.Inst.Ref.bool_true;
+ } else {
+ return Air.Inst.Ref.bool_false;
+ }
+ }
+ if (try Value.compareHeteroAdvanced(lhs_val, op, rhs_val, target, sema.kit(block, src))) {
return Air.Inst.Ref.bool_true;
} else {
return Air.Inst.Ref.bool_false;
@@ -15803,9 +22906,10 @@ fn cmpNumeric(
.Float, .ComptimeFloat => true,
else => false,
};
- const target = sema.mod.getTarget();
+
if (lhs_is_float and rhs_is_float) {
- // Implicit cast the smaller one to the larger one.
+ // Smaller fixed-width floats coerce to larger fixed-width floats.
+ // comptime_float coerces to fixed-width float.
const dest_ty = x: {
if (lhs_ty_tag == .ComptimeFloat) {
break :x rhs_ty;
@@ -15829,11 +22933,11 @@ fn cmpNumeric(
// a signed integer with mantissa bits + 1, and if there was any non-integral part of the float,
// add/subtract 1.
const lhs_is_signed = if (try sema.resolveDefinedValue(block, lhs_src, lhs)) |lhs_val|
- lhs_val.compareWithZero(.lt)
+ (try lhs_val.compareWithZeroAdvanced(.lt, sema.kit(block, src)))
else
(lhs_ty.isRuntimeFloat() or lhs_ty.isSignedInt());
const rhs_is_signed = if (try sema.resolveDefinedValue(block, rhs_src, rhs)) |rhs_val|
- rhs_val.compareWithZero(.lt)
+ (try rhs_val.compareWithZeroAdvanced(.lt, sema.kit(block, src)))
else
(rhs_ty.isRuntimeFloat() or rhs_ty.isSignedInt());
const dest_int_is_signed = lhs_is_signed or rhs_is_signed;
@@ -15844,30 +22948,41 @@ fn cmpNumeric(
if (try sema.resolveMaybeUndefVal(block, lhs_src, lhs)) |lhs_val| {
if (lhs_val.isUndef())
return sema.addConstUndef(Type.bool);
- const is_unsigned = if (lhs_is_float) x: {
+ if (!rhs_is_signed) {
+ switch (lhs_val.orderAgainstZero()) {
+ .gt => {},
+ .eq => switch (op) { // LHS = 0, RHS is unsigned
+ .lte => return Air.Inst.Ref.bool_true,
+ .gt => return Air.Inst.Ref.bool_false,
+ else => {},
+ },
+ .lt => switch (op) { // LHS < 0, RHS is unsigned
+ .neq, .lt, .lte => return Air.Inst.Ref.bool_true,
+ .eq, .gt, .gte => return Air.Inst.Ref.bool_false,
+ },
+ }
+ }
+ if (lhs_is_float) {
var bigint_space: Value.BigIntSpace = undefined;
- var bigint = try lhs_val.toBigInt(&bigint_space).toManaged(sema.gpa);
+ var bigint = try lhs_val.toBigInt(&bigint_space, target).toManaged(sema.gpa);
defer bigint.deinit();
- const zcmp = lhs_val.orderAgainstZero();
if (lhs_val.floatHasFraction()) {
switch (op) {
.eq => return Air.Inst.Ref.bool_false,
.neq => return Air.Inst.Ref.bool_true,
else => {},
}
- if (zcmp == .lt) {
- try bigint.addScalar(bigint.toConst(), -1);
+ if (lhs_is_signed) {
+ try bigint.addScalar(&bigint, -1);
} else {
- try bigint.addScalar(bigint.toConst(), 1);
+ try bigint.addScalar(&bigint, 1);
}
}
lhs_bits = bigint.toConst().bitCountTwosComp();
- break :x (zcmp != .lt);
- } else x: {
+ } else {
lhs_bits = lhs_val.intBitCountTwosComp(target);
- break :x (lhs_val.orderAgainstZero() != .lt);
- };
- lhs_bits += @boolToInt(is_unsigned and dest_int_is_signed);
+ }
+ lhs_bits += @boolToInt(!lhs_is_signed and dest_int_is_signed);
} else if (lhs_is_float) {
dest_float_type = lhs_ty;
} else {
@@ -15879,30 +22994,41 @@ fn cmpNumeric(
if (try sema.resolveMaybeUndefVal(block, rhs_src, rhs)) |rhs_val| {
if (rhs_val.isUndef())
return sema.addConstUndef(Type.bool);
- const is_unsigned = if (rhs_is_float) x: {
+ if (!lhs_is_signed) {
+ switch (rhs_val.orderAgainstZero()) {
+ .gt => {},
+ .eq => switch (op) { // RHS = 0, LHS is unsigned
+ .gte => return Air.Inst.Ref.bool_true,
+ .lt => return Air.Inst.Ref.bool_false,
+ else => {},
+ },
+ .lt => switch (op) { // RHS < 0, LHS is unsigned
+ .neq, .gt, .gte => return Air.Inst.Ref.bool_true,
+ .eq, .lt, .lte => return Air.Inst.Ref.bool_false,
+ },
+ }
+ }
+ if (rhs_is_float) {
var bigint_space: Value.BigIntSpace = undefined;
- var bigint = try rhs_val.toBigInt(&bigint_space).toManaged(sema.gpa);
+ var bigint = try rhs_val.toBigInt(&bigint_space, target).toManaged(sema.gpa);
defer bigint.deinit();
- const zcmp = rhs_val.orderAgainstZero();
if (rhs_val.floatHasFraction()) {
switch (op) {
.eq => return Air.Inst.Ref.bool_false,
.neq => return Air.Inst.Ref.bool_true,
else => {},
}
- if (zcmp == .lt) {
- try bigint.addScalar(bigint.toConst(), -1);
+ if (rhs_is_signed) {
+ try bigint.addScalar(&bigint, -1);
} else {
- try bigint.addScalar(bigint.toConst(), 1);
+ try bigint.addScalar(&bigint, 1);
}
}
rhs_bits = bigint.toConst().bitCountTwosComp();
- break :x (zcmp != .lt);
- } else x: {
+ } else {
rhs_bits = rhs_val.intBitCountTwosComp(target);
- break :x (rhs_val.orderAgainstZero() != .lt);
- };
- rhs_bits += @boolToInt(is_unsigned and dest_int_is_signed);
+ }
+ rhs_bits += @boolToInt(!rhs_is_signed and dest_int_is_signed);
} else if (rhs_is_float) {
dest_float_type = rhs_ty;
} else {
@@ -15912,9 +23038,7 @@ fn cmpNumeric(
const dest_ty = if (dest_float_type) |ft| ft else blk: {
const max_bits = std.math.max(lhs_bits, rhs_bits);
- const casted_bits = std.math.cast(u16, max_bits) catch |err| switch (err) {
- error.Overflow => return sema.fail(block, src, "{d} exceeds maximum integer bit count", .{max_bits}),
- };
+ const casted_bits = std.math.cast(u16, max_bits) orelse return sema.fail(block, src, "{d} exceeds maximum integer bit count", .{max_bits});
const signedness: std.builtin.Signedness = if (dest_int_is_signed) .signed else .unsigned;
break :blk try Module.makeIntType(sema.arena, signedness, casted_bits);
};
@@ -15924,6 +23048,46 @@ fn cmpNumeric(
return block.addBinOp(Air.Inst.Tag.fromCmpOp(op), casted_lhs, casted_rhs);
}
+/// Asserts that lhs and rhs types are both vectors.
+fn cmpVector(
+ sema: *Sema,
+ block: *Block,
+ src: LazySrcLoc,
+ lhs: Air.Inst.Ref,
+ rhs: Air.Inst.Ref,
+ op: std.math.CompareOperator,
+ lhs_src: LazySrcLoc,
+ rhs_src: LazySrcLoc,
+) CompileError!Air.Inst.Ref {
+ const lhs_ty = sema.typeOf(lhs);
+ const rhs_ty = sema.typeOf(rhs);
+ assert(lhs_ty.zigTypeTag() == .Vector);
+ assert(rhs_ty.zigTypeTag() == .Vector);
+ try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src);
+
+ const result_ty = try Type.vector(sema.arena, lhs_ty.vectorLen(), Type.@"bool");
+
+ const runtime_src: LazySrcLoc = src: {
+ if (try sema.resolveMaybeUndefVal(block, lhs_src, lhs)) |lhs_val| {
+ if (try sema.resolveMaybeUndefVal(block, rhs_src, rhs)) |rhs_val| {
+ if (lhs_val.isUndef() or rhs_val.isUndef()) {
+ return sema.addConstUndef(result_ty);
+ }
+ const cmp_val = try sema.compareVector(block, src, lhs_val, op, rhs_val, lhs_ty);
+ return sema.addConstant(result_ty, cmp_val);
+ } else {
+ break :src rhs_src;
+ }
+ } else {
+ break :src lhs_src;
+ }
+ };
+
+ try sema.requireRuntimeBlock(block, runtime_src);
+ const result_ty_inst = try sema.addType(result_ty);
+ return block.addCmpVector(lhs, rhs, op, result_ty_inst);
+}
+
fn wrapOptional(
sema: *Sema,
block: *Block,
@@ -15939,7 +23103,24 @@ fn wrapOptional(
return block.addTyOp(.wrap_optional, dest_ty, inst);
}
-fn wrapErrorUnion(
+fn wrapErrorUnionPayload(
+ sema: *Sema,
+ block: *Block,
+ dest_ty: Type,
+ inst: Air.Inst.Ref,
+ inst_src: LazySrcLoc,
+) !Air.Inst.Ref {
+ const dest_payload_ty = dest_ty.errorUnionPayload();
+ const coerced = try sema.coerce(block, dest_payload_ty, inst, inst_src);
+ if (try sema.resolveMaybeUndefVal(block, inst_src, coerced)) |val| {
+ return sema.addConstant(dest_ty, try Value.Tag.eu_payload.create(sema.arena, val));
+ }
+ try sema.requireRuntimeBlock(block, inst_src);
+ try sema.queueFullTypeResolution(dest_payload_ty);
+ return block.addTyOp(.wrap_errunion_payload, dest_ty, coerced);
+}
+
+fn wrapErrorUnionSet(
sema: *Sema,
block: *Block,
dest_ty: Type,
@@ -15948,12 +23129,7 @@ fn wrapErrorUnion(
) !Air.Inst.Ref {
const inst_ty = sema.typeOf(inst);
const dest_err_set_ty = dest_ty.errorUnionSet();
- const dest_payload_ty = dest_ty.errorUnionPayload();
if (try sema.resolveMaybeUndefVal(block, inst_src, inst)) |val| {
- if (inst_ty.zigTypeTag() != .ErrorSet) {
- _ = try sema.coerce(block, dest_payload_ty, inst, inst_src);
- return sema.addConstant(dest_ty, try Value.Tag.eu_payload.create(sema.arena, val));
- }
switch (dest_err_set_ty.tag()) {
.anyerror => {},
.error_set_single => ok: {
@@ -15971,10 +23147,16 @@ fn wrapErrorUnion(
},
.error_set_inferred => ok: {
const expected_name = val.castTag(.@"error").?.data.name;
- const data = dest_err_set_ty.castTag(.error_set_inferred).?.data;
- try sema.resolveInferredErrorSet(data);
- if (data.is_anyerror) break :ok;
- if (data.errors.contains(expected_name)) break :ok;
+ const ies = dest_err_set_ty.castTag(.error_set_inferred).?.data;
+
+ // We carefully do this in an order that avoids unnecessarily
+ // resolving the destination error set type.
+ if (ies.is_anyerror) break :ok;
+ if (ies.errors.contains(expected_name)) break :ok;
+ if (.ok == try sema.coerceInMemoryAllowedErrorSets(block, dest_err_set_ty, inst_ty, inst_src, inst_src)) {
+ break :ok;
+ }
+
return sema.failWithErrorSetCodeMissing(block, inst_src, dest_err_set_ty, inst_ty);
},
.error_set_merged => {
@@ -15990,15 +23172,8 @@ fn wrapErrorUnion(
}
try sema.requireRuntimeBlock(block, inst_src);
-
- // we are coercing from E to E!T
- if (inst_ty.zigTypeTag() == .ErrorSet) {
- var coerced = try sema.coerce(block, dest_err_set_ty, inst, inst_src);
- return block.addTyOp(.wrap_errunion_err, dest_ty, coerced);
- } else {
- var coerced = try sema.coerce(block, dest_payload_ty, inst, inst_src);
- return block.addTyOp(.wrap_errunion_payload, dest_ty, coerced);
- }
+ const coerced = try sema.coerce(block, dest_err_set_ty, inst, inst_src);
+ return block.addTyOp(.wrap_errunion_err, dest_ty, coerced);
}
fn unionToTag(
@@ -16022,7 +23197,7 @@ fn resolvePeerTypes(
sema: *Sema,
block: *Block,
src: LazySrcLoc,
- instructions: []Air.Inst.Ref,
+ instructions: []const Air.Inst.Ref,
candidate_srcs: Module.PeerTypeCandidateSrc,
) !Type {
switch (instructions.len) {
@@ -16034,15 +23209,24 @@ fn resolvePeerTypes(
const target = sema.mod.getTarget();
var chosen = instructions[0];
+ // If this is non-null then it does the following thing, depending on the chosen zigTypeTag().
+ // * ErrorSet: this is an override
+ // * ErrorUnion: this is an override of the error set only
+ // * other: at the end we make an ErrorUnion with the other thing and this
+ var err_set_ty: ?Type = null;
var any_are_null = false;
+ var seen_const = false;
+ var convert_to_slice = false;
var chosen_i: usize = 0;
for (instructions[1..]) |candidate, candidate_i| {
const candidate_ty = sema.typeOf(candidate);
const chosen_ty = sema.typeOf(chosen);
- if (candidate_ty.eql(chosen_ty))
+
+ const candidate_ty_tag = try candidate_ty.zigTypeTagOrPoison();
+ const chosen_ty_tag = try chosen_ty.zigTypeTagOrPoison();
+
+ if (candidate_ty.eql(chosen_ty, sema.mod))
continue;
- const candidate_ty_tag = candidate_ty.zigTypeTag();
- const chosen_ty_tag = chosen_ty.zigTypeTag();
switch (candidate_ty_tag) {
.NoReturn, .Undefined => continue,
@@ -16121,30 +23305,289 @@ fn resolvePeerTypes(
},
else => {},
},
- .Pointer => {
- if (candidate_ty.ptrSize() == .C) {
- if (chosen_ty_tag == .Int or chosen_ty_tag == .ComptimeInt) {
+ .ErrorSet => switch (chosen_ty_tag) {
+ .ErrorSet => {
+ // If chosen is superset of candidate, keep it.
+ // If candidate is superset of chosen, switch it.
+ // If neither is a superset, merge errors.
+ const chosen_set_ty = err_set_ty orelse chosen_ty;
+
+ if (.ok == try sema.coerceInMemoryAllowedErrorSets(block, chosen_set_ty, candidate_ty, src, src)) {
+ continue;
+ }
+ if (.ok == try sema.coerceInMemoryAllowedErrorSets(block, candidate_ty, chosen_set_ty, src, src)) {
+ err_set_ty = null;
chosen = candidate;
chosen_i = candidate_i + 1;
continue;
}
- if (chosen_ty_tag == .Pointer and chosen_ty.ptrSize() != .Slice) {
+
+ err_set_ty = try chosen_set_ty.errorSetMerge(sema.arena, candidate_ty);
+ continue;
+ },
+ .ErrorUnion => {
+ const chosen_set_ty = err_set_ty orelse chosen_ty.errorUnionSet();
+
+ if (.ok == try sema.coerceInMemoryAllowedErrorSets(block, chosen_set_ty, candidate_ty, src, src)) {
continue;
}
- }
+ if (.ok == try sema.coerceInMemoryAllowedErrorSets(block, candidate_ty, chosen_set_ty, src, src)) {
+ err_set_ty = candidate_ty;
+ continue;
+ }
+
+ err_set_ty = try chosen_set_ty.errorSetMerge(sema.arena, candidate_ty);
+ continue;
+ },
+ else => {
+ if (err_set_ty) |chosen_set_ty| {
+ if (.ok == try sema.coerceInMemoryAllowedErrorSets(block, chosen_set_ty, candidate_ty, src, src)) {
+ continue;
+ }
+ if (.ok == try sema.coerceInMemoryAllowedErrorSets(block, candidate_ty, chosen_set_ty, src, src)) {
+ err_set_ty = candidate_ty;
+ continue;
+ }
+
+ err_set_ty = try chosen_set_ty.errorSetMerge(sema.arena, candidate_ty);
+ continue;
+ } else {
+ err_set_ty = candidate_ty;
+ continue;
+ }
+ },
},
- .Optional => {
- var opt_child_buf: Type.Payload.ElemType = undefined;
- const opt_child_ty = candidate_ty.optionalChild(&opt_child_buf);
- if ((try sema.coerceInMemoryAllowed(block, opt_child_ty, chosen_ty, false, target, src, src)) == .ok) {
+ .ErrorUnion => switch (chosen_ty_tag) {
+ .ErrorSet => {
+ const chosen_set_ty = err_set_ty orelse chosen_ty;
+ const candidate_set_ty = candidate_ty.errorUnionSet();
+
+ if (.ok == try sema.coerceInMemoryAllowedErrorSets(block, chosen_set_ty, candidate_set_ty, src, src)) {
+ err_set_ty = chosen_set_ty;
+ } else if (.ok == try sema.coerceInMemoryAllowedErrorSets(block, candidate_set_ty, chosen_set_ty, src, src)) {
+ err_set_ty = null;
+ } else {
+ err_set_ty = try chosen_set_ty.errorSetMerge(sema.arena, candidate_set_ty);
+ }
chosen = candidate;
chosen_i = candidate_i + 1;
continue;
+ },
+
+ .ErrorUnion => {
+ const chosen_payload_ty = chosen_ty.errorUnionPayload();
+ const candidate_payload_ty = candidate_ty.errorUnionPayload();
+
+ const coerce_chosen = (try sema.coerceInMemoryAllowed(block, chosen_payload_ty, candidate_payload_ty, false, target, src, src)) == .ok;
+ const coerce_candidate = (try sema.coerceInMemoryAllowed(block, candidate_payload_ty, chosen_payload_ty, false, target, src, src)) == .ok;
+
+ if (coerce_chosen or coerce_candidate) {
+ // If we can coerce to the candidate, we switch to that
+ // type. This is the same logic as the bare (non-union)
+ // coercion check we do at the top of this func.
+ if (coerce_candidate) {
+ chosen = candidate;
+ chosen_i = candidate_i + 1;
+ }
+
+ const chosen_set_ty = err_set_ty orelse chosen_ty.errorUnionSet();
+ const candidate_set_ty = candidate_ty.errorUnionSet();
+
+ if (.ok == try sema.coerceInMemoryAllowedErrorSets(block, chosen_set_ty, candidate_set_ty, src, src)) {
+ err_set_ty = chosen_set_ty;
+ } else if (.ok == try sema.coerceInMemoryAllowedErrorSets(block, candidate_set_ty, chosen_set_ty, src, src)) {
+ err_set_ty = candidate_set_ty;
+ } else {
+ err_set_ty = try chosen_set_ty.errorSetMerge(sema.arena, candidate_set_ty);
+ }
+ continue;
+ }
+ },
+
+ else => {
+ if (err_set_ty) |chosen_set_ty| {
+ const candidate_set_ty = candidate_ty.errorUnionSet();
+ if (.ok == try sema.coerceInMemoryAllowedErrorSets(block, chosen_set_ty, candidate_set_ty, src, src)) {
+ err_set_ty = chosen_set_ty;
+ } else if (.ok == try sema.coerceInMemoryAllowedErrorSets(block, candidate_set_ty, chosen_set_ty, src, src)) {
+ err_set_ty = null;
+ } else {
+ err_set_ty = try chosen_set_ty.errorSetMerge(sema.arena, candidate_set_ty);
+ }
+ }
+ seen_const = seen_const or chosen_ty.isConstPtr();
+ chosen = candidate;
+ chosen_i = candidate_i + 1;
+ continue;
+ },
+ },
+ .Pointer => {
+ const cand_info = candidate_ty.ptrInfo().data;
+ switch (chosen_ty_tag) {
+ .Pointer => {
+ const chosen_info = chosen_ty.ptrInfo().data;
+
+ seen_const = seen_const or !chosen_info.mutable or !cand_info.mutable;
+
+ // *[N]T to [*]T
+ // *[N]T to []T
+ if ((cand_info.size == .Many or cand_info.size == .Slice) and
+ chosen_info.size == .One and
+ chosen_info.pointee_type.zigTypeTag() == .Array)
+ {
+ // In case we see i.e.: `*[1]T`, `*[2]T`, `[*]T`
+ convert_to_slice = false;
+ chosen = candidate;
+ chosen_i = candidate_i + 1;
+ continue;
+ }
+ if (cand_info.size == .One and
+ cand_info.pointee_type.zigTypeTag() == .Array and
+ (chosen_info.size == .Many or chosen_info.size == .Slice))
+ {
+ // In case we see i.e.: `*[1]T`, `*[2]T`, `[*]T`
+ convert_to_slice = false;
+ continue;
+ }
+
+ // *[N]T and *[M]T
+ // Verify both are single-pointers to arrays.
+ // Keep the one whose element type can be coerced into.
+ if (chosen_info.size == .One and
+ cand_info.size == .One and
+ chosen_info.pointee_type.zigTypeTag() == .Array and
+ cand_info.pointee_type.zigTypeTag() == .Array)
+ {
+ const chosen_elem_ty = chosen_info.pointee_type.childType();
+ const cand_elem_ty = cand_info.pointee_type.childType();
+
+ const chosen_ok = .ok == try sema.coerceInMemoryAllowed(block, chosen_elem_ty, cand_elem_ty, chosen_info.mutable, target, src, src);
+ if (chosen_ok) {
+ convert_to_slice = true;
+ continue;
+ }
+
+ const cand_ok = .ok == try sema.coerceInMemoryAllowed(block, cand_elem_ty, chosen_elem_ty, cand_info.mutable, target, src, src);
+ if (cand_ok) {
+ convert_to_slice = true;
+ chosen = candidate;
+ chosen_i = candidate_i + 1;
+ continue;
+ }
+
+ // They're both bad. Report error.
+ // In the future we probably want to use the
+ // coerceInMemoryAllowed error reporting mechanism,
+ // however, for now we just fall through for the
+ // "incompatible types" error below.
+ }
+
+ // [*c]T and any other pointer size
+ // Whichever element type can coerce to the other one, is
+ // the one we will keep. If they're both OK then we keep the
+ // C pointer since it matches both single and many pointers.
+ if (cand_info.size == .C or chosen_info.size == .C) {
+ const cand_ok = .ok == try sema.coerceInMemoryAllowed(block, cand_info.pointee_type, chosen_info.pointee_type, cand_info.mutable, target, src, src);
+ const chosen_ok = .ok == try sema.coerceInMemoryAllowed(block, chosen_info.pointee_type, cand_info.pointee_type, chosen_info.mutable, target, src, src);
+
+ if (cand_ok) {
+ if (chosen_ok) {
+ if (chosen_info.size == .C) {
+ continue;
+ } else {
+ chosen = candidate;
+ chosen_i = candidate_i + 1;
+ continue;
+ }
+ } else {
+ chosen = candidate;
+ chosen_i = candidate_i + 1;
+ continue;
+ }
+ } else {
+ if (chosen_ok) {
+ continue;
+ } else {
+ // They're both bad. Report error.
+ // In the future we probably want to use the
+ // coerceInMemoryAllowed error reporting mechanism,
+ // however, for now we just fall through for the
+ // "incompatible types" error below.
+ }
+ }
+ }
+ },
+ .Int, .ComptimeInt => {
+ if (cand_info.size == .C) {
+ chosen = candidate;
+ chosen_i = candidate_i + 1;
+ continue;
+ }
+ },
+ .Optional => {
+ var opt_child_buf: Type.Payload.ElemType = undefined;
+ const chosen_ptr_ty = chosen_ty.optionalChild(&opt_child_buf);
+ if (chosen_ptr_ty.zigTypeTag() == .Pointer) {
+ const chosen_info = chosen_ptr_ty.ptrInfo().data;
+
+ seen_const = seen_const or !chosen_info.mutable or !cand_info.mutable;
+
+ // *[N]T to ?![*]T
+ // *[N]T to ?![]T
+ if (cand_info.size == .One and
+ cand_info.pointee_type.zigTypeTag() == .Array and
+ (chosen_info.size == .Many or chosen_info.size == .Slice))
+ {
+ continue;
+ }
+ }
+ },
+ .ErrorUnion => {
+ const chosen_ptr_ty = chosen_ty.errorUnionPayload();
+ if (chosen_ptr_ty.zigTypeTag() == .Pointer) {
+ const chosen_info = chosen_ptr_ty.ptrInfo().data;
+
+ seen_const = seen_const or !chosen_info.mutable or !cand_info.mutable;
+
+ // *[N]T to E![*]T
+ // *[N]T to E![]T
+ if (cand_info.size == .One and
+ cand_info.pointee_type.zigTypeTag() == .Array and
+ (chosen_info.size == .Many or chosen_info.size == .Slice))
+ {
+ continue;
+ }
+ }
+ },
+ else => {},
}
+ },
+ .Optional => {
+ var opt_child_buf: Type.Payload.ElemType = undefined;
+ const opt_child_ty = candidate_ty.optionalChild(&opt_child_buf);
if ((try sema.coerceInMemoryAllowed(block, chosen_ty, opt_child_ty, false, target, src, src)) == .ok) {
+ seen_const = seen_const or opt_child_ty.isConstPtr();
any_are_null = true;
continue;
}
+
+ seen_const = seen_const or chosen_ty.isConstPtr();
+ any_are_null = false;
+ chosen = candidate;
+ chosen_i = candidate_i + 1;
+ continue;
+ },
+ .Vector => switch (chosen_ty_tag) {
+ .Array => {
+ chosen = candidate;
+ chosen_i = candidate_i + 1;
+ continue;
+ },
+ else => {},
+ },
+ .Array => switch (chosen_ty_tag) {
+ .Vector => continue,
+ else => {},
},
else => {},
}
@@ -16174,65 +23617,187 @@ fn resolvePeerTypes(
continue;
}
},
+ .ErrorUnion => {
+ const payload_ty = chosen_ty.errorUnionPayload();
+ if ((try sema.coerceInMemoryAllowed(block, payload_ty, candidate_ty, false, target, src, src)) == .ok) {
+ continue;
+ }
+ },
else => {},
}
+ // If the candidate can coerce into our chosen type, we're done.
+ // If the chosen type can coerce into the candidate, use that.
+ if ((try sema.coerceInMemoryAllowed(block, chosen_ty, candidate_ty, false, target, src, src)) == .ok) {
+ continue;
+ }
+ if ((try sema.coerceInMemoryAllowed(block, candidate_ty, chosen_ty, false, target, src, src)) == .ok) {
+ chosen = candidate;
+ chosen_i = candidate_i + 1;
+ continue;
+ }
+
// At this point, we hit a compile error. We need to recover
// the source locations.
const chosen_src = candidate_srcs.resolve(
sema.gpa,
- block.src_decl,
+ sema.mod.declPtr(block.src_decl),
chosen_i,
);
const candidate_src = candidate_srcs.resolve(
sema.gpa,
- block.src_decl,
+ sema.mod.declPtr(block.src_decl),
candidate_i + 1,
);
const msg = msg: {
- const msg = try sema.errMsg(block, src, "incompatible types: '{}' and '{}'", .{ chosen_ty, candidate_ty });
+ const msg = try sema.errMsg(block, src, "incompatible types: '{}' and '{}'", .{
+ chosen_ty.fmt(sema.mod),
+ candidate_ty.fmt(sema.mod),
+ });
errdefer msg.destroy(sema.gpa);
if (chosen_src) |src_loc|
- try sema.errNote(block, src_loc, msg, "type '{}' here", .{chosen_ty});
+ try sema.errNote(block, src_loc, msg, "type '{}' here", .{chosen_ty.fmt(sema.mod)});
if (candidate_src) |src_loc|
- try sema.errNote(block, src_loc, msg, "type '{}' here", .{candidate_ty});
+ try sema.errNote(block, src_loc, msg, "type '{}' here", .{candidate_ty.fmt(sema.mod)});
break :msg msg;
};
- return sema.failWithOwnedErrorMsg(msg);
+ return sema.failWithOwnedErrorMsg(block, msg);
}
const chosen_ty = sema.typeOf(chosen);
- if (any_are_null) {
+ if (convert_to_slice) {
+ // turn *[N]T => []T
+ const chosen_child_ty = chosen_ty.childType();
+ var info = chosen_ty.ptrInfo();
+ info.data.sentinel = chosen_child_ty.sentinel();
+ info.data.size = .Slice;
+ info.data.mutable = !(seen_const or chosen_child_ty.isConstPtr());
+ info.data.pointee_type = chosen_child_ty.elemType2();
+
+ const new_ptr_ty = try Type.ptr(sema.arena, sema.mod, info.data);
+ const opt_ptr_ty = if (any_are_null)
+ try Type.optional(sema.arena, new_ptr_ty)
+ else
+ new_ptr_ty;
+ const set_ty = err_set_ty orelse return opt_ptr_ty;
+ return try Type.errorUnion(sema.arena, set_ty, opt_ptr_ty, sema.mod);
+ }
+
+ if (seen_const) {
+ // turn []T => []const T
switch (chosen_ty.zigTypeTag()) {
- .Null, .Optional => return chosen_ty,
- else => return Type.optional(sema.arena, chosen_ty),
+ .ErrorUnion => {
+ const ptr_ty = chosen_ty.errorUnionPayload();
+ var info = ptr_ty.ptrInfo();
+ info.data.mutable = false;
+ const new_ptr_ty = try Type.ptr(sema.arena, sema.mod, info.data);
+ const opt_ptr_ty = if (any_are_null)
+ try Type.optional(sema.arena, new_ptr_ty)
+ else
+ new_ptr_ty;
+ const set_ty = err_set_ty orelse chosen_ty.errorUnionSet();
+ return try Type.errorUnion(sema.arena, set_ty, opt_ptr_ty, sema.mod);
+ },
+ .Pointer => {
+ var info = chosen_ty.ptrInfo();
+ info.data.mutable = false;
+ const new_ptr_ty = try Type.ptr(sema.arena, sema.mod, info.data);
+ const opt_ptr_ty = if (any_are_null)
+ try Type.optional(sema.arena, new_ptr_ty)
+ else
+ new_ptr_ty;
+ const set_ty = err_set_ty orelse return opt_ptr_ty;
+ return try Type.errorUnion(sema.arena, set_ty, opt_ptr_ty, sema.mod);
+ },
+ else => return chosen_ty,
}
}
+ if (any_are_null) {
+ const opt_ty = switch (chosen_ty.zigTypeTag()) {
+ .Null, .Optional => chosen_ty,
+ else => try Type.optional(sema.arena, chosen_ty),
+ };
+ const set_ty = err_set_ty orelse return opt_ty;
+ return try Type.errorUnion(sema.arena, set_ty, opt_ty, sema.mod);
+ }
+
+ if (err_set_ty) |ty| switch (chosen_ty.zigTypeTag()) {
+ .ErrorSet => return ty,
+ .ErrorUnion => {
+ const payload_ty = chosen_ty.errorUnionPayload();
+ return try Type.errorUnion(sema.arena, ty, payload_ty, sema.mod);
+ },
+ else => return try Type.errorUnion(sema.arena, ty, chosen_ty, sema.mod),
+ };
+
return chosen_ty;
}
+pub fn resolveFnTypes(
+ sema: *Sema,
+ block: *Block,
+ src: LazySrcLoc,
+ fn_info: Type.Payload.Function.Data,
+) CompileError!void {
+ try sema.resolveTypeFully(block, src, fn_info.return_type);
+
+ if (sema.mod.comp.bin_file.options.error_return_tracing and fn_info.return_type.isError()) {
+ // Ensure the type exists so that backends can assume that.
+ _ = try sema.getBuiltinType(block, src, "StackTrace");
+ }
+
+ for (fn_info.param_types) |param_ty| {
+ try sema.resolveTypeFully(block, src, param_ty);
+ }
+}
+
+/// Make it so that calling hash() and eql() on `val` will not assert due
+/// to a type not having its layout resolved.
+fn resolveLazyValue(
+ sema: *Sema,
+ block: *Block,
+ src: LazySrcLoc,
+ val: Value,
+) CompileError!void {
+ switch (val.tag()) {
+ .lazy_align => {
+ const ty = val.castTag(.lazy_align).?.data;
+ return sema.resolveTypeLayout(block, src, ty);
+ },
+ else => return,
+ }
+}
+
pub fn resolveTypeLayout(
sema: *Sema,
block: *Block,
src: LazySrcLoc,
ty: Type,
) CompileError!void {
+ if (build_options.omit_stage2)
+ @panic("sadly stage2 is omitted from this build to save memory on the CI server");
+
switch (ty.zigTypeTag()) {
.Struct => return sema.resolveStructLayout(block, src, ty),
.Union => return sema.resolveUnionLayout(block, src, ty),
.Array => {
+ if (ty.arrayLenIncludingSentinel() == 0) return;
const elem_ty = ty.childType();
return sema.resolveTypeLayout(block, src, elem_ty);
},
.Optional => {
var buf: Type.Payload.ElemType = undefined;
const payload_ty = ty.optionalChild(&buf);
+ // In case of querying the ABI alignment of this optional, we will ask
+ // for hasRuntimeBits() of the payload type, so we need "requires comptime"
+ // to be known already before this function returns.
+ _ = try sema.typeRequiresComptime(block, src, payload_ty);
return sema.resolveTypeLayout(block, src, payload_ty);
},
.ErrorUnion => {
@@ -16255,7 +23820,7 @@ fn resolveStructLayout(
switch (struct_obj.status) {
.none, .have_field_types => {},
.field_types_wip, .layout_wip => {
- return sema.fail(block, src, "struct {} depends on itself", .{ty});
+ return sema.fail(block, src, "struct '{}' depends on itself", .{ty.fmt(sema.mod)});
},
.have_layout, .fully_resolved_wip, .fully_resolved => return,
}
@@ -16264,6 +23829,13 @@ fn resolveStructLayout(
try sema.resolveTypeLayout(block, src, field.ty);
}
struct_obj.status = .have_layout;
+
+ // In case of querying the ABI alignment of this struct, we will ask
+ // for hasRuntimeBits() of each field, so we need "requires comptime"
+ // to be known already before this function returns.
+ for (struct_obj.fields.values()) |field| {
+ _ = try sema.typeRequiresComptime(block, src, field.ty);
+ }
}
// otherwise it's a tuple; no need to resolve anything
}
@@ -16279,7 +23851,7 @@ fn resolveUnionLayout(
switch (union_obj.status) {
.none, .have_field_types => {},
.field_types_wip, .layout_wip => {
- return sema.fail(block, src, "union {} depends on itself", .{ty});
+ return sema.fail(block, src, "union '{}' depends on itself", .{ty.fmt(sema.mod)});
},
.have_layout, .fully_resolved_wip, .fully_resolved => return,
}
@@ -16290,7 +23862,9 @@ fn resolveUnionLayout(
union_obj.status = .have_layout;
}
-fn resolveTypeFully(
+/// Returns `error.AnalysisFail` if any of the types (recursively) failed to
+/// be resolved.
+pub fn resolveTypeFully(
sema: *Sema,
block: *Block,
src: LazySrcLoc,
@@ -16301,7 +23875,17 @@ fn resolveTypeFully(
const child_ty = try sema.resolveTypeFields(block, src, ty.childType());
return resolveTypeFully(sema, block, src, child_ty);
},
- .Struct => return resolveStructFully(sema, block, src, ty),
+ .Struct => switch (ty.tag()) {
+ .@"struct" => return resolveStructFully(sema, block, src, ty),
+ .tuple, .anon_struct => {
+ const tuple = ty.tupleFields();
+
+ for (tuple.types) |field_ty| {
+ try sema.resolveTypeFully(block, src, field_ty);
+ }
+ },
+ else => {},
+ },
.Union => return resolveUnionFully(sema, block, src, ty),
.Array => return resolveTypeFully(sema, block, src, ty.childType()),
.Optional => {
@@ -16309,6 +23893,20 @@ fn resolveTypeFully(
return resolveTypeFully(sema, block, src, ty.optionalChild(&buf));
},
.ErrorUnion => return resolveTypeFully(sema, block, src, ty.errorUnionPayload()),
+ .Fn => {
+ const info = ty.fnInfo();
+ if (info.is_generic) {
+ // Resolving of generic function types is defeerred to when
+ // the function is instantiated.
+ return;
+ }
+ for (info.param_types) |param_ty| {
+ const param_ty_src = src; // TODO better source location
+ try sema.resolveTypeFully(block, param_ty_src, param_ty);
+ }
+ const return_ty_src = src; // TODO better source location
+ try sema.resolveTypeFully(block, return_ty_src, info.return_type);
+ },
else => {},
}
}
@@ -16322,19 +23920,30 @@ fn resolveStructFully(
try resolveStructLayout(sema, block, src, ty);
const resolved_ty = try sema.resolveTypeFields(block, src, ty);
- const struct_obj = resolved_ty.castTag(.@"struct").?.data;
+ const payload = resolved_ty.castTag(.@"struct").?;
+ const struct_obj = payload.data;
+
switch (struct_obj.status) {
.none, .have_field_types, .field_types_wip, .layout_wip, .have_layout => {},
.fully_resolved_wip, .fully_resolved => return,
}
- // After we have resolve struct layout we have to go over the fields again to
- // make sure pointer fields get their child types resolved as well
- struct_obj.status = .fully_resolved_wip;
- for (struct_obj.fields.values()) |field| {
- try sema.resolveTypeFully(block, src, field.ty);
+ {
+ // After we have resolve struct layout we have to go over the fields again to
+ // make sure pointer fields get their child types resolved as well.
+ // See also similar code for unions.
+ const prev_status = struct_obj.status;
+ errdefer struct_obj.status = prev_status;
+
+ struct_obj.status = .fully_resolved_wip;
+ for (struct_obj.fields.values()) |field| {
+ try sema.resolveTypeFully(block, src, field.ty);
+ }
+ struct_obj.status = .fully_resolved;
}
- struct_obj.status = .fully_resolved;
+
+ // And let's not forget comptime-only status.
+ _ = try sema.typeRequiresComptime(block, src, ty);
}
fn resolveUnionFully(
@@ -16352,15 +23961,27 @@ fn resolveUnionFully(
.fully_resolved_wip, .fully_resolved => return,
}
- // Same goes for unions (see comment about structs)
- union_obj.status = .fully_resolved_wip;
- for (union_obj.fields.values()) |field| {
- try sema.resolveTypeFully(block, src, field.ty);
+ {
+ // After we have resolve union layout we have to go over the fields again to
+ // make sure pointer fields get their child types resolved as well.
+ // See also similar code for structs.
+ const prev_status = union_obj.status;
+ errdefer union_obj.status = prev_status;
+
+ union_obj.status = .fully_resolved_wip;
+ for (union_obj.fields.values()) |field| {
+ try sema.resolveTypeFully(block, src, field.ty);
+ }
+ union_obj.status = .fully_resolved;
}
- union_obj.status = .fully_resolved;
+
+ // And let's not forget comptime-only status.
+ _ = try sema.typeRequiresComptime(block, src, ty);
}
-fn resolveTypeFields(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) CompileError!Type {
+pub fn resolveTypeFields(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) CompileError!Type {
+ if (build_options.omit_stage2)
+ @panic("sadly stage2 is omitted from this build to save memory on the CI server");
switch (ty.tag()) {
.@"struct" => {
const struct_obj = ty.castTag(.@"struct").?.data;
@@ -16372,7 +23993,7 @@ fn resolveTypeFields(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) Comp
try sema.resolveTypeFieldsUnion(block, src, ty, union_obj);
return ty;
},
- .type_info => return sema.resolveBuiltinTypeFields(block, src, "TypeInfo"),
+ .type_info => return sema.resolveBuiltinTypeFields(block, src, "Type"),
.extern_options => return sema.resolveBuiltinTypeFields(block, src, "ExternOptions"),
.export_options => return sema.resolveBuiltinTypeFields(block, src, "ExportOptions"),
.atomic_order => return sema.resolveBuiltinTypeFields(block, src, "AtomicOrder"),
@@ -16398,7 +24019,7 @@ fn resolveTypeFieldsStruct(
switch (struct_obj.status) {
.none => {},
.field_types_wip => {
- return sema.fail(block, src, "struct {} depends on itself", .{ty});
+ return sema.fail(block, src, "struct '{}' depends on itself", .{ty.fmt(sema.mod)});
},
.have_field_types,
.have_layout,
@@ -16428,7 +24049,7 @@ fn resolveTypeFieldsUnion(
switch (union_obj.status) {
.none => {},
.field_types_wip => {
- return sema.fail(block, src, "union {} depends on itself", .{ty});
+ return sema.fail(block, src, "union '{}' depends on itself", .{ty.fmt(sema.mod)});
},
.have_field_types,
.have_layout,
@@ -16439,7 +24060,7 @@ fn resolveTypeFieldsUnion(
}
union_obj.status = .field_types_wip;
- try semaUnionFields(sema.mod, union_obj);
+ try semaUnionFields(block, sema.mod, union_obj);
union_obj.status = .have_field_types;
}
@@ -16453,40 +24074,57 @@ fn resolveBuiltinTypeFields(
return sema.resolveTypeFields(block, src, resolved_ty);
}
-fn resolveInferredErrorSet(sema: *Sema, inferred_error_set: *Module.Fn.InferredErrorSet) CompileError!void {
- // Ensuring that a particular decl is analyzed does not neccesarily mean that
- // it's error set is inferred, so traverse all of them to get the complete
- // picture.
- // Note: We want to skip re-resolving the current function, as recursion
- // doesn't change the error set. We can just check for state == .in_progress for this.
- // TODO: Is that correct?
+fn resolveInferredErrorSet(
+ sema: *Sema,
+ block: *Block,
+ src: LazySrcLoc,
+ ies: *Module.Fn.InferredErrorSet,
+) CompileError!void {
+ if (ies.is_resolved) return;
- if (inferred_error_set.is_resolved) {
- return;
+ if (ies.func.state == .in_progress) {
+ return sema.fail(block, src, "unable to resolve inferred error set", .{});
}
- var it = inferred_error_set.inferred_error_sets.keyIterator();
- while (it.next()) |other_error_set_ptr| {
- const func = other_error_set_ptr.*.func;
- const decl = func.*.owner_decl;
+ // In order to ensure that all dependencies are properly added to the set, we
+ // need to ensure the function body is analyzed of the inferred error set.
+ // However, in the case of comptime/inline function calls with inferred error sets,
+ // each call gets a new InferredErrorSet object, which points to the same
+ // `*Module.Fn`. Not only is the function not relevant to the inferred error set
+ // in this case, it may be a generic function which would cause an assertion failure
+ // if we called `ensureFuncBodyAnalyzed` on it here.
+ const ies_func_owner_decl = sema.mod.declPtr(ies.func.owner_decl);
+ if (ies_func_owner_decl.ty.fnInfo().return_type.errorUnionSet().castTag(.error_set_inferred).?.data == ies) {
+ // In this case we are dealing with the actual InferredErrorSet object that
+ // corresponds to the function, not one created to track an inline/comptime call.
+ try sema.ensureFuncBodyAnalyzed(ies.func);
+ }
- if (func.*.state == .in_progress) {
- // Recursion, doesn't alter current error set, keep going.
- continue;
- }
+ ies.is_resolved = true;
- try sema.ensureDeclAnalyzed(decl); // To ensure that all dependencies are properly added to the set.
- try sema.resolveInferredErrorSet(other_error_set_ptr.*);
+ var it = ies.inferred_error_sets.keyIterator();
+ while (it.next()) |other_error_set_ptr| {
+ const other_ies: *Module.Fn.InferredErrorSet = other_error_set_ptr.*;
+ if (ies == other_ies) continue;
+ try sema.resolveInferredErrorSet(block, src, other_ies);
- var error_it = other_error_set_ptr.*.errors.keyIterator();
- while (error_it.next()) |entry| {
- try inferred_error_set.errors.put(sema.gpa, entry.*, {});
+ for (other_ies.errors.keys()) |key| {
+ try ies.errors.put(sema.gpa, key, {});
}
- if (other_error_set_ptr.*.is_anyerror)
- inferred_error_set.is_anyerror = true;
+ if (other_ies.is_anyerror)
+ ies.is_anyerror = true;
}
+}
- inferred_error_set.is_resolved = true;
+fn resolveInferredErrorSetTy(
+ sema: *Sema,
+ block: *Block,
+ src: LazySrcLoc,
+ ty: Type,
+) CompileError!void {
+ if (ty.castTag(.error_set_inferred)) |inferred| {
+ try sema.resolveInferredErrorSet(block, src, inferred.data);
+ }
}
fn semaStructFields(
@@ -16497,14 +24135,14 @@ fn semaStructFields(
defer tracy.end();
const gpa = mod.gpa;
- const decl = struct_obj.owner_decl;
+ const decl_index = struct_obj.owner_decl;
const zir = struct_obj.namespace.file_scope.zir;
const extended = zir.instructions.items(.data)[struct_obj.zir_index].extended;
assert(extended.opcode == .struct_decl);
const small = @bitCast(Zir.Inst.StructDecl.Small, extended.small);
var extra_index: usize = extended.operand;
- const src: LazySrcLoc = .{ .node_offset = struct_obj.node_offset };
+ const src = LazySrcLoc.nodeOffset(struct_obj.node_offset);
extra_index += @boolToInt(small.has_src_node);
const body_len = if (small.has_body_len) blk: {
@@ -16537,6 +24175,7 @@ fn semaStructFields(
}
extra_index += body.len;
+ const decl = mod.declPtr(decl_index);
var decl_arena = decl.value_arena.?.promote(gpa);
defer decl.value_arena.?.* = decl_arena.state;
const decl_arena_allocator = decl_arena.allocator();
@@ -16551,6 +24190,7 @@ fn semaStructFields(
.perm_arena = decl_arena_allocator,
.code = zir,
.owner_decl = decl,
+ .owner_decl_index = decl_index,
.func = null,
.fn_ret_ty = Type.void,
.owner_func = null,
@@ -16563,7 +24203,7 @@ fn semaStructFields(
var block_scope: Block = .{
.parent = null,
.sema = &sema,
- .src_decl = decl,
+ .src_decl = decl_index,
.namespace = &struct_obj.namespace,
.wip_capture_scope = wip_captures.scope,
.instructions = .{},
@@ -16576,7 +24216,7 @@ fn semaStructFields(
}
if (body.len != 0) {
- _ = try sema.analyzeBody(&block_scope, body);
+ try sema.analyzeBody(&block_scope, body);
}
try wip_captures.finalize();
@@ -16624,11 +24264,32 @@ fn semaStructFields(
// But only resolve the source location if we need to emit a compile error.
try sema.resolveType(&block_scope, src, field_type_ref);
+ // TODO emit compile errors for invalid field types
+ // such as arrays and pointers inside packed structs.
+
+ if (field_ty.tag() == .generic_poison) {
+ return error.GenericPoison;
+ }
+
const gop = struct_obj.fields.getOrPutAssumeCapacity(field_name);
- assert(!gop.found_existing);
+ if (gop.found_existing) {
+ const msg = msg: {
+ const tree = try sema.getAstTree(&block_scope);
+ const field_src = enumFieldSrcLoc(decl, tree.*, struct_obj.node_offset, field_i);
+ const msg = try sema.errMsg(&block_scope, field_src, "duplicate struct field: '{s}'", .{field_name});
+ errdefer msg.destroy(gpa);
+
+ const prev_field_index = struct_obj.fields.getIndex(field_name).?;
+ const prev_field_src = enumFieldSrcLoc(decl, tree.*, struct_obj.node_offset, prev_field_index);
+ try sema.mod.errNoteNonLazy(prev_field_src.toSrcLoc(decl), msg, "other field here", .{});
+ try sema.errNote(&block_scope, src, msg, "struct declared here", .{});
+ break :msg msg;
+ };
+ return sema.failWithOwnedErrorMsg(&block_scope, msg);
+ }
gop.value_ptr.* = .{
.ty = try field_ty.copy(decl_arena_allocator),
- .abi_align = Value.initTag(.abi_align_default),
+ .abi_align = 0,
.default_val = Value.initTag(.unreachable_value),
.is_comptime = is_comptime,
.offset = undefined,
@@ -16640,13 +24301,12 @@ fn semaStructFields(
// TODO: if we need to report an error here, use a source location
// that points to this alignment expression rather than the struct.
// But only resolve the source location if we need to emit a compile error.
- const abi_align_val = (try sema.resolveInstConst(&block_scope, src, align_ref)).val;
- gop.value_ptr.abi_align = try abi_align_val.copy(decl_arena_allocator);
+ gop.value_ptr.abi_align = try sema.resolveAlign(&block_scope, src, align_ref);
}
if (has_default) {
const default_ref = @intToEnum(Zir.Inst.Ref, zir.extra[extra_index]);
extra_index += 1;
- const default_inst = sema.resolveInst(default_ref);
+ const default_inst = try sema.resolveInst(default_ref);
// TODO: if we need to report an error here, use a source location
// that points to this default value expression rather than the struct.
// But only resolve the source location if we need to emit a compile error.
@@ -16657,19 +24317,19 @@ fn semaStructFields(
}
}
-fn semaUnionFields(mod: *Module, union_obj: *Module.Union) CompileError!void {
+fn semaUnionFields(block: *Block, mod: *Module, union_obj: *Module.Union) CompileError!void {
const tracy = trace(@src());
defer tracy.end();
const gpa = mod.gpa;
- const decl = union_obj.owner_decl;
+ const decl_index = union_obj.owner_decl;
const zir = union_obj.namespace.file_scope.zir;
const extended = zir.instructions.items(.data)[union_obj.zir_index].extended;
assert(extended.opcode == .union_decl);
const small = @bitCast(Zir.Inst.UnionDecl.Small, extended.small);
var extra_index: usize = extended.operand;
- const src: LazySrcLoc = .{ .node_offset = union_obj.node_offset };
+ const src = LazySrcLoc.nodeOffset(union_obj.node_offset);
extra_index += @boolToInt(small.has_src_node);
const tag_type_ref: Zir.Inst.Ref = if (small.has_tag_type) blk: {
@@ -16708,8 +24368,10 @@ fn semaUnionFields(mod: *Module, union_obj: *Module.Union) CompileError!void {
}
extra_index += body.len;
- var decl_arena = union_obj.owner_decl.value_arena.?.promote(gpa);
- defer union_obj.owner_decl.value_arena.?.* = decl_arena.state;
+ const decl = mod.declPtr(decl_index);
+
+ var decl_arena = decl.value_arena.?.promote(gpa);
+ defer decl.value_arena.?.* = decl_arena.state;
const decl_arena_allocator = decl_arena.allocator();
var analysis_arena = std.heap.ArenaAllocator.init(gpa);
@@ -16722,6 +24384,7 @@ fn semaUnionFields(mod: *Module, union_obj: *Module.Union) CompileError!void {
.perm_arena = decl_arena_allocator,
.code = zir,
.owner_decl = decl,
+ .owner_decl_index = decl_index,
.func = null,
.fn_ret_ty = Type.void,
.owner_func = null,
@@ -16734,7 +24397,7 @@ fn semaUnionFields(mod: *Module, union_obj: *Module.Union) CompileError!void {
var block_scope: Block = .{
.parent = null,
.sema = &sema,
- .src_decl = decl,
+ .src_decl = decl_index,
.namespace = &union_obj.namespace,
.wip_capture_scope = wip_captures.scope,
.instructions = .{},
@@ -16747,7 +24410,7 @@ fn semaUnionFields(mod: *Module, union_obj: *Module.Union) CompileError!void {
}
if (body.len != 0) {
- _ = try sema.analyzeBody(&block_scope, body);
+ try sema.analyzeBody(&block_scope, body);
}
try wip_captures.finalize();
@@ -16757,24 +24420,29 @@ fn semaUnionFields(mod: *Module, union_obj: *Module.Union) CompileError!void {
var int_tag_ty: Type = undefined;
var enum_field_names: ?*Module.EnumNumbered.NameMap = null;
var enum_value_map: ?*Module.EnumNumbered.ValueMap = null;
+ var tag_ty_field_names: ?Module.EnumFull.NameMap = null;
if (tag_type_ref != .none) {
const provided_ty = try sema.resolveType(&block_scope, src, tag_type_ref);
if (small.auto_enum_tag) {
// The provided type is an integer type and we must construct the enum tag type here.
int_tag_ty = provided_ty;
- union_obj.tag_ty = try sema.generateUnionTagTypeNumbered(&block_scope, fields_len, provided_ty);
+ union_obj.tag_ty = try sema.generateUnionTagTypeNumbered(&block_scope, fields_len, provided_ty, union_obj);
const enum_obj = union_obj.tag_ty.castTag(.enum_numbered).?.data;
enum_field_names = &enum_obj.fields;
enum_value_map = &enum_obj.values;
} else {
// The provided type is the enum tag type.
union_obj.tag_ty = try provided_ty.copy(decl_arena_allocator);
+ // The fields of the union must match the enum exactly.
+ // Store a copy of the enum field names so we can check for
+ // missing or extraneous fields later.
+ tag_ty_field_names = try union_obj.tag_ty.enumFields().clone(sema.arena);
}
} else {
// If auto_enum_tag is false, this is an untagged union. However, for semantic analysis
// purposes, we still auto-generate an enum tag type the same way. That the union is
// untagged is represented by the Type tag (union vs union_tagged).
- union_obj.tag_ty = try sema.generateUnionTagTypeSimple(&block_scope, fields_len);
+ union_obj.tag_ty = try sema.generateUnionTagTypeSimple(&block_scope, fields_len, union_obj);
enum_field_names = &union_obj.tag_ty.castTag(.enum_simple).?.data.fields;
}
@@ -16785,6 +24453,7 @@ fn semaUnionFields(mod: *Module, union_obj: *Module.Union) CompileError!void {
extra_index += bit_bags_count;
var cur_bit_bag: u32 = undefined;
var field_i: u32 = 0;
+ var last_tag_val: ?Value = null;
while (field_i < fields_len) : (field_i += 1) {
if (field_i % fields_per_u32 == 0) {
cur_bit_bag = zir.extra[bit_bag_index];
@@ -16821,19 +24490,36 @@ fn semaUnionFields(mod: *Module, union_obj: *Module.Union) CompileError!void {
const tag_ref: Zir.Inst.Ref = if (has_tag) blk: {
const tag_ref = @intToEnum(Zir.Inst.Ref, zir.extra[extra_index]);
extra_index += 1;
- break :blk sema.resolveInst(tag_ref);
+ break :blk try sema.resolveInst(tag_ref);
} else .none;
if (enum_value_map) |map| {
- const tag_src = src; // TODO better source location
- const coerced = try sema.coerce(&block_scope, int_tag_ty, tag_ref, tag_src);
- const val = try sema.resolveConstValue(&block_scope, tag_src, coerced);
-
- // This puts the memory into the union arena, not the enum arena, but
- // it is OK since they share the same lifetime.
- const copied_val = try val.copy(decl_arena_allocator);
+ if (tag_ref != .none) {
+ const tag_src = src; // TODO better source location
+ const coerced = try sema.coerce(&block_scope, int_tag_ty, tag_ref, tag_src);
+ const val = try sema.resolveConstValue(&block_scope, tag_src, coerced);
+ last_tag_val = val;
+
+ // This puts the memory into the union arena, not the enum arena, but
+ // it is OK since they share the same lifetime.
+ const copied_val = try val.copy(decl_arena_allocator);
+ map.putAssumeCapacityContext(copied_val, {}, .{
+ .ty = int_tag_ty,
+ .mod = mod,
+ });
+ } else {
+ const val = if (last_tag_val) |val|
+ try sema.intAdd(block, src, val, Value.one, int_tag_ty)
+ else
+ Value.zero;
+ last_tag_val = val;
- map.putAssumeCapacityContext(copied_val, {}, .{ .ty = int_tag_ty });
+ const copied_val = try val.copy(decl_arena_allocator);
+ map.putAssumeCapacityContext(copied_val, {}, .{
+ .ty = int_tag_ty,
+ .mod = mod,
+ });
+ }
}
// This string needs to outlive the ZIR code.
@@ -16852,21 +24538,70 @@ fn semaUnionFields(mod: *Module, union_obj: *Module.Union) CompileError!void {
// But only resolve the source location if we need to emit a compile error.
try sema.resolveType(&block_scope, src, field_type_ref);
+ if (field_ty.tag() == .generic_poison) {
+ return error.GenericPoison;
+ }
+
const gop = union_obj.fields.getOrPutAssumeCapacity(field_name);
- assert(!gop.found_existing);
+ if (gop.found_existing) {
+ const msg = msg: {
+ const tree = try sema.getAstTree(&block_scope);
+ const field_src = enumFieldSrcLoc(decl, tree.*, union_obj.node_offset, field_i);
+ const msg = try sema.errMsg(&block_scope, field_src, "duplicate union field: '{s}'", .{field_name});
+ errdefer msg.destroy(gpa);
+
+ const prev_field_index = union_obj.fields.getIndex(field_name).?;
+ const prev_field_src = enumFieldSrcLoc(decl, tree.*, union_obj.node_offset, prev_field_index);
+ try sema.mod.errNoteNonLazy(prev_field_src.toSrcLoc(decl), msg, "other field here", .{});
+ try sema.errNote(&block_scope, src, msg, "union declared here", .{});
+ break :msg msg;
+ };
+ return sema.failWithOwnedErrorMsg(&block_scope, msg);
+ }
+
+ if (tag_ty_field_names) |*names| {
+ const enum_has_field = names.orderedRemove(field_name);
+ if (!enum_has_field) {
+ const msg = msg: {
+ const msg = try sema.errMsg(block, src, "enum '{}' has no field named '{s}'", .{ union_obj.tag_ty.fmt(sema.mod), field_name });
+ errdefer msg.destroy(sema.gpa);
+ try sema.addDeclaredHereNote(msg, union_obj.tag_ty);
+ break :msg msg;
+ };
+ return sema.failWithOwnedErrorMsg(block, msg);
+ }
+ }
+
gop.value_ptr.* = .{
.ty = try field_ty.copy(decl_arena_allocator),
- .abi_align = Value.initTag(.abi_align_default),
+ .abi_align = 0,
};
if (align_ref != .none) {
// TODO: if we need to report an error here, use a source location
// that points to this alignment expression rather than the struct.
// But only resolve the source location if we need to emit a compile error.
- const abi_align_val = (try sema.resolveInstConst(&block_scope, src, align_ref)).val;
- gop.value_ptr.abi_align = try abi_align_val.copy(decl_arena_allocator);
+ gop.value_ptr.abi_align = try sema.resolveAlign(&block_scope, src, align_ref);
} else {
- gop.value_ptr.abi_align = Value.initTag(.abi_align_default);
+ gop.value_ptr.abi_align = 0;
+ }
+ }
+
+ if (tag_ty_field_names) |names| {
+ if (names.count() > 0) {
+ const msg = msg: {
+ const msg = try sema.errMsg(block, src, "enum field(s) missing in union", .{});
+ errdefer msg.destroy(sema.gpa);
+
+ const enum_ty = union_obj.tag_ty;
+ for (names.keys()) |field_name| {
+ const field_index = enum_ty.enumFieldIndex(field_name).?;
+ try sema.addFieldErrNote(block, enum_ty, field_index, msg, "field '{s}' missing, declared here", .{field_name});
+ }
+ try sema.addDeclaredHereNote(msg, union_obj.tag_ty);
+ break :msg msg;
+ };
+ return sema.failWithOwnedErrorMsg(block, msg);
}
}
}
@@ -16876,6 +24611,7 @@ fn generateUnionTagTypeNumbered(
block: *Block,
fields_len: u32,
int_ty: Type,
+ union_obj: *Module.Union,
) !Type {
const mod = sema.mod;
@@ -16891,16 +24627,28 @@ fn generateUnionTagTypeNumbered(
};
const enum_ty = Type.initPayload(&enum_ty_payload.base);
const enum_val = try Value.Tag.ty.create(new_decl_arena_allocator, enum_ty);
- // TODO better type name
- const new_decl = try mod.createAnonymousDecl(block, .{
+
+ const src_decl = mod.declPtr(block.src_decl);
+ const new_decl_index = try mod.allocateNewDecl(block.namespace, src_decl.src_node, block.wip_capture_scope);
+ errdefer mod.destroyDecl(new_decl_index);
+ const name = name: {
+ const fqn = try union_obj.getFullyQualifiedName(mod);
+ defer sema.gpa.free(fqn);
+ break :name try std.fmt.allocPrintZ(mod.gpa, "@typeInfo({s}).Union.tag_type.?", .{fqn});
+ };
+ try mod.initNewAnonDecl(new_decl_index, src_decl.src_line, block.namespace, .{
.ty = Type.type,
.val = enum_val,
- });
+ }, name);
+ sema.mod.declPtr(new_decl_index).name_fully_qualified = true;
+
+ const new_decl = mod.declPtr(new_decl_index);
new_decl.owns_tv = true;
- errdefer mod.abortAnonDecl(new_decl);
+ new_decl.name_fully_qualified = true;
+ errdefer mod.abortAnonDecl(new_decl_index);
enum_obj.* = .{
- .owner_decl = new_decl,
+ .owner_decl = new_decl_index,
.tag_ty = int_ty,
.fields = .{},
.values = .{},
@@ -16908,12 +24656,15 @@ fn generateUnionTagTypeNumbered(
};
// Here we pre-allocate the maps using the decl arena.
try enum_obj.fields.ensureTotalCapacity(new_decl_arena_allocator, fields_len);
- try enum_obj.values.ensureTotalCapacityContext(new_decl_arena_allocator, fields_len, .{ .ty = int_ty });
+ try enum_obj.values.ensureTotalCapacityContext(new_decl_arena_allocator, fields_len, .{
+ .ty = int_ty,
+ .mod = mod,
+ });
try new_decl.finalizeNewArena(&new_decl_arena);
return enum_ty;
}
-fn generateUnionTagTypeSimple(sema: *Sema, block: *Block, fields_len: u32) !Type {
+fn generateUnionTagTypeSimple(sema: *Sema, block: *Block, fields_len: usize, maybe_union_obj: ?*Module.Union) !Type {
const mod = sema.mod;
var new_decl_arena = std.heap.ArenaAllocator.init(sema.gpa);
@@ -16928,16 +24679,36 @@ fn generateUnionTagTypeSimple(sema: *Sema, block: *Block, fields_len: u32) !Type
};
const enum_ty = Type.initPayload(&enum_ty_payload.base);
const enum_val = try Value.Tag.ty.create(new_decl_arena_allocator, enum_ty);
- // TODO better type name
- const new_decl = try mod.createAnonymousDecl(block, .{
- .ty = Type.type,
- .val = enum_val,
- });
+
+ const new_decl_index = new_decl_index: {
+ const union_obj = maybe_union_obj orelse {
+ break :new_decl_index try mod.createAnonymousDecl(block, .{
+ .ty = Type.type,
+ .val = enum_val,
+ });
+ };
+ const src_decl = mod.declPtr(block.src_decl);
+ const new_decl_index = try mod.allocateNewDecl(block.namespace, src_decl.src_node, block.wip_capture_scope);
+ errdefer mod.destroyDecl(new_decl_index);
+ const name = name: {
+ const fqn = try union_obj.getFullyQualifiedName(mod);
+ defer sema.gpa.free(fqn);
+ break :name try std.fmt.allocPrintZ(mod.gpa, "@typeInfo({s}).Union.tag_type.?", .{fqn});
+ };
+ try mod.initNewAnonDecl(new_decl_index, src_decl.src_line, block.namespace, .{
+ .ty = Type.type,
+ .val = enum_val,
+ }, name);
+ sema.mod.declPtr(new_decl_index).name_fully_qualified = true;
+ break :new_decl_index new_decl_index;
+ };
+
+ const new_decl = mod.declPtr(new_decl_index);
new_decl.owns_tv = true;
- errdefer mod.abortAnonDecl(new_decl);
+ errdefer mod.abortAnonDecl(new_decl_index);
enum_obj.* = .{
- .owner_decl = new_decl,
+ .owner_decl = new_decl_index,
.fields = .{},
.node_offset = 0,
};
@@ -16959,7 +24730,7 @@ fn getBuiltin(
const opt_builtin_inst = try sema.namespaceLookupRef(
block,
src,
- std_file.root_decl.?.src_namespace,
+ mod.declPtr(std_file.root_decl.unwrap().?).src_namespace,
"builtin",
);
const builtin_inst = try sema.analyzeLoad(block, src, opt_builtin_inst.?, src);
@@ -16980,7 +24751,9 @@ fn getBuiltinType(
name: []const u8,
) CompileError!Type {
const ty_inst = try sema.getBuiltin(block, src, name);
- return sema.analyzeAsType(block, src, ty_inst);
+ const result_ty = try sema.analyzeAsType(block, src, ty_inst);
+ try sema.queueFullTypeResolution(result_ty);
+ return result_ty;
}
/// There is another implementation of this in `Type.onePossibleValue`. This one
@@ -17008,6 +24781,7 @@ pub fn typeHasOnePossibleValue(
.i8,
.u16,
.i16,
+ .u29,
.u32,
.i32,
.u64,
@@ -17027,6 +24801,10 @@ pub fn typeHasOnePossibleValue(
.bool,
.type,
.anyerror,
+ .error_set_single,
+ .error_set,
+ .error_set_merged,
+ .error_union,
.fn_noreturn_no_args,
.fn_void_no_args,
.fn_naked_noreturn_no_args,
@@ -17040,16 +24818,11 @@ pub fn typeHasOnePossibleValue(
.const_slice,
.mut_slice,
.anyopaque,
- .optional,
.optional_single_mut_pointer,
.optional_single_const_pointer,
.enum_literal,
.anyerror_void_error_union,
- .error_union,
- .error_set,
- .error_set_single,
.error_set_inferred,
- .error_set_merged,
.@"opaque",
.var_args_param,
.manyptr_u8,
@@ -17078,10 +24851,21 @@ pub fn typeHasOnePossibleValue(
.bound_fn,
=> return null,
+ .optional => {
+ var buf: Type.Payload.ElemType = undefined;
+ const child_ty = ty.optionalChild(&buf);
+ if (child_ty.isNoReturn()) {
+ return Value.@"null";
+ } else {
+ return null;
+ }
+ },
+
.@"struct" => {
const resolved_ty = try sema.resolveTypeFields(block, src, ty);
const s = resolved_ty.castTag(.@"struct").?.data;
for (s.fields.values()) |value| {
+ if (value.is_comptime) continue;
if ((try sema.typeHasOnePossibleValue(block, src, value.ty)) == null) {
return null;
}
@@ -17089,8 +24873,8 @@ pub fn typeHasOnePossibleValue(
return Value.initTag(.empty_struct_value);
},
- .tuple => {
- const tuple = ty.castTag(.tuple).?.data;
+ .tuple, .anon_struct => {
+ const tuple = ty.tupleFields();
for (tuple.values) |val| {
if (val.tag() == .unreachable_value) {
return null; // non-comptime field
@@ -17226,7 +25010,7 @@ fn enumFieldSrcLoc(
.container_field,
=> {
if (it_index == field_index) {
- return .{ .node_offset = decl.nodeIndexToRelative(member_node) };
+ return LazySrcLoc.nodeOffset(decl.nodeIndexToRelative(member_node));
}
it_index += 1;
},
@@ -17241,7 +25025,7 @@ fn typeOf(sema: *Sema, inst: Air.Inst.Ref) Type {
return sema.getTmpAir().typeOf(inst);
}
-fn getTmpAir(sema: Sema) Air {
+pub fn getTmpAir(sema: Sema) Air {
return .{
.instructions = sema.air_instructions.slice(),
.extra = sema.air_extra.items,
@@ -17255,6 +25039,7 @@ pub fn addType(sema: *Sema, ty: Type) !Air.Inst.Ref {
.u8 => return .u8_type,
.i8 => return .i8_type,
.u16 => return .u16_type,
+ .u29 => return .u29_type,
.i16 => return .i16_type,
.u32 => return .u32_type,
.i32 => return .i32_type,
@@ -17324,8 +25109,16 @@ fn addIntUnsigned(sema: *Sema, ty: Type, int: u64) CompileError!Air.Inst.Ref {
return sema.addConstant(ty, try Value.Tag.int_u64.create(sema.arena, int));
}
+fn addBool(sema: *Sema, ty: Type, boolean: bool) CompileError!Air.Inst.Ref {
+ return switch (ty.zigTypeTag()) {
+ .Vector => sema.addConstant(ty, try Value.Tag.repeated.create(sema.arena, Value.makeBool(boolean))),
+ .Bool => try sema.resolveInst(if (boolean) .bool_true else .bool_false),
+ else => unreachable,
+ };
+}
+
fn addConstUndef(sema: *Sema, ty: Type) CompileError!Air.Inst.Ref {
- return sema.addConstant(ty, Value.initTag(.undef));
+ return sema.addConstant(ty, Value.undef);
}
pub fn addConstant(sema: *Sema, ty: Type, val: Value) SemaError!Air.Inst.Ref {
@@ -17363,7 +25156,7 @@ pub fn addExtraAssumeCapacity(sema: *Sema, extra: anytype) u32 {
}
fn appendRefsAssumeCapacity(sema: *Sema, refs: []const Air.Inst.Ref) void {
- const coerced = @bitCast([]const u32, refs);
+ const coerced = @ptrCast([]const u32, refs);
sema.air_extra.appendSliceAssumeCapacity(coerced);
}
@@ -17395,7 +25188,7 @@ fn analyzeComptimeAlloc(
// Needed to make an anon decl with type `var_type` (the `finish()` call below).
_ = try sema.typeHasOnePossibleValue(block, src, var_type);
- const ptr_type = try Type.ptr(sema.arena, .{
+ const ptr_type = try Type.ptr(sema.arena, sema.mod, .{
.pointee_type = var_type,
.@"addrspace" = target_util.defaultAddressSpace(sema.mod.getTarget(), .global_constant),
.@"align" = alignment,
@@ -17404,24 +25197,21 @@ fn analyzeComptimeAlloc(
var anon_decl = try block.startAnonDecl(src);
defer anon_decl.deinit();
- const align_val = if (alignment == 0)
- Value.@"null"
- else
- try Value.Tag.int_u64.create(anon_decl.arena(), alignment);
-
- const decl = try anon_decl.finish(
+ const decl_index = try anon_decl.finish(
try var_type.copy(anon_decl.arena()),
// There will be stores before the first load, but they may be to sub-elements or
// sub-fields. So we need to initialize with undef to allow the mechanism to expand
// into fields/elements and have those overridden with stored values.
Value.undef,
+ alignment,
);
- decl.align_val = align_val;
+ const decl = sema.mod.declPtr(decl_index);
+ decl.@"align" = alignment;
- try sema.mod.declareDeclDependency(sema.owner_decl, decl);
+ try sema.mod.declareDeclDependency(sema.owner_decl_index, decl_index);
return sema.addConstant(ptr_type, try Value.Tag.decl_ref_mut.create(sema.arena, .{
.runtime_index = block.runtime_index,
- .decl = decl,
+ .decl_index = decl_index,
}));
}
@@ -17454,10 +25244,14 @@ pub fn analyzeAddrspace(
const address_space = addrspace_tv.val.toEnum(std.builtin.AddressSpace);
const target = sema.mod.getTarget();
const arch = target.cpu.arch;
+ const is_gpu = arch == .nvptx or arch == .nvptx64;
const supported = switch (address_space) {
.generic => true,
.gs, .fs, .ss => (arch == .i386 or arch == .x86_64) and ctx == .pointer,
+ // TODO: check that .shared and .local are left uninitialized
+ .global, .param, .shared, .local => is_gpu,
+ .constant => is_gpu and (ctx == .constant),
};
if (!supported) {
@@ -17468,7 +25262,6 @@ pub fn analyzeAddrspace(
.constant => "constant values",
.pointer => "pointers",
};
-
return sema.fail(
block,
src,
@@ -17483,51 +25276,61 @@ pub fn analyzeAddrspace(
/// Asserts the value is a pointer and dereferences it.
/// Returns `null` if the pointer contents cannot be loaded at comptime.
fn pointerDeref(sema: *Sema, block: *Block, src: LazySrcLoc, ptr_val: Value, ptr_ty: Type) CompileError!?Value {
- const target = sema.mod.getTarget();
const load_ty = ptr_ty.childType();
- const parent = sema.beginComptimePtrLoad(block, src, ptr_val) catch |err| switch (err) {
+ const target = sema.mod.getTarget();
+ const deref = sema.beginComptimePtrLoad(block, src, ptr_val, load_ty) catch |err| switch (err) {
error.RuntimeLoad => return null,
else => |e| return e,
};
- // We have a Value that lines up in virtual memory exactly with what we want to load.
- // If the Type is in-memory coercable to `load_ty`, it may be returned without modifications.
- const coerce_in_mem_ok =
- (try sema.coerceInMemoryAllowed(block, load_ty, parent.ty, false, target, src, src)) == .ok or
- (try sema.coerceInMemoryAllowed(block, parent.ty, load_ty, false, target, src, src)) == .ok;
- if (coerce_in_mem_ok) {
- if (parent.is_mutable) {
- // The decl whose value we are obtaining here may be overwritten with
- // a different value upon further semantic analysis, which would
- // invalidate this memory. So we must copy here.
- return try parent.val.copy(sema.arena);
+
+ if (deref.pointee) |tv| {
+ const coerce_in_mem_ok =
+ (try sema.coerceInMemoryAllowed(block, load_ty, tv.ty, false, target, src, src)) == .ok or
+ (try sema.coerceInMemoryAllowed(block, tv.ty, load_ty, false, target, src, src)) == .ok;
+ if (coerce_in_mem_ok) {
+ // We have a Value that lines up in virtual memory exactly with what we want to load,
+ // and it is in-memory coercible to load_ty. It may be returned without modifications.
+ if (deref.is_mutable) {
+ // The decl whose value we are obtaining here may be overwritten with
+ // a different value upon further semantic analysis, which would
+ // invalidate this memory. So we must copy here.
+ return try tv.val.copy(sema.arena);
+ }
+ return tv.val;
}
- return parent.val;
}
- // The type is not in-memory coercable, so it must be bitcasted according
- // to the pointer type we are performing the load through.
+ // The type is not in-memory coercible or the direct dereference failed, so it must
+ // be bitcast according to the pointer type we are performing the load through.
+ if (!load_ty.hasWellDefinedLayout())
+ return sema.fail(block, src, "comptime dereference requires '{}' to have a well-defined layout, but it does not.", .{load_ty.fmt(sema.mod)});
- // TODO emit a compile error if the types are not allowed to be bitcasted
+ const load_sz = try sema.typeAbiSize(block, src, load_ty);
- if (parent.ty.abiSize(target) >= load_ty.abiSize(target)) {
- // The Type it is stored as in the compiler has an ABI size greater or equal to
- // the ABI size of `load_ty`. We may perform the bitcast based on
- // `parent.val` alone (more efficient).
- return try sema.bitCastVal(block, src, parent.val, parent.ty, load_ty);
- }
+ // Try the smaller bit-cast first, since that's more efficient than using the larger `parent`
+ if (deref.pointee) |tv| if (load_sz <= try sema.typeAbiSize(block, src, tv.ty))
+ return try sema.bitCastVal(block, src, tv.val, tv.ty, load_ty, 0);
- // The Type it is stored as in the compiler has an ABI size less than the ABI size
- // of `load_ty`. The bitcast must be performed based on the `parent.root_val`
- // and reinterpreted starting at `parent.byte_offset`.
- return sema.fail(block, src, "TODO: implement bitcast with index offset", .{});
+ // If that fails, try to bit-cast from the largest parent value with a well-defined layout
+ if (deref.parent) |parent| if (load_sz + parent.byte_offset <= try sema.typeAbiSize(block, src, parent.tv.ty))
+ return try sema.bitCastVal(block, src, parent.tv.val, parent.tv.ty, load_ty, parent.byte_offset);
+
+ if (deref.ty_without_well_defined_layout) |bad_ty| {
+ // We got no parent for bit-casting, or the parent we got was too small. Either way, the problem
+ // is that some type we encountered when de-referencing does not have a well-defined layout.
+ return sema.fail(block, src, "comptime dereference requires '{}' to have a well-defined layout, but it does not.", .{bad_ty.fmt(sema.mod)});
+ } else {
+ // If all encountered types had well-defined layouts, the parent is the root decl and it just
+ // wasn't big enough for the load.
+ return sema.fail(block, src, "dereference of '{}' exceeds bounds of containing decl of type '{}'", .{ ptr_ty.fmt(sema.mod), deref.parent.?.tv.ty.fmt(sema.mod) });
+ }
}
/// Used to convert a u64 value to a usize value, emitting a compile error if the number
/// is too big to fit.
fn usizeCast(sema: *Sema, block: *Block, src: LazySrcLoc, int: u64) CompileError!usize {
- return std.math.cast(usize, int) catch |err| switch (err) {
- error.Overflow => return sema.fail(block, src, "expression produces integer value {d} which is too big for this compiler implementation to handle", .{int}),
- };
+ if (@bitSizeOf(u64) <= @bitSizeOf(usize)) return int;
+ return std.math.cast(usize, int) orelse return sema.fail(block, src, "expression produces integer value '{d}' which is too big for this compiler implementation to handle", .{int});
}
/// For pointer-like optionals, it returns the pointer type. For pointers,
@@ -17556,6 +25359,7 @@ fn typePtrOrOptionalPtrTy(
.many_mut_pointer,
.manyptr_u8,
.manyptr_const_u8,
+ .manyptr_const_u8_sentinel_0,
=> return ty,
.pointer => switch (ty.ptrSize()) {
@@ -17595,13 +25399,18 @@ fn typePtrOrOptionalPtrTy(
/// This function returns false negatives when structs and unions are having their
/// field types resolved.
/// TODO assert the return value matches `ty.comptimeOnly`
-fn typeRequiresComptime(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) CompileError!bool {
+/// TODO merge these implementations together with the "advanced"/sema_kit pattern seen
+/// elsewhere in value.zig
+pub fn typeRequiresComptime(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) CompileError!bool {
+ if (build_options.omit_stage2)
+ @panic("sadly stage2 is omitted from this build to save memory on the CI server");
return switch (ty.tag()) {
.u1,
.u8,
.i8,
.u16,
.i16,
+ .u29,
.u32,
.i32,
.u64,
@@ -17713,10 +25522,11 @@ fn typeRequiresComptime(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) C
return sema.typeRequiresComptime(block, src, ty.optionalChild(&buf));
},
- .tuple => {
- const tuple = ty.castTag(.tuple).?.data;
- for (tuple.types) |field_ty| {
- if (try sema.typeRequiresComptime(block, src, field_ty)) {
+ .tuple, .anon_struct => {
+ const tuple = ty.tupleFields();
+ for (tuple.types) |field_ty, i| {
+ const have_comptime_val = tuple.values[i].tag() != .unreachable_value;
+ if (!have_comptime_val and try sema.typeRequiresComptime(block, src, field_ty)) {
return true;
}
}
@@ -17736,6 +25546,7 @@ fn typeRequiresComptime(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) C
struct_obj.requires_comptime = .wip;
for (struct_obj.fields.values()) |field| {
+ if (field.is_comptime) continue;
if (try sema.typeRequiresComptime(block, src, field.ty)) {
struct_obj.requires_comptime = .yes;
return true;
@@ -17788,9 +25599,33 @@ fn typeRequiresComptime(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) C
}
pub fn typeHasRuntimeBits(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) CompileError!bool {
- if ((try sema.typeHasOnePossibleValue(block, src, ty)) != null) return false;
- if (try sema.typeRequiresComptime(block, src, ty)) return false;
- return true;
+ return ty.hasRuntimeBitsAdvanced(false, sema.kit(block, src));
+}
+
+fn typeAbiSize(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) !u64 {
+ try sema.resolveTypeLayout(block, src, ty);
+ const target = sema.mod.getTarget();
+ return ty.abiSize(target);
+}
+
+fn typeAbiAlignment(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) CompileError!u32 {
+ const target = sema.mod.getTarget();
+ return (try ty.abiAlignmentAdvanced(target, .{ .sema_kit = sema.kit(block, src) })).scalar;
+}
+
+/// Not valid to call for packed unions.
+/// Keep implementation in sync with `Module.Union.Field.normalAlignment`.
+fn unionFieldAlignment(
+ sema: *Sema,
+ block: *Block,
+ src: LazySrcLoc,
+ field: Module.Union.Field,
+) !u32 {
+ if (field.abi_align == 0) {
+ return sema.typeAbiAlignment(block, src, field.ty);
+ } else {
+ return field.abi_align;
+ }
}
/// Synchronize logic with `Type.isFnOrHasRuntimeBits`.
@@ -17808,3 +25643,803 @@ pub fn fnHasRuntimeBits(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) C
}
return true;
}
+
+fn unionFieldIndex(
+ sema: *Sema,
+ block: *Block,
+ unresolved_union_ty: Type,
+ field_name: []const u8,
+ field_src: LazySrcLoc,
+) !u32 {
+ const union_ty = try sema.resolveTypeFields(block, field_src, unresolved_union_ty);
+ const union_obj = union_ty.cast(Type.Payload.Union).?.data;
+ const field_index_usize = union_obj.fields.getIndex(field_name) orelse
+ return sema.failWithBadUnionFieldAccess(block, union_obj, field_src, field_name);
+ return @intCast(u32, field_index_usize);
+}
+
+fn structFieldIndex(
+ sema: *Sema,
+ block: *Block,
+ unresolved_struct_ty: Type,
+ field_name: []const u8,
+ field_src: LazySrcLoc,
+) !u32 {
+ const struct_ty = try sema.resolveTypeFields(block, field_src, unresolved_struct_ty);
+ const struct_obj = struct_ty.castTag(.@"struct").?.data;
+ const field_index_usize = struct_obj.fields.getIndex(field_name) orelse
+ return sema.failWithBadStructFieldAccess(block, struct_obj, field_src, field_name);
+ return @intCast(u32, field_index_usize);
+}
+
+fn anonStructFieldIndex(
+ sema: *Sema,
+ block: *Block,
+ struct_ty: Type,
+ field_name: []const u8,
+ field_src: LazySrcLoc,
+) !u32 {
+ const anon_struct = struct_ty.castTag(.anon_struct).?.data;
+ for (anon_struct.names) |name, i| {
+ if (mem.eql(u8, name, field_name)) {
+ return @intCast(u32, i);
+ }
+ }
+ return sema.fail(block, field_src, "anonymous struct '{}' has no such field '{s}'", .{
+ struct_ty.fmt(sema.mod), field_name,
+ });
+}
+
+fn kit(sema: *Sema, block: *Block, src: LazySrcLoc) Module.WipAnalysis {
+ return .{ .sema = sema, .block = block, .src = src };
+}
+
+fn queueFullTypeResolution(sema: *Sema, ty: Type) !void {
+ const inst_ref = try sema.addType(ty);
+ try sema.types_to_resolve.append(sema.gpa, inst_ref);
+}
+
+fn intAdd(sema: *Sema, block: *Block, src: LazySrcLoc, lhs: Value, rhs: Value, ty: Type) !Value {
+ if (ty.zigTypeTag() == .Vector) {
+ const result_data = try sema.arena.alloc(Value, ty.vectorLen());
+ for (result_data) |*scalar, i| {
+ scalar.* = try sema.intAddScalar(block, src, lhs.indexVectorlike(i), rhs.indexVectorlike(i));
+ }
+ return Value.Tag.aggregate.create(sema.arena, result_data);
+ }
+ return sema.intAddScalar(block, src, lhs, rhs);
+}
+
+fn intAddScalar(sema: *Sema, block: *Block, src: LazySrcLoc, lhs: Value, rhs: Value) !Value {
+ // TODO is this a performance issue? maybe we should try the operation without
+ // resorting to BigInt first.
+ var lhs_space: Value.BigIntSpace = undefined;
+ var rhs_space: Value.BigIntSpace = undefined;
+ const target = sema.mod.getTarget();
+ const lhs_bigint = try lhs.toBigIntAdvanced(&lhs_space, target, sema.kit(block, src));
+ const rhs_bigint = try rhs.toBigIntAdvanced(&rhs_space, target, sema.kit(block, src));
+ const limbs = try sema.arena.alloc(
+ std.math.big.Limb,
+ std.math.max(lhs_bigint.limbs.len, rhs_bigint.limbs.len) + 1,
+ );
+ var result_bigint = std.math.big.int.Mutable{ .limbs = limbs, .positive = undefined, .len = undefined };
+ result_bigint.add(lhs_bigint, rhs_bigint);
+ return Value.fromBigInt(sema.arena, result_bigint.toConst());
+}
+
+/// Supports both (vectors of) floats and ints; handles undefined scalars.
+fn numberAddWrap(
+ sema: *Sema,
+ block: *Block,
+ src: LazySrcLoc,
+ lhs: Value,
+ rhs: Value,
+ ty: Type,
+) !Value {
+ if (ty.zigTypeTag() == .Vector) {
+ const result_data = try sema.arena.alloc(Value, ty.vectorLen());
+ for (result_data) |*scalar, i| {
+ scalar.* = try sema.numberAddWrapScalar(block, src, lhs.indexVectorlike(i), rhs.indexVectorlike(i), ty.scalarType());
+ }
+ return Value.Tag.aggregate.create(sema.arena, result_data);
+ }
+ return sema.numberAddWrapScalar(block, src, lhs, rhs, ty);
+}
+
+/// Supports both floats and ints; handles undefined.
+fn numberAddWrapScalar(
+ sema: *Sema,
+ block: *Block,
+ src: LazySrcLoc,
+ lhs: Value,
+ rhs: Value,
+ ty: Type,
+) !Value {
+ if (lhs.isUndef() or rhs.isUndef()) return Value.initTag(.undef);
+
+ if (ty.zigTypeTag() == .ComptimeInt) {
+ return sema.intAdd(block, src, lhs, rhs, ty);
+ }
+
+ if (ty.isAnyFloat()) {
+ return sema.floatAdd(lhs, rhs, ty);
+ }
+
+ const overflow_result = try sema.intAddWithOverflow(block, src, lhs, rhs, ty);
+ return overflow_result.wrapped_result;
+}
+
+fn intSub(
+ sema: *Sema,
+ block: *Block,
+ src: LazySrcLoc,
+ lhs: Value,
+ rhs: Value,
+ ty: Type,
+) !Value {
+ if (ty.zigTypeTag() == .Vector) {
+ const result_data = try sema.arena.alloc(Value, ty.vectorLen());
+ for (result_data) |*scalar, i| {
+ scalar.* = try sema.intSubScalar(block, src, lhs.indexVectorlike(i), rhs.indexVectorlike(i));
+ }
+ return Value.Tag.aggregate.create(sema.arena, result_data);
+ }
+ return sema.intSubScalar(block, src, lhs, rhs);
+}
+
+fn intSubScalar(sema: *Sema, block: *Block, src: LazySrcLoc, lhs: Value, rhs: Value) !Value {
+ // TODO is this a performance issue? maybe we should try the operation without
+ // resorting to BigInt first.
+ var lhs_space: Value.BigIntSpace = undefined;
+ var rhs_space: Value.BigIntSpace = undefined;
+ const target = sema.mod.getTarget();
+ const lhs_bigint = try lhs.toBigIntAdvanced(&lhs_space, target, sema.kit(block, src));
+ const rhs_bigint = try rhs.toBigIntAdvanced(&rhs_space, target, sema.kit(block, src));
+ const limbs = try sema.arena.alloc(
+ std.math.big.Limb,
+ std.math.max(lhs_bigint.limbs.len, rhs_bigint.limbs.len) + 1,
+ );
+ var result_bigint = std.math.big.int.Mutable{ .limbs = limbs, .positive = undefined, .len = undefined };
+ result_bigint.sub(lhs_bigint, rhs_bigint);
+ return Value.fromBigInt(sema.arena, result_bigint.toConst());
+}
+
+/// Supports both (vectors of) floats and ints; handles undefined scalars.
+fn numberSubWrap(
+ sema: *Sema,
+ block: *Block,
+ src: LazySrcLoc,
+ lhs: Value,
+ rhs: Value,
+ ty: Type,
+) !Value {
+ if (ty.zigTypeTag() == .Vector) {
+ const result_data = try sema.arena.alloc(Value, ty.vectorLen());
+ for (result_data) |*scalar, i| {
+ scalar.* = try sema.numberSubWrapScalar(block, src, lhs.indexVectorlike(i), rhs.indexVectorlike(i), ty.scalarType());
+ }
+ return Value.Tag.aggregate.create(sema.arena, result_data);
+ }
+ return sema.numberSubWrapScalar(block, src, lhs, rhs, ty);
+}
+
+/// Supports both floats and ints; handles undefined.
+fn numberSubWrapScalar(
+ sema: *Sema,
+ block: *Block,
+ src: LazySrcLoc,
+ lhs: Value,
+ rhs: Value,
+ ty: Type,
+) !Value {
+ if (lhs.isUndef() or rhs.isUndef()) return Value.initTag(.undef);
+
+ if (ty.zigTypeTag() == .ComptimeInt) {
+ return sema.intSub(block, src, lhs, rhs, ty);
+ }
+
+ if (ty.isAnyFloat()) {
+ return sema.floatSub(lhs, rhs, ty);
+ }
+
+ const overflow_result = try sema.intSubWithOverflow(block, src, lhs, rhs, ty);
+ return overflow_result.wrapped_result;
+}
+
+fn floatAdd(
+ sema: *Sema,
+ lhs: Value,
+ rhs: Value,
+ float_type: Type,
+) !Value {
+ if (float_type.zigTypeTag() == .Vector) {
+ const result_data = try sema.arena.alloc(Value, float_type.vectorLen());
+ for (result_data) |*scalar, i| {
+ scalar.* = try sema.floatAddScalar(lhs.indexVectorlike(i), rhs.indexVectorlike(i), float_type.scalarType());
+ }
+ return Value.Tag.aggregate.create(sema.arena, result_data);
+ }
+ return sema.floatAddScalar(lhs, rhs, float_type);
+}
+
+fn floatAddScalar(
+ sema: *Sema,
+ lhs: Value,
+ rhs: Value,
+ float_type: Type,
+) !Value {
+ const target = sema.mod.getTarget();
+ switch (float_type.floatBits(target)) {
+ 16 => {
+ const lhs_val = lhs.toFloat(f16);
+ const rhs_val = rhs.toFloat(f16);
+ return Value.Tag.float_16.create(sema.arena, lhs_val + rhs_val);
+ },
+ 32 => {
+ const lhs_val = lhs.toFloat(f32);
+ const rhs_val = rhs.toFloat(f32);
+ return Value.Tag.float_32.create(sema.arena, lhs_val + rhs_val);
+ },
+ 64 => {
+ const lhs_val = lhs.toFloat(f64);
+ const rhs_val = rhs.toFloat(f64);
+ return Value.Tag.float_64.create(sema.arena, lhs_val + rhs_val);
+ },
+ 80 => {
+ const lhs_val = lhs.toFloat(f80);
+ const rhs_val = rhs.toFloat(f80);
+ return Value.Tag.float_80.create(sema.arena, lhs_val + rhs_val);
+ },
+ 128 => {
+ const lhs_val = lhs.toFloat(f128);
+ const rhs_val = rhs.toFloat(f128);
+ return Value.Tag.float_128.create(sema.arena, lhs_val + rhs_val);
+ },
+ else => unreachable,
+ }
+}
+
+fn floatSub(
+ sema: *Sema,
+ lhs: Value,
+ rhs: Value,
+ float_type: Type,
+) !Value {
+ if (float_type.zigTypeTag() == .Vector) {
+ const result_data = try sema.arena.alloc(Value, float_type.vectorLen());
+ for (result_data) |*scalar, i| {
+ scalar.* = try sema.floatSubScalar(lhs.indexVectorlike(i), rhs.indexVectorlike(i), float_type.scalarType());
+ }
+ return Value.Tag.aggregate.create(sema.arena, result_data);
+ }
+ return sema.floatSubScalar(lhs, rhs, float_type);
+}
+
+fn floatSubScalar(
+ sema: *Sema,
+ lhs: Value,
+ rhs: Value,
+ float_type: Type,
+) !Value {
+ const target = sema.mod.getTarget();
+ switch (float_type.floatBits(target)) {
+ 16 => {
+ const lhs_val = lhs.toFloat(f16);
+ const rhs_val = rhs.toFloat(f16);
+ return Value.Tag.float_16.create(sema.arena, lhs_val - rhs_val);
+ },
+ 32 => {
+ const lhs_val = lhs.toFloat(f32);
+ const rhs_val = rhs.toFloat(f32);
+ return Value.Tag.float_32.create(sema.arena, lhs_val - rhs_val);
+ },
+ 64 => {
+ const lhs_val = lhs.toFloat(f64);
+ const rhs_val = rhs.toFloat(f64);
+ return Value.Tag.float_64.create(sema.arena, lhs_val - rhs_val);
+ },
+ 80 => {
+ const lhs_val = lhs.toFloat(f80);
+ const rhs_val = rhs.toFloat(f80);
+ return Value.Tag.float_80.create(sema.arena, lhs_val - rhs_val);
+ },
+ 128 => {
+ const lhs_val = lhs.toFloat(f128);
+ const rhs_val = rhs.toFloat(f128);
+ return Value.Tag.float_128.create(sema.arena, lhs_val - rhs_val);
+ },
+ else => unreachable,
+ }
+}
+
+fn intSubWithOverflow(
+ sema: *Sema,
+ block: *Block,
+ src: LazySrcLoc,
+ lhs: Value,
+ rhs: Value,
+ ty: Type,
+) !Value.OverflowArithmeticResult {
+ if (ty.zigTypeTag() == .Vector) {
+ const overflowed_data = try sema.arena.alloc(Value, ty.vectorLen());
+ const result_data = try sema.arena.alloc(Value, ty.vectorLen());
+ for (result_data) |*scalar, i| {
+ const of_math_result = try sema.intSubWithOverflowScalar(block, src, lhs.indexVectorlike(i), rhs.indexVectorlike(i), ty.scalarType());
+ overflowed_data[i] = of_math_result.overflowed;
+ scalar.* = of_math_result.wrapped_result;
+ }
+ return Value.OverflowArithmeticResult{
+ .overflowed = try Value.Tag.aggregate.create(sema.arena, overflowed_data),
+ .wrapped_result = try Value.Tag.aggregate.create(sema.arena, result_data),
+ };
+ }
+ return sema.intSubWithOverflowScalar(block, src, lhs, rhs, ty);
+}
+
+fn intSubWithOverflowScalar(
+ sema: *Sema,
+ block: *Block,
+ src: LazySrcLoc,
+ lhs: Value,
+ rhs: Value,
+ ty: Type,
+) !Value.OverflowArithmeticResult {
+ const target = sema.mod.getTarget();
+ const info = ty.intInfo(target);
+
+ var lhs_space: Value.BigIntSpace = undefined;
+ var rhs_space: Value.BigIntSpace = undefined;
+ const lhs_bigint = try lhs.toBigIntAdvanced(&lhs_space, target, sema.kit(block, src));
+ const rhs_bigint = try rhs.toBigIntAdvanced(&rhs_space, target, sema.kit(block, src));
+ const limbs = try sema.arena.alloc(
+ std.math.big.Limb,
+ std.math.big.int.calcTwosCompLimbCount(info.bits),
+ );
+ var result_bigint = std.math.big.int.Mutable{ .limbs = limbs, .positive = undefined, .len = undefined };
+ const overflowed = result_bigint.subWrap(lhs_bigint, rhs_bigint, info.signedness, info.bits);
+ const wrapped_result = try Value.fromBigInt(sema.arena, result_bigint.toConst());
+ return Value.OverflowArithmeticResult{
+ .overflowed = Value.makeBool(overflowed),
+ .wrapped_result = wrapped_result,
+ };
+}
+
+fn floatToInt(
+ sema: *Sema,
+ block: *Block,
+ src: LazySrcLoc,
+ val: Value,
+ float_ty: Type,
+ int_ty: Type,
+) CompileError!Value {
+ if (float_ty.zigTypeTag() == .Vector) {
+ const elem_ty = float_ty.childType();
+ const result_data = try sema.arena.alloc(Value, float_ty.vectorLen());
+ for (result_data) |*scalar, i| {
+ scalar.* = try sema.floatToIntScalar(block, src, val.indexVectorlike(i), elem_ty, int_ty.scalarType());
+ }
+ return Value.Tag.aggregate.create(sema.arena, result_data);
+ }
+ return sema.floatToIntScalar(block, src, val, float_ty, int_ty);
+}
+
+fn floatToIntScalar(
+ sema: *Sema,
+ block: *Block,
+ src: LazySrcLoc,
+ val: Value,
+ float_ty: Type,
+ int_ty: Type,
+) CompileError!Value {
+ const Limb = std.math.big.Limb;
+
+ const float = val.toFloat(f128);
+ if (std.math.isNan(float)) {
+ return sema.fail(block, src, "float value NaN cannot be stored in integer type '{}'", .{
+ int_ty.fmt(sema.mod),
+ });
+ }
+ if (std.math.isInf(float)) {
+ return sema.fail(block, src, "float value Inf cannot be stored in integer type '{}'", .{
+ int_ty.fmt(sema.mod),
+ });
+ }
+
+ const is_negative = std.math.signbit(float);
+ const floored = @floor(@fabs(float));
+
+ var rational = try std.math.big.Rational.init(sema.arena);
+ defer rational.deinit();
+ rational.setFloat(f128, floored) catch |err| switch (err) {
+ error.NonFiniteFloat => unreachable,
+ error.OutOfMemory => return error.OutOfMemory,
+ };
+
+ // The float is reduced in rational.setFloat, so we assert that denominator is equal to one
+ const big_one = std.math.big.int.Const{ .limbs = &.{1}, .positive = true };
+ assert(rational.q.toConst().eqAbs(big_one));
+
+ const result_limbs = try sema.arena.dupe(Limb, rational.p.toConst().limbs);
+ const result = if (is_negative)
+ try Value.Tag.int_big_negative.create(sema.arena, result_limbs)
+ else
+ try Value.Tag.int_big_positive.create(sema.arena, result_limbs);
+
+ if (!(try sema.intFitsInType(block, src, result, int_ty, null))) {
+ return sema.fail(block, src, "float value '{}' cannot be stored in integer type '{}'", .{
+ val.fmtValue(float_ty, sema.mod), int_ty.fmt(sema.mod),
+ });
+ }
+ return result;
+}
+
+/// Asserts the value is an integer, and the destination type is ComptimeInt or Int.
+/// Vectors are also accepted. Vector results are reduced with AND.
+fn intFitsInType(
+ sema: *Sema,
+ block: *Block,
+ src: LazySrcLoc,
+ self: Value,
+ ty: Type,
+ vector_index: ?*usize,
+) CompileError!bool {
+ const target = sema.mod.getTarget();
+ switch (self.tag()) {
+ .zero,
+ .undef,
+ .bool_false,
+ => return true,
+
+ .one,
+ .bool_true,
+ => switch (ty.zigTypeTag()) {
+ .Int => {
+ const info = ty.intInfo(target);
+ return switch (info.signedness) {
+ .signed => info.bits >= 2,
+ .unsigned => info.bits >= 1,
+ };
+ },
+ .ComptimeInt => return true,
+ else => unreachable,
+ },
+
+ .lazy_align => {
+ const info = ty.intInfo(target);
+ const max_needed_bits = @as(u16, 16) + @boolToInt(info.signedness == .signed);
+ // If it is u16 or bigger we know the alignment fits without resolving it.
+ if (info.bits >= max_needed_bits) return true;
+ const x = try sema.typeAbiAlignment(block, src, self.castTag(.lazy_align).?.data);
+ if (x == 0) return true;
+ const actual_needed_bits = std.math.log2(x) + 1 + @boolToInt(info.signedness == .signed);
+ return info.bits >= actual_needed_bits;
+ },
+ .lazy_size => {
+ const info = ty.intInfo(target);
+ const max_needed_bits = @as(u16, 64) + @boolToInt(info.signedness == .signed);
+ // If it is u64 or bigger we know the size fits without resolving it.
+ if (info.bits >= max_needed_bits) return true;
+ const x = try sema.typeAbiSize(block, src, self.castTag(.lazy_size).?.data);
+ if (x == 0) return true;
+ const actual_needed_bits = std.math.log2(x) + 1 + @boolToInt(info.signedness == .signed);
+ return info.bits >= actual_needed_bits;
+ },
+
+ .int_u64 => switch (ty.zigTypeTag()) {
+ .Int => {
+ const x = self.castTag(.int_u64).?.data;
+ if (x == 0) return true;
+ const info = ty.intInfo(target);
+ const needed_bits = std.math.log2(x) + 1 + @boolToInt(info.signedness == .signed);
+ return info.bits >= needed_bits;
+ },
+ .ComptimeInt => return true,
+ else => unreachable,
+ },
+ .int_i64 => switch (ty.zigTypeTag()) {
+ .Int => {
+ const x = self.castTag(.int_i64).?.data;
+ if (x == 0) return true;
+ const info = ty.intInfo(target);
+ if (info.signedness == .unsigned and x < 0)
+ return false;
+ var buffer: Value.BigIntSpace = undefined;
+ return (try self.toBigIntAdvanced(&buffer, target, sema.kit(block, src))).fitsInTwosComp(info.signedness, info.bits);
+ },
+ .ComptimeInt => return true,
+ else => unreachable,
+ },
+ .int_big_positive => switch (ty.zigTypeTag()) {
+ .Int => {
+ const info = ty.intInfo(target);
+ return self.castTag(.int_big_positive).?.asBigInt().fitsInTwosComp(info.signedness, info.bits);
+ },
+ .ComptimeInt => return true,
+ else => unreachable,
+ },
+ .int_big_negative => switch (ty.zigTypeTag()) {
+ .Int => {
+ const info = ty.intInfo(target);
+ return self.castTag(.int_big_negative).?.asBigInt().fitsInTwosComp(info.signedness, info.bits);
+ },
+ .ComptimeInt => return true,
+ else => unreachable,
+ },
+
+ .the_only_possible_value => {
+ assert(ty.intInfo(target).bits == 0);
+ return true;
+ },
+
+ .decl_ref_mut,
+ .extern_fn,
+ .decl_ref,
+ .function,
+ .variable,
+ => switch (ty.zigTypeTag()) {
+ .Int => {
+ const info = ty.intInfo(target);
+ const ptr_bits = target.cpu.arch.ptrBitWidth();
+ return switch (info.signedness) {
+ .signed => info.bits > ptr_bits,
+ .unsigned => info.bits >= ptr_bits,
+ };
+ },
+ .ComptimeInt => return true,
+ else => unreachable,
+ },
+
+ .aggregate => {
+ assert(ty.zigTypeTag() == .Vector);
+ for (self.castTag(.aggregate).?.data) |elem, i| {
+ if (!(try sema.intFitsInType(block, src, elem, ty.scalarType(), null))) {
+ if (vector_index) |some| some.* = i;
+ return false;
+ }
+ }
+ return true;
+ },
+
+ else => unreachable,
+ }
+}
+
+fn intInRange(
+ sema: *Sema,
+ block: *Block,
+ src: LazySrcLoc,
+ tag_ty: Type,
+ int_val: Value,
+ end: usize,
+) !bool {
+ if (try int_val.compareWithZeroAdvanced(.lt, sema.kit(block, src))) return false;
+ var end_payload: Value.Payload.U64 = .{
+ .base = .{ .tag = .int_u64 },
+ .data = end,
+ };
+ const end_val = Value.initPayload(&end_payload.base);
+ if (try sema.compare(block, src, int_val, .gte, end_val, tag_ty)) return false;
+ return true;
+}
+
+/// Asserts the type is an enum.
+fn enumHasInt(
+ sema: *Sema,
+ block: *Block,
+ src: LazySrcLoc,
+ ty: Type,
+ int: Value,
+) CompileError!bool {
+ switch (ty.tag()) {
+ .enum_nonexhaustive => return sema.intFitsInType(block, src, int, ty, null),
+ .enum_full => {
+ const enum_full = ty.castTag(.enum_full).?.data;
+ const tag_ty = enum_full.tag_ty;
+ if (enum_full.values.count() == 0) {
+ return intInRange(sema, block, src, tag_ty, int, enum_full.fields.count());
+ } else {
+ return enum_full.values.containsContext(int, .{
+ .ty = tag_ty,
+ .mod = sema.mod,
+ });
+ }
+ },
+ .enum_numbered => {
+ const enum_obj = ty.castTag(.enum_numbered).?.data;
+ const tag_ty = enum_obj.tag_ty;
+ if (enum_obj.values.count() == 0) {
+ return intInRange(sema, block, src, tag_ty, int, enum_obj.fields.count());
+ } else {
+ return enum_obj.values.containsContext(int, .{
+ .ty = tag_ty,
+ .mod = sema.mod,
+ });
+ }
+ },
+ .enum_simple => {
+ const enum_simple = ty.castTag(.enum_simple).?.data;
+ const fields_len = enum_simple.fields.count();
+ const bits = std.math.log2_int_ceil(usize, fields_len);
+ var buffer: Type.Payload.Bits = .{
+ .base = .{ .tag = .int_unsigned },
+ .data = bits,
+ };
+ const tag_ty = Type.initPayload(&buffer.base);
+ return intInRange(sema, block, src, tag_ty, int, fields_len);
+ },
+ .atomic_order,
+ .atomic_rmw_op,
+ .calling_convention,
+ .address_space,
+ .float_mode,
+ .reduce_op,
+ .call_options,
+ .prefetch_options,
+ .export_options,
+ .extern_options,
+ => unreachable,
+
+ else => unreachable,
+ }
+}
+
+fn intAddWithOverflow(
+ sema: *Sema,
+ block: *Block,
+ src: LazySrcLoc,
+ lhs: Value,
+ rhs: Value,
+ ty: Type,
+) !Value.OverflowArithmeticResult {
+ if (ty.zigTypeTag() == .Vector) {
+ const overflowed_data = try sema.arena.alloc(Value, ty.vectorLen());
+ const result_data = try sema.arena.alloc(Value, ty.vectorLen());
+ for (result_data) |*scalar, i| {
+ const of_math_result = try sema.intAddWithOverflowScalar(block, src, lhs.indexVectorlike(i), rhs.indexVectorlike(i), ty.scalarType());
+ overflowed_data[i] = of_math_result.overflowed;
+ scalar.* = of_math_result.wrapped_result;
+ }
+ return Value.OverflowArithmeticResult{
+ .overflowed = try Value.Tag.aggregate.create(sema.arena, overflowed_data),
+ .wrapped_result = try Value.Tag.aggregate.create(sema.arena, result_data),
+ };
+ }
+ return sema.intAddWithOverflowScalar(block, src, lhs, rhs, ty);
+}
+
+fn intAddWithOverflowScalar(
+ sema: *Sema,
+ block: *Block,
+ src: LazySrcLoc,
+ lhs: Value,
+ rhs: Value,
+ ty: Type,
+) !Value.OverflowArithmeticResult {
+ const target = sema.mod.getTarget();
+ const info = ty.intInfo(target);
+
+ var lhs_space: Value.BigIntSpace = undefined;
+ var rhs_space: Value.BigIntSpace = undefined;
+ const lhs_bigint = try lhs.toBigIntAdvanced(&lhs_space, target, sema.kit(block, src));
+ const rhs_bigint = try rhs.toBigIntAdvanced(&rhs_space, target, sema.kit(block, src));
+ const limbs = try sema.arena.alloc(
+ std.math.big.Limb,
+ std.math.big.int.calcTwosCompLimbCount(info.bits),
+ );
+ var result_bigint = std.math.big.int.Mutable{ .limbs = limbs, .positive = undefined, .len = undefined };
+ const overflowed = result_bigint.addWrap(lhs_bigint, rhs_bigint, info.signedness, info.bits);
+ const result = try Value.fromBigInt(sema.arena, result_bigint.toConst());
+ return Value.OverflowArithmeticResult{
+ .overflowed = Value.makeBool(overflowed),
+ .wrapped_result = result,
+ };
+}
+
+/// Asserts the values are comparable. Both operands have type `ty`.
+/// Vector results will be reduced with AND.
+fn compare(
+ sema: *Sema,
+ block: *Block,
+ src: LazySrcLoc,
+ lhs: Value,
+ op: std.math.CompareOperator,
+ rhs: Value,
+ ty: Type,
+) CompileError!bool {
+ if (ty.zigTypeTag() == .Vector) {
+ var i: usize = 0;
+ while (i < ty.vectorLen()) : (i += 1) {
+ if (!(try sema.compareScalar(block, src, lhs.indexVectorlike(i), op, rhs.indexVectorlike(i), ty.scalarType()))) {
+ return false;
+ }
+ }
+ return true;
+ }
+ return sema.compareScalar(block, src, lhs, op, rhs, ty);
+}
+
+/// Asserts the values are comparable. Both operands have type `ty`.
+fn compareScalar(
+ sema: *Sema,
+ block: *Block,
+ src: LazySrcLoc,
+ lhs: Value,
+ op: std.math.CompareOperator,
+ rhs: Value,
+ ty: Type,
+) CompileError!bool {
+ switch (op) {
+ .eq => return sema.valuesEqual(block, src, lhs, rhs, ty),
+ .neq => return !(try sema.valuesEqual(block, src, lhs, rhs, ty)),
+ else => return Value.compareHeteroAdvanced(lhs, op, rhs, sema.mod.getTarget(), sema.kit(block, src)),
+ }
+}
+
+fn valuesEqual(
+ sema: *Sema,
+ block: *Block,
+ src: LazySrcLoc,
+ lhs: Value,
+ rhs: Value,
+ ty: Type,
+) CompileError!bool {
+ return Value.eqlAdvanced(lhs, rhs, ty, sema.mod, sema.kit(block, src));
+}
+
+/// Asserts the values are comparable vectors of type `ty`.
+fn compareVector(
+ sema: *Sema,
+ block: *Block,
+ src: LazySrcLoc,
+ lhs: Value,
+ op: std.math.CompareOperator,
+ rhs: Value,
+ ty: Type,
+) !Value {
+ assert(ty.zigTypeTag() == .Vector);
+ const result_data = try sema.arena.alloc(Value, ty.vectorLen());
+ for (result_data) |*scalar, i| {
+ const res_bool = try sema.compareScalar(block, src, lhs.indexVectorlike(i), op, rhs.indexVectorlike(i), ty.scalarType());
+ scalar.* = Value.makeBool(res_bool);
+ }
+ return Value.Tag.aggregate.create(sema.arena, result_data);
+}
+
+/// Returns the type of a pointer to an element.
+/// Asserts that the type is a pointer, and that the element type is indexable.
+/// For *[N]T, return *T
+/// For [*]T, returns *T
+/// For []T, returns *T
+/// Handles const-ness and address spaces in particular.
+/// This code is duplicated in `analyzePtrArithmetic`.
+fn elemPtrType(sema: *Sema, ptr_ty: Type, offset: ?usize) !Type {
+ const ptr_info = ptr_ty.ptrInfo().data;
+ const elem_ty = ptr_ty.elemType2();
+ const allow_zero = ptr_info.@"allowzero" and (offset orelse 0) == 0;
+ const alignment: u32 = a: {
+ // Calculate the new pointer alignment.
+ if (ptr_info.@"align" == 0) {
+ // ABI-aligned pointer. Any pointer arithmetic maintains the same ABI-alignedness.
+ break :a 0;
+ }
+ // If the addend is not a comptime-known value we can still count on
+ // it being a multiple of the type size.
+ const target = sema.mod.getTarget();
+ const elem_size = elem_ty.abiSize(target);
+ const addend = if (offset) |off| elem_size * off else elem_size;
+
+ // The resulting pointer is aligned to the lcd between the offset (an
+ // arbitrary number) and the alignment factor (always a power of two,
+ // non zero).
+ const new_align = @as(u32, 1) << @intCast(u5, @ctz(u64, addend | ptr_info.@"align"));
+ break :a new_align;
+ };
+ return try Type.ptr(sema.arena, sema.mod, .{
+ .pointee_type = elem_ty,
+ .mutable = ptr_info.mutable,
+ .@"addrspace" = ptr_info.@"addrspace",
+ .@"allowzero" = allow_zero,
+ .@"volatile" = ptr_info.@"volatile",
+ .@"align" = alignment,
+ });
+}