diff --git a/src/AstGen.zig b/src/AstGen.zig index 1f4bd7dd66..7534a0d2cc 100644 --- a/src/AstGen.zig +++ b/src/AstGen.zig @@ -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| { diff --git a/src/Sema.zig b/src/Sema.zig index 02f6712d6c..2bebc95efd 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -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; diff --git a/src/Zir.zig b/src/Zir.zig index add8bad801..890109fcb0 100644 --- a/src/Zir.zig +++ b/src/Zir.zig @@ -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; diff --git a/src/print_zir.zig b/src/print_zir.zig index d383664c16..8f055e9ddd 100644 --- a/src/print_zir.zig +++ b/src/print_zir.zig @@ -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), diff --git a/src/type.zig b/src/type.zig index 1fef525062..7c35eccbe6 100644 --- a/src/type.zig +++ b/src/type.zig @@ -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); diff --git a/test/behavior.zig b/test/behavior.zig index 8f4657e634..6c181d38bb 100644 --- a/test/behavior.zig +++ b/test/behavior.zig @@ -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"); diff --git a/test/behavior/bugs/12890.zig b/test/behavior/bugs/12890.zig new file mode 100644 index 0000000000..cae79136dc --- /dev/null +++ b/test/behavior/bugs/12890.zig @@ -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); +} diff --git a/test/behavior/error.zig b/test/behavior/error.zig index ba5bdfdc20..c93d7aebf7 100644 --- a/test/behavior/error.zig +++ b/test/behavior/error.zig @@ -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 diff --git a/test/behavior/eval.zig b/test/behavior/eval.zig index 849e0ca6cc..5faabe142f 100644 --- a/test/behavior/eval.zig +++ b/test/behavior/eval.zig @@ -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); +} diff --git a/test/behavior/inline_switch.zig b/test/behavior/inline_switch.zig index ecc7bba280..153e36ca2f 100644 --- a/test/behavior/inline_switch.zig +++ b/test/behavior/inline_switch.zig @@ -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); + }, + } +} diff --git a/test/behavior/pointers.zig b/test/behavior/pointers.zig index 91d398e84d..b0fb0f3a42 100644 --- a/test/behavior/pointers.zig +++ b/test/behavior/pointers.zig @@ -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)); +} diff --git a/test/behavior/sizeof_and_typeof.zig b/test/behavior/sizeof_and_typeof.zig index 748fefa695..45a99f9d32 100644 --- a/test/behavior/sizeof_and_typeof.zig +++ b/test/behavior/sizeof_and_typeof.zig @@ -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); +} diff --git a/test/cases/compile_errors/accessing_runtime_paramter_outside_function_scope.zig b/test/cases/compile_errors/accessing_runtime_paramter_outside_function_scope.zig new file mode 100644 index 0000000000..583b7128c7 --- /dev/null +++ b/test/cases/compile_errors/accessing_runtime_paramter_outside_function_scope.zig @@ -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 diff --git a/test/cases/compile_errors/fieldParentPtr_on_comptime_field.zig b/test/cases/compile_errors/fieldParentPtr_on_comptime_field.zig new file mode 100644 index 0000000000..fb95ea691c --- /dev/null +++ b/test/cases/compile_errors/fieldParentPtr_on_comptime_field.zig @@ -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 diff --git a/test/cases/compile_errors/generic_funciton_instantiation_inherits_parent_branch_quota.zig b/test/cases/compile_errors/generic_funciton_instantiation_inherits_parent_branch_quota.zig new file mode 100644 index 0000000000..1d45ce86db --- /dev/null +++ b/test/cases/compile_errors/generic_funciton_instantiation_inherits_parent_branch_quota.zig @@ -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 diff --git a/test/cases/compile_errors/non_void_error_union_payload_ignored.zig b/test/cases/compile_errors/non_void_error_union_payload_ignored.zig new file mode 100644 index 0000000000..41b5b6803d --- /dev/null +++ b/test/cases/compile_errors/non_void_error_union_payload_ignored.zig @@ -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 '|_|' diff --git a/test/cases/compile_errors/old_fn_ptr_in_extern_context.zig b/test/cases/compile_errors/old_fn_ptr_in_extern_context.zig new file mode 100644 index 0000000000..db778408bf --- /dev/null +++ b/test/cases/compile_errors/old_fn_ptr_in_extern_context.zig @@ -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 diff --git a/test/cases/compile_errors/sema_src_used_after_inline_call.zig b/test/cases/compile_errors/sema_src_used_after_inline_call.zig new file mode 100644 index 0000000000..4e3f31e410 --- /dev/null +++ b/test/cases/compile_errors/sema_src_used_after_inline_call.zig @@ -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 diff --git a/test/cases/compile_errors/stage1/obj/runtime_value_in_switch_prong.zig b/test/cases/compile_errors/stage1/obj/runtime_value_in_switch_prong.zig new file mode 100644 index 0000000000..0d15195ef1 --- /dev/null +++ b/test/cases/compile_errors/stage1/obj/runtime_value_in_switch_prong.zig @@ -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