mirror of
https://github.com/ziglang/zig.git
synced 2025-12-06 06:13:07 +00:00
Merge pull request #22777 from mlugg/some-bugs
Fix a bunch of frontend bugs
This commit is contained in:
commit
43e52ec5c5
@ -920,7 +920,10 @@ fn expr(gz: *GenZir, scope: *Scope, ri: ResultInfo, node: Ast.Node.Index) InnerE
|
|||||||
const cursor = maybeAdvanceSourceCursorToMainToken(gz, node);
|
const cursor = maybeAdvanceSourceCursorToMainToken(gz, node);
|
||||||
const start = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, full.ast.start);
|
const start = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, full.ast.start);
|
||||||
const end = if (full.ast.end != 0) try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, full.ast.end) else .none;
|
const end = if (full.ast.end != 0) try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, full.ast.end) else .none;
|
||||||
const sentinel = if (full.ast.sentinel != 0) try expr(gz, scope, .{ .rl = .none }, full.ast.sentinel) else .none;
|
const sentinel = if (full.ast.sentinel != 0) s: {
|
||||||
|
const sentinel_ty = try gz.addUnNode(.slice_sentinel_ty, lhs, node);
|
||||||
|
break :s try expr(gz, scope, .{ .rl = .{ .coerced_ty = sentinel_ty } }, full.ast.sentinel);
|
||||||
|
} else .none;
|
||||||
try emitDbgStmt(gz, cursor);
|
try emitDbgStmt(gz, cursor);
|
||||||
if (sentinel != .none) {
|
if (sentinel != .none) {
|
||||||
const result = try gz.addPlNode(.slice_sentinel, node, Zir.Inst.SliceSentinel{
|
const result = try gz.addPlNode(.slice_sentinel, node, Zir.Inst.SliceSentinel{
|
||||||
@ -2855,6 +2858,7 @@ fn addEnsureResult(gz: *GenZir, maybe_unused_result: Zir.Inst.Ref, statement: As
|
|||||||
.slice_end,
|
.slice_end,
|
||||||
.slice_sentinel,
|
.slice_sentinel,
|
||||||
.slice_length,
|
.slice_length,
|
||||||
|
.slice_sentinel_ty,
|
||||||
.import,
|
.import,
|
||||||
.switch_block,
|
.switch_block,
|
||||||
.switch_block_ref,
|
.switch_block_ref,
|
||||||
|
|||||||
@ -599,6 +599,10 @@ pub const Inst = struct {
|
|||||||
/// Returns a pointer to the subslice.
|
/// Returns a pointer to the subslice.
|
||||||
/// Uses the `pl_node` field. AST node is the slice syntax. Payload is `SliceLength`.
|
/// Uses the `pl_node` field. AST node is the slice syntax. Payload is `SliceLength`.
|
||||||
slice_length,
|
slice_length,
|
||||||
|
/// Given a value which is a pointer to the LHS of a slice operation, return the sentinel
|
||||||
|
/// type, used as the result type of the slice sentinel (i.e. `s` in `lhs[a..b :s]`).
|
||||||
|
/// Uses the `un_node` field. AST node is the slice syntax. Operand is `lhs`.
|
||||||
|
slice_sentinel_ty,
|
||||||
/// Same as `store` except provides a source location.
|
/// Same as `store` except provides a source location.
|
||||||
/// Uses the `pl_node` union field. Payload is `Bin`.
|
/// Uses the `pl_node` union field. Payload is `Bin`.
|
||||||
store_node,
|
store_node,
|
||||||
@ -1185,6 +1189,7 @@ pub const Inst = struct {
|
|||||||
.slice_end,
|
.slice_end,
|
||||||
.slice_sentinel,
|
.slice_sentinel,
|
||||||
.slice_length,
|
.slice_length,
|
||||||
|
.slice_sentinel_ty,
|
||||||
.import,
|
.import,
|
||||||
.typeof_log2_int_type,
|
.typeof_log2_int_type,
|
||||||
.resolve_inferred_alloc,
|
.resolve_inferred_alloc,
|
||||||
@ -1472,6 +1477,7 @@ pub const Inst = struct {
|
|||||||
.slice_end,
|
.slice_end,
|
||||||
.slice_sentinel,
|
.slice_sentinel,
|
||||||
.slice_length,
|
.slice_length,
|
||||||
|
.slice_sentinel_ty,
|
||||||
.import,
|
.import,
|
||||||
.typeof_log2_int_type,
|
.typeof_log2_int_type,
|
||||||
.switch_block,
|
.switch_block,
|
||||||
@ -1702,6 +1708,7 @@ pub const Inst = struct {
|
|||||||
.slice_end = .pl_node,
|
.slice_end = .pl_node,
|
||||||
.slice_sentinel = .pl_node,
|
.slice_sentinel = .pl_node,
|
||||||
.slice_length = .pl_node,
|
.slice_length = .pl_node,
|
||||||
|
.slice_sentinel_ty = .un_node,
|
||||||
.store_node = .pl_node,
|
.store_node = .pl_node,
|
||||||
.store_to_inferred_ptr = .pl_node,
|
.store_to_inferred_ptr = .pl_node,
|
||||||
.str = .str,
|
.str = .str,
|
||||||
@ -4162,6 +4169,7 @@ fn findTrackableInner(
|
|||||||
.slice_end,
|
.slice_end,
|
||||||
.slice_sentinel,
|
.slice_sentinel,
|
||||||
.slice_length,
|
.slice_length,
|
||||||
|
.slice_sentinel_ty,
|
||||||
.store_node,
|
.store_node,
|
||||||
.store_to_inferred_ptr,
|
.store_to_inferred_ptr,
|
||||||
.str,
|
.str,
|
||||||
|
|||||||
@ -2196,6 +2196,8 @@ pub fn update(comp: *Compilation, main_progress_node: std.Progress.Node) !void {
|
|||||||
|
|
||||||
zcu.compile_log_text.shrinkAndFree(gpa, 0);
|
zcu.compile_log_text.shrinkAndFree(gpa, 0);
|
||||||
|
|
||||||
|
zcu.skip_analysis_errors = false;
|
||||||
|
|
||||||
// Make sure std.zig is inside the import_table. We unconditionally need
|
// Make sure std.zig is inside the import_table. We unconditionally need
|
||||||
// it for start.zig.
|
// it for start.zig.
|
||||||
const std_mod = zcu.std_mod;
|
const std_mod = zcu.std_mod;
|
||||||
@ -3208,7 +3210,7 @@ pub fn getAllErrorsAlloc(comp: *Compilation) !ErrorBundle {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (comp.zcu) |zcu| {
|
if (comp.zcu) |zcu| zcu_errors: {
|
||||||
for (zcu.failed_files.keys(), zcu.failed_files.values()) |file, error_msg| {
|
for (zcu.failed_files.keys(), zcu.failed_files.values()) |file, error_msg| {
|
||||||
if (error_msg) |msg| {
|
if (error_msg) |msg| {
|
||||||
try addModuleErrorMsg(zcu, &bundle, msg.*);
|
try addModuleErrorMsg(zcu, &bundle, msg.*);
|
||||||
@ -3225,6 +3227,7 @@ pub fn getAllErrorsAlloc(comp: *Compilation) !ErrorBundle {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (zcu.skip_analysis_errors) break :zcu_errors;
|
||||||
var sorted_failed_analysis: std.AutoArrayHashMapUnmanaged(InternPool.AnalUnit, *Zcu.ErrorMsg).DataList.Slice = s: {
|
var sorted_failed_analysis: std.AutoArrayHashMapUnmanaged(InternPool.AnalUnit, *Zcu.ErrorMsg).DataList.Slice = s: {
|
||||||
const SortOrder = struct {
|
const SortOrder = struct {
|
||||||
zcu: *Zcu,
|
zcu: *Zcu,
|
||||||
@ -3360,7 +3363,7 @@ pub fn getAllErrorsAlloc(comp: *Compilation) !ErrorBundle {
|
|||||||
try comp.link_diags.addMessagesToBundle(&bundle, comp.bin_file);
|
try comp.link_diags.addMessagesToBundle(&bundle, comp.bin_file);
|
||||||
|
|
||||||
if (comp.zcu) |zcu| {
|
if (comp.zcu) |zcu| {
|
||||||
if (bundle.root_list.items.len == 0 and zcu.compile_log_sources.count() != 0) {
|
if (!zcu.skip_analysis_errors and bundle.root_list.items.len == 0 and zcu.compile_log_sources.count() != 0) {
|
||||||
const values = zcu.compile_log_sources.values();
|
const values = zcu.compile_log_sources.values();
|
||||||
// First one will be the error; subsequent ones will be notes.
|
// First one will be the error; subsequent ones will be notes.
|
||||||
const src_loc = values[0].src();
|
const src_loc = values[0].src();
|
||||||
@ -3861,10 +3864,9 @@ fn performAllTheWorkInner(
|
|||||||
// We give up right now! No updating of ZIR refs, no nothing. The idea is that this prevents
|
// We give up right now! No updating of ZIR refs, no nothing. The idea is that this prevents
|
||||||
// us from invalidating lots of incremental dependencies due to files with e.g. parse errors.
|
// us from invalidating lots of incremental dependencies due to files with e.g. parse errors.
|
||||||
// However, this means our analysis data is invalid, so we want to omit all analysis errors.
|
// However, this means our analysis data is invalid, so we want to omit all analysis errors.
|
||||||
// To do that, let's just clear the analysis roots!
|
|
||||||
|
|
||||||
assert(zcu.failed_files.count() > 0); // we will get an error
|
assert(zcu.failed_files.count() > 0); // we will get an error
|
||||||
zcu.analysis_roots.clear(); // no analysis happened
|
zcu.skip_analysis_errors = true;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -4011,7 +4011,7 @@ pub const LoadedStructType = struct {
|
|||||||
|
|
||||||
pub fn haveFieldTypes(s: LoadedStructType, ip: *const InternPool) bool {
|
pub fn haveFieldTypes(s: LoadedStructType, ip: *const InternPool) bool {
|
||||||
const types = s.field_types.get(ip);
|
const types = s.field_types.get(ip);
|
||||||
return types.len == 0 or types[0] != .none;
|
return types.len == 0 or types[types.len - 1] != .none;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn haveFieldInits(s: LoadedStructType, ip: *const InternPool) bool {
|
pub fn haveFieldInits(s: LoadedStructType, ip: *const InternPool) bool {
|
||||||
|
|||||||
236
src/Sema.zig
236
src/Sema.zig
@ -504,7 +504,17 @@ pub const Block = struct {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn wantSafety(block: *const Block) bool {
|
fn wantSafeTypes(block: *const Block) bool {
|
||||||
|
return block.want_safety orelse switch (block.sema.pt.zcu.optimizeMode()) {
|
||||||
|
.Debug => true,
|
||||||
|
.ReleaseSafe => true,
|
||||||
|
.ReleaseFast => false,
|
||||||
|
.ReleaseSmall => false,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
fn wantSafety(block: *const Block) bool {
|
||||||
|
if (block.isComptime()) return false; // runtime safety checks are pointless in comptime blocks
|
||||||
return block.want_safety orelse switch (block.sema.pt.zcu.optimizeMode()) {
|
return block.want_safety orelse switch (block.sema.pt.zcu.optimizeMode()) {
|
||||||
.Debug => true,
|
.Debug => true,
|
||||||
.ReleaseSafe => true,
|
.ReleaseSafe => true,
|
||||||
@ -1197,6 +1207,7 @@ fn analyzeBodyInner(
|
|||||||
.slice_sentinel => try sema.zirSliceSentinel(block, inst),
|
.slice_sentinel => try sema.zirSliceSentinel(block, inst),
|
||||||
.slice_start => try sema.zirSliceStart(block, inst),
|
.slice_start => try sema.zirSliceStart(block, inst),
|
||||||
.slice_length => try sema.zirSliceLength(block, inst),
|
.slice_length => try sema.zirSliceLength(block, inst),
|
||||||
|
.slice_sentinel_ty => try sema.zirSliceSentinelTy(block, inst),
|
||||||
.str => try sema.zirStr(inst),
|
.str => try sema.zirStr(inst),
|
||||||
.switch_block => try sema.zirSwitchBlock(block, inst, false),
|
.switch_block => try sema.zirSwitchBlock(block, inst, false),
|
||||||
.switch_block_ref => try sema.zirSwitchBlock(block, inst, true),
|
.switch_block_ref => try sema.zirSwitchBlock(block, inst, true),
|
||||||
@ -3293,7 +3304,7 @@ fn zirUnionDecl(
|
|||||||
.tagged
|
.tagged
|
||||||
else if (small.layout != .auto)
|
else if (small.layout != .auto)
|
||||||
.none
|
.none
|
||||||
else switch (block.wantSafety()) {
|
else switch (block.wantSafeTypes()) {
|
||||||
true => .safety,
|
true => .safety,
|
||||||
false => .none,
|
false => .none,
|
||||||
},
|
},
|
||||||
@ -9144,6 +9155,7 @@ fn zirErrUnionCode(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileErro
|
|||||||
return sema.analyzeErrUnionCode(block, src, operand);
|
return sema.analyzeErrUnionCode(block, src, operand);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// If `operand` is comptime-known, asserts that it is an error value rather than a payload value.
|
||||||
fn analyzeErrUnionCode(sema: *Sema, block: *Block, src: LazySrcLoc, operand: Air.Inst.Ref) CompileError!Air.Inst.Ref {
|
fn analyzeErrUnionCode(sema: *Sema, block: *Block, src: LazySrcLoc, operand: Air.Inst.Ref) CompileError!Air.Inst.Ref {
|
||||||
const pt = sema.pt;
|
const pt = sema.pt;
|
||||||
const zcu = pt.zcu;
|
const zcu = pt.zcu;
|
||||||
@ -10753,6 +10765,46 @@ fn zirSliceLength(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
|
|||||||
return sema.analyzeSlice(block, src, array_ptr, start, len, sentinel, sentinel_src, ptr_src, start_src, end_src, true);
|
return sema.analyzeSlice(block, src, array_ptr, start, len, sentinel, sentinel_src, ptr_src, start_src, end_src, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn zirSliceSentinelTy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
|
||||||
|
const tracy = trace(@src());
|
||||||
|
defer tracy.end();
|
||||||
|
|
||||||
|
const pt = sema.pt;
|
||||||
|
const zcu = pt.zcu;
|
||||||
|
|
||||||
|
const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
|
||||||
|
|
||||||
|
const src = block.nodeOffset(inst_data.src_node);
|
||||||
|
const ptr_src = block.src(.{ .node_offset_slice_ptr = inst_data.src_node });
|
||||||
|
const sentinel_src = block.src(.{ .node_offset_slice_sentinel = inst_data.src_node });
|
||||||
|
|
||||||
|
// This is like the logic in `analyzeSlice`; since we've evaluated the LHS as an lvalue, we will
|
||||||
|
// have a double pointer if it was already a pointer.
|
||||||
|
|
||||||
|
const lhs_ptr_ty = sema.typeOf(try sema.resolveInst(inst_data.operand));
|
||||||
|
const lhs_ty = switch (lhs_ptr_ty.zigTypeTag(zcu)) {
|
||||||
|
.pointer => lhs_ptr_ty.childType(zcu),
|
||||||
|
else => return sema.fail(block, ptr_src, "expected pointer, found '{}'", .{lhs_ptr_ty.fmt(pt)}),
|
||||||
|
};
|
||||||
|
|
||||||
|
const sentinel_ty: Type = switch (lhs_ty.zigTypeTag(zcu)) {
|
||||||
|
.array => lhs_ty.childType(zcu),
|
||||||
|
.pointer => switch (lhs_ty.ptrSize(zcu)) {
|
||||||
|
.many, .c, .slice => lhs_ty.childType(zcu),
|
||||||
|
.one => s: {
|
||||||
|
const lhs_elem_ty = lhs_ty.childType(zcu);
|
||||||
|
break :s switch (lhs_elem_ty.zigTypeTag(zcu)) {
|
||||||
|
.array => lhs_elem_ty.childType(zcu), // array element type
|
||||||
|
else => return sema.fail(block, sentinel_src, "slice of single-item pointer cannot have sentinel", .{}),
|
||||||
|
};
|
||||||
|
},
|
||||||
|
},
|
||||||
|
else => return sema.fail(block, src, "slice of non-array type '{}'", .{lhs_ty.fmt(pt)}),
|
||||||
|
};
|
||||||
|
|
||||||
|
return Air.internedToRef(sentinel_ty.toIntern());
|
||||||
|
}
|
||||||
|
|
||||||
/// Holds common data used when analyzing or resolving switch prong bodies,
|
/// Holds common data used when analyzing or resolving switch prong bodies,
|
||||||
/// including setting up captures.
|
/// including setting up captures.
|
||||||
const SwitchProngAnalysis = struct {
|
const SwitchProngAnalysis = struct {
|
||||||
@ -17558,10 +17610,16 @@ fn analyzeCmp(
|
|||||||
return sema.cmpNumeric(block, src, lhs, rhs, op, lhs_src, rhs_src);
|
return sema.cmpNumeric(block, src, lhs, rhs, op, lhs_src, rhs_src);
|
||||||
}
|
}
|
||||||
if (is_equality_cmp and lhs_ty.zigTypeTag(zcu) == .error_union and rhs_ty.zigTypeTag(zcu) == .error_set) {
|
if (is_equality_cmp and lhs_ty.zigTypeTag(zcu) == .error_union and rhs_ty.zigTypeTag(zcu) == .error_set) {
|
||||||
|
if (try sema.resolveDefinedValue(block, lhs_src, lhs)) |lhs_val| {
|
||||||
|
if (lhs_val.errorUnionIsPayload(zcu)) return .bool_false;
|
||||||
|
}
|
||||||
const casted_lhs = try sema.analyzeErrUnionCode(block, lhs_src, lhs);
|
const casted_lhs = try sema.analyzeErrUnionCode(block, lhs_src, lhs);
|
||||||
return sema.cmpSelf(block, src, casted_lhs, rhs, op, lhs_src, rhs_src);
|
return sema.cmpSelf(block, src, casted_lhs, rhs, op, lhs_src, rhs_src);
|
||||||
}
|
}
|
||||||
if (is_equality_cmp and lhs_ty.zigTypeTag(zcu) == .error_set and rhs_ty.zigTypeTag(zcu) == .error_union) {
|
if (is_equality_cmp and lhs_ty.zigTypeTag(zcu) == .error_set and rhs_ty.zigTypeTag(zcu) == .error_union) {
|
||||||
|
if (try sema.resolveDefinedValue(block, rhs_src, rhs)) |rhs_val| {
|
||||||
|
if (rhs_val.errorUnionIsPayload(zcu)) return .bool_false;
|
||||||
|
}
|
||||||
const casted_rhs = try sema.analyzeErrUnionCode(block, rhs_src, rhs);
|
const casted_rhs = try sema.analyzeErrUnionCode(block, rhs_src, rhs);
|
||||||
return sema.cmpSelf(block, src, lhs, casted_rhs, op, lhs_src, rhs_src);
|
return sema.cmpSelf(block, src, lhs, casted_rhs, op, lhs_src, rhs_src);
|
||||||
}
|
}
|
||||||
@ -18087,10 +18145,16 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
|
|||||||
|
|
||||||
const ret_ty_opt = try pt.intern(.{ .opt = .{
|
const ret_ty_opt = try pt.intern(.{ .opt = .{
|
||||||
.ty = try pt.intern(.{ .opt_type = .type_type }),
|
.ty = try pt.intern(.{ .opt_type = .type_type }),
|
||||||
.val = if (func_ty_info.return_type == .generic_poison_type)
|
.val = opt_val: {
|
||||||
.none
|
const ret_ty: Type = .fromInterned(func_ty_info.return_type);
|
||||||
else
|
if (ret_ty.toIntern() == .generic_poison_type) break :opt_val .none;
|
||||||
func_ty_info.return_type,
|
if (ret_ty.zigTypeTag(zcu) == .error_union) {
|
||||||
|
if (ret_ty.errorUnionPayload(zcu).toIntern() == .generic_poison_type) {
|
||||||
|
break :opt_val .none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break :opt_val ret_ty.toIntern();
|
||||||
|
},
|
||||||
} });
|
} });
|
||||||
|
|
||||||
const callconv_ty = try sema.getBuiltinType(src, .CallingConvention);
|
const callconv_ty = try sema.getBuiltinType(src, .CallingConvention);
|
||||||
@ -21401,7 +21465,7 @@ fn zirTagName(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air
|
|||||||
try operand_ty.resolveLayout(pt);
|
try operand_ty.resolveLayout(pt);
|
||||||
const enum_ty = switch (operand_ty.zigTypeTag(zcu)) {
|
const enum_ty = switch (operand_ty.zigTypeTag(zcu)) {
|
||||||
.enum_literal => {
|
.enum_literal => {
|
||||||
const val = try sema.resolveConstDefinedValue(block, LazySrcLoc.unneeded, operand, undefined);
|
const val = (try sema.resolveDefinedValue(block, operand_src, operand)).?;
|
||||||
const tag_name = ip.indexToKey(val.toIntern()).enum_literal;
|
const tag_name = ip.indexToKey(val.toIntern()).enum_literal;
|
||||||
return sema.addNullTerminatedStrLit(tag_name);
|
return sema.addNullTerminatedStrLit(tag_name);
|
||||||
},
|
},
|
||||||
@ -22171,7 +22235,7 @@ fn reifyUnion(
|
|||||||
.tagged
|
.tagged
|
||||||
else if (layout != .auto)
|
else if (layout != .auto)
|
||||||
.none
|
.none
|
||||||
else switch (block.wantSafety()) {
|
else switch (block.wantSafeTypes()) {
|
||||||
true => .safety,
|
true => .safety,
|
||||||
false => .none,
|
false => .none,
|
||||||
},
|
},
|
||||||
@ -23117,11 +23181,12 @@ fn zirErrorCast(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData
|
|||||||
const extra = sema.code.extraData(Zir.Inst.BinNode, extended.operand).data;
|
const extra = sema.code.extraData(Zir.Inst.BinNode, extended.operand).data;
|
||||||
const src = block.nodeOffset(extra.node);
|
const src = block.nodeOffset(extra.node);
|
||||||
const operand_src = block.builtinCallArgSrc(extra.node, 0);
|
const operand_src = block.builtinCallArgSrc(extra.node, 0);
|
||||||
const base_dest_ty = try sema.resolveDestType(block, src, extra.lhs, .remove_opt, "@errorCast");
|
const dest_ty = try sema.resolveDestType(block, src, extra.lhs, .remove_opt, "@errorCast");
|
||||||
const operand = try sema.resolveInst(extra.rhs);
|
const operand = try sema.resolveInst(extra.rhs);
|
||||||
const base_operand_ty = sema.typeOf(operand);
|
const operand_ty = sema.typeOf(operand);
|
||||||
const dest_tag = base_dest_ty.zigTypeTag(zcu);
|
|
||||||
const operand_tag = base_operand_ty.zigTypeTag(zcu);
|
const dest_tag = dest_ty.zigTypeTag(zcu);
|
||||||
|
const operand_tag = operand_ty.zigTypeTag(zcu);
|
||||||
|
|
||||||
if (dest_tag != .error_set and dest_tag != .error_union) {
|
if (dest_tag != .error_set and dest_tag != .error_union) {
|
||||||
return sema.fail(block, src, "expected error set or error union type, found '{s}'", .{@tagName(dest_tag)});
|
return sema.fail(block, src, "expected error set or error union type, found '{s}'", .{@tagName(dest_tag)});
|
||||||
@ -23133,107 +23198,133 @@ fn zirErrorCast(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData
|
|||||||
return sema.fail(block, src, "cannot cast an error union type to error set", .{});
|
return sema.fail(block, src, "cannot cast an error union type to error set", .{});
|
||||||
}
|
}
|
||||||
if (dest_tag == .error_union and operand_tag == .error_union and
|
if (dest_tag == .error_union and operand_tag == .error_union and
|
||||||
base_dest_ty.errorUnionPayload(zcu).toIntern() != base_operand_ty.errorUnionPayload(zcu).toIntern())
|
dest_ty.errorUnionPayload(zcu).toIntern() != operand_ty.errorUnionPayload(zcu).toIntern())
|
||||||
{
|
{
|
||||||
return sema.failWithOwnedErrorMsg(block, msg: {
|
return sema.failWithOwnedErrorMsg(block, msg: {
|
||||||
const msg = try sema.errMsg(src, "payload types of error unions must match", .{});
|
const msg = try sema.errMsg(src, "payload types of error unions must match", .{});
|
||||||
errdefer msg.destroy(sema.gpa);
|
errdefer msg.destroy(sema.gpa);
|
||||||
const dest_ty = base_dest_ty.errorUnionPayload(zcu);
|
const dest_payload_ty = dest_ty.errorUnionPayload(zcu);
|
||||||
const operand_ty = base_operand_ty.errorUnionPayload(zcu);
|
const operand_payload_ty = operand_ty.errorUnionPayload(zcu);
|
||||||
try sema.errNote(src, msg, "destination payload is '{}'", .{dest_ty.fmt(pt)});
|
try sema.errNote(src, msg, "destination payload is '{}'", .{dest_payload_ty.fmt(pt)});
|
||||||
try sema.errNote(src, msg, "operand payload is '{}'", .{operand_ty.fmt(pt)});
|
try sema.errNote(src, msg, "operand payload is '{}'", .{operand_payload_ty.fmt(pt)});
|
||||||
try addDeclaredHereNote(sema, msg, dest_ty);
|
try addDeclaredHereNote(sema, msg, dest_ty);
|
||||||
try addDeclaredHereNote(sema, msg, operand_ty);
|
try addDeclaredHereNote(sema, msg, operand_ty);
|
||||||
break :msg msg;
|
break :msg msg;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
const dest_ty = if (dest_tag == .error_union) base_dest_ty.errorUnionSet(zcu) else base_dest_ty;
|
const dest_err_ty = switch (dest_tag) {
|
||||||
const operand_ty = if (operand_tag == .error_union) base_operand_ty.errorUnionSet(zcu) else base_operand_ty;
|
.error_union => dest_ty.errorUnionSet(zcu),
|
||||||
|
.error_set => dest_ty,
|
||||||
// operand must be defined since it can be an invalid error value
|
else => unreachable,
|
||||||
const maybe_operand_val = try sema.resolveDefinedValue(block, operand_src, operand);
|
};
|
||||||
|
const operand_err_ty = switch (operand_tag) {
|
||||||
|
.error_union => operand_ty.errorUnionSet(zcu),
|
||||||
|
.error_set => operand_ty,
|
||||||
|
else => unreachable,
|
||||||
|
};
|
||||||
|
|
||||||
const disjoint = disjoint: {
|
const disjoint = disjoint: {
|
||||||
// Try avoiding resolving inferred error sets if we can
|
// Try avoiding resolving inferred error sets if we can
|
||||||
if (!dest_ty.isAnyError(zcu) and dest_ty.errorSetIsEmpty(zcu)) break :disjoint true;
|
if (!dest_err_ty.isAnyError(zcu) and dest_err_ty.errorSetIsEmpty(zcu)) break :disjoint true;
|
||||||
if (!operand_ty.isAnyError(zcu) and operand_ty.errorSetIsEmpty(zcu)) break :disjoint true;
|
if (!operand_err_ty.isAnyError(zcu) and operand_err_ty.errorSetIsEmpty(zcu)) break :disjoint true;
|
||||||
if (dest_ty.isAnyError(zcu)) break :disjoint false;
|
if (dest_err_ty.isAnyError(zcu)) break :disjoint false;
|
||||||
if (operand_ty.isAnyError(zcu)) break :disjoint false;
|
if (operand_err_ty.isAnyError(zcu)) break :disjoint false;
|
||||||
const dest_err_names = dest_ty.errorSetNames(zcu);
|
const dest_err_names = dest_err_ty.errorSetNames(zcu);
|
||||||
for (0..dest_err_names.len) |dest_err_index| {
|
for (0..dest_err_names.len) |dest_err_index| {
|
||||||
if (Type.errorSetHasFieldIp(ip, operand_ty.toIntern(), dest_err_names.get(ip)[dest_err_index]))
|
if (Type.errorSetHasFieldIp(ip, operand_err_ty.toIntern(), dest_err_names.get(ip)[dest_err_index]))
|
||||||
break :disjoint false;
|
break :disjoint false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!ip.isInferredErrorSetType(dest_ty.toIntern()) and
|
if (!ip.isInferredErrorSetType(dest_err_ty.toIntern()) and
|
||||||
!ip.isInferredErrorSetType(operand_ty.toIntern()))
|
!ip.isInferredErrorSetType(operand_err_ty.toIntern()))
|
||||||
{
|
{
|
||||||
break :disjoint true;
|
break :disjoint true;
|
||||||
}
|
}
|
||||||
|
|
||||||
_ = try sema.resolveInferredErrorSetTy(block, src, dest_ty.toIntern());
|
_ = try sema.resolveInferredErrorSetTy(block, src, dest_err_ty.toIntern());
|
||||||
_ = try sema.resolveInferredErrorSetTy(block, operand_src, operand_ty.toIntern());
|
_ = try sema.resolveInferredErrorSetTy(block, operand_src, operand_err_ty.toIntern());
|
||||||
for (0..dest_err_names.len) |dest_err_index| {
|
for (0..dest_err_names.len) |dest_err_index| {
|
||||||
if (Type.errorSetHasFieldIp(ip, operand_ty.toIntern(), dest_err_names.get(ip)[dest_err_index]))
|
if (Type.errorSetHasFieldIp(ip, operand_err_ty.toIntern(), dest_err_names.get(ip)[dest_err_index]))
|
||||||
break :disjoint false;
|
break :disjoint false;
|
||||||
}
|
}
|
||||||
|
|
||||||
break :disjoint true;
|
break :disjoint true;
|
||||||
};
|
};
|
||||||
if (disjoint and dest_tag != .error_union) {
|
if (disjoint and !(operand_tag == .error_union and dest_tag == .error_union)) {
|
||||||
return sema.fail(block, src, "error sets '{}' and '{}' have no common errors", .{
|
return sema.fail(block, src, "error sets '{}' and '{}' have no common errors", .{
|
||||||
operand_ty.fmt(pt), dest_ty.fmt(pt),
|
operand_err_ty.fmt(pt), dest_err_ty.fmt(pt),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (maybe_operand_val) |val| {
|
// operand must be defined since it can be an invalid error value
|
||||||
if (!dest_ty.isAnyError(zcu)) check: {
|
if (try sema.resolveDefinedValue(block, operand_src, operand)) |operand_val| {
|
||||||
const operand_val = zcu.intern_pool.indexToKey(val.toIntern());
|
const err_name: InternPool.NullTerminatedString = switch (operand_tag) {
|
||||||
var error_name: InternPool.NullTerminatedString = undefined;
|
.error_set => ip.indexToKey(operand_val.toIntern()).err.name,
|
||||||
if (operand_tag == .error_union) {
|
.error_union => switch (ip.indexToKey(operand_val.toIntern()).error_union.val) {
|
||||||
if (operand_val.error_union.val != .err_name) break :check;
|
.err_name => |name| name,
|
||||||
error_name = operand_val.error_union.val.err_name;
|
.payload => |payload_val| {
|
||||||
} else {
|
assert(dest_tag == .error_union); // should be guaranteed from the type checks above
|
||||||
error_name = operand_val.err.name;
|
return sema.coerce(block, dest_ty, Air.internedToRef(payload_val), operand_src);
|
||||||
}
|
},
|
||||||
if (!Type.errorSetHasFieldIp(ip, dest_ty.toIntern(), error_name)) {
|
},
|
||||||
return sema.fail(block, src, "'error.{}' not a member of error set '{}'", .{
|
else => unreachable,
|
||||||
error_name.fmt(ip), dest_ty.fmt(pt),
|
};
|
||||||
});
|
|
||||||
}
|
if (!dest_err_ty.isAnyError(zcu) and !Type.errorSetHasFieldIp(ip, dest_err_ty.toIntern(), err_name)) {
|
||||||
|
return sema.fail(block, src, "'error.{}' not a member of error set '{}'", .{
|
||||||
|
err_name.fmt(ip), dest_err_ty.fmt(pt),
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return Air.internedToRef((try pt.getCoerced(val, base_dest_ty)).toIntern());
|
return Air.internedToRef(try pt.intern(switch (dest_tag) {
|
||||||
|
.error_set => .{ .err = .{
|
||||||
|
.ty = dest_ty.toIntern(),
|
||||||
|
.name = err_name,
|
||||||
|
} },
|
||||||
|
.error_union => .{ .error_union = .{
|
||||||
|
.ty = dest_ty.toIntern(),
|
||||||
|
.val = .{ .err_name = err_name },
|
||||||
|
} },
|
||||||
|
else => unreachable,
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
try sema.requireRuntimeBlock(block, src, operand_src);
|
|
||||||
const err_int_ty = try pt.errorIntType();
|
const err_int_ty = try pt.errorIntType();
|
||||||
if (block.wantSafety() and !dest_ty.isAnyError(zcu) and
|
if (block.wantSafety() and !dest_err_ty.isAnyError(zcu) and
|
||||||
dest_ty.toIntern() != .adhoc_inferred_error_set_type and
|
dest_err_ty.toIntern() != .adhoc_inferred_error_set_type and
|
||||||
zcu.backendSupportsFeature(.error_set_has_value))
|
zcu.backendSupportsFeature(.error_set_has_value))
|
||||||
{
|
{
|
||||||
if (dest_tag == .error_union) {
|
const err_code_inst = switch (operand_tag) {
|
||||||
const err_code = try sema.analyzeErrUnionCode(block, operand_src, operand);
|
.error_set => operand,
|
||||||
const err_int = try block.addBitCast(err_int_ty, err_code);
|
.error_union => try block.addTyOp(.unwrap_errunion_err, operand_err_ty, operand),
|
||||||
const zero_err = try pt.intRef(try pt.errorIntType(), 0);
|
else => unreachable,
|
||||||
|
};
|
||||||
|
const err_int_inst = try block.addBitCast(err_int_ty, err_code_inst);
|
||||||
|
|
||||||
const is_zero = try block.addBinOp(.cmp_eq, err_int, zero_err);
|
if (dest_tag == .error_union) {
|
||||||
|
const zero_err = try pt.intRef(err_int_ty, 0);
|
||||||
|
const is_zero = try block.addBinOp(.cmp_eq, err_int_inst, zero_err);
|
||||||
if (disjoint) {
|
if (disjoint) {
|
||||||
// Error must be zero.
|
// Error must be zero.
|
||||||
try sema.addSafetyCheck(block, src, is_zero, .invalid_error_code);
|
try sema.addSafetyCheck(block, src, is_zero, .invalid_error_code);
|
||||||
} else {
|
} else {
|
||||||
// Error must be in destination set or zero.
|
// Error must be in destination set or zero.
|
||||||
const has_value = try block.addTyOp(.error_set_has_value, dest_ty, err_code);
|
const has_value = try block.addTyOp(.error_set_has_value, dest_err_ty, err_int_inst);
|
||||||
const ok = try block.addBinOp(.bool_or, has_value, is_zero);
|
const ok = try block.addBinOp(.bool_or, has_value, is_zero);
|
||||||
try sema.addSafetyCheck(block, src, ok, .invalid_error_code);
|
try sema.addSafetyCheck(block, src, ok, .invalid_error_code);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
const err_int_inst = try block.addBitCast(err_int_ty, operand);
|
const ok = try block.addTyOp(.error_set_has_value, dest_err_ty, err_int_inst);
|
||||||
const ok = try block.addTyOp(.error_set_has_value, dest_ty, err_int_inst);
|
|
||||||
try sema.addSafetyCheck(block, src, ok, .invalid_error_code);
|
try sema.addSafetyCheck(block, src, ok, .invalid_error_code);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return block.addBitCast(base_dest_ty, operand);
|
|
||||||
|
if (operand_tag == .error_set and dest_tag == .error_union) {
|
||||||
|
const err_val = try block.addBitCast(dest_err_ty, operand);
|
||||||
|
return block.addTyOp(.wrap_errunion_err, dest_ty, err_val);
|
||||||
|
} else {
|
||||||
|
return block.addBitCast(dest_ty, operand);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn zirPtrCastFull(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref {
|
fn zirPtrCastFull(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref {
|
||||||
@ -28952,6 +29043,7 @@ fn elemValArray(
|
|||||||
}
|
}
|
||||||
|
|
||||||
try sema.validateRuntimeElemAccess(block, elem_index_src, elem_ty, array_ty, array_src);
|
try sema.validateRuntimeElemAccess(block, elem_index_src, elem_ty, array_ty, array_src);
|
||||||
|
try sema.validateRuntimeValue(block, array_src, array);
|
||||||
|
|
||||||
if (oob_safety and block.wantSafety()) {
|
if (oob_safety and block.wantSafety()) {
|
||||||
// Runtime check is only needed if unable to comptime check.
|
// Runtime check is only needed if unable to comptime check.
|
||||||
@ -29016,6 +29108,7 @@ fn elemPtrArray(
|
|||||||
|
|
||||||
if (!init) {
|
if (!init) {
|
||||||
try sema.validateRuntimeElemAccess(block, elem_index_src, array_ty.elemType2(zcu), array_ty, array_ptr_src);
|
try sema.validateRuntimeElemAccess(block, elem_index_src, array_ty.elemType2(zcu), array_ty, array_ptr_src);
|
||||||
|
try sema.validateRuntimeValue(block, array_ptr_src, array_ptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Runtime check is only needed if unable to comptime check.
|
// Runtime check is only needed if unable to comptime check.
|
||||||
@ -29073,6 +29166,7 @@ fn elemValSlice(
|
|||||||
}
|
}
|
||||||
|
|
||||||
try sema.validateRuntimeElemAccess(block, elem_index_src, elem_ty, slice_ty, slice_src);
|
try sema.validateRuntimeElemAccess(block, elem_index_src, elem_ty, slice_ty, slice_src);
|
||||||
|
try sema.validateRuntimeValue(block, slice_src, slice);
|
||||||
|
|
||||||
if (oob_safety and block.wantSafety()) {
|
if (oob_safety and block.wantSafety()) {
|
||||||
const len_inst = if (maybe_slice_val) |slice_val|
|
const len_inst = if (maybe_slice_val) |slice_val|
|
||||||
@ -29129,6 +29223,7 @@ fn elemPtrSlice(
|
|||||||
}
|
}
|
||||||
|
|
||||||
try sema.validateRuntimeElemAccess(block, elem_index_src, elem_ptr_ty, slice_ty, slice_src);
|
try sema.validateRuntimeElemAccess(block, elem_index_src, elem_ptr_ty, slice_ty, slice_src);
|
||||||
|
try sema.validateRuntimeValue(block, slice_src, slice);
|
||||||
|
|
||||||
if (oob_safety and block.wantSafety()) {
|
if (oob_safety and block.wantSafety()) {
|
||||||
const len_inst = len: {
|
const len_inst = len: {
|
||||||
@ -33889,6 +33984,17 @@ fn resolvePeerTypes(
|
|||||||
else => {},
|
else => {},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Fast path: check if everything has the same type to bypass the main PTR logic.
|
||||||
|
same_type: {
|
||||||
|
const ty = sema.typeOf(instructions[0]);
|
||||||
|
for (instructions[1..]) |inst| {
|
||||||
|
if (sema.typeOf(inst).toIntern() != ty.toIntern()) {
|
||||||
|
break :same_type;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ty;
|
||||||
|
}
|
||||||
|
|
||||||
const peer_tys = try sema.arena.alloc(?Type, instructions.len);
|
const peer_tys = try sema.arena.alloc(?Type, instructions.len);
|
||||||
const peer_vals = try sema.arena.alloc(?Value, instructions.len);
|
const peer_vals = try sema.arena.alloc(?Value, instructions.len);
|
||||||
|
|
||||||
@ -34562,14 +34668,14 @@ fn resolvePeerTypesInner(
|
|||||||
}
|
}
|
||||||
// Clear existing sentinel
|
// Clear existing sentinel
|
||||||
ptr_info.sentinel = .none;
|
ptr_info.sentinel = .none;
|
||||||
switch (ip.indexToKey(ptr_info.child)) {
|
if (ptr_info.flags.size == .one) switch (ip.indexToKey(ptr_info.child)) {
|
||||||
.array_type => |array_type| ptr_info.child = (try pt.arrayType(.{
|
.array_type => |array_type| ptr_info.child = (try pt.arrayType(.{
|
||||||
.len = array_type.len,
|
.len = array_type.len,
|
||||||
.child = array_type.child,
|
.child = array_type.child,
|
||||||
.sentinel = .none,
|
.sentinel = .none,
|
||||||
})).toIntern(),
|
})).toIntern(),
|
||||||
else => {},
|
else => {},
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
opt_ptr_info = ptr_info;
|
opt_ptr_info = ptr_info;
|
||||||
|
|||||||
@ -181,6 +181,8 @@ analysis_roots: std.BoundedArray(*Package.Module, 3) = .{},
|
|||||||
/// Allocated into `gpa`.
|
/// Allocated into `gpa`.
|
||||||
resolved_references: ?std.AutoHashMapUnmanaged(AnalUnit, ?ResolvedReference) = null,
|
resolved_references: ?std.AutoHashMapUnmanaged(AnalUnit, ?ResolvedReference) = null,
|
||||||
|
|
||||||
|
skip_analysis_errors: bool = false,
|
||||||
|
|
||||||
stage1_flags: packed struct {
|
stage1_flags: packed struct {
|
||||||
have_winmain: bool = false,
|
have_winmain: bool = false,
|
||||||
have_wwinmain: bool = false,
|
have_wwinmain: bool = false,
|
||||||
|
|||||||
@ -208,6 +208,7 @@ const Writer = struct {
|
|||||||
.anyframe_type,
|
.anyframe_type,
|
||||||
.bit_not,
|
.bit_not,
|
||||||
.bool_not,
|
.bool_not,
|
||||||
|
.slice_sentinel_ty,
|
||||||
.negate,
|
.negate,
|
||||||
.negate_wrap,
|
.negate_wrap,
|
||||||
.load,
|
.load,
|
||||||
|
|||||||
@ -2649,3 +2649,19 @@ test "bitcast vector" {
|
|||||||
const bigsum: u32x8 = @bitCast(zerox32);
|
const bigsum: u32x8 = @bitCast(zerox32);
|
||||||
try std.testing.expectEqual(0, @reduce(.Add, bigsum));
|
try std.testing.expectEqual(0, @reduce(.Add, bigsum));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
test "peer type resolution: slice of sentinel-terminated array" {
|
||||||
|
var f: bool = undefined;
|
||||||
|
f = false;
|
||||||
|
|
||||||
|
const a: [][2:0]u8 = &.{};
|
||||||
|
const b: []const [2:0]u8 = &.{.{ 10, 20 }};
|
||||||
|
|
||||||
|
const result = if (f) a else b;
|
||||||
|
|
||||||
|
comptime assert(@TypeOf(result) == []const [2:0]u8);
|
||||||
|
try expect(result.len == 1);
|
||||||
|
try expect(result[0].len == 2);
|
||||||
|
try expect(result[0][0] == 10);
|
||||||
|
try expect(result[0][1] == 20);
|
||||||
|
}
|
||||||
|
|||||||
@ -1060,9 +1060,24 @@ test "errorCast to adhoc inferred error set" {
|
|||||||
try std.testing.expect((try S.baz()) == 1234);
|
try std.testing.expect((try S.baz()) == 1234);
|
||||||
}
|
}
|
||||||
|
|
||||||
test "errorCast from error sets to error unions" {
|
test "@errorCast from error set to error union" {
|
||||||
const err_union: Set1!void = @errorCast(error.A);
|
const S = struct {
|
||||||
try expectError(error.A, err_union);
|
fn doTheTest(set: error{ A, B }) error{A}!i32 {
|
||||||
|
return @errorCast(set);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
try expectError(error.A, S.doTheTest(error.A));
|
||||||
|
try expectError(error.A, comptime S.doTheTest(error.A));
|
||||||
|
}
|
||||||
|
|
||||||
|
test "@errorCast from error union to error union" {
|
||||||
|
const S = struct {
|
||||||
|
fn doTheTest(set: error{ A, B }!i32) error{A}!i32 {
|
||||||
|
return @errorCast(set);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
try expectError(error.A, S.doTheTest(error.A));
|
||||||
|
try expectError(error.A, comptime S.doTheTest(error.A));
|
||||||
}
|
}
|
||||||
|
|
||||||
test "result location initialization of error union with OPV payload" {
|
test "result location initialization of error union with OPV payload" {
|
||||||
@ -1100,3 +1115,14 @@ test "return error union with i65" {
|
|||||||
fn add(x: i65, y: i65) anyerror!i65 {
|
fn add(x: i65, y: i65) anyerror!i65 {
|
||||||
return x + y;
|
return x + y;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
test "compare error union to error set" {
|
||||||
|
const S = struct {
|
||||||
|
fn doTheTest(val: error{Foo}!i32) !void {
|
||||||
|
if (error.Foo == val) return error.Unexpected;
|
||||||
|
if (val == error.Foo) return error.Unexpected;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
try S.doTheTest(0);
|
||||||
|
try comptime S.doTheTest(0);
|
||||||
|
}
|
||||||
|
|||||||
@ -1751,3 +1751,14 @@ test "comptime labeled block implicit exit" {
|
|||||||
};
|
};
|
||||||
comptime assert(result == {});
|
comptime assert(result == {});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
test "comptime block has intermediate runtime-known values" {
|
||||||
|
const arr: [2]u8 = .{ 1, 2 };
|
||||||
|
|
||||||
|
var idx: usize = undefined;
|
||||||
|
idx = 0;
|
||||||
|
|
||||||
|
comptime {
|
||||||
|
_ = arr[idx];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -711,3 +711,20 @@ test "inline call propagates comptime-known argument to generic parameter and re
|
|||||||
try expect(a1 == 12340);
|
try expect(a1 == 12340);
|
||||||
try expect(b1 == 12340);
|
try expect(b1 == 12340);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
test "inline function return type is evaluated at comptime" {
|
||||||
|
const S = struct {
|
||||||
|
inline fn assertComptimeAndRet(x: anytype) @TypeOf(x) {
|
||||||
|
if (!@inComptime()) comptime unreachable;
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline fn foo(val: anytype) assertComptimeAndRet(u16) {
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const result = S.foo(123);
|
||||||
|
comptime assert(@TypeOf(result) == u16);
|
||||||
|
try expect(result == 123);
|
||||||
|
}
|
||||||
|
|||||||
@ -589,3 +589,19 @@ comptime {
|
|||||||
// should override the result of the previous analysis.
|
// should override the result of the previous analysis.
|
||||||
for (0..2) |_| _ = fn (void) void;
|
for (0..2) |_| _ = fn (void) void;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
test "generic parameter resolves to comptime-only type but is not marked comptime" {
|
||||||
|
const S = struct {
|
||||||
|
fn foo(comptime T: type, rt_false: bool, func: fn (T) void) T {
|
||||||
|
if (rt_false) _ = foo(T, rt_false, func);
|
||||||
|
return 123;
|
||||||
|
}
|
||||||
|
fn bar(_: u8) void {}
|
||||||
|
};
|
||||||
|
|
||||||
|
const rt_result = S.foo(u8, false, S.bar);
|
||||||
|
try expect(rt_result == 123);
|
||||||
|
|
||||||
|
const ct_result = comptime S.foo(u8, false, S.bar);
|
||||||
|
comptime std.debug.assert(ct_result == 123);
|
||||||
|
}
|
||||||
|
|||||||
@ -1037,3 +1037,16 @@ test "peer slices keep abi alignment with empty struct" {
|
|||||||
comptime assert(@TypeOf(slice) == []const u32);
|
comptime assert(@TypeOf(slice) == []const u32);
|
||||||
try expect(slice.len == 0);
|
try expect(slice.len == 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
test "sentinel expression in slice operation has result type" {
|
||||||
|
const sentinel = std.math.maxInt(u16);
|
||||||
|
|
||||||
|
const arr: [3]u16 = .{ 1, 2, sentinel };
|
||||||
|
const slice = arr[0..2 :@intCast(sentinel)];
|
||||||
|
|
||||||
|
comptime assert(@TypeOf(slice) == *const [2:sentinel]u16);
|
||||||
|
comptime assert(slice[2] == sentinel);
|
||||||
|
comptime assert(slice.len == 2);
|
||||||
|
comptime assert(slice[0] == 1);
|
||||||
|
comptime assert(slice[1] == 2);
|
||||||
|
}
|
||||||
|
|||||||
@ -675,3 +675,12 @@ test "@typeInfo only contains pub decls" {
|
|||||||
try std.testing.expectEqualStrings("Enum", decls[0].name);
|
try std.testing.expectEqualStrings("Enum", decls[0].name);
|
||||||
try std.testing.expectEqualStrings("Struct", decls[1].name);
|
try std.testing.expectEqualStrings("Struct", decls[1].name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
test "@typeInfo function with generic return type and inferred error set" {
|
||||||
|
const S = struct {
|
||||||
|
fn testFn(comptime T: type) !T {}
|
||||||
|
};
|
||||||
|
|
||||||
|
const ret_ty = @typeInfo(@TypeOf(S.testFn)).@"fn".return_type;
|
||||||
|
comptime assert(ret_ty == null);
|
||||||
|
}
|
||||||
|
|||||||
@ -2322,3 +2322,19 @@ test "assign global tagged union" {
|
|||||||
try expect(U.global == .b);
|
try expect(U.global == .b);
|
||||||
try expect(U.global.b == 123456);
|
try expect(U.global.b == 123456);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
test "set mutable union by switching on same union" {
|
||||||
|
const U = union(enum) {
|
||||||
|
foo,
|
||||||
|
bar: usize,
|
||||||
|
};
|
||||||
|
|
||||||
|
var val: U = .foo;
|
||||||
|
val = switch (val) {
|
||||||
|
.foo => .{ .bar = 2 },
|
||||||
|
.bar => .foo,
|
||||||
|
};
|
||||||
|
|
||||||
|
try expect(val == .bar);
|
||||||
|
try expect(val.bar == 2);
|
||||||
|
}
|
||||||
|
|||||||
12
test/cases/compile_errors/for_comptime_array_pointer.zig
Normal file
12
test/cases/compile_errors/for_comptime_array_pointer.zig
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
export fn foo() void {
|
||||||
|
comptime var elems: [3]u32 = undefined;
|
||||||
|
for (&elems) |*elem| {
|
||||||
|
_ = elem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// error
|
||||||
|
//
|
||||||
|
// :3:10: error: runtime value contains reference to comptime var
|
||||||
|
// :3:10: note: comptime var pointers are not available at runtime
|
||||||
|
// :2:34: note: 'runtime_value' points to comptime var declared here
|
||||||
@ -0,0 +1,12 @@
|
|||||||
|
const A = struct {
|
||||||
|
a: u8,
|
||||||
|
bytes: [@sizeOf(A)]u8,
|
||||||
|
};
|
||||||
|
|
||||||
|
comptime {
|
||||||
|
_ = A;
|
||||||
|
}
|
||||||
|
|
||||||
|
// error
|
||||||
|
//
|
||||||
|
// :1:11: error: struct 'tmp.A' depends on itself
|
||||||
@ -0,0 +1,8 @@
|
|||||||
|
comptime {
|
||||||
|
const undef: @Type(.enum_literal) = undefined;
|
||||||
|
_ = @tagName(undef);
|
||||||
|
}
|
||||||
|
|
||||||
|
// error
|
||||||
|
//
|
||||||
|
// :3:18: error: use of undefined value here causes undefined behavior
|
||||||
42
test/incremental/analysis_error_and_syntax_error
Normal file
42
test/incremental/analysis_error_and_syntax_error
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
#target=x86_64-linux-selfhosted
|
||||||
|
#target=x86_64-linux-cbe
|
||||||
|
#target=x86_64-windows-cbe
|
||||||
|
#target=wasm32-wasi-selfhosted
|
||||||
|
#update=initial version
|
||||||
|
#file=main.zig
|
||||||
|
pub fn main() !void {
|
||||||
|
@compileError("uh oh");
|
||||||
|
}
|
||||||
|
#expect_error=main.zig:2:5: error: uh oh
|
||||||
|
|
||||||
|
#update=add parse error
|
||||||
|
#file=main.zig
|
||||||
|
pub fn main() !void {
|
||||||
|
@compileError("uh oh");
|
||||||
|
#expect_error=main.zig:3:1: error: expected statement, found 'EOF'
|
||||||
|
|
||||||
|
#update=fix parse error
|
||||||
|
#file=main.zig
|
||||||
|
pub fn main() !void {
|
||||||
|
@compileError("uh oh");
|
||||||
|
}
|
||||||
|
#expect_error=main.zig:2:5: error: uh oh
|
||||||
|
|
||||||
|
#update=add parse error again
|
||||||
|
#file=main.zig
|
||||||
|
pub fn main() !void {
|
||||||
|
@compileError("uh oh");
|
||||||
|
#expect_error=main.zig:3:1: error: expected statement, found 'EOF'
|
||||||
|
|
||||||
|
#update=comment @compileError call
|
||||||
|
#file=main.zig
|
||||||
|
pub fn main() !void {
|
||||||
|
//@compileError("uh oh");
|
||||||
|
#expect_error=main.zig:3:1: error: expected statement, found 'EOF'
|
||||||
|
|
||||||
|
#update=fix parse error again
|
||||||
|
#file=main.zig
|
||||||
|
pub fn main() !void {
|
||||||
|
//@compileError("uh oh");
|
||||||
|
}
|
||||||
|
#expect_stdout=""
|
||||||
Loading…
x
Reference in New Issue
Block a user