Merge pull request #13075 from Vexu/stage2-fixes

Stage2 misc fixes
This commit is contained in:
Veikka Tuominen 2022-10-10 23:27:17 +02:00 committed by GitHub
commit b316c25cc6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 424 additions and 77 deletions

View File

@ -2506,10 +2506,10 @@ fn addEnsureResult(gz: *GenZir, maybe_unused_result: Zir.Inst.Ref, statement: As
.dbg_block_end,
.ensure_result_used,
.ensure_result_non_error,
.ensure_err_union_payload_void,
.@"export",
.export_value,
.set_eval_branch_quota,
.ensure_err_payload_void,
.atomic_store,
.store,
.store_node,
@ -5499,6 +5499,7 @@ fn ifExpr(
try then_scope.addDbgVar(.dbg_var_val, ident_name, payload_inst);
break :s &payload_val_scope.base;
} else {
_ = try then_scope.addUnNode(.ensure_err_union_payload_void, cond.inst, node);
break :s &then_scope.base;
}
} else if (if_full.payload_token) |payload_token| {
@ -5836,6 +5837,7 @@ fn whileExpr(
dbg_var_inst = indexToRef(payload_inst);
break :s &payload_val_scope.base;
} else {
_ = try then_scope.addUnNode(.ensure_err_union_payload_void, cond.inst, node);
break :s &then_scope.base;
}
} else if (while_full.payload_token) |payload_token| {

View File

@ -1034,8 +1034,8 @@ fn analyzeBodyInner(
i += 1;
continue;
},
.ensure_err_payload_void => {
try sema.zirEnsureErrPayloadVoid(block, inst);
.ensure_err_union_payload_void => {
try sema.zirEnsureErrUnionPayloadVoid(block, inst);
i += 1;
continue;
},
@ -1302,17 +1302,28 @@ fn analyzeBodyInner(
// 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;
const prev_inline_block = block.inline_block;
if (tags[inline_body[inline_body.len - 1]] == .repeat_inline) {
block.inline_block = inline_body[0];
const need_sub_block = tags[inline_body[inline_body.len - 1]] == .repeat_inline;
var sub_block = block;
var block_space: Block = undefined;
// NOTE: this has to be done like this because branching in
// defers here breaks stage1.
block_space.instructions = .{};
if (need_sub_block) {
block_space = block.makeSubBlock();
block_space.inline_block = inline_body[0];
sub_block = &block_space;
}
block.params = .{};
defer {
block.params.deinit(gpa);
block.params = prev_params;
block.inline_block = prev_inline_block;
block_space.instructions.deinit(gpa);
}
const opt_break_data = try sema.analyzeBodyBreak(block, inline_body);
const opt_break_data = try sema.analyzeBodyBreak(sub_block, inline_body);
if (need_sub_block) {
try block.instructions.appendSlice(gpa, block_space.instructions.items);
}
// 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: {
@ -2469,7 +2480,7 @@ fn createAnonymousDeclTypeNamed(
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, undefined) catch unreachable;
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)});
@ -3089,6 +3100,33 @@ fn zirEnsureResultNonError(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Com
}
}
fn zirEnsureErrUnionPayloadVoid(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void {
const tracy = trace(@src());
defer tracy.end();
const inst_data = sema.code.instructions.items(.data)[inst].un_node;
const src = inst_data.src();
const operand = try sema.resolveInst(inst_data.operand);
const operand_ty = sema.typeOf(operand);
const err_union_ty = if (operand_ty.zigTypeTag() == .Pointer)
operand_ty.childType()
else
operand_ty;
// TODO this should be validated in a more generic instruction that is
// emitted for all ifs and whiles with an error union condition.
if (err_union_ty.zigTypeTag() != .ErrorUnion) return;
const payload_ty = err_union_ty.errorUnionPayload().zigTypeTag();
if (payload_ty != .Void and payload_ty != .NoReturn) {
const msg = msg: {
const msg = try sema.errMsg(block, src, "error union payload is ignored", .{});
errdefer msg.destroy(sema.gpa);
try sema.errNote(block, src, msg, "payload value can be explicitly ignored with '|_|'", .{});
break :msg msg;
};
return sema.failWithOwnedErrorMsg(msg);
}
}
fn zirIndexablePtrLen(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
const tracy = trace(@src());
defer tracy.end();
@ -5631,7 +5669,7 @@ fn zirCall(
var bound_arg_src: ?LazySrcLoc = null;
if (func_type.tag() == .bound_fn) {
bound_arg_src = func_src;
const bound_func = try sema.resolveValue(block, .unneeded, func, undefined);
const bound_func = try sema.resolveValue(block, .unneeded, 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_len + 1);
@ -6010,6 +6048,7 @@ fn analyzeCall(
const parent_inst_map = sema.inst_map;
sema.inst_map = .{};
defer {
sema.src = call_src;
sema.inst_map.deinit(gpa);
sema.inst_map = parent_inst_map;
}
@ -6213,7 +6252,7 @@ fn analyzeCall(
}
if (should_memoize and is_comptime_call) {
const result_val = try sema.resolveConstMaybeUndefVal(block, .unneeded, result, undefined);
const result_val = try sema.resolveConstMaybeUndefVal(block, .unneeded, result, "");
// TODO: check whether any external comptime memory was mutated by the
// comptime function call. If so, then do not memoize the call here.
@ -6675,6 +6714,8 @@ fn instantiateGenericCall(
.comptime_args_fn_inst = module_fn.zir_body_inst,
.preallocated_new_func = new_module_func,
.is_generic_instantiation = true,
.branch_quota = sema.branch_quota,
.branch_count = sema.branch_count,
};
defer child_sema.deinit();
@ -6724,12 +6765,12 @@ fn instantiateGenericCall(
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, .unneeded, undefined);
return sema.failWithNeededComptime(block, .unneeded, "");
}
} else if (is_anytype) {
const arg_ty = sema.typeOf(arg);
if (try sema.typeRequiresComptime(arg_ty)) {
const arg_val = try sema.resolveConstValue(block, .unneeded, arg, undefined);
const arg_val = try sema.resolveConstValue(block, .unneeded, arg, "");
const child_arg = try child_sema.addConstant(arg_ty, arg_val);
child_sema.inst_map.putAssumeCapacityNoClobber(inst, child_arg);
} else {
@ -6751,7 +6792,7 @@ fn instantiateGenericCall(
}
return err;
};
const new_func_val = child_sema.resolveConstValue(&child_block, .unneeded, new_func_inst, undefined) catch unreachable;
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);
@ -7674,24 +7715,6 @@ fn zirErrUnionCodePtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileE
return block.addTyOp(.unwrap_errunion_err_ptr, result_ty, operand);
}
fn zirEnsureErrPayloadVoid(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void {
const tracy = trace(@src());
defer tracy.end();
const inst_data = sema.code.instructions.items(.data)[inst].un_tok;
const src = inst_data.src();
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.fmt(sema.mod),
});
}
if (operand_ty.errorUnionPayload().zigTypeTag() != .Void) {
return sema.fail(block, src, "expression value is ignored", .{});
}
}
fn zirFunc(
sema: *Sema,
block: *Block,
@ -9119,7 +9142,7 @@ fn zirSwitchCapture(
const union_obj = operand_ty.cast(Type.Payload.Union).?.data;
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, undefined) catch unreachable;
const first_item_val = sema.resolveConstValue(block, .unneeded, first_item, "") catch unreachable;
const first_field_index = @intCast(u32, operand_ty.unionTagFieldIndex(first_item_val, sema.mod).?);
const first_field = union_obj.fields.values()[first_field_index];
@ -9127,7 +9150,7 @@ fn zirSwitchCapture(
for (items[1..]) |item, i| {
const item_ref = try sema.resolveInst(item);
// Previous switch validation ensured this will succeed
const item_val = sema.resolveConstValue(block, .unneeded, item_ref, undefined) catch unreachable;
const item_val = sema.resolveConstValue(block, .unneeded, item_ref, "") catch unreachable;
const field_index = operand_ty.unionTagFieldIndex(item_val, sema.mod).?;
const field = union_obj.fields.values()[field_index];
@ -9188,7 +9211,7 @@ fn zirSwitchCapture(
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, undefined) catch unreachable;
const item_val = sema.resolveConstValue(block, .unneeded, item_ref, "") catch unreachable;
names.putAssumeCapacityNoClobber(
item_val.getError().?,
{},
@ -9202,7 +9225,7 @@ fn zirSwitchCapture(
} 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, undefined) catch unreachable;
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);
@ -9941,14 +9964,17 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
const item_ref = @intToEnum(Zir.Inst.Ref, sema.code.extra[extra_index]);
extra_index += 1;
const body_len = @truncate(u31, sema.code.extra[extra_index]);
const is_inline = sema.code.extra[extra_index] >> 31 != 0;
extra_index += 1;
const body = sema.code.extra[extra_index..][0..body_len];
extra_index += body_len;
const item = try sema.resolveInst(item_ref);
// Validation above ensured these will succeed.
const item_val = sema.resolveConstValue(&child_block, .unneeded, item, undefined) catch unreachable;
const item_val = sema.resolveConstValue(&child_block, .unneeded, item, "") catch unreachable;
if (operand_val.eql(item_val, operand_ty, sema.mod)) {
if (is_inline) child_block.inline_case_capture = operand;
if (err_set) try sema.maybeErrorUnwrapComptime(&child_block, body, operand);
return sema.resolveBlockBody(block, src, &child_block, body, inst, merges);
}
@ -9962,6 +9988,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
const ranges_len = sema.code.extra[extra_index];
extra_index += 1;
const body_len = @truncate(u31, sema.code.extra[extra_index]);
const is_inline = sema.code.extra[extra_index] >> 31 != 0;
extra_index += 1;
const items = sema.code.refSlice(extra_index, items_len);
extra_index += items_len;
@ -9970,8 +9997,10 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
for (items) |item_ref| {
const item = try sema.resolveInst(item_ref);
// Validation above ensured these will succeed.
const item_val = sema.resolveConstValue(&child_block, .unneeded, item, undefined) catch unreachable;
const item_val = sema.resolveConstValue(&child_block, .unneeded, item, "") catch unreachable;
if (operand_val.eql(item_val, operand_ty, sema.mod)) {
if (is_inline) child_block.inline_case_capture = operand;
if (err_set) try sema.maybeErrorUnwrapComptime(&child_block, body, operand);
return sema.resolveBlockBody(block, src, &child_block, body, inst, merges);
}
@ -9985,11 +10014,12 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
extra_index += 1;
// Validation above ensured these will succeed.
const first_tv = sema.resolveInstConst(&child_block, .unneeded, item_first, undefined) catch unreachable;
const last_tv = sema.resolveInstConst(&child_block, .unneeded, item_last, undefined) catch unreachable;
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 ((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)))
{
if (is_inline) child_block.inline_case_capture = operand;
if (err_set) try sema.maybeErrorUnwrapComptime(&child_block, body, operand);
return sema.resolveBlockBody(block, src, &child_block, body, inst, merges);
}
@ -9999,6 +10029,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
}
}
if (err_set) try sema.maybeErrorUnwrapComptime(&child_block, special.body, operand);
if (special.is_inline) child_block.inline_case_capture = operand;
return sema.resolveBlockBody(block, src, &child_block, special.body, inst, merges);
}
@ -10060,7 +10091,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
// `item` is already guaranteed to be constant known.
const analyze_body = if (union_originally) blk: {
const item_val = sema.resolveConstValue(block, .unneeded, item, undefined) catch unreachable;
const item_val = sema.resolveConstValue(block, .unneeded, item, "") catch unreachable;
const field_ty = maybe_union_ty.unionFieldType(item_val, sema.mod);
break :blk field_ty.zigTypeTag() != .NoReturn;
} else true;
@ -10211,7 +10242,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
const analyze_body = if (union_originally)
for (items) |item_ref| {
const item = try sema.resolveInst(item_ref);
const item_val = sema.resolveConstValue(block, .unneeded, item, undefined) catch unreachable;
const item_val = sema.resolveConstValue(block, .unneeded, item, "") catch unreachable;
const field_ty = maybe_union_ty.unionFieldType(item_val, sema.mod);
if (field_ty.zigTypeTag() != .NoReturn) break true;
} else false
@ -10606,7 +10637,7 @@ fn resolveSwitchItemVal(
// 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
// full source locations.
if (sema.resolveConstValue(block, .unneeded, item, undefined)) |val| {
if (sema.resolveConstValue(block, .unneeded, item, "")) |val| {
return TypedValue{ .ty = item_ty, .val = val };
} else |err| switch (err) {
error.NeededSourceLocation => {
@ -14291,6 +14322,38 @@ fn zirBitSizeOf(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A
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);
switch (operand_ty.zigTypeTag()) {
.Fn,
.NoReturn,
.Undefined,
.Null,
.BoundFn,
.Opaque,
=> return sema.fail(block, operand_src, "no size available for type '{}'", .{operand_ty.fmt(sema.mod)}),
.Type,
.EnumLiteral,
.ComptimeFloat,
.ComptimeInt,
.Void,
=> return sema.addIntUnsigned(Type.comptime_int, 0),
.Bool,
.Int,
.Float,
.Pointer,
.Array,
.Struct,
.Optional,
.ErrorUnion,
.ErrorSet,
.Enum,
.Union,
.Vector,
.Frame,
.AnyFrame,
=> {},
}
const target = sema.mod.getTarget();
const bit_size = try operand_ty.bitSizeAdvanced(target, sema.kit(block, src));
return sema.addIntUnsigned(Type.comptime_int, bit_size);
@ -14321,7 +14384,7 @@ fn zirClosureCapture(
// 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);
Value.initTag(.unreachable_value);
try block.wip_capture_scope.captures.putNoClobber(sema.gpa, inst, .{
.ty = try sema.typeOf(operand).copy(sema.perm_arena),
@ -14358,7 +14421,35 @@ fn zirClosureGet(
scope = scope.parent.?;
} else unreachable;
if (tv.val.tag() == .generic_poison and !block.is_typeof and !block.is_comptime and sema.func != null) {
if (tv.val.tag() == .unreachable_value and !block.is_typeof and sema.func == null) {
const msg = msg: {
const name = name: {
const file = sema.owner_decl.getFileScope();
const tree = file.getTree(sema.mod.gpa) catch |err| {
// In this case we emit a warning + a less precise source location.
log.warn("unable to load {s}: {s}", .{
file.sub_file_path, @errorName(err),
});
break :name null;
};
const node = sema.owner_decl.relativeToNodeIndex(inst_data.src_node);
const token = tree.nodes.items(.main_token)[node];
break :name tree.tokenSlice(token);
};
const msg = if (name) |some|
try sema.errMsg(block, inst_data.src(), "'{s}' not accessible outside function scope", .{some})
else
try sema.errMsg(block, inst_data.src(), "variable not accessible outside function scope", .{});
errdefer msg.destroy(sema.gpa);
// TODO add "declared here" note
break :msg msg;
};
return sema.failWithOwnedErrorMsg(msg);
}
if (tv.val.tag() == .unreachable_value and !block.is_typeof and !block.is_comptime and sema.func != null) {
const msg = msg: {
const name = name: {
const file = sema.owner_decl.getFileScope();
@ -17121,7 +17212,7 @@ fn zirTagName(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air
try sema.resolveTypeLayout(block, operand_src, operand_ty);
const enum_ty = switch (operand_ty.zigTypeTag()) {
.EnumLiteral => {
const val = try sema.resolveConstValue(block, .unneeded, operand, undefined);
const val = try sema.resolveConstValue(block, .unneeded, operand, "");
const bytes = val.castTag(.enum_literal).?.data;
return sema.addStrLit(block, bytes);
},
@ -18379,7 +18470,7 @@ fn zirPtrCast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air
return sema.failWithOwnedErrorMsg(msg);
}
if (try sema.resolveMaybeUndefVal(block, operand_src, operand)) |operand_val| {
if (try sema.resolveMaybeUndefVal(block, operand_src, ptr)) |operand_val| {
if (!dest_ty.ptrAllowsZero() and operand_val.isUndef()) {
return sema.failWithUseOfUndef(block, operand_src);
}
@ -18756,6 +18847,10 @@ fn bitOffsetOf(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!u6
break :blk try sema.tupleFieldIndex(block, ty, field_name, rhs_src);
} else try sema.structFieldIndex(block, ty, field_name, rhs_src);
if (ty.structFieldIsComptime(field_index)) {
return sema.fail(block, src, "no offset available for comptime field", .{});
}
switch (ty.containerLayout()) {
.Packed => {
var bit_sum: u64 = 0;
@ -20096,7 +20191,7 @@ fn zirBuiltinCall(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
var bound_arg_src: ?LazySrcLoc = null;
if (sema.typeOf(func).tag() == .bound_fn) {
bound_arg_src = func_src;
const bound_func = try sema.resolveValue(block, .unneeded, func, undefined);
const bound_func = try sema.resolveValue(block, .unneeded, 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);
@ -20139,6 +20234,10 @@ fn zirFieldParentPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileEr
break :blk try sema.tupleFieldIndex(block, struct_ty, field_name, name_src);
} else try sema.structFieldIndex(block, struct_ty, field_name, name_src);
if (struct_ty.structFieldIsComptime(field_index)) {
return sema.fail(block, src, "cannot get @fieldParentPtr of a comptime field", .{});
}
try sema.checkPtrOperand(block, ptr_src, field_ptr_ty);
const field_ptr_ty_info = field_ptr_ty.ptrInfo().data;
@ -21197,6 +21296,8 @@ const ExternPosition = enum {
ret_ty,
param_ty,
union_field,
struct_field,
element,
other,
};
@ -21234,7 +21335,10 @@ fn validateExternType(
8, 16, 32, 64, 128 => return true,
else => return false,
},
.Fn => return !Type.fnCallingConventionAllowsZigTypes(ty.fnCallingConvention()),
.Fn => {
if (position != .other) return false;
return !Type.fnCallingConventionAllowsZigTypes(ty.fnCallingConvention());
},
.Enum => {
var buf: Type.Payload.Bits = undefined;
return sema.validateExternType(block, src, ty.intTagType(&buf), position);
@ -21253,9 +21357,9 @@ fn validateExternType(
},
.Array => {
if (position == .ret_ty or position == .param_ty) return false;
return sema.validateExternType(block, src, ty.elemType2(), .other);
return sema.validateExternType(block, src, ty.elemType2(), .element);
},
.Vector => return sema.validateExternType(block, src, ty.elemType2(), .other),
.Vector => return sema.validateExternType(block, src, ty.elemType2(), .element),
.Optional => return ty.isPtrLikeOptional(),
}
}
@ -21295,11 +21399,18 @@ fn explainWhyTypeIsNotExtern(
} else {
try mod.errNoteNonLazy(src_loc, msg, "only integers with power of two bits are extern compatible", .{});
},
.Fn => switch (ty.fnCallingConvention()) {
.Unspecified => try mod.errNoteNonLazy(src_loc, msg, "extern function must specify calling convention", .{}),
.Async => try mod.errNoteNonLazy(src_loc, msg, "async function cannot be extern", .{}),
.Inline => try mod.errNoteNonLazy(src_loc, msg, "inline function cannot be extern", .{}),
else => return,
.Fn => {
if (position != .other) {
try mod.errNoteNonLazy(src_loc, msg, "type has no guaranteed in-memory representation", .{});
try mod.errNoteNonLazy(src_loc, msg, "use '*const ' to make a function pointer type", .{});
return;
}
switch (ty.fnCallingConvention()) {
.Unspecified => try mod.errNoteNonLazy(src_loc, msg, "extern function must specify calling convention", .{}),
.Async => try mod.errNoteNonLazy(src_loc, msg, "async function cannot be extern", .{}),
.Inline => try mod.errNoteNonLazy(src_loc, msg, "inline function cannot be extern", .{}),
else => return,
}
},
.Enum => {
var buf: Type.Payload.Bits = undefined;
@ -21315,9 +21426,9 @@ fn explainWhyTypeIsNotExtern(
} else if (position == .param_ty) {
return mod.errNoteNonLazy(src_loc, msg, "arrays are not allowed as a parameter type", .{});
}
try sema.explainWhyTypeIsNotExtern(msg, src_loc, ty.elemType2(), position);
try sema.explainWhyTypeIsNotExtern(msg, src_loc, ty.elemType2(), .element);
},
.Vector => try sema.explainWhyTypeIsNotExtern(msg, src_loc, ty.elemType2(), position),
.Vector => try sema.explainWhyTypeIsNotExtern(msg, src_loc, ty.elemType2(), .element),
.Optional => try mod.errNoteNonLazy(src_loc, msg, "only pointer like optionals are extern compatible", .{}),
}
}
@ -22047,7 +22158,7 @@ fn fieldPtr(
}
},
.Type => {
_ = try sema.resolveConstValue(block, .unneeded, object_ptr, undefined);
_ = try sema.resolveConstValue(block, .unneeded, object_ptr, "");
const result = try sema.analyzeLoad(block, src, object_ptr, object_ptr_src);
const inner = if (is_pointer_to)
try sema.analyzeLoad(block, src, result, object_ptr_src)
@ -23375,7 +23486,7 @@ fn coerceExtra(
// Function body to function pointer.
if (inst_ty.zigTypeTag() == .Fn) {
const fn_val = try sema.resolveConstValue(block, .unneeded, inst, undefined);
const fn_val = try sema.resolveConstValue(block, .unneeded, inst, "");
const fn_decl = fn_val.pointerDecl().?;
const inst_as_ptr = try sema.analyzeDeclRef(fn_decl);
return sema.coerce(block, dest_ty, inst_as_ptr, inst_src);
@ -23677,7 +23788,7 @@ fn coerceExtra(
},
.Float, .ComptimeFloat => switch (inst_ty.zigTypeTag()) {
.ComptimeFloat => {
const val = try sema.resolveConstValue(block, .unneeded, inst, undefined);
const val = try sema.resolveConstValue(block, .unneeded, inst, "");
const result_val = try val.floatCast(sema.arena, dest_ty, target);
return try sema.addConstant(dest_ty, result_val);
},
@ -23735,7 +23846,7 @@ fn coerceExtra(
.Enum => switch (inst_ty.zigTypeTag()) {
.EnumLiteral => {
// enum literal to enum
const val = try sema.resolveConstValue(block, .unneeded, inst, undefined);
const val = try sema.resolveConstValue(block, .unneeded, inst, "");
const bytes = val.castTag(.enum_literal).?.data;
const field_index = dest_ty.enumFieldIndex(bytes) orelse {
const msg = msg: {
@ -24805,7 +24916,7 @@ fn coerceVarArgParam(
.{},
),
.Fn => blk: {
const fn_val = try sema.resolveConstValue(block, .unneeded, inst, undefined);
const fn_val = try sema.resolveConstValue(block, .unneeded, inst, "");
const fn_decl = fn_val.pointerDecl().?;
break :blk try sema.analyzeDeclRef(fn_decl);
},
@ -24814,13 +24925,13 @@ fn coerceVarArgParam(
};
const coerced_ty = sema.typeOf(coerced);
if (!try sema.validateExternType(block, inst_src, coerced_ty, .other)) {
if (!try sema.validateExternType(block, inst_src, coerced_ty, .param_ty)) {
const msg = msg: {
const msg = try sema.errMsg(block, inst_src, "cannot pass '{}' to variadic function", .{coerced_ty.fmt(sema.mod)});
errdefer msg.destroy(sema.gpa);
const src_decl = sema.mod.declPtr(block.src_decl);
try sema.explainWhyTypeIsNotExtern(msg, inst_src.toSrcLoc(src_decl), coerced_ty, .other);
try sema.explainWhyTypeIsNotExtern(msg, inst_src.toSrcLoc(src_decl), coerced_ty, .param_ty);
try sema.addDeclaredHereNote(msg, coerced_ty);
break :msg msg;
@ -26904,10 +27015,14 @@ fn analyzeIsNull(
}
}
const inverted_non_null_res = if (invert_logic) Air.Inst.Ref.bool_true else Air.Inst.Ref.bool_false;
const operand_ty = sema.typeOf(operand);
var buf: Type.Payload.ElemType = undefined;
if (operand_ty.zigTypeTag() == .Optional and operand_ty.optionalChild(&buf).zigTypeTag() == .NoReturn) {
return Air.Inst.Ref.bool_true;
return inverted_non_null_res;
}
if (operand_ty.zigTypeTag() != .Optional and !operand_ty.isPtrLikeOptional()) {
return inverted_non_null_res;
}
try sema.requireRuntimeBlock(block, src, null);
const air_tag: Air.Inst.Tag = if (invert_logic) .is_non_null else .is_null;
@ -29105,14 +29220,14 @@ fn semaStructFields(mod: *Module, struct_obj: *Module.Struct) CompileError!void
};
return sema.failWithOwnedErrorMsg(msg);
}
if (struct_obj.layout == .Extern and !try sema.validateExternType(&block_scope, src, field.ty, .other)) {
if (struct_obj.layout == .Extern and !try sema.validateExternType(&block_scope, src, field.ty, .struct_field)) {
const msg = msg: {
const tree = try sema.getAstTree(&block_scope);
const fields_src = enumFieldSrcLoc(decl, tree.*, 0, i);
const msg = try sema.errMsg(&block_scope, fields_src, "extern structs cannot contain fields of type '{}'", .{field.ty.fmt(sema.mod)});
errdefer msg.destroy(sema.gpa);
try sema.explainWhyTypeIsNotExtern(msg, fields_src.toSrcLoc(decl), field.ty, .other);
try sema.explainWhyTypeIsNotExtern(msg, fields_src.toSrcLoc(decl), field.ty, .struct_field);
try sema.addDeclaredHereNote(msg, field.ty);
break :msg msg;

View File

@ -402,6 +402,8 @@ pub const Inst = struct {
/// Emits a compile error if an error is ignored.
/// Uses the `un_node` field.
ensure_result_non_error,
/// Emits a compile error error union payload is not void.
ensure_err_union_payload_void,
/// Create a `E!T` type.
/// Uses the `pl_node` field with `Bin` payload.
error_union_type,
@ -646,9 +648,6 @@ pub const Inst = struct {
/// Given a pointer to an error union value, returns the error code. No safety checks.
/// Uses the `un_node` field.
err_union_code_ptr,
/// Takes a *E!T and raises a compiler error if T != void
/// Uses the `un_tok` field.
ensure_err_payload_void,
/// An enum literal. Uses the `str_tok` union field.
enum_literal,
/// A switch expression. Uses the `pl_node` union field.
@ -1060,6 +1059,7 @@ pub const Inst = struct {
.elem_val_node,
.ensure_result_used,
.ensure_result_non_error,
.ensure_err_union_payload_void,
.@"export",
.export_value,
.field_ptr,
@ -1113,7 +1113,6 @@ pub const Inst = struct {
.err_union_code_ptr,
.ptr_type,
.overflow_arithmetic_ptr,
.ensure_err_payload_void,
.enum_literal,
.merge_error_sets,
.error_union_type,
@ -1282,7 +1281,7 @@ pub const Inst = struct {
.dbg_block_end,
.ensure_result_used,
.ensure_result_non_error,
.ensure_err_payload_void,
.ensure_err_union_payload_void,
.set_eval_branch_quota,
.atomic_store,
.store,
@ -1615,6 +1614,7 @@ pub const Inst = struct {
.elem_val_node = .pl_node,
.ensure_result_used = .un_node,
.ensure_result_non_error = .un_node,
.ensure_err_union_payload_void = .un_node,
.error_union_type = .pl_node,
.error_value = .str_tok,
.@"export" = .pl_node,
@ -1677,7 +1677,6 @@ pub const Inst = struct {
.err_union_payload_unsafe_ptr = .un_node,
.err_union_code = .un_node,
.err_union_code_ptr = .un_node,
.ensure_err_payload_void = .un_tok,
.enum_literal = .str_tok,
.switch_block = .pl_node,
.switch_cond = .un_node,
@ -3051,11 +3050,16 @@ pub const Inst = struct {
var multi_i: u32 = 0;
while (true) : (multi_i += 1) {
const items_len = zir.extra[extra_index];
extra_index += 2;
extra_index += 1;
const ranges_len = zir.extra[extra_index];
extra_index += 1;
const body_len = @truncate(u31, zir.extra[extra_index]);
extra_index += 1;
const items = zir.refSlice(extra_index, items_len);
extra_index += items_len;
// Each range has a start and an end.
extra_index += 2 * ranges_len;
const body = zir.extra[extra_index..][0..body_len];
extra_index += body_len;

View File

@ -162,6 +162,7 @@ const Writer = struct {
.load,
.ensure_result_used,
.ensure_result_non_error,
.ensure_err_union_payload_void,
.ret_node,
.ret_load,
.resolve_inferred_alloc,
@ -235,7 +236,6 @@ const Writer = struct {
.ref,
.ret_tok,
.ensure_err_payload_void,
.closure_capture,
.switch_capture_tag,
=> try self.writeUnTok(stream, inst),

View File

@ -5664,6 +5664,28 @@ pub const Type = extern union {
}
}
pub fn structFieldIsComptime(ty: Type, index: usize) bool {
switch (ty.tag()) {
.@"struct" => {
const struct_obj = ty.castTag(.@"struct").?.data;
if (struct_obj.layout == .Packed) return false;
const field = struct_obj.fields.values()[index];
return field.is_comptime;
},
.tuple => {
const tuple = ty.castTag(.tuple).?.data;
const val = tuple.values[index];
return val.tag() != .unreachable_value;
},
.anon_struct => {
const anon_struct = ty.castTag(.anon_struct).?.data;
const val = anon_struct.values[index];
return val.tag() != .unreachable_value;
},
else => unreachable,
}
}
pub fn packedStructFieldByteOffset(ty: Type, field_index: usize, target: Target) u32 {
const struct_obj = ty.castTag(.@"struct").?.data;
assert(struct_obj.layout == .Packed);

View File

@ -95,6 +95,7 @@ test {
_ = @import("behavior/bugs/12801-1.zig");
_ = @import("behavior/bugs/12801-2.zig");
_ = @import("behavior/bugs/12885.zig");
_ = @import("behavior/bugs/12890.zig");
_ = @import("behavior/bugs/12911.zig");
_ = @import("behavior/bugs/12928.zig");
_ = @import("behavior/bugs/12945.zig");

View File

@ -0,0 +1,18 @@
const expect = @import("std").testing.expect;
const builtin = @import("builtin");
fn a(b: []u3, c: u3) void {
switch (c) {
0...1 => b[c] = c,
2...3 => b[c] = c,
4...7 => |d| b[d] = c,
}
}
test {
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
var arr: [8]u3 = undefined;
a(&arr, 5);
try expect(arr[5] == 5);
}

View File

@ -7,7 +7,7 @@ const mem = std.mem;
/// A more basic implementation of std.testing.expectError which
/// does not require formatter/printing support
fn expectError(expected_err: anyerror, observed_err_union: anytype) !void {
if (observed_err_union) {
if (observed_err_union) |_| {
return error.TestExpectedError;
} else |err| if (err == expected_err) {
return; // Success

View File

@ -1398,3 +1398,17 @@ test "continue in inline for inside a comptime switch" {
}
try expect(count == 4);
}
test "continue nested inline for loop" {
var a: u8 = 0;
loop: inline for ([_]u8{ 1, 2 }) |x| {
inline for ([_]u8{1}) |y| {
if (x == y) {
continue :loop;
}
}
a = x;
try expect(x == 2);
}
try expect(a == 2);
}

View File

@ -129,3 +129,16 @@ test "inline else int all values" {
},
}
}
test "inline switch capture is set when switch operand is comptime known" {
const U2 = union(enum) {
a: u32,
};
var u: U2 = undefined;
switch (u) {
inline else => |*f, tag| {
try expect(@TypeOf(f) == *u32);
try expect(tag == .a);
},
}
}

View File

@ -483,3 +483,14 @@ test "pointer to constant decl preserves alignment" {
const alignment = @typeInfo(@TypeOf(&S.aligned)).Pointer.alignment;
try std.testing.expect(alignment == 8);
}
test "ptrCast comptime known slice to C pointer" {
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
const s: [:0]const u8 = "foo";
var p = @ptrCast([*c]const u8, s);
try std.testing.expectEqualStrings(s, std.mem.sliceTo(p, 0));
}

View File

@ -310,3 +310,7 @@ test "lazy size cast to float" {
try expect(@as(f32, @sizeOf(S)) == 1.0);
}
}
test "bitSizeOf comptime_int" {
try expect(@bitSizeOf(comptime_int) == 0);
}

View File

@ -0,0 +1,12 @@
export fn entry(y: u8) void {
const Thing = struct {
y: u8 = y,
};
_ = @sizeOf(Thing);
}
// error
// backend=stage2
// target=native
//
// :3:17: error: 'y' not accessible outside function scope

View File

@ -0,0 +1,16 @@
const T = struct {
comptime a: u32 = 2,
};
pub export fn entry1() void {
@offsetOf(T, "a");
}
pub export fn entry2() void {
@fieldParentPtr(T, "a", undefined);
}
// error
// backend=stage2
// target=native
//
// :5:5: error: no offset available for comptime field
// :8:5: error: cannot get @fieldParentPtr of a comptime field

View File

@ -0,0 +1,30 @@
pub export fn entry1() void {
@setEvalBranchQuota(1001);
// Return type evaluation should inherit both the
// parent's branch quota and count meaning
// at least 2002 backwards branches are required.
comptime var i = 0;
inline while (i < 1000) : (i += 1) {}
_ = simple(10);
}
pub export fn entry2() void {
@setEvalBranchQuota(2001);
comptime var i = 0;
inline while (i < 1000) : (i += 1) {}
_ = simple(10);
}
fn simple(comptime n: usize) Type(n) {
return n;
}
fn Type(comptime n: usize) type {
if (n <= 1) return usize;
return Type(n - 1);
}
// error
// backend=stage2
// target=native
//
// :21:16: error: evaluation exceeded 1001 backwards branches
// :21:16: note: use @setEvalBranchQuota() to raise the branch limit from 1001
// :16:34: note: called from here

View File

@ -0,0 +1,25 @@
pub export fn entry1() void {
var x: anyerror!usize = 5;
if (x) {
// foo
} else |_| {
// bar
}
}
pub export fn entry2() void {
var x: anyerror!usize = 5;
while (x) {
// foo
} else |_| {
// bar
}
}
// error
// backend=stage2
// target=native
//
// :3:5: error: error union payload is ignored
// :3:5: note: payload value can be explicitly ignored with '|_|'
// :11:5: error: error union payload is ignored
// :11:5: note: payload value can be explicitly ignored with '|_|'

View File

@ -0,0 +1,20 @@
const S = extern struct {
a: fn () callconv(.C) void,
};
comptime {
_ = @sizeOf(S) == 1;
}
comptime {
_ = [*c][4]fn() callconv(.C) void;
}
// error
// backend=stage2
// target=native
//
// :2:5: error: extern structs cannot contain fields of type 'fn() callconv(.C) void'
// :2:5: note: type has no guaranteed in-memory representation
// :2:5: note: use '*const ' to make a function pointer type
// :8:13: error: C pointers cannot point to non-C-ABI-compatible type '[4]fn() callconv(.C) void'
// :8:13: note: type has no guaranteed in-memory representation
// :8:13: note: use '*const ' to make a function pointer type

View File

@ -0,0 +1,26 @@
inline fn bit_count(value: i32) i32 {
var i = value;
// Algo from : http://aggregate.ee.engr.uky.edu/MAGIC/#Population%20Count%20(ones%20Count)
i -= ((i >> 1) & 0x55555555);
i = (i & 0x33333333) + ((i >> 2) & 0x33333333);
i = (((i >> 4) + i) & 0x0F0F0F0F);
i += (i >> 8);
i += (i >> 16);
return (i & 0x0000003F);
}
inline fn number_of_trailing_zeros(i: i32) u32 {
return @as(u32, bit_count((i & -i) - 1));
}
export fn entry() void {
_ = number_of_trailing_zeros(0);
}
// error
// backend=stage2
// target=native
//
// :13:30: error: expected type 'u32', found 'i32'
// :13:30: note: unsigned 32-bit int cannot represent all possible signed 32-bit values
// :17:33: note: called from here

View File

@ -0,0 +1,14 @@
pub export fn entry() void {
var byte: u8 = 1;
switch (byte) {
byte => {},
else => {},
}
}
// error
// backend=stage2
// target=native
//
// :4:9: error: unable to resolve comptime value
// :4:9: note: switch prong values must be comptime known