diff options
| author | Veikka Tuominen <git@vexu.eu> | 2022-07-22 13:07:32 +0300 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2022-07-22 13:07:32 +0300 |
| commit | 8e75ba653b03477229cf72211e8a8bfe7b071254 (patch) | |
| tree | 4689ba96ff2ba3090d83c8b8c27f3f61ae34df92 /src/Sema.zig | |
| parent | 460211431f407c9f707e3ac3bbff61610a487926 (diff) | |
| parent | b749487f48cad2dd779d7fa6d0afcafc975ba26c (diff) | |
| download | zig-8e75ba653b03477229cf72211e8a8bfe7b071254.tar.gz zig-8e75ba653b03477229cf72211e8a8bfe7b071254.zip | |
Merge pull request #12117 from Vexu/stage2-compile-errors
Stage2: explain why value must be comptime known
Diffstat (limited to 'src/Sema.zig')
| -rw-r--r-- | src/Sema.zig | 1565 |
1 files changed, 974 insertions, 591 deletions
diff --git a/src/Sema.zig b/src/Sema.zig index 702991bf5e..ce14b2ae45 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -768,7 +768,7 @@ fn analyzeBodyInner( .optional_type => try sema.zirOptionalType(block, inst), .param_type => try sema.zirParamType(block, inst), .ptr_type => try sema.zirPtrType(block, inst), - .ptr_type_simple => try sema.zirPtrTypeSimple(block, inst), + .overflow_arithmetic_ptr => try sema.zirOverflowArithmeticPtr(block, inst), .ref => try sema.zirRef(block, inst), .ret_err_value_code => try sema.zirRetErrValueCode(inst), .shr => try sema.zirShr(block, inst, .shr), @@ -892,7 +892,7 @@ fn analyzeBodyInner( .shl_sat => try sema.zirShl(block, inst, .shl_sat), .ret_ptr => try sema.zirRetPtr(block, inst), - .ret_type => try sema.zirRetType(block, inst), + .ret_type => try sema.addType(sema.fn_ret_ty), // Instructions that we know to *always* be noreturn based solely on their tag. // These functions match the return type of analyzeBody so that we can @@ -1173,7 +1173,7 @@ fn analyzeBodyInner( } else { const src_node = sema.code.instructions.items(.data)[inst].node; const src = LazySrcLoc.nodeOffset(src_node); - try sema.requireRuntimeBlock(block, src); + try sema.requireFunctionBlock(block, src); break always_noreturn; } }, @@ -1303,7 +1303,7 @@ fn analyzeBodyInner( const extra = sema.code.extraData(Zir.Inst.CondBr, inst_data.payload_index); const then_body = sema.code.extra[extra.end..][0..extra.data.then_body_len]; const else_body = sema.code.extra[extra.end + then_body.len ..][0..extra.data.else_body_len]; - const cond = try sema.resolveInstConst(block, cond_src, extra.data.condition); + const cond = try sema.resolveInstConst(block, cond_src, extra.data.condition, "condition in comptime branch must be comptime known"); const inline_body = if (cond.val.toBool()) then_body else else_body; const break_data = (try sema.analyzeBodyBreak(block, inline_body)) orelse break always_noreturn; @@ -1319,7 +1319,7 @@ fn analyzeBodyInner( const extra = sema.code.extraData(Zir.Inst.CondBr, inst_data.payload_index); const then_body = sema.code.extra[extra.end..][0..extra.data.then_body_len]; const else_body = sema.code.extra[extra.end + then_body.len ..][0..extra.data.else_body_len]; - const cond = try sema.resolveInstConst(block, cond_src, extra.data.condition); + const cond = try sema.resolveInstConst(block, cond_src, extra.data.condition, "condition in comptime branch must be comptime known"); const inline_body = if (cond.val.toBool()) then_body else else_body; const break_data = (try sema.analyzeBodyBreak(block, inline_body)) orelse break always_noreturn; @@ -1339,7 +1339,7 @@ fn analyzeBodyInner( const err_union = try sema.resolveInst(extra.data.operand); const is_non_err = try sema.analyzeIsNonErrComptimeOnly(block, operand_src, err_union); assert(is_non_err != .none); - const is_non_err_tv = try sema.resolveInstConst(block, operand_src, is_non_err); + const is_non_err_tv = try sema.resolveInstConst(block, operand_src, is_non_err, "try operand inside comptime block must be comptime known"); if (is_non_err_tv.val.toBool()) { const err_union_ty = sema.typeOf(err_union); break :blk try sema.analyzeErrUnionPayload(block, src, err_union_ty, err_union, operand_src, false); @@ -1395,7 +1395,7 @@ fn analyzeBodyInner( const err_union = try sema.analyzeLoad(block, src, operand, operand_src); const is_non_err = try sema.analyzeIsNonErrComptimeOnly(block, operand_src, err_union); assert(is_non_err != .none); - const is_non_err_tv = try sema.resolveInstConst(block, operand_src, is_non_err); + const is_non_err_tv = try sema.resolveInstConst(block, operand_src, is_non_err, "try operand inside comptime block must be comptime known"); if (is_non_err_tv.val.toBool()) { break :blk try sema.analyzeErrUnionPayloadPtr(block, src, operand, false, false); } @@ -1478,11 +1478,12 @@ fn resolveConstBool( block: *Block, src: LazySrcLoc, zir_ref: Zir.Inst.Ref, + reason: []const u8, ) !bool { const air_inst = try sema.resolveInst(zir_ref); const wanted_type = Type.bool; const coerced_inst = try sema.coerce(block, wanted_type, air_inst, src); - const val = try sema.resolveConstValue(block, src, coerced_inst); + const val = try sema.resolveConstValue(block, src, coerced_inst, reason); return val.toBool(); } @@ -1491,11 +1492,12 @@ pub fn resolveConstString( block: *Block, src: LazySrcLoc, zir_ref: Zir.Inst.Ref, + reason: []const u8, ) ![]u8 { const air_inst = try sema.resolveInst(zir_ref); const wanted_type = Type.initTag(.const_slice_u8); const coerced_inst = try sema.coerce(block, wanted_type, air_inst, src); - const val = try sema.resolveConstValue(block, src, coerced_inst); + const val = try sema.resolveConstValue(block, src, coerced_inst, reason); return val.toAllocatedBytes(wanted_type, sema.arena, sema.mod); } @@ -1514,7 +1516,7 @@ fn analyzeAsType( ) !Type { const wanted_type = Type.initTag(.@"type"); const coerced_inst = try sema.coerce(block, wanted_type, air_inst, src); - const val = try sema.resolveConstValue(block, src, coerced_inst); + const val = try sema.resolveConstValue(block, src, coerced_inst, "types must be comptime known"); var buffer: Value.ToTypeBuffer = undefined; const ty = val.toType(&buffer); return ty.copy(sema.arena); @@ -1567,12 +1569,13 @@ fn resolveValue( block: *Block, src: LazySrcLoc, air_ref: Air.Inst.Ref, + reason: []const u8, ) CompileError!Value { if (try sema.resolveMaybeUndefValAllowVariables(block, src, air_ref)) |val| { if (val.tag() == .generic_poison) return error.GenericPoison; return val; } - return sema.failWithNeededComptime(block, src); + return sema.failWithNeededComptime(block, src, reason); } /// Value Tag `variable` will cause a compile error. @@ -1582,15 +1585,16 @@ fn resolveConstMaybeUndefVal( block: *Block, src: LazySrcLoc, inst: Air.Inst.Ref, + reason: []const u8, ) CompileError!Value { if (try sema.resolveMaybeUndefValAllowVariables(block, src, inst)) |val| { switch (val.tag()) { - .variable => return sema.failWithNeededComptime(block, src), + .variable => return sema.failWithNeededComptime(block, src, reason), .generic_poison => return error.GenericPoison, else => return val, } } - return sema.failWithNeededComptime(block, src); + return sema.failWithNeededComptime(block, src, reason); } /// Will not return Value Tags: `variable`, `undef`. Instead they will emit compile errors. @@ -1600,16 +1604,17 @@ fn resolveConstValue( block: *Block, src: LazySrcLoc, air_ref: Air.Inst.Ref, + reason: []const u8, ) CompileError!Value { if (try sema.resolveMaybeUndefValAllowVariables(block, src, air_ref)) |val| { switch (val.tag()) { .undef => return sema.failWithUseOfUndef(block, src), - .variable => return sema.failWithNeededComptime(block, src), + .variable => return sema.failWithNeededComptime(block, src, reason), .generic_poison => return error.GenericPoison, else => return val, } } - return sema.failWithNeededComptime(block, src); + return sema.failWithNeededComptime(block, src, reason); } /// Value Tag `variable` causes this function to return `null`. @@ -1697,8 +1702,15 @@ fn resolveMaybeUndefValAllowVariables( } } -fn failWithNeededComptime(sema: *Sema, block: *Block, src: LazySrcLoc) CompileError { - return sema.fail(block, src, "unable to resolve comptime value", .{}); +fn failWithNeededComptime(sema: *Sema, block: *Block, src: LazySrcLoc, reason: []const u8) CompileError { + const msg = msg: { + const msg = try sema.errMsg(block, src, "unable to resolve comptime value", .{}); + errdefer msg.destroy(sema.gpa); + + try sema.errNote(block, src, msg, "{s}", .{reason}); + break :msg msg; + }; + return sema.failWithOwnedErrorMsg(block, msg); } fn failWithUseOfUndef(sema: *Sema, block: *Block, src: LazySrcLoc) CompileError { @@ -1870,15 +1882,24 @@ fn analyzeAsAlign( src: LazySrcLoc, air_ref: Air.Inst.Ref, ) !u32 { - const alignment_big = try sema.analyzeAsInt(block, src, air_ref, align_ty); + const alignment_big = try sema.analyzeAsInt(block, src, air_ref, align_ty, "alignment must be comptime known"); const alignment = @intCast(u32, alignment_big); // We coerce to u16 in the prev line. + try sema.validateAlign(block, src, alignment); + return alignment; +} + +fn validateAlign( + sema: *Sema, + block: *Block, + src: LazySrcLoc, + alignment: u32, +) !void { if (alignment == 0) return sema.fail(block, src, "alignment must be >= 1", .{}); if (!std.math.isPowerOfTwo(alignment)) { return sema.fail(block, src, "alignment value '{d}' is not a power of two", .{ alignment, }); } - return alignment; } pub fn resolveAlign( @@ -1897,9 +1918,10 @@ fn resolveInt( src: LazySrcLoc, zir_ref: Zir.Inst.Ref, dest_ty: Type, + reason: []const u8, ) !u64 { const air_ref = try sema.resolveInst(zir_ref); - return analyzeAsInt(sema, block, src, air_ref, dest_ty); + return analyzeAsInt(sema, block, src, air_ref, dest_ty, reason); } fn analyzeAsInt( @@ -1908,9 +1930,10 @@ fn analyzeAsInt( src: LazySrcLoc, air_ref: Air.Inst.Ref, dest_ty: Type, + reason: []const u8, ) !u64 { const coerced = try sema.coerce(block, dest_ty, air_ref, src); - const val = try sema.resolveConstValue(block, src, coerced); + const val = try sema.resolveConstValue(block, src, coerced, reason); const target = sema.mod.getTarget(); return (try val.getUnsignedIntAdvanced(target, sema.kit(block, src))).?; } @@ -1922,9 +1945,10 @@ pub fn resolveInstConst( block: *Block, src: LazySrcLoc, zir_ref: Zir.Inst.Ref, + reason: []const u8, ) CompileError!TypedValue { const air_ref = try sema.resolveInst(zir_ref); - const val = try sema.resolveConstValue(block, src, air_ref); + const val = try sema.resolveConstValue(block, src, air_ref, reason); return TypedValue{ .ty = sema.typeOf(air_ref), .val = val, @@ -1938,9 +1962,10 @@ pub fn resolveInstValue( block: *Block, src: LazySrcLoc, zir_ref: Zir.Inst.Ref, + reason: []const u8, ) CompileError!TypedValue { const air_ref = try sema.resolveInst(zir_ref); - const val = try sema.resolveValue(block, src, air_ref); + const val = try sema.resolveValue(block, src, air_ref, reason); return TypedValue{ .ty = sema.typeOf(air_ref), .val = val, @@ -1974,7 +1999,7 @@ fn zirCoerceResultPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileE defer trash_block.instructions.deinit(sema.gpa); const operand = try trash_block.addBitCast(pointee_ty, .void_value); - try sema.requireRuntimeBlock(block, src); + try sema.requireFunctionBlock(block, src); const ptr_ty = try Type.ptr(sema.arena, sema.mod, .{ .pointee_type = pointee_ty, .@"align" = inferred_alloc.alignment, @@ -2259,7 +2284,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) catch unreachable; + const arg_val = sema.resolveConstMaybeUndefVal(block, .unneeded, arg, undefined) catch unreachable; if (arg_i != 0) try buf.appendSlice(","); try buf.writer().print("{}", .{arg_val.fmtValue(sema.typeOf(arg), sema.mod)}); @@ -2319,6 +2344,7 @@ fn zirEnumDecl( extra_index += 1; break :blk LazySrcLoc.nodeOffset(node_offset); } else sema.src; + const tag_ty_src: LazySrcLoc = .{ .node_offset_container_tag = src.node_offset.x }; const tag_type_ref = if (small.has_tag_type) blk: { const tag_type_ref = @intToEnum(Zir.Inst.Ref, sema.code.extra[extra_index]); @@ -2344,8 +2370,10 @@ fn zirEnumDecl( break :blk decls_len; } else 0; + var done = false; + var new_decl_arena = std.heap.ArenaAllocator.init(gpa); - errdefer new_decl_arena.deinit(); + errdefer if (!done) new_decl_arena.deinit(); const new_decl_arena_allocator = new_decl_arena.allocator(); const enum_obj = try new_decl_arena_allocator.create(Module.EnumFull); @@ -2362,7 +2390,7 @@ fn zirEnumDecl( }, small.name_strategy, "enum", inst); const new_decl = mod.declPtr(new_decl_index); new_decl.owns_tv = true; - errdefer mod.abortAnonDecl(new_decl_index); + errdefer if (!done) mod.abortAnonDecl(new_decl_index); enum_obj.* = .{ .owner_decl = new_decl_index, @@ -2381,19 +2409,28 @@ fn zirEnumDecl( &enum_obj.namespace, new_decl, new_decl.name, }); + try new_decl.finalizeNewArena(&new_decl_arena); + const decl_val = try sema.analyzeDeclVal(block, src, new_decl_index); + done = true; + + var decl_arena = new_decl.value_arena.?.promote(gpa); + defer new_decl.value_arena.?.* = decl_arena.state; + const decl_arena_allocator = decl_arena.allocator(); + extra_index = try mod.scanNamespace(&enum_obj.namespace, extra_index, decls_len, new_decl); const body = sema.code.extra[extra_index..][0..body_len]; if (fields_len == 0) { assert(body.len == 0); if (tag_type_ref != .none) { - // TODO better source location - const ty = try sema.resolveType(block, src, tag_type_ref); + const ty = try sema.resolveType(block, tag_ty_src, tag_type_ref); + if (ty.zigTypeTag() != .Int and ty.zigTypeTag() != .ComptimeInt) { + return sema.fail(block, tag_ty_src, "expected integer tag type, found '{}'", .{ty.fmt(sema.mod)}); + } enum_obj.tag_ty = try ty.copy(new_decl_arena_allocator); enum_obj.tag_ty_inferred = false; } - try new_decl.finalizeNewArena(&new_decl_arena); - return sema.analyzeDeclVal(block, src, new_decl_index); + return decl_val; } extra_index += body.len; @@ -2446,13 +2483,15 @@ fn zirEnumDecl( try wip_captures.finalize(); if (tag_type_ref != .none) { - // TODO better source location - const ty = try sema.resolveType(block, src, tag_type_ref); - enum_obj.tag_ty = try ty.copy(new_decl_arena_allocator); + const ty = try sema.resolveType(block, tag_ty_src, tag_type_ref); + if (ty.zigTypeTag() != .Int and ty.zigTypeTag() != .ComptimeInt) { + return sema.fail(block, tag_ty_src, "expected integer tag type, found '{}'", .{ty.fmt(sema.mod)}); + } + enum_obj.tag_ty = try ty.copy(decl_arena_allocator); enum_obj.tag_ty_inferred = false; } else { const bits = std.math.log2_int_ceil(usize, fields_len); - enum_obj.tag_ty = try Type.Tag.int_unsigned.create(new_decl_arena_allocator, bits); + enum_obj.tag_ty = try Type.Tag.int_unsigned.create(decl_arena_allocator, bits); enum_obj.tag_ty_inferred = true; } } @@ -2463,12 +2502,12 @@ fn zirEnumDecl( } } - try enum_obj.fields.ensureTotalCapacity(new_decl_arena_allocator, fields_len); + try enum_obj.fields.ensureTotalCapacity(decl_arena_allocator, fields_len); const any_values = for (sema.code.extra[body_end..][0..bit_bags_count]) |bag| { if (bag != 0) break true; } else false; if (any_values) { - try enum_obj.values.ensureTotalCapacityContext(new_decl_arena_allocator, fields_len, .{ + try enum_obj.values.ensureTotalCapacityContext(decl_arena_allocator, fields_len, .{ .ty = enum_obj.tag_ty, .mod = mod, }); @@ -2493,7 +2532,7 @@ fn zirEnumDecl( extra_index += 1; // This string needs to outlive the ZIR code. - const field_name = try new_decl_arena_allocator.dupe(u8, field_name_zir); + const field_name = try decl_arena_allocator.dupe(u8, field_name_zir); const gop = enum_obj.fields.getOrPutAssumeCapacity(field_name); if (gop.found_existing) { @@ -2515,9 +2554,9 @@ fn zirEnumDecl( // TODO: if we need to report an error here, use a source location // that points to this default value expression rather than the struct. // But only resolve the source location if we need to emit a compile error. - const tag_val = (try sema.resolveInstConst(block, src, tag_val_ref)).val; + const tag_val = (try sema.resolveInstConst(block, src, tag_val_ref, "enum tag value must be comptime known")).val; last_tag_val = tag_val; - const copied_tag_val = try tag_val.copy(new_decl_arena_allocator); + const copied_tag_val = try tag_val.copy(decl_arena_allocator); enum_obj.values.putAssumeCapacityNoClobberContext(copied_tag_val, {}, .{ .ty = enum_obj.tag_ty, .mod = mod, @@ -2528,16 +2567,14 @@ fn zirEnumDecl( else Value.zero; last_tag_val = tag_val; - const copied_tag_val = try tag_val.copy(new_decl_arena_allocator); + const copied_tag_val = try tag_val.copy(decl_arena_allocator); enum_obj.values.putAssumeCapacityNoClobberContext(copied_tag_val, {}, .{ .ty = enum_obj.tag_ty, .mod = mod, }); } } - - try new_decl.finalizeNewArena(&new_decl_arena); - return sema.analyzeDeclVal(block, src, new_decl_index); + return decl_val; } fn zirUnionDecl( @@ -2738,7 +2775,6 @@ fn zirRetPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air. const inst_data = sema.code.instructions.items(.data)[inst].node; const src = LazySrcLoc.nodeOffset(inst_data); - try sema.requireFunctionBlock(block, src); if (block.is_comptime or try sema.typeRequiresComptime(block, src, sema.fn_ret_ty)) { const fn_ret_ty = try sema.resolveTypeFields(block, src, sema.fn_ret_ty); @@ -2770,16 +2806,6 @@ fn zirRef(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Ins return sema.analyzeRef(block, inst_data.src(), operand); } -fn zirRetType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { - const tracy = trace(@src()); - defer tracy.end(); - - const inst_data = sema.code.instructions.items(.data)[inst].node; - const src = LazySrcLoc.nodeOffset(inst_data); - try sema.requireFunctionBlock(block, src); - return sema.addType(sema.fn_ret_ty); -} - fn zirEnsureResultUsed(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void { const tracy = trace(@src()); defer tracy.end(); @@ -2925,7 +2951,7 @@ fn zirAllocExtended( try sema.validateVarType(block, ty_src, var_ty, false); } const target = sema.mod.getTarget(); - try sema.requireRuntimeBlock(block, src); + try sema.requireFunctionBlock(block, src); try sema.resolveTypeLayout(block, src, var_ty); const ptr_type = try Type.ptr(sema.arena, sema.mod, .{ .pointee_type = var_ty, @@ -3024,7 +3050,7 @@ fn zirMakePtrConst(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileErro return sema.addConstant(const_ptr_ty, val); } - try sema.requireRuntimeBlock(block, src); + try sema.requireFunctionBlock(block, src); return block.addBitCast(const_ptr_ty, alloc); } @@ -3061,7 +3087,7 @@ fn zirAlloc(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I .pointee_type = var_ty, .@"addrspace" = target_util.defaultAddressSpace(target, .local), }); - try sema.requireRuntimeBlock(block, var_decl_src); + try sema.requireFunctionBlock(block, var_decl_src); try sema.queueFullTypeResolution(var_ty); return block.addTy(.alloc, ptr_type); } @@ -3083,7 +3109,7 @@ fn zirAllocMut(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai .pointee_type = var_ty, .@"addrspace" = target_util.defaultAddressSpace(target, .local), }); - try sema.requireRuntimeBlock(block, var_decl_src); + try sema.requireFunctionBlock(block, var_decl_src); try sema.queueFullTypeResolution(var_ty); return block.addTy(.alloc, ptr_type); } @@ -3272,7 +3298,7 @@ fn zirResolveInferredAlloc(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Com return; } - try sema.requireRuntimeBlock(block, src); + try sema.requireFunctionBlock(block, src); try sema.queueFullTypeResolution(final_elem_ty); // Change it to a normal alloc. @@ -3593,7 +3619,7 @@ fn validateUnionInit( return; } - try sema.requireRuntimeBlock(block, init_src); + try sema.requireFunctionBlock(block, init_src); const new_tag = try sema.addConstant(union_obj.tag_ty, tag_val); _ = try block.addBinOp(.set_union_tag, union_ptr, new_tag); } @@ -3859,7 +3885,7 @@ fn zirValidateArrayInit( // any ZIR instructions at comptime; we need to do that here. if (array_ty.sentinel()) |sentinel_val| { const array_len_ref = try sema.addIntUnsigned(Type.usize, array_len); - const sentinel_ptr = try sema.elemPtrArray(block, init_src, array_ptr, init_src, array_len_ref, true); + const sentinel_ptr = try sema.elemPtrArray(block, init_src, init_src, array_ptr, init_src, array_len_ref, true); const sentinel = try sema.addConstant(array_ty.childType(), sentinel_val); try sema.storePtr2(block, init_src, sentinel_ptr, init_src, sentinel, init_src, .store); } @@ -4207,10 +4233,8 @@ fn storeToInferredAllocComptime( const operand_ty = sema.typeOf(operand); // There will be only one store_to_inferred_ptr because we are running at comptime. // The alloc will turn into a Decl. - if (try sema.resolveMaybeUndefValAllowVariables(block, src, operand)) |operand_val| { - if (operand_val.tag() == .variable) { - return sema.failWithNeededComptime(block, src); - } + if (try sema.resolveMaybeUndefValAllowVariables(block, src, operand)) |operand_val| store: { + if (operand_val.tag() == .variable) break :store; var anon_decl = try block.startAnonDecl(src); defer anon_decl.deinit(); iac.data.decl_index = try anon_decl.finish( @@ -4219,15 +4243,15 @@ fn storeToInferredAllocComptime( iac.data.alignment, ); return; - } else { - return sema.failWithNeededComptime(block, src); } + + return sema.failWithNeededComptime(block, src, "value being stored to a comptime variable must be comptime known"); } fn zirSetEvalBranchQuota(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void { const inst_data = sema.code.instructions.items(.data)[inst].un_node; const src = inst_data.src(); - const quota = @intCast(u32, try sema.resolveInt(block, src, inst_data.operand, Type.u32)); + const quota = @intCast(u32, try sema.resolveInt(block, src, inst_data.operand, Type.u32, "eval branch quota must be comptime known")); sema.branch_quota = @maximum(sema.branch_quota, quota); } @@ -4282,7 +4306,7 @@ fn zirParamType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A var param_index = inst_data.param_index; const fn_ty = if (callee_ty.tag() == .bound_fn) fn_ty: { - const bound_fn_val = try sema.resolveConstValue(block, callee_src, callee); + const bound_fn_val = try sema.resolveConstValue(block, .unneeded, callee, undefined); const bound_fn = bound_fn_val.castTag(.bound_fn).?.data; const fn_ty = sema.typeOf(bound_fn.func_inst); param_index += 1; @@ -4417,7 +4441,7 @@ fn zirCompileError(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileErro const inst_data = sema.code.instructions.items(.data)[inst].un_node; const src = inst_data.src(); const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; - const msg = try sema.resolveConstString(block, operand_src, inst_data.operand); + const msg = try sema.resolveConstString(block, operand_src, inst_data.operand, "compile error string must be comptime known"); return sema.fail(block, src, "{s}", .{msg}); } @@ -4466,7 +4490,7 @@ fn zirPanic(sema: *Sema, block: *Block, inst: Zir.Inst.Index, force_comptime: bo if (block.is_comptime or force_comptime) { return sema.fail(block, src, "encountered @panic at comptime", .{}); } - try sema.requireRuntimeBlock(block, src); + try sema.requireFunctionBlock(block, src); return sema.panicWithMsg(block, src, msg_inst); } @@ -4854,7 +4878,7 @@ fn zirExportValue(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError const src = inst_data.src(); const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; const options_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node }; - const operand = try sema.resolveInstConst(block, operand_src, extra.operand); + const operand = try sema.resolveInstConst(block, operand_src, extra.operand, "export target must be comptime known"); const options = try sema.resolveExportOptions(block, options_src, extra.options); const decl_index = switch (operand.val.tag()) { .function => operand.val.castTag(.function).?.data.owner_decl, @@ -4989,7 +5013,7 @@ fn zirSetAlignStack(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.Inst fn zirSetCold(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void { const inst_data = sema.code.instructions.items(.data)[inst].un_node; const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; - const is_cold = try sema.resolveConstBool(block, operand_src, inst_data.operand); + const is_cold = try sema.resolveConstBool(block, operand_src, inst_data.operand, "operand to @setCold must be comptime known"); const func = sema.func orelse return; // does nothing outside a function func.is_cold = is_cold; } @@ -4997,7 +5021,7 @@ fn zirSetCold(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!voi fn zirSetFloatMode(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!void { const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data; const src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = extra.node }; - const float_mode = try sema.resolveBuiltinEnum(block, src, extra.operand, "FloatMode"); + const float_mode = try sema.resolveBuiltinEnum(block, src, extra.operand, "FloatMode", "operand to @setFloatMode must be comptime known"); switch (float_mode) { .Strict => return, .Optimized => { @@ -5009,7 +5033,7 @@ fn zirSetFloatMode(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstD fn zirSetRuntimeSafety(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void { const inst_data = sema.code.instructions.items(.data)[inst].un_node; const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; - block.want_safety = try sema.resolveConstBool(block, operand_src, inst_data.operand); + block.want_safety = try sema.resolveConstBool(block, operand_src, inst_data.operand, "operand to @setRuntimeSafety must be comptime known"); } fn zirFence(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!void { @@ -5017,7 +5041,7 @@ fn zirFence(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) Co const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data; const order_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = extra.node }; - const order = try sema.resolveAtomicOrder(block, order_src, extra.operand); + const order = try sema.resolveAtomicOrder(block, order_src, extra.operand, "atomic order of @fence must be comptime known"); if (@enumToInt(order) < @enumToInt(std.builtin.AtomicOrder.Acquire)) { return sema.fail(block, order_src, "atomic ordering must be Acquire or stricter", .{}); @@ -5292,7 +5316,7 @@ fn zirCall( // Desugar bound functions here if (func_type.tag() == .bound_fn) { - const bound_func = try sema.resolveValue(block, func_src, func); + const bound_func = try sema.resolveValue(block, .unneeded, func, undefined); 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); @@ -5489,7 +5513,8 @@ fn analyzeCall( } const result: Air.Inst.Ref = if (is_inline_call) res: { - const func_val = try sema.resolveConstValue(block, func_src, func); + // TODO explain why function is being called at comptime + const func_val = try sema.resolveConstValue(block, func_src, func, "function being called at comptime must be comptime known"); const module_fn = switch (func_val.tag()) { .decl_ref => mod.declPtr(func_val.castTag(.decl_ref).?.data).val.castTag(.function).?.data, .function => func_val.castTag(.function).?.data, @@ -5588,82 +5613,39 @@ fn analyzeCall( // which means its parameter type expressions must be resolved in order and used // to successively coerce the arguments. const fn_info = sema.code.getFnInfo(module_fn.zir_body_inst); - const zir_tags = sema.code.instructions.items(.tag); var arg_i: usize = 0; - for (fn_info.param_body) |inst| switch (zir_tags[inst]) { - .param, .param_comptime => { - // Evaluate the parameter type expression now that previous ones have - // been mapped, and coerce the corresponding argument to it. - const pl_tok = sema.code.instructions.items(.data)[inst].pl_tok; - const param_src = pl_tok.src(); - const extra = sema.code.extraData(Zir.Inst.Param, pl_tok.payload_index); - const param_body = sema.code.extra[extra.end..][0..extra.data.body_len]; - const param_ty_inst = try sema.resolveBody(&child_block, param_body, inst); - const param_ty = try sema.analyzeAsType(&child_block, param_src, param_ty_inst); - new_fn_info.param_types[arg_i] = param_ty; - const arg_src = call_src; // TODO: better source location - const casted_arg = try sema.coerce(&child_block, param_ty, uncasted_args[arg_i], arg_src); - try sema.inst_map.putNoClobber(gpa, inst, casted_arg); - - if (is_comptime_call) { - const arg_val = try sema.resolveConstMaybeUndefVal(&child_block, arg_src, casted_arg); - switch (arg_val.tag()) { - .generic_poison, .generic_poison_type => { - // This function is currently evaluated as part of an as-of-yet unresolvable - // parameter or return type. - return error.GenericPoison; - }, - else => { - // Needed so that lazy values do not trigger - // assertion due to type not being resolved - // when the hash function is called. - try sema.resolveLazyValue(&child_block, arg_src, arg_val); - }, - } - should_memoize = should_memoize and !arg_val.canMutateComptimeVarState(); - memoized_call_key.args[arg_i] = .{ - .ty = param_ty, - .val = arg_val, - }; - } - - arg_i += 1; - continue; - }, - .param_anytype, .param_anytype_comptime => { - // No coercion needed. - const uncasted_arg = uncasted_args[arg_i]; - new_fn_info.param_types[arg_i] = sema.typeOf(uncasted_arg); - try sema.inst_map.putNoClobber(gpa, inst, uncasted_arg); - - if (is_comptime_call) { - const arg_src = call_src; // TODO: better source location - const arg_val = try sema.resolveConstMaybeUndefVal(&child_block, arg_src, uncasted_arg); - switch (arg_val.tag()) { - .generic_poison, .generic_poison_type => { - // This function is currently evaluated as part of an as-of-yet unresolvable - // parameter or return type. - return error.GenericPoison; - }, - else => { - // Needed so that lazy values do not trigger - // assertion due to type not being resolved - // when the hash function is called. - try sema.resolveLazyValue(&child_block, arg_src, arg_val); - }, - } - should_memoize = should_memoize and !arg_val.canMutateComptimeVarState(); - memoized_call_key.args[arg_i] = .{ - .ty = sema.typeOf(uncasted_arg), - .val = arg_val, - }; - } - - arg_i += 1; - continue; - }, - else => continue, - }; + for (fn_info.param_body) |inst| { + sema.analyzeInlineCallArg( + &child_block, + .unneeded, + inst, + new_fn_info, + &arg_i, + uncasted_args, + is_comptime_call, + &should_memoize, + memoized_call_key, + ) catch |err| switch (err) { + error.NeededSourceLocation => { + const decl = sema.mod.declPtr(block.src_decl); + try sema.analyzeInlineCallArg( + // Intentionally use the wrong block here since we know it's + // going to fail and `argSrc` is relative to `block.src_decl`. + block, + Module.argSrc(call_src.node_offset.x, sema.gpa, decl, arg_i), + inst, + new_fn_info, + &arg_i, + uncasted_args, + is_comptime_call, + &should_memoize, + memoized_call_key, + ); + return error.AnalysisFail; + }, + else => |e| return e, + }; + } // In case it is a generic function with an expression for the return type that depends // on parameters, we must now do the same for the return type as we just did with @@ -5719,6 +5701,7 @@ fn analyzeCall( if (!is_comptime_call) { try sema.emitDbgInline(block, parent_func.?, module_fn, new_func_resolved_ty, .dbg_inline_begin); + const zir_tags = sema.code.instructions.items(.tag); for (fn_info.param_body) |param| switch (zir_tags[param]) { .param, .param_comptime => { const inst_data = sema.code.instructions.items(.data)[param].pl_tok; @@ -5764,7 +5747,7 @@ fn analyzeCall( } if (should_memoize and is_comptime_call) { - const result_val = try sema.resolveConstMaybeUndefVal(block, call_src, result); + const result_val = try sema.resolveConstMaybeUndefVal(block, .unneeded, result, undefined); // TODO: check whether any external comptime memory was mutated by the // comptime function call. If so, then do not memoize the call here. @@ -5795,15 +5778,30 @@ fn analyzeCall( break :res res2; } else res: { assert(!func_ty_info.is_generic); - try sema.requireRuntimeBlock(block, call_src); + try sema.requireFunctionBlock(block, call_src); const args = try sema.arena.alloc(Air.Inst.Ref, uncasted_args.len); for (uncasted_args) |uncasted_arg, i| { - const arg_src = call_src; // TODO: better source location if (i < fn_params_len) { const param_ty = func_ty.fnParamType(i); - try sema.resolveTypeFully(block, arg_src, param_ty); - args[i] = try sema.coerce(block, param_ty, uncasted_arg, arg_src); + args[i] = sema.analyzeCallArg( + block, + .unneeded, + param_ty, + uncasted_arg, + ) catch |err| switch (err) { + error.NeededSourceLocation => { + const decl = sema.mod.declPtr(block.src_decl); + _ = try sema.analyzeCallArg( + block, + Module.argSrc(call_src.node_offset.x, sema.gpa, decl, i), + param_ty, + uncasted_arg, + ); + return error.AnalysisFail; + }, + else => |e| return e, + }; } else { args[i] = uncasted_arg; } @@ -5835,6 +5833,136 @@ fn analyzeCall( return result; } +fn analyzeInlineCallArg( + sema: *Sema, + block: *Block, + arg_src: LazySrcLoc, + inst: Zir.Inst.Index, + new_fn_info: Type.Payload.Function.Data, + arg_i: *usize, + uncasted_args: []const Air.Inst.Ref, + is_comptime_call: bool, + should_memoize: *bool, + memoized_call_key: Module.MemoizedCall.Key, +) !void { + const zir_tags = sema.code.instructions.items(.tag); + switch (zir_tags[inst]) { + .param, .param_comptime => { + // Evaluate the parameter type expression now that previous ones have + // been mapped, and coerce the corresponding argument to it. + const pl_tok = sema.code.instructions.items(.data)[inst].pl_tok; + const param_src = pl_tok.src(); + const extra = sema.code.extraData(Zir.Inst.Param, pl_tok.payload_index); + const param_body = sema.code.extra[extra.end..][0..extra.data.body_len]; + const param_ty_inst = try sema.resolveBody(block, param_body, inst); + const param_ty = try sema.analyzeAsType(block, param_src, param_ty_inst); + new_fn_info.param_types[arg_i.*] = param_ty; + const uncasted_arg = uncasted_args[arg_i.*]; + if (try sema.typeRequiresComptime(block, arg_src, param_ty)) { + _ = try sema.resolveConstMaybeUndefVal(block, arg_src, uncasted_arg, "argument to parameter with comptime only type must be comptime known"); + } + const casted_arg = try sema.coerce(block, param_ty, uncasted_arg, arg_src); + try sema.inst_map.putNoClobber(sema.gpa, inst, casted_arg); + + if (is_comptime_call) { + // TODO explain why function is being called at comptime + const arg_val = try sema.resolveConstMaybeUndefVal(block, arg_src, casted_arg, "argument to function being called at comptime must be comptime known"); + switch (arg_val.tag()) { + .generic_poison, .generic_poison_type => { + // This function is currently evaluated as part of an as-of-yet unresolvable + // parameter or return type. + return error.GenericPoison; + }, + else => { + // Needed so that lazy values do not trigger + // assertion due to type not being resolved + // when the hash function is called. + try sema.resolveLazyValue(block, arg_src, arg_val); + }, + } + should_memoize.* = should_memoize.* and !arg_val.canMutateComptimeVarState(); + memoized_call_key.args[arg_i.*] = .{ + .ty = param_ty, + .val = arg_val, + }; + } + + arg_i.* += 1; + }, + .param_anytype, .param_anytype_comptime => { + // No coercion needed. + const uncasted_arg = uncasted_args[arg_i.*]; + new_fn_info.param_types[arg_i.*] = sema.typeOf(uncasted_arg); + try sema.inst_map.putNoClobber(sema.gpa, inst, uncasted_arg); + + if (is_comptime_call) { + // TODO explain why function is being called at comptime + const arg_val = try sema.resolveConstMaybeUndefVal(block, arg_src, uncasted_arg, "argument to function being called at comptime must be comptime known"); + switch (arg_val.tag()) { + .generic_poison, .generic_poison_type => { + // This function is currently evaluated as part of an as-of-yet unresolvable + // parameter or return type. + return error.GenericPoison; + }, + else => { + // Needed so that lazy values do not trigger + // assertion due to type not being resolved + // when the hash function is called. + try sema.resolveLazyValue(block, arg_src, arg_val); + }, + } + should_memoize.* = should_memoize.* and !arg_val.canMutateComptimeVarState(); + memoized_call_key.args[arg_i.*] = .{ + .ty = sema.typeOf(uncasted_arg), + .val = arg_val, + }; + } + + arg_i.* += 1; + }, + else => {}, + } +} + +fn analyzeCallArg( + sema: *Sema, + block: *Block, + arg_src: LazySrcLoc, + param_ty: Type, + uncasted_arg: Air.Inst.Ref, +) !Air.Inst.Ref { + try sema.resolveTypeFully(block, arg_src, param_ty); + return sema.coerce(block, param_ty, uncasted_arg, arg_src); +} + +fn analyzeGenericCallArg( + sema: *Sema, + block: *Block, + arg_src: LazySrcLoc, + uncasted_arg: Air.Inst.Ref, + comptime_arg: TypedValue, + runtime_args: []Air.Inst.Ref, + new_fn_info: Type.Payload.Function.Data, + runtime_i: *u32, +) !void { + const is_runtime = comptime_arg.val.tag() == .generic_poison and + comptime_arg.ty.hasRuntimeBits() and + !(try sema.typeRequiresComptime(block, arg_src, comptime_arg.ty)); + if (is_runtime) { + const param_ty = new_fn_info.param_types[runtime_i.*]; + const casted_arg = try sema.coerce(block, param_ty, uncasted_arg, arg_src); + try sema.queueFullTypeResolution(param_ty); + runtime_args[runtime_i.*] = casted_arg; + runtime_i.* += 1; + } +} + +fn analyzeGenericCallArgVal(sema: *Sema, block: *Block, arg_src: LazySrcLoc, uncasted_arg: Air.Inst.Ref) !Value { + const arg_val = try sema.resolveValue(block, arg_src, uncasted_arg, "parameter is comptime"); + try sema.resolveLazyValue(block, arg_src, arg_val); + return arg_val; +} + fn instantiateGenericCall( sema: *Sema, block: *Block, @@ -5849,7 +5977,7 @@ fn instantiateGenericCall( const mod = sema.mod; const gpa = sema.gpa; - const func_val = try sema.resolveConstValue(block, func_src, func); + const func_val = try sema.resolveConstValue(block, func_src, func, "generic function being called must be comptime known"); const module_fn = switch (func_val.tag()) { .function => func_val.castTag(.function).?.data, .decl_ref => mod.declPtr(func_val.castTag(.decl_ref).?.data).val.castTag(.function).?.data, @@ -5900,10 +6028,16 @@ fn instantiateGenericCall( } if (is_comptime) { - const arg_src = call_src; // TODO better source location const arg_ty = sema.typeOf(uncasted_args[i]); - const arg_val = try sema.resolveValue(block, arg_src, uncasted_args[i]); - try sema.resolveLazyValue(block, arg_src, arg_val); + const arg_val = sema.analyzeGenericCallArgVal(block, .unneeded, uncasted_args[i]) catch |err| switch (err) { + error.NeededSourceLocation => { + const decl = sema.mod.declPtr(block.src_decl); + const arg_src = Module.argSrc(call_src.node_offset.x, sema.gpa, decl, i); + _ = try sema.analyzeGenericCallArgVal(block, arg_src, uncasted_args[i]); + return error.AnalysisFail; + }, + else => |e| return e, + }; arg_val.hash(arg_ty, &hasher, mod); if (is_anytype) { arg_ty.hashWithHasher(&hasher, mod); @@ -6059,19 +6193,18 @@ fn instantiateGenericCall( }, else => continue, } - const arg_src = call_src; // TODO: better source location const arg = uncasted_args[arg_i]; if (is_comptime) { - if (try sema.resolveMaybeUndefVal(block, arg_src, arg)) |arg_val| { + if (try sema.resolveMaybeUndefVal(block, .unneeded, arg)) |arg_val| { const child_arg = try child_sema.addConstant(sema.typeOf(arg), arg_val); child_sema.inst_map.putAssumeCapacityNoClobber(inst, child_arg); } else { - return sema.failWithNeededComptime(block, arg_src); + return sema.failWithNeededComptime(block, .unneeded, undefined); } } else if (is_anytype) { const arg_ty = sema.typeOf(arg); - if (try sema.typeRequiresComptime(block, arg_src, arg_ty)) { - const arg_val = try sema.resolveConstValue(block, arg_src, arg); + if (try sema.typeRequiresComptime(block, .unneeded, arg_ty)) { + const arg_val = try sema.resolveConstValue(block, .unneeded, arg, undefined); const child_arg = try child_sema.addConstant(arg_ty, arg_val); child_sema.inst_map.putAssumeCapacityNoClobber(inst, child_arg); } else { @@ -6093,7 +6226,7 @@ fn instantiateGenericCall( } return err; }; - const new_func_val = child_sema.resolveConstValue(&child_block, .unneeded, new_func_inst) catch unreachable; + const new_func_val = child_sema.resolveConstValue(&child_block, .unneeded, new_func_inst, undefined) catch unreachable; const new_func = new_func_val.castTag(.function).?.data; errdefer new_func.deinit(gpa); assert(new_func == new_module_func); @@ -6129,8 +6262,7 @@ fn instantiateGenericCall( const copied_arg_ty = try child_sema.typeOf(arg).copy(new_decl_arena_allocator); anytype_args[arg_i] = is_anytype; - const arg_src = call_src; // TODO: better source location - if (try sema.typeRequiresComptime(block, arg_src, copied_arg_ty)) { + if (try sema.typeRequiresComptime(block, .unneeded, copied_arg_ty)) { is_comptime = true; } @@ -6195,7 +6327,7 @@ fn instantiateGenericCall( const callee_inst = try sema.analyzeDeclVal(block, func_src, callee.owner_decl); // Make a runtime call to the new function, making sure to omit the comptime args. - try sema.requireRuntimeBlock(block, call_src); + try sema.requireFunctionBlock(block, call_src); const comptime_args = callee.comptime_args.?; const new_fn_info = mod.declPtr(callee.owner_decl).ty.fnInfo(); @@ -6209,18 +6341,30 @@ fn instantiateGenericCall( .param_comptime, .param_anytype_comptime, .param, .param_anytype => {}, else => continue, } - const arg_src = call_src; // TODO: better source location - const is_runtime = comptime_args[total_i].val.tag() == .generic_poison and - comptime_args[total_i].ty.hasRuntimeBits() and - !(try sema.typeRequiresComptime(block, arg_src, comptime_args[total_i].ty)); - if (is_runtime) { - const param_ty = new_fn_info.param_types[runtime_i]; - const uncasted_arg = uncasted_args[total_i]; - const casted_arg = try sema.coerce(block, param_ty, uncasted_arg, arg_src); - try sema.queueFullTypeResolution(param_ty); - runtime_args[runtime_i] = casted_arg; - runtime_i += 1; - } + sema.analyzeGenericCallArg( + block, + .unneeded, + uncasted_args[total_i], + comptime_args[total_i], + runtime_args, + new_fn_info, + &runtime_i, + ) catch |err| switch (err) { + error.NeededSourceLocation => { + const decl = sema.mod.declPtr(block.src_decl); + _ = try sema.analyzeGenericCallArg( + block, + Module.argSrc(call_src.node_offset.x, sema.gpa, decl, total_i), + uncasted_args[total_i], + comptime_args[total_i], + runtime_args, + new_fn_info, + &runtime_i, + ); + return error.AnalysisFail; + }, + else => |e| return e, + }; total_i += 1; } @@ -6314,7 +6458,7 @@ fn zirVectorType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError! const elem_type_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; const len_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node }; const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; - const len = try sema.resolveInt(block, len_src, extra.lhs, Type.u32); + const len = try sema.resolveInt(block, len_src, extra.lhs, Type.u32, "vector length must be comptime known"); const elem_type = try sema.resolveType(block, elem_type_src, extra.rhs); try sema.checkVectorElemType(block, elem_type_src, elem_type); const vector_type = try Type.Tag.vector.create(sema.arena, .{ @@ -6332,7 +6476,7 @@ fn zirArrayType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; const len_src: LazySrcLoc = .{ .node_offset_array_type_len = inst_data.src_node }; const elem_src: LazySrcLoc = .{ .node_offset_array_type_elem = inst_data.src_node }; - const len = try sema.resolveInt(block, len_src, extra.lhs, Type.usize); + const len = try sema.resolveInt(block, len_src, extra.lhs, Type.usize, "array length must be comptime known"); const elem_type = try sema.resolveType(block, elem_src, extra.rhs); const array_ty = try Type.array(sema.arena, len, null, elem_type, sema.mod); @@ -6348,11 +6492,11 @@ fn zirArrayTypeSentinel(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Compil const len_src: LazySrcLoc = .{ .node_offset_array_type_len = inst_data.src_node }; const sentinel_src: LazySrcLoc = .{ .node_offset_array_type_sentinel = inst_data.src_node }; const elem_src: LazySrcLoc = .{ .node_offset_array_type_elem = inst_data.src_node }; - const len = try sema.resolveInt(block, len_src, extra.len, Type.usize); + const len = try sema.resolveInt(block, len_src, extra.len, Type.usize, "array length must be comptime known"); const elem_type = try sema.resolveType(block, elem_src, extra.elem_type); const uncasted_sentinel = try sema.resolveInst(extra.sentinel); const sentinel = try sema.coerce(block, elem_type, uncasted_sentinel, sentinel_src); - const sentinel_val = try sema.resolveConstValue(block, sentinel_src, sentinel); + const sentinel_val = try sema.resolveConstValue(block, sentinel_src, sentinel, "array sentinel value must be comptime known"); const array_ty = try Type.array(sema.arena, len, sentinel_val, elem_type, sema.mod); return sema.addType(array_ty); @@ -6452,7 +6596,7 @@ fn zirErrorToInt(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstDat } } - try sema.requireRuntimeBlock(block, src); + try sema.requireRuntimeBlock(block, src, operand_src); return block.addBitCast(result_ty, operand); } @@ -6478,7 +6622,7 @@ fn zirIntToError(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstDat }; return sema.addConstant(Type.anyerror, Value.initPayload(&payload.base)); } - try sema.requireRuntimeBlock(block, src); + try sema.requireRuntimeBlock(block, src, operand_src); if (block.wantSafety()) { const is_lt_len = try block.addUnOp(.cmp_lt_errors_len, operand); try sema.addSafetyCheck(block, is_lt_len, .invalid_error_code); @@ -6598,7 +6742,7 @@ fn zirEnumToInt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A return sema.addConstant(int_tag_ty, try val.copy(sema.arena)); } - try sema.requireRuntimeBlock(block, src); + try sema.requireRuntimeBlock(block, src, operand_src); return block.addBitCast(int_tag_ty, enum_tag); } @@ -6645,7 +6789,7 @@ fn zirIntToEnum(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A return sema.addConstant(dest_ty, int_val); } - try sema.requireRuntimeBlock(block, src); + try sema.requireRuntimeBlock(block, src, operand_src); // TODO insert safety check to make sure the value matches an enum value return block.addTyOp(.intcast, dest_ty, operand); } @@ -6696,7 +6840,7 @@ fn analyzeOptionalPayloadPtr( // If the pointer resulting from this function was stored at comptime, // the optional non-null bit would be set that way. But in this case, // we need to emit a runtime instruction to do it. - try sema.requireRuntimeBlock(block, src); + try sema.requireFunctionBlock(block, src); _ = try block.addTyOp(.optional_payload_ptr_set, child_pointer, optional_ptr); } return sema.addConstant( @@ -6722,7 +6866,7 @@ fn analyzeOptionalPayloadPtr( } } - try sema.requireRuntimeBlock(block, src); + try sema.requireRuntimeBlock(block, src, null); if (safety_check and block.wantSafety()) { const is_non_null = try block.addUnOp(.is_non_null_ptr, optional_ptr); try sema.addSafetyCheck(block, is_non_null, .unwrap_null); @@ -6778,7 +6922,7 @@ fn zirOptionalPayload( return sema.addConstant(result_ty, val); } - try sema.requireRuntimeBlock(block, src); + try sema.requireRuntimeBlock(block, src, null); if (safety_check and block.wantSafety()) { const is_non_null = try block.addUnOp(.is_non_null, operand); try sema.addSafetyCheck(block, is_non_null, .unwrap_null); @@ -6827,7 +6971,7 @@ fn analyzeErrUnionPayload( return sema.addConstant(payload_ty, data); } - try sema.requireRuntimeBlock(block, src); + try sema.requireRuntimeBlock(block, src, null); // If the error set has no fields then no safety check is needed. if (safety_check and block.wantSafety() and @@ -6887,7 +7031,7 @@ fn analyzeErrUnionPayloadPtr( // If the pointer resulting from this function was stored at comptime, // the error union error code would be set that way. But in this case, // we need to emit a runtime instruction to do it. - try sema.requireRuntimeBlock(block, src); + try sema.requireRuntimeBlock(block, src, null); _ = try block.addTyOp(.errunion_payload_ptr_set, operand_pointer_ty, operand); } return sema.addConstant( @@ -6913,7 +7057,7 @@ fn analyzeErrUnionPayloadPtr( } } - try sema.requireRuntimeBlock(block, src); + try sema.requireRuntimeBlock(block, src, null); // If the error set has no fields then no safety check is needed. if (safety_check and block.wantSafety() and @@ -6951,7 +7095,7 @@ fn zirErrUnionCode(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileErro return sema.addConstant(result_ty, val); } - try sema.requireRuntimeBlock(block, src); + try sema.requireRuntimeBlock(block, src, null); return block.addTyOp(.unwrap_errunion_err, result_ty, operand); } @@ -6981,7 +7125,7 @@ fn zirErrUnionCodePtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileE } } - try sema.requireRuntimeBlock(block, src); + try sema.requireRuntimeBlock(block, src, null); return block.addTyOp(.unwrap_errunion_err_ptr, result_ty, operand); } @@ -7037,7 +7181,7 @@ fn zirFunc( const ret_ty_body = sema.code.extra[extra_index..][0..extra.data.ret_body_len]; extra_index += ret_ty_body.len; - const ret_ty_val = try sema.resolveGenericBody(block, ret_ty_src, ret_ty_body, inst, Type.type); + const ret_ty_val = try sema.resolveGenericBody(block, ret_ty_src, ret_ty_body, inst, Type.type, "return type must be comptime known"); var buffer: Value.ToTypeBuffer = undefined; break :blk try ret_ty_val.toType(&buffer).copy(sema.arena); }, @@ -7085,6 +7229,7 @@ fn resolveGenericBody( body: []const Zir.Inst.Index, func_inst: Zir.Inst.Index, dest_ty: Type, + reason: []const u8, ) !Value { assert(body.len != 0); @@ -7098,7 +7243,7 @@ fn resolveGenericBody( } const uncasted = sema.resolveBody(block, body, func_inst) catch |err| break :err err; const result = sema.coerce(block, dest_ty, uncasted, src) catch |err| break :err err; - const val = sema.resolveConstValue(block, src, result) catch |err| break :err err; + const val = sema.resolveConstValue(block, src, result, reason) catch |err| break :err err; return val; }; switch (err) { @@ -7205,6 +7350,7 @@ fn funcCommon( opt_lib_name: ?[]const u8, noalias_bits: u32, ) CompileError!Air.Inst.Ref { + const fn_src = LazySrcLoc.nodeOffset(src_node_offset); const ret_ty_src: LazySrcLoc = .{ .node_offset_fn_type_ret_ty = src_node_offset }; const cc_src: LazySrcLoc = .{ .node_offset_fn_type_cc = src_node_offset }; @@ -7213,10 +7359,6 @@ fn funcCommon( address_space == null or section == .generic or cc == null; - // Check for generic params. - for (block.params.items) |param| { - if (param.ty.tag() == .generic_poison) is_generic = true; - } var destroy_fn_on_error = false; const new_func: *Module.Fn = new_func: { @@ -7227,7 +7369,10 @@ fn funcCommon( break :new_func new_func; } destroy_fn_on_error = true; - break :new_func try sema.gpa.create(Module.Fn); + const new_func = try sema.gpa.create(Module.Fn); + // Set this here so that the inferred return type can be printed correctly if it appears in an error. + new_func.owner_decl = sema.owner_decl_index; + break :new_func new_func; }; errdefer if (destroy_fn_on_error) sema.gpa.destroy(new_func); @@ -7261,18 +7406,47 @@ fn funcCommon( } } + // These locals are pulled out from the init expression below to work around + // a stage1 compiler bug. + // In the case of generic calling convention, or generic alignment, we use + // default values which are only meaningful for the generic function, *not* + // the instantiation, which can depend on comptime parameters. + // Related proposal: https://github.com/ziglang/zig/issues/11834 + const cc_workaround = cc orelse .Unspecified; + const align_workaround = alignment orelse 0; + const param_types = try sema.arena.alloc(Type, block.params.items.len); const comptime_params = try sema.arena.alloc(bool, block.params.items.len); for (block.params.items) |param, i| { - const param_src = LazySrcLoc.nodeOffset(src_node_offset); // TODO better soruce location param_types[i] = param.ty; - comptime_params[i] = param.is_comptime or - try sema.typeRequiresComptime(block, param_src, param.ty); - is_generic = is_generic or comptime_params[i] or param.ty.tag() == .generic_poison; - if (is_extern and is_generic) { - // TODO add note: function is generic because of this parameter - return sema.fail(block, param_src, "extern function cannot be generic", .{}); - } + sema.analyzeParameter( + block, + fn_src, + .unneeded, + param, + comptime_params, + i, + &is_generic, + is_extern, + cc_workaround, + ) catch |err| switch (err) { + error.NeededSourceLocation => { + const decl = sema.mod.declPtr(block.src_decl); + try sema.analyzeParameter( + block, + fn_src, + Module.paramSrc(src_node_offset, sema.gpa, decl, i), + param, + comptime_params, + i, + &is_generic, + is_extern, + cc_workaround, + ); + return error.AnalysisFail; + }, + else => |e| return e, + }; } const ret_poison = if (!is_generic) rp: { @@ -7302,14 +7476,34 @@ fn funcCommon( }); }; - // These locals are pulled out from the init expression below to work around - // a stage1 compiler bug. - // In the case of generic calling convention, or generic alignment, we use - // default values which are only meaningful for the generic function, *not* - // the instantiation, which can depend on comptime parameters. - // Related proposal: https://github.com/ziglang/zig/issues/11834 - const cc_workaround = cc orelse .Unspecified; - const align_workaround = alignment orelse 0; + if (!bare_return_type.isValidReturnType()) { + const opaque_str = if (bare_return_type.zigTypeTag() == .Opaque) "opaque " else ""; + const msg = msg: { + const msg = try sema.errMsg(block, ret_ty_src, "{s}return type '{}' not allowed", .{ + opaque_str, bare_return_type.fmt(sema.mod), + }); + errdefer msg.destroy(sema.gpa); + + try sema.addDeclaredHereNote(msg, bare_return_type); + break :msg msg; + }; + return sema.failWithOwnedErrorMsg(block, msg); + } + if (!Type.fnCallingConventionAllowsZigTypes(cc_workaround) and !(try sema.validateExternType(return_type, .ret_ty))) { + const msg = msg: { + const msg = try sema.errMsg(block, ret_ty_src, "return type '{}' not allowed in function with calling convention '{s}'", .{ + return_type.fmt(sema.mod), @tagName(cc_workaround), + }); + errdefer msg.destroy(sema.gpa); + + const src_decl = sema.mod.declPtr(block.src_decl); + try sema.explainWhyTypeIsNotExtern(block, ret_ty_src, msg, ret_ty_src.toSrcLoc(src_decl), return_type, .ret_ty); + + try sema.addDeclaredHereNote(msg, return_type); + break :msg msg; + }; + return sema.failWithOwnedErrorMsg(block, msg); + } const arch = sema.mod.getTarget().cpu.arch; if (switch (cc_workaround) { @@ -7442,6 +7636,79 @@ fn funcCommon( return sema.addConstant(fn_ty, Value.initPayload(&fn_payload.base)); } +fn analyzeParameter( + sema: *Sema, + block: *Block, + func_src: LazySrcLoc, + param_src: LazySrcLoc, + param: Block.Param, + comptime_params: []bool, + i: usize, + is_generic: *bool, + is_extern: bool, + cc: std.builtin.CallingConvention, +) !void { + const requires_comptime = try sema.typeRequiresComptime(block, param_src, param.ty); + comptime_params[i] = param.is_comptime or requires_comptime; + const this_generic = comptime_params[i] or param.ty.tag() == .generic_poison; + is_generic.* = is_generic.* or this_generic; + if (is_extern and this_generic) { + // TODO this check should exist somewhere for notes. + if (param_src == .unneeded) return error.NeededSourceLocation; + const msg = msg: { + const msg = try sema.errMsg(block, func_src, "extern function cannot be generic", .{}); + errdefer msg.destroy(sema.gpa); + + try sema.errNote(block, param_src, msg, "function is generic because of this parameter", .{}); + break :msg msg; + }; + return sema.failWithOwnedErrorMsg(block, msg); + } + if (this_generic and !Type.fnCallingConventionAllowsZigTypes(cc)) { + return sema.fail(block, param_src, "generic parameters not allowed in function with calling convention '{s}'", .{@tagName(cc)}); + } + if (!param.ty.isValidParamType()) { + const opaque_str = if (param.ty.zigTypeTag() == .Opaque) "opaque " else ""; + const msg = msg: { + const msg = try sema.errMsg(block, param_src, "parameter of {s}type '{}' not allowed", .{ + opaque_str, param.ty.fmt(sema.mod), + }); + errdefer msg.destroy(sema.gpa); + + try sema.addDeclaredHereNote(msg, param.ty); + break :msg msg; + }; + return sema.failWithOwnedErrorMsg(block, msg); + } + if (!Type.fnCallingConventionAllowsZigTypes(cc) and !(try sema.validateExternType(param.ty, .param_ty))) { + const msg = msg: { + const msg = try sema.errMsg(block, param_src, "parameter of type '{}' not allowed in function with calling convention '{s}'", .{ + param.ty.fmt(sema.mod), @tagName(cc), + }); + errdefer msg.destroy(sema.gpa); + + const src_decl = sema.mod.declPtr(block.src_decl); + try sema.explainWhyTypeIsNotExtern(block, param_src, msg, param_src.toSrcLoc(src_decl), param.ty, .param_ty); + + try sema.addDeclaredHereNote(msg, param.ty); + break :msg msg; + }; + return sema.failWithOwnedErrorMsg(block, msg); + } + if (requires_comptime and !param.is_comptime) { + const msg = msg: { + const msg = try sema.errMsg(block, param_src, "parametter of type '{}' must be declared comptime", .{ + param.ty.fmt(sema.mod), + }); + errdefer msg.destroy(sema.gpa); + + try sema.addDeclaredHereNote(msg, param.ty); + break :msg msg; + }; + return sema.failWithOwnedErrorMsg(block, msg); + } +} + fn zirParam( sema: *Sema, block: *Block, @@ -7629,7 +7896,7 @@ fn zirPtrToInt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai if (try sema.resolveMaybeUndefVal(block, ptr_src, ptr)) |ptr_val| { return sema.addConstant(Type.usize, ptr_val); } - try sema.requireRuntimeBlock(block, ptr_src); + try sema.requireRuntimeBlock(block, ptr_src, ptr_src); return block.addUnOp(.ptrtoint, ptr); } @@ -7681,7 +7948,7 @@ fn zirFieldValNamed(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileErr const field_name_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node }; const extra = sema.code.extraData(Zir.Inst.FieldNamed, inst_data.payload_index).data; const object = try sema.resolveInst(extra.lhs); - const field_name = try sema.resolveConstString(block, field_name_src, extra.field_name); + const field_name = try sema.resolveConstString(block, field_name_src, extra.field_name, "field name must be comptime known"); return sema.fieldVal(block, src, object, field_name, field_name_src); } @@ -7694,7 +7961,7 @@ fn zirFieldPtrNamed(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileErr const field_name_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node }; const extra = sema.code.extraData(Zir.Inst.FieldNamed, inst_data.payload_index).data; const object_ptr = try sema.resolveInst(extra.lhs); - const field_name = try sema.resolveConstString(block, field_name_src, extra.field_name); + const field_name = try sema.resolveConstString(block, field_name_src, extra.field_name, "field name must be comptime known"); return sema.fieldPtr(block, src, object_ptr, field_name, field_name_src); } @@ -7706,7 +7973,7 @@ fn zirFieldCallBindNamed(sema: *Sema, block: *Block, extended: Zir.Inst.Extended const src = LazySrcLoc.nodeOffset(extra.node); const field_name_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = extra.node }; const object_ptr = try sema.resolveInst(extra.lhs); - const field_name = try sema.resolveConstString(block, field_name_src, extra.field_name); + const field_name = try sema.resolveConstString(block, field_name_src, extra.field_name, "field name must be comptime known"); return sema.fieldCallBind(block, src, object_ptr, field_name, field_name_src); } @@ -7722,12 +7989,13 @@ fn zirIntCast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air const dest_ty = try sema.resolveType(block, dest_ty_src, extra.lhs); const operand = try sema.resolveInst(extra.rhs); - return sema.intCast(block, dest_ty, dest_ty_src, operand, operand_src, true); + return sema.intCast(block, inst_data.src(), dest_ty, dest_ty_src, operand, operand_src, true); } fn intCast( sema: *Sema, block: *Block, + src: LazySrcLoc, dest_ty: Type, dest_ty_src: LazySrcLoc, operand: Air.Inst.Ref, @@ -7750,7 +8018,7 @@ fn intCast( if ((try sema.typeHasOnePossibleValue(block, dest_ty_src, dest_ty))) |opv| { // requirement: intCast(u0, input) iff input == 0 if (runtime_safety and block.wantSafety()) { - try sema.requireRuntimeBlock(block, operand_src); + try sema.requireRuntimeBlock(block, src, operand_src); const target = sema.mod.getTarget(); const wanted_info = dest_scalar_ty.intInfo(target); const wanted_bits = wanted_info.bits; @@ -7765,7 +8033,7 @@ fn intCast( return sema.addConstant(dest_ty, opv); } - try sema.requireRuntimeBlock(block, operand_src); + try sema.requireRuntimeBlock(block, src, operand_src); if (runtime_safety and block.wantSafety()) { const target = sema.mod.getTarget(); const actual_info = operand_scalar_ty.intInfo(target); @@ -7972,7 +8240,7 @@ fn zirFloatCast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A if (dst_bits >= src_bits) { return sema.coerce(block, dest_ty, operand, operand_src); } - try sema.requireRuntimeBlock(block, operand_src); + try sema.requireRuntimeBlock(block, inst_data.src(), operand_src); return block.addTyOp(.fptrunc, dest_ty, operand); } @@ -8096,7 +8364,6 @@ fn zirSwitchCapture( const switch_info = zir_datas[capture_info.switch_inst].pl_node; const switch_extra = sema.code.extraData(Zir.Inst.SwitchBlock, switch_info.payload_index); const operand_src: LazySrcLoc = .{ .node_offset_switch_operand = switch_info.src_node }; - const switch_src = switch_info.src(); const operand_is_ref = switch_extra.data.bits.is_ref; const cond_inst = Zir.refToIndex(switch_extra.data.operand).?; const cond_info = sema.code.instructions.items(.data)[cond_inst].un_node; @@ -8141,24 +8408,30 @@ fn zirSwitchCapture( const first_item = try sema.resolveInst(items[0]); // Previous switch validation ensured this will succeed - const first_item_val = sema.resolveConstValue(block, .unneeded, first_item) catch unreachable; + const first_item_val = sema.resolveConstValue(block, .unneeded, first_item, undefined) catch unreachable; const first_field_index = @intCast(u32, enum_ty.enumTagFieldIndex(first_item_val, sema.mod).?); const first_field = union_obj.fields.values()[first_field_index]; - for (items[1..]) |item| { + 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) catch unreachable; + const item_val = sema.resolveConstValue(block, .unneeded, item_ref, undefined) catch unreachable; const field_index = enum_ty.enumTagFieldIndex(item_val, sema.mod).?; const field = union_obj.fields.values()[field_index]; if (!field.ty.eql(first_field.ty, sema.mod)) { - const first_item_src = switch_src; // TODO better source location - const item_src = switch_src; const msg = msg: { - const msg = try sema.errMsg(block, switch_src, "capture group with incompatible types", .{}); + const raw_capture_src = Module.SwitchProngSrc{ .multi_capture = capture_info.prong_index }; + const capture_src = raw_capture_src.resolve(sema.gpa, sema.mod.declPtr(block.src_decl), switch_info.src_node, .first); + + const msg = try sema.errMsg(block, capture_src, "capture group with incompatible types", .{}); errdefer msg.destroy(sema.gpa); + + const raw_first_item_src = Module.SwitchProngSrc{ .multi = .{ .prong = capture_info.prong_index, .item = 0 } }; + const first_item_src = raw_first_item_src.resolve(sema.gpa, sema.mod.declPtr(block.src_decl), switch_info.src_node, .first); + const raw_item_src = Module.SwitchProngSrc{ .multi = .{ .prong = capture_info.prong_index, .item = 1 + @intCast(u32, i) } }; + const item_src = raw_item_src.resolve(sema.gpa, sema.mod.declPtr(block.src_decl), switch_info.src_node, .first); try sema.errNote(block, first_item_src, msg, "type '{}' here", .{first_field.ty.fmt(sema.mod)}); try sema.errNote(block, item_src, msg, "type '{}' here", .{field.ty.fmt(sema.mod)}); break :msg msg; @@ -8186,7 +8459,7 @@ fn zirSwitchCapture( }), ); } - try sema.requireRuntimeBlock(block, operand_src); + try sema.requireRuntimeBlock(block, operand_src, null); return block.addStructFieldPtr(operand_ptr, first_field_index, field_ty_ptr); } @@ -8196,7 +8469,7 @@ fn zirSwitchCapture( operand_val.castTag(.@"union").?.data.val, ); } - try sema.requireRuntimeBlock(block, operand_src); + try sema.requireRuntimeBlock(block, operand_src, null); return block.addStructFieldVal(operand, first_field_index, first_field.ty); }, .ErrorSet => { @@ -8206,7 +8479,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) catch unreachable; + const item_val = sema.resolveConstValue(block, .unneeded, item_ref, undefined) catch unreachable; names.putAssumeCapacityNoClobber( item_val.getError().?, {}, @@ -8220,7 +8493,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) catch unreachable; + const item_val = sema.resolveConstValue(block, .unneeded, item_ref, undefined) 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); @@ -8247,7 +8520,7 @@ fn zirSwitchCond( ) CompileError!Air.Inst.Ref { const inst_data = sema.code.instructions.items(.data)[inst].un_node; const src = inst_data.src(); - const operand_src = src; // TODO make this point at the switch operand + const operand_src: LazySrcLoc = .{ .node_offset_switch_operand = inst_data.src_node }; const operand_ptr = try sema.resolveInst(inst_data.operand); const operand = if (is_ref) try sema.analyzeLoad(block, src, operand_ptr, operand_src) @@ -8345,12 +8618,19 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError }, }; + const union_originally = blk: { + const zir_data = sema.code.instructions.items(.data); + const cond_index = Zir.refToIndex(extra.data.operand).?; + const raw_operand = sema.resolveInst(zir_data[cond_index].un_node.operand) catch unreachable; + break :blk sema.typeOf(raw_operand).zigTypeTag() == .Union; + }; + const operand_ty = sema.typeOf(operand); var else_error_ty: ?Type = null; // Validate usage of '_' prongs. - if (special_prong == .under and !operand_ty.isNonexhaustiveEnum()) { + if (special_prong == .under and (!operand_ty.isNonexhaustiveEnum() or union_originally)) { const msg = msg: { const msg = try sema.errMsg( block, @@ -8375,6 +8655,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError // Validate for duplicate items, missing else prong, and invalid range. switch (operand_ty.zigTypeTag()) { + .Union => unreachable, // handled in zirSwitchCond .Enum => { var seen_fields = try gpa.alloc(?Module.SwitchProngSrc, operand_ty.enumFieldCount()); defer gpa.free(seen_fields); @@ -8432,60 +8713,53 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError } const all_tags_handled = for (seen_fields) |seen_src| { if (seen_src == null) break false; - } else !operand_ty.isNonexhaustiveEnum(); + } else true; - switch (special_prong) { - .none => { - if (!all_tags_handled) { - const msg = msg: { - const msg = try sema.errMsg( - block, - src, - "switch must handle all possibilities", - .{}, - ); - errdefer msg.destroy(sema.gpa); - for (seen_fields) |seen_src, i| { - if (seen_src != null) continue; - - const field_name = operand_ty.enumFieldName(i); - - // TODO have this point to the tag decl instead of here - try sema.errNote( - block, - src, - msg, - "unhandled enumeration value: '{s}'", - .{field_name}, - ); - } - try sema.mod.errNoteNonLazy( - operand_ty.declSrcLoc(sema.mod), - msg, - "enum '{}' declared here", - .{operand_ty.fmt(sema.mod)}, - ); - break :msg msg; - }; - return sema.failWithOwnedErrorMsg(block, msg); - } - }, - .under => { - if (all_tags_handled) return sema.fail( + if (special_prong == .@"else") { + if (all_tags_handled and !operand_ty.isNonexhaustiveEnum()) return sema.fail( + block, + special_prong_src, + "unreachable else prong; all cases already handled", + .{}, + ); + } else if (!all_tags_handled) { + const msg = msg: { + const msg = try sema.errMsg( block, - special_prong_src, - "unreachable '_' prong; all cases already handled", + src, + "switch must handle all possibilities", .{}, ); - }, - .@"else" => { - if (all_tags_handled) return sema.fail( - block, - special_prong_src, - "unreachable else prong; all cases already handled", - .{}, + errdefer msg.destroy(sema.gpa); + for (seen_fields) |seen_src, i| { + if (seen_src != null) continue; + + const field_name = operand_ty.enumFieldName(i); + try sema.addFieldErrNote( + block, + operand_ty, + i, + msg, + "unhandled enumeration value: '{s}'", + .{field_name}, + ); + } + try sema.mod.errNoteNonLazy( + operand_ty.declSrcLoc(sema.mod), + msg, + "enum '{}' declared here", + .{operand_ty.fmt(sema.mod)}, ); - }, + break :msg msg; + }; + return sema.failWithOwnedErrorMsg(block, msg); + } else if (special_prong == .none and operand_ty.isNonexhaustiveEnum() and !union_originally) { + return sema.fail( + block, + src, + "switch on non-exhaustive enum must include 'else' or '_' prong", + .{}, + ); } }, .ErrorSet => { @@ -8625,7 +8899,6 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError else_error_ty = try Type.Tag.error_set_merged.create(sema.arena, names); } }, - .Union => return sema.fail(block, src, "TODO validate switch .Union", .{}), .Int, .ComptimeInt => { var range_set = RangeSet.init(gpa, sema.mod); defer range_set.deinit(); @@ -8923,7 +9196,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError const item = try sema.resolveInst(item_ref); // Validation above ensured these will succeed. - const item_val = sema.resolveConstValue(&child_block, .unneeded, item) catch unreachable; + const item_val = sema.resolveConstValue(&child_block, .unneeded, item, undefined) catch unreachable; if (operand_val.eql(item_val, operand_ty, sema.mod)) { return sema.resolveBlockBody(block, src, &child_block, body, inst, merges); } @@ -8945,7 +9218,7 @@ 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) catch unreachable; + const item_val = sema.resolveConstValue(&child_block, .unneeded, item, undefined) catch unreachable; if (operand_val.eql(item_val, operand_ty, sema.mod)) { return sema.resolveBlockBody(block, src, &child_block, body, inst, merges); } @@ -8959,8 +9232,8 @@ 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) catch unreachable; - const last_tv = sema.resolveInstConst(&child_block, .unneeded, item_last) catch unreachable; + 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; 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))) { @@ -8981,7 +9254,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError return sema.resolveBlockBody(block, src, &child_block, special.body, inst, merges); } - try sema.requireRuntimeBlock(block, src); + try sema.requireRuntimeBlock(block, src, operand_src); const estimated_cases_extra = (scalar_cases_len + multi_cases_len) * @typeInfo(Air.SwitchBr.Case).Struct.fields.len + 2; @@ -9270,14 +9543,14 @@ 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)) |val| { + if (sema.resolveConstValue(block, .unneeded, item, undefined)) |val| { return TypedValue{ .ty = item_ty, .val = val }; } else |err| switch (err) { error.NeededSourceLocation => { const src = switch_prong_src.resolve(sema.gpa, sema.mod.declPtr(block.src_decl), switch_node_offset, range_expand); return TypedValue{ .ty = item_ty, - .val = try sema.resolveConstValue(block, src, item), + .val = try sema.resolveConstValue(block, src, item, "switch prong values must be comptime known"), }; }, else => |e| return e, @@ -9463,7 +9736,7 @@ fn zirHasField(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai const ty_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; const name_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node }; const unresolved_ty = try sema.resolveType(block, ty_src, extra.lhs); - const field_name = try sema.resolveConstString(block, name_src, extra.rhs); + const field_name = try sema.resolveConstString(block, name_src, extra.rhs, "field name must be comptime known"); const ty = try sema.resolveTypeFields(block, ty_src, unresolved_ty); const has_field = hf: { @@ -9505,7 +9778,7 @@ fn zirHasDecl(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air const lhs_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; const rhs_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node }; const container_type = try sema.resolveType(block, lhs_src, extra.lhs); - const decl_name = try sema.resolveConstString(block, rhs_src, extra.rhs); + const decl_name = try sema.resolveConstString(block, rhs_src, extra.rhs, "decl name must be comptime known"); try checkNamespaceType(sema, block, lhs_src, container_type); @@ -9552,7 +9825,7 @@ fn zirEmbedFile(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A const mod = sema.mod; const inst_data = sema.code.instructions.items(.data)[inst].un_node; const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; - const name = try sema.resolveConstString(block, operand_src, inst_data.operand); + const name = try sema.resolveConstString(block, operand_src, inst_data.operand, "file path name must be comptime known"); const embed_file = mod.embedFile(block.getFileScope(), name) catch |err| switch (err) { error.ImportOutsidePkgPath => { @@ -9706,13 +9979,13 @@ fn zirShl( try lhs_ty.maxInt(sema.arena, target), ); const rhs_limited = try sema.analyzeMinMax(block, rhs_src, rhs, max_int, .min, rhs_src, rhs_src); - break :rhs try sema.intCast(block, lhs_ty, rhs_src, rhs_limited, rhs_src, false); + break :rhs try sema.intCast(block, src, lhs_ty, rhs_src, rhs_limited, rhs_src, false); } else { break :rhs rhs; } } else rhs; - try sema.requireRuntimeBlock(block, runtime_src); + try sema.requireRuntimeBlock(block, src, runtime_src); if (block.wantSafety()) { const maybe_op_ov: ?Air.Inst.Tag = switch (air_tag) { .shl_exact => .shl_with_overflow, @@ -9823,7 +10096,7 @@ fn zirShr( } } else rhs_src; - try sema.requireRuntimeBlock(block, runtime_src); + try sema.requireRuntimeBlock(block, src, runtime_src); return block.addBinOp(air_tag, lhs, rhs); } @@ -9882,7 +10155,7 @@ fn zirBitwise( } }; - try sema.requireRuntimeBlock(block, runtime_src); + try sema.requireRuntimeBlock(block, src, runtime_src); return block.addBinOp(air_tag, casted_lhs, casted_rhs); } @@ -9926,7 +10199,7 @@ fn zirBitNot(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air. } } - try sema.requireRuntimeBlock(block, src); + try sema.requireRuntimeBlock(block, src, null); return block.addTyOp(.not, operand_type, operand); } @@ -9939,6 +10212,7 @@ fn analyzeTupleCat( ) CompileError!Air.Inst.Ref { const lhs_ty = sema.typeOf(lhs); const rhs_ty = sema.typeOf(rhs); + const src = LazySrcLoc.nodeOffset(src_node); const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = src_node }; const rhs_src: LazySrcLoc = .{ .node_offset_bin_rhs = src_node }; @@ -9986,7 +10260,7 @@ fn analyzeTupleCat( return sema.addConstant(tuple_ty, tuple_val); }; - try sema.requireRuntimeBlock(block, runtime_src); + try sema.requireRuntimeBlock(block, src, runtime_src); const element_refs = try sema.arena.alloc(Air.Inst.Ref, final_len); for (lhs_tuple.types) |_, i| { @@ -10049,8 +10323,8 @@ fn zirArrayCat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai const rhs_sent = try sema.addConstant(rhs_info.elem_type, rhs_sent_val); const lhs_sent_casted = try sema.coerce(block, resolved_elem_ty, lhs_sent, lhs_src); const rhs_sent_casted = try sema.coerce(block, resolved_elem_ty, rhs_sent, rhs_src); - const lhs_sent_casted_val = try sema.resolveConstValue(block, lhs_src, lhs_sent_casted); - const rhs_sent_casted_val = try sema.resolveConstValue(block, rhs_src, rhs_sent_casted); + const lhs_sent_casted_val = try sema.resolveConstValue(block, lhs_src, lhs_sent_casted, "array sentinel value must be comptime known"); + const rhs_sent_casted_val = try sema.resolveConstValue(block, rhs_src, rhs_sent_casted, "array sentinel value must be comptime known"); if (try sema.valuesEqual(block, src, lhs_sent_casted_val, rhs_sent_casted_val, resolved_elem_ty)) { break :s lhs_sent_casted_val; } else { @@ -10058,14 +10332,14 @@ fn zirArrayCat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai } } else { const lhs_sent_casted = try sema.coerce(block, resolved_elem_ty, lhs_sent, lhs_src); - const lhs_sent_casted_val = try sema.resolveConstValue(block, lhs_src, lhs_sent_casted); + const lhs_sent_casted_val = try sema.resolveConstValue(block, lhs_src, lhs_sent_casted, "array sentinel value must be comptime known"); break :s lhs_sent_casted_val; } } else { if (rhs_info.sentinel) |rhs_sent_val| { const rhs_sent = try sema.addConstant(rhs_info.elem_type, rhs_sent_val); const rhs_sent_casted = try sema.coerce(block, resolved_elem_ty, rhs_sent, rhs_src); - const rhs_sent_casted_val = try sema.resolveConstValue(block, rhs_src, rhs_sent_casted); + const rhs_sent_casted_val = try sema.resolveConstValue(block, rhs_src, rhs_sent_casted, "array sentinel value must be comptime known"); break :s rhs_sent_casted_val; } else { break :s null; @@ -10120,7 +10394,7 @@ fn zirArrayCat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai } else break :rs rhs_src; } else lhs_src; - try sema.requireRuntimeBlock(block, runtime_src); + try sema.requireRuntimeBlock(block, src, runtime_src); if (ptr_addrspace) |ptr_as| { const alloc_ty = try Type.ptr(sema.arena, sema.mod, .{ @@ -10186,7 +10460,7 @@ fn getArrayCatInfo(sema: *Sema, block: *Block, src: LazySrcLoc, operand: Air.Ins // has a sentinel, and this code should compute the length based // on the sentinel value. .Slice, .Many => { - const val = try sema.resolveConstValue(block, src, operand); + const val = try sema.resolveConstValue(block, src, operand, "slice value being concatenated must be comptime known"); return Type.ArrayInfo{ .elem_type = ptr_info.pointee_type, .sentinel = ptr_info.sentinel, @@ -10215,6 +10489,7 @@ fn analyzeTupleMul( ) CompileError!Air.Inst.Ref { const operand_ty = sema.typeOf(operand); const operand_tuple = operand_ty.tupleFields(); + const src = LazySrcLoc.nodeOffset(src_node); const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = src_node }; const rhs_src: LazySrcLoc = .{ .node_offset_bin_rhs = src_node }; @@ -10258,7 +10533,7 @@ fn analyzeTupleMul( return sema.addConstant(tuple_ty, tuple_val); }; - try sema.requireRuntimeBlock(block, runtime_src); + try sema.requireRuntimeBlock(block, src, runtime_src); const element_refs = try sema.arena.alloc(Air.Inst.Ref, final_len); for (operand_tuple.types) |_, i| { @@ -10286,7 +10561,7 @@ fn zirArrayMul(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai const rhs_src: LazySrcLoc = .{ .node_offset_bin_rhs = inst_data.src_node }; // In `**` rhs must be comptime-known, but lhs can be runtime-known - const factor = try sema.resolveInt(block, rhs_src, extra.rhs, Type.usize); + const factor = try sema.resolveInt(block, rhs_src, extra.rhs, Type.usize, "array multiplication factor must be comptime known"); if (lhs_ty.isTuple()) { return sema.analyzeTupleMul(block, inst_data.src_node, lhs, factor); @@ -10337,7 +10612,7 @@ fn zirArrayMul(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai return sema.addConstantMaybeRef(block, src, result_ty, val, ptr_addrspace != null); } - try sema.requireRuntimeBlock(block, lhs_src); + try sema.requireRuntimeBlock(block, src, lhs_src); if (ptr_addrspace) |ptr_as| { const alloc_ty = try Type.ptr(sema.arena, sema.mod, .{ @@ -10411,7 +10686,7 @@ fn zirNegate(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air. const target = sema.mod.getTarget(); return sema.addConstant(rhs_ty, try rhs_val.floatNeg(rhs_ty, sema.arena, target)); } - try sema.requireRuntimeBlock(block, rhs_src); + try sema.requireRuntimeBlock(block, src, null); return block.addUnOp(.neg, rhs); } @@ -10495,7 +10770,7 @@ fn zirOverflowArithmetic( try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src); const dest_ty = lhs_ty; if (dest_ty.scalarType().zigTypeTag() != .Int) { - return sema.fail(block, src, "expected vector of integers or integer type, found '{}'", .{dest_ty.fmt(mod)}); + return sema.fail(block, src, "expected vector of integers or integer tag type, found '{}'", .{dest_ty.fmt(mod)}); } const maybe_lhs_val = try sema.resolveMaybeUndefVal(block, lhs_src, lhs); @@ -10635,7 +10910,8 @@ fn zirOverflowArithmetic( else => unreachable, }; - try sema.requireRuntimeBlock(block, src); + const runtime_src = if (maybe_lhs_val == null) lhs_src else rhs_src; + try sema.requireRuntimeBlock(block, src, runtime_src); const tuple = try block.addInst(.{ .tag = air_tag, @@ -11542,7 +11818,7 @@ fn analyzeArithmetic( } }; - try sema.requireRuntimeBlock(block, rs.src); + try sema.requireRuntimeBlock(block, src, rs.src); if (block.wantSafety()) { if (scalar_tag == .Int) { const maybe_op_ov: ?Air.Inst.Tag = switch (rs.air_tag) { @@ -11666,7 +11942,7 @@ fn analyzePtrArithmetic( } else break :rs ptr_src; }; - try sema.requireRuntimeBlock(block, runtime_src); + try sema.requireRuntimeBlock(block, op_src, runtime_src); return block.addInst(.{ .tag = air_tag, .data = .{ .ty_pl = .{ @@ -11685,7 +11961,7 @@ fn zirLoad(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.In const inst_data = sema.code.instructions.items(.data)[inst].un_node; const src = inst_data.src(); - const ptr_src: LazySrcLoc = .{ .node_offset_deref_ptr = inst_data.src_node }; + const ptr_src = src; // TODO better source location const ptr = try sema.resolveInst(inst_data.operand); return sema.analyzeLoad(block, src, ptr, ptr_src); } @@ -11733,7 +12009,7 @@ fn zirAsm( } if (block.is_comptime) { - try sema.requireRuntimeBlock(block, src); + try sema.requireRuntimeBlock(block, src, null); } var extra_i = extra.end; @@ -11895,10 +12171,10 @@ fn zirCmpEq( } if (lhs_ty_tag == .Union and (rhs_ty_tag == .EnumLiteral or rhs_ty_tag == .Enum)) { - return sema.analyzeCmpUnionTag(block, lhs, lhs_src, rhs, rhs_src, op); + return sema.analyzeCmpUnionTag(block, src, lhs, lhs_src, rhs, rhs_src, op); } if (rhs_ty_tag == .Union and (lhs_ty_tag == .EnumLiteral or lhs_ty_tag == .Enum)) { - return sema.analyzeCmpUnionTag(block, rhs, rhs_src, lhs, lhs_src, op); + return sema.analyzeCmpUnionTag(block, src, rhs, rhs_src, lhs, lhs_src, op); } if (lhs_ty_tag == .ErrorSet and rhs_ty_tag == .ErrorSet) { @@ -11925,7 +12201,7 @@ fn zirCmpEq( break :src lhs_src; } }; - try sema.requireRuntimeBlock(block, runtime_src); + try sema.requireRuntimeBlock(block, src, runtime_src); return block.addBinOp(air_tag, lhs, rhs); } if (lhs_ty_tag == .Type and rhs_ty_tag == .Type) { @@ -11943,6 +12219,7 @@ fn zirCmpEq( fn analyzeCmpUnionTag( sema: *Sema, block: *Block, + src: LazySrcLoc, un: Air.Inst.Ref, un_src: LazySrcLoc, tag: Air.Inst.Ref, @@ -11964,7 +12241,7 @@ fn analyzeCmpUnionTag( const coerced_tag = try sema.coerce(block, union_tag_ty, tag, tag_src); const coerced_union = try sema.coerce(block, union_tag_ty, un, un_src); - return sema.cmpSelf(block, coerced_union, coerced_tag, op, un_src, tag_src); + return sema.cmpSelf(block, src, coerced_union, coerced_tag, op, un_src, tag_src); } /// Only called for non-equality operators. See also `zirCmpEq`. @@ -12020,7 +12297,7 @@ fn analyzeCmp( } const casted_lhs = try sema.coerce(block, resolved_type, lhs, lhs_src); const casted_rhs = try sema.coerce(block, resolved_type, rhs, rhs_src); - return sema.cmpSelf(block, casted_lhs, casted_rhs, op, lhs_src, rhs_src); + return sema.cmpSelf(block, src, casted_lhs, casted_rhs, op, lhs_src, rhs_src); } fn compareOperatorName(comp: std.math.CompareOperator) []const u8 { @@ -12037,6 +12314,7 @@ fn compareOperatorName(comp: std.math.CompareOperator) []const u8 { fn cmpSelf( sema: *Sema, block: *Block, + src: LazySrcLoc, casted_lhs: Air.Inst.Ref, casted_rhs: Air.Inst.Ref, op: std.math.CompareOperator, @@ -12064,7 +12342,7 @@ fn cmpSelf( } else { if (resolved_type.zigTypeTag() == .Bool) { // We can lower bool eq/neq more efficiently. - return sema.runtimeBoolCmp(block, op, casted_rhs, lhs_val.toBool(), rhs_src); + return sema.runtimeBoolCmp(block, src, op, casted_rhs, lhs_val.toBool(), rhs_src); } break :src rhs_src; } @@ -12074,13 +12352,13 @@ fn cmpSelf( if (resolved_type.zigTypeTag() == .Bool) { if (try sema.resolveMaybeUndefVal(block, rhs_src, casted_rhs)) |rhs_val| { if (rhs_val.isUndef()) return sema.addConstUndef(Type.bool); - return sema.runtimeBoolCmp(block, op, casted_lhs, rhs_val.toBool(), lhs_src); + return sema.runtimeBoolCmp(block, src, op, casted_lhs, rhs_val.toBool(), lhs_src); } } break :src lhs_src; } }; - try sema.requireRuntimeBlock(block, runtime_src); + try sema.requireRuntimeBlock(block, src, runtime_src); if (resolved_type.zigTypeTag() == .Vector) { const result_ty = try Type.vector(sema.arena, resolved_type.vectorLen(), Type.@"bool"); const result_ty_ref = try sema.addType(result_ty); @@ -12097,13 +12375,14 @@ fn cmpSelf( fn runtimeBoolCmp( sema: *Sema, block: *Block, + src: LazySrcLoc, op: std.math.CompareOperator, lhs: Air.Inst.Ref, rhs: bool, runtime_src: LazySrcLoc, ) CompileError!Air.Inst.Ref { if ((op == .neq) == rhs) { - try sema.requireRuntimeBlock(block, runtime_src); + try sema.requireRuntimeBlock(block, src, runtime_src); return block.addTyOp(.not, Type.bool, lhs); } else { return lhs; @@ -12225,7 +12504,7 @@ fn zirRetAddr( extended: Zir.Inst.Extended.InstData, ) CompileError!Air.Inst.Ref { const src = LazySrcLoc.nodeOffset(@bitCast(i32, extended.operand)); - try sema.requireRuntimeBlock(block, src); + try sema.requireRuntimeBlock(block, src, null); return try block.addNoOp(.ret_addr); } @@ -12235,7 +12514,7 @@ fn zirFrameAddress( extended: Zir.Inst.Extended.InstData, ) CompileError!Air.Inst.Ref { const src = LazySrcLoc.nodeOffset(@bitCast(i32, extended.operand)); - try sema.requireRuntimeBlock(block, src); + try sema.requireRuntimeBlock(block, src, null); return try block.addNoOp(.frame_addr); } @@ -13304,7 +13583,7 @@ fn zirBoolNot(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air else Air.Inst.Ref.bool_true; } - try sema.requireRuntimeBlock(block, src); + try sema.requireRuntimeBlock(block, src, null); return block.addTyOp(.not, Type.bool, operand); } @@ -13689,7 +13968,7 @@ fn zirUnreachable(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError if (block.is_comptime or inst_data.force_comptime) { return sema.fail(block, src, "reached unreachable code", .{}); } - try sema.requireRuntimeBlock(block, src); + try sema.requireFunctionBlock(block, src); // TODO Add compile error for @optimizeFor occurring too late in a scope. try block.addUnreachable(src, true); return always_noreturn; @@ -13751,7 +14030,6 @@ fn zirRetLoad(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Zir const operand = try sema.analyzeLoad(block, src, ret_ptr, src); return sema.analyzeRet(block, operand, src); } - try sema.requireRuntimeBlock(block, src); _ = try block.addUnOp(.ret_load, ret_ptr); return always_noreturn; } @@ -13834,22 +14112,21 @@ fn floatOpAllowed(tag: Zir.Inst.Tag) bool { }; } -fn zirPtrTypeSimple(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { +fn zirOverflowArithmeticPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { const tracy = trace(@src()); defer tracy.end(); - const inst_data = sema.code.instructions.items(.data)[inst].ptr_type_simple; - const elem_ty_src = sema.src; // TODO better source location - const elem_type = try sema.resolveType(block, elem_ty_src, inst_data.elem_type); + const inst_data = sema.code.instructions.items(.data)[inst].un_node; + const elem_ty_src = inst_data.src(); + const elem_type = try sema.resolveType(block, elem_ty_src, inst_data.operand); const ty = try Type.ptr(sema.arena, sema.mod, .{ .pointee_type = elem_type, .@"addrspace" = .generic, - .mutable = inst_data.is_mutable, - .@"allowzero" = inst_data.is_allowzero or inst_data.size == .C, - .@"volatile" = inst_data.is_volatile, - .size = inst_data.size, + .mutable = true, + .@"allowzero" = false, + .@"volatile" = false, + .size = .One, }); - try sema.validatePtrTy(block, elem_ty_src, ty); return sema.addType(ty); } @@ -13857,14 +14134,15 @@ fn zirPtrType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air const tracy = trace(@src()); defer tracy.end(); - const src: LazySrcLoc = sema.src; // TODO better source location - const elem_ty_src: LazySrcLoc = sema.src; // TODO better source location - const sentinel_src: LazySrcLoc = sema.src; // TODO better source location - const addrspace_src: LazySrcLoc = sema.src; // TODO better source location - const bitoffset_src: LazySrcLoc = sema.src; // TODO better source location - const hostsize_src: LazySrcLoc = sema.src; // TODO better source location const inst_data = sema.code.instructions.items(.data)[inst].ptr_type; const extra = sema.code.extraData(Zir.Inst.PtrType, inst_data.payload_index); + const elem_ty_src: LazySrcLoc = .{ .node_offset_ptr_elem = extra.data.src_node }; + const sentinel_src: LazySrcLoc = .{ .node_offset_ptr_sentinel = extra.data.src_node }; + const align_src: LazySrcLoc = .{ .node_offset_ptr_align = extra.data.src_node }; + const addrspace_src: LazySrcLoc = .{ .node_offset_ptr_addrspace = extra.data.src_node }; + const bitoffset_src: LazySrcLoc = .{ .node_offset_ptr_bitoffset = extra.data.src_node }; + const hostsize_src: LazySrcLoc = .{ .node_offset_ptr_hostsize = extra.data.src_node }; + const unresolved_elem_ty = try sema.resolveType(block, elem_ty_src, extra.data.elem_type); const target = sema.mod.getTarget(); @@ -13873,14 +14151,14 @@ fn zirPtrType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air const sentinel = if (inst_data.flags.has_sentinel) blk: { const ref = @intToEnum(Zir.Inst.Ref, sema.code.extra[extra_i]); extra_i += 1; - break :blk (try sema.resolveInstConst(block, sentinel_src, ref)).val; + break :blk (try sema.resolveInstConst(block, sentinel_src, ref, "pointer sentinel value must be comptime known")).val; } else null; const abi_align: u32 = if (inst_data.flags.has_align) blk: { const ref = @intToEnum(Zir.Inst.Ref, sema.code.extra[extra_i]); extra_i += 1; - const coerced = try sema.coerce(block, Type.u32, try sema.resolveInst(ref), src); - const val = try sema.resolveConstValue(block, src, coerced); + const coerced = try sema.coerce(block, Type.u32, try sema.resolveInst(ref), align_src); + const val = try sema.resolveConstValue(block, align_src, coerced, "pointer alignment must be comptime known"); // Check if this happens to be the lazy alignment of our element type, in // which case we can make this 0 without resolving it. if (val.castTag(.lazy_align)) |payload| { @@ -13888,8 +14166,9 @@ fn zirPtrType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air break :blk 0; } } - const abi_align = (try val.getUnsignedIntAdvanced(target, sema.kit(block, src))).?; - break :blk @intCast(u32, abi_align); + const abi_align = @intCast(u32, (try val.getUnsignedIntAdvanced(target, sema.kit(block, align_src))).?); + try sema.validateAlign(block, align_src, abi_align); + break :blk abi_align; } else 0; const address_space = if (inst_data.flags.has_addrspace) blk: { @@ -13901,19 +14180,19 @@ fn zirPtrType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air const bit_offset = if (inst_data.flags.has_bit_range) blk: { const ref = @intToEnum(Zir.Inst.Ref, sema.code.extra[extra_i]); extra_i += 1; - const bit_offset = try sema.resolveInt(block, bitoffset_src, ref, Type.u16); + const bit_offset = try sema.resolveInt(block, bitoffset_src, ref, Type.u16, "pointer bit-offset must be comptime known"); break :blk @intCast(u16, bit_offset); } else 0; const host_size: u16 = if (inst_data.flags.has_bit_range) blk: { const ref = @intToEnum(Zir.Inst.Ref, sema.code.extra[extra_i]); extra_i += 1; - const host_size = try sema.resolveInt(block, hostsize_src, ref, Type.u16); + const host_size = try sema.resolveInt(block, hostsize_src, ref, Type.u16, "pointer host size must be comptime known"); break :blk @intCast(u16, host_size); } else 0; if (host_size != 0 and bit_offset >= host_size * 8) { - return sema.fail(block, src, "bit offset starts after end of host integer", .{}); + return sema.fail(block, bitoffset_src, "bit offset starts after end of host integer", .{}); } const elem_ty = if (abi_align == 0) @@ -13923,48 +14202,53 @@ fn zirPtrType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air try sema.resolveTypeLayout(block, elem_ty_src, elem_ty); break :t elem_ty; }; - const ty = try Type.ptr(sema.arena, sema.mod, .{ - .pointee_type = elem_ty, - .sentinel = sentinel, - .@"align" = abi_align, - .@"addrspace" = address_space, - .bit_offset = bit_offset, - .host_size = host_size, - .mutable = inst_data.flags.is_mutable, - .@"allowzero" = inst_data.flags.is_allowzero, - .@"volatile" = inst_data.flags.is_volatile, - .size = inst_data.size, - }); - try sema.validatePtrTy(block, elem_ty_src, ty); - return sema.addType(ty); -} -fn validatePtrTy(sema: *Sema, block: *Block, elem_src: LazySrcLoc, ty: Type) CompileError!void { - const ptr_info = ty.ptrInfo().data; - const pointee_tag = ptr_info.pointee_type.zigTypeTag(); - if (pointee_tag == .NoReturn) { - return sema.fail(block, elem_src, "pointer to noreturn not allowed", .{}); - } else if (ptr_info.size == .Many and pointee_tag == .Opaque) { - return sema.fail(block, elem_src, "unknown-length pointer to opaque not allowed", .{}); - } else if (ptr_info.size == .C) { - const elem_ty = ptr_info.pointee_type; + if (elem_ty.zigTypeTag() == .NoReturn) { + return sema.fail(block, elem_ty_src, "pointer to noreturn not allowed", .{}); + } else if (elem_ty.zigTypeTag() == .Fn) { + if (inst_data.size != .One) { + return sema.fail(block, elem_ty_src, "function pointers must be single pointers", .{}); + } + const fn_align = elem_ty.fnInfo().alignment; + if (inst_data.flags.has_align and abi_align != 0 and fn_align != 0 and + abi_align != fn_align) + { + return sema.fail(block, align_src, "function pointer alignment disagrees with function alignment", .{}); + } + } else if (inst_data.size == .Many and elem_ty.zigTypeTag() == .Opaque) { + return sema.fail(block, elem_ty_src, "unknown-length pointer to opaque not allowed", .{}); + } else if (inst_data.size == .C) { if (!(try sema.validateExternType(elem_ty, .other))) { const msg = msg: { - const msg = try sema.errMsg(block, elem_src, "C pointers cannot point to non-C-ABI-compatible type '{}'", .{elem_ty.fmt(sema.mod)}); + const msg = try sema.errMsg(block, elem_ty_src, "C pointers cannot point to non-C-ABI-compatible type '{}'", .{elem_ty.fmt(sema.mod)}); errdefer msg.destroy(sema.gpa); const src_decl = sema.mod.declPtr(block.src_decl); - try sema.explainWhyTypeIsNotExtern(block, elem_src, msg, elem_src.toSrcLoc(src_decl), elem_ty, .other); + try sema.explainWhyTypeIsNotExtern(block, elem_ty_src, msg, elem_ty_src.toSrcLoc(src_decl), elem_ty, .other); try sema.addDeclaredHereNote(msg, elem_ty); break :msg msg; }; return sema.failWithOwnedErrorMsg(block, msg); } - if (pointee_tag == .Opaque) { - return sema.fail(block, elem_src, "C pointers cannot point to opaque types", .{}); + if (elem_ty.zigTypeTag() == .Opaque) { + return sema.fail(block, elem_ty_src, "C pointers cannot point to opaque types", .{}); } } + + const ty = try Type.ptr(sema.arena, sema.mod, .{ + .pointee_type = elem_ty, + .sentinel = sentinel, + .@"align" = abi_align, + .@"addrspace" = address_space, + .bit_offset = bit_offset, + .host_size = host_size, + .mutable = inst_data.flags.is_mutable, + .@"allowzero" = inst_data.flags.is_allowzero, + .@"volatile" = inst_data.flags.is_volatile, + .size = inst_data.size, + }); + return sema.addType(ty); } fn zirStructInitEmpty(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { @@ -14018,7 +14302,7 @@ fn zirUnionInit(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A const init_src: LazySrcLoc = .{ .node_offset_builtin_call_arg2 = inst_data.src_node }; const extra = sema.code.extraData(Zir.Inst.UnionInit, inst_data.payload_index).data; const union_ty = try sema.resolveType(block, ty_src, extra.union_type); - const field_name = try sema.resolveConstString(block, field_src, extra.field_name); + const field_name = try sema.resolveConstString(block, field_src, extra.field_name, "name of field being initialized must be comptime known"); const init = try sema.resolveInst(extra.init); return sema.unionInit(block, init, init_src, union_ty, ty_src, field_name, field_src); } @@ -14045,7 +14329,7 @@ fn unionInit( })); } - try sema.requireRuntimeBlock(block, init_src); + try sema.requireRuntimeBlock(block, init_src, null); _ = union_ty_src; try sema.queueFullTypeResolution(union_ty); return block.addUnionInit(union_ty, field_index, init); @@ -14150,7 +14434,7 @@ fn zirStructInit( return alloc; } - try sema.requireRuntimeBlock(block, src); + try sema.requireRuntimeBlock(block, src, null); try sema.queueFullTypeResolution(resolved_ty); return block.addUnionInit(resolved_ty, field_index, init_inst); } else if (resolved_ty.isAnonStruct()) { @@ -14255,7 +14539,7 @@ fn finishStructInit( return alloc; } - try sema.requireRuntimeBlock(block, dest_src); + try sema.requireRuntimeBlock(block, dest_src, null); try sema.queueFullTypeResolution(struct_ty); return block.addAggregateInit(struct_ty, field_inits); } @@ -14277,13 +14561,23 @@ fn zirStructInitAnon( var runtime_src: ?LazySrcLoc = null; var extra_index = extra.end; for (types) |*field_ty, i| { + const init_src = src; // TODO better source location const item = sema.code.extraData(Zir.Inst.StructInitAnon.Item, extra_index); extra_index = item.end; names[i] = sema.code.nullTerminatedString(item.data.field_name); const init = try sema.resolveInst(item.data.init); field_ty.* = sema.typeOf(init); - const init_src = src; // TODO better source location + if (types[i].zigTypeTag() == .Opaque) { + const msg = msg: { + const msg = try sema.errMsg(block, init_src, "opaque types have unknown size and therefore cannot be directly embedded in structs", .{}); + errdefer msg.destroy(sema.gpa); + + try sema.addDeclaredHereNote(msg, types[i]); + break :msg msg; + }; + return sema.failWithOwnedErrorMsg(block, msg); + } if (try sema.resolveMaybeUndefVal(block, init_src, init)) |init_val| { values[i] = init_val; } else { @@ -14305,7 +14599,7 @@ fn zirStructInitAnon( return sema.addConstantMaybeRef(block, src, tuple_ty, tuple_val, is_ref); }; - try sema.requireRuntimeBlock(block, runtime_src); + try sema.requireRuntimeBlock(block, src, runtime_src); if (is_ref) { const target = sema.mod.getTarget(); @@ -14397,7 +14691,7 @@ fn zirArrayInit( return sema.addConstantMaybeRef(block, src, array_ty, array_val, is_ref); }; - try sema.requireRuntimeBlock(block, runtime_src); + try sema.requireRuntimeBlock(block, src, runtime_src); try sema.queueFullTypeResolution(array_ty); if (is_ref) { @@ -14460,9 +14754,19 @@ fn zirArrayInitAnon( const opt_runtime_src = rs: { var runtime_src: ?LazySrcLoc = null; for (operands) |operand, i| { + const operand_src = src; // TODO better source location const elem = try sema.resolveInst(operand); types[i] = sema.typeOf(elem); - const operand_src = src; // TODO better source location + if (types[i].zigTypeTag() == .Opaque) { + const msg = msg: { + const msg = try sema.errMsg(block, operand_src, "opaque types have unknown size and therefore cannot be directly embedded in structs", .{}); + errdefer msg.destroy(sema.gpa); + + try sema.addDeclaredHereNote(msg, types[i]); + break :msg msg; + }; + return sema.failWithOwnedErrorMsg(block, msg); + } if (try sema.resolveMaybeUndefVal(block, operand_src, elem)) |val| { values[i] = val; } else { @@ -14483,7 +14787,7 @@ fn zirArrayInitAnon( return sema.addConstantMaybeRef(block, src, tuple_ty, tuple_val, is_ref); }; - try sema.requireRuntimeBlock(block, runtime_src); + try sema.requireRuntimeBlock(block, src, runtime_src); if (is_ref) { const target = sema.mod.getTarget(); @@ -14542,7 +14846,7 @@ fn zirFieldTypeRef(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileErro const ty_src = inst_data.src(); const field_src = inst_data.src(); const aggregate_ty = try sema.resolveType(block, ty_src, extra.container_type); - const field_name = try sema.resolveConstString(block, field_src, extra.field_name); + const field_name = try sema.resolveConstString(block, field_src, extra.field_name, "field name must be comptime known"); return sema.fieldType(block, aggregate_ty, field_name, field_src, ty_src); } @@ -14732,7 +15036,7 @@ fn zirUnaryMath( ); } - try sema.requireRuntimeBlock(block, operand_src); + try sema.requireRuntimeBlock(block, operand_src, null); return block.addUnOp(air_tag, operand); }, .ComptimeFloat, .Float => { @@ -14743,7 +15047,7 @@ fn zirUnaryMath( return sema.addConstant(operand_ty, result_val); } - try sema.requireRuntimeBlock(block, operand_src); + try sema.requireRuntimeBlock(block, operand_src, null); return block.addUnOp(air_tag, operand); }, else => unreachable, @@ -14761,7 +15065,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, operand_src, operand); + const val = try sema.resolveConstValue(block, .unneeded, operand, undefined); const bytes = val.castTag(.enum_literal).?.data; return sema.addStrLit(block, bytes); }, @@ -14813,7 +15117,7 @@ fn zirReify(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I const uncasted_operand = try sema.resolveInst(inst_data.operand); const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; const type_info = try sema.coerce(block, type_info_ty, uncasted_operand, operand_src); - const val = try sema.resolveConstValue(block, operand_src, type_info); + const val = try sema.resolveConstValue(block, operand_src, type_info, "operand to @Type must be comptime known"); const union_val = val.cast(Value.Payload.Union).?.data; const tag_ty = type_info_ty.unionTagType().?; const target = mod.getTarget(); @@ -15497,10 +15801,10 @@ fn zirFloatToInt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError! const result_val = try sema.floatToInt(block, operand_src, val, operand_ty, dest_ty); return sema.addConstant(dest_ty, result_val); } else if (dest_ty.zigTypeTag() == .ComptimeInt) { - return sema.failWithNeededComptime(block, operand_src); + return sema.failWithNeededComptime(block, operand_src, "value being casted to 'comptime_int' must be comptime known"); } - try sema.requireRuntimeBlock(block, operand_src); + try sema.requireRuntimeBlock(block, inst_data.src(), operand_src); return block.addTyOp(.float_to_int, dest_ty, operand); } @@ -15521,10 +15825,10 @@ fn zirIntToFloat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError! const result_val = try val.intToFloat(sema.arena, operand_ty, dest_ty, target); return sema.addConstant(dest_ty, result_val); } else if (dest_ty.zigTypeTag() == .ComptimeFloat) { - return sema.failWithNeededComptime(block, operand_src); + return sema.failWithNeededComptime(block, operand_src, "value being casted to 'comptime_float' must be comptime known"); } - try sema.requireRuntimeBlock(block, operand_src); + try sema.requireRuntimeBlock(block, inst_data.src(), operand_src); return block.addTyOp(.int_to_float, dest_ty, operand); } @@ -15560,7 +15864,7 @@ fn zirIntToPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai return sema.addConstant(type_res, Value.initPayload(&val_payload.base)); } - try sema.requireRuntimeBlock(block, src); + try sema.requireRuntimeBlock(block, src, operand_src); if (block.wantSafety()) { if (!type_res.isAllowzeroPtr()) { const is_non_zero = try block.addBinOp(.cmp_neq, operand_coerced, .zero_usize); @@ -15657,7 +15961,7 @@ fn zirErrSetCast(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstDat return sema.addConstant(dest_ty, val); } - try sema.requireRuntimeBlock(block, src); + try sema.requireRuntimeBlock(block, src, operand_src); if (block.wantSafety() and !dest_ty.isAnyError()) { const err_int_inst = try block.addBitCast(Type.u16, operand); // TODO: Output a switch instead of chained OR's. @@ -15812,7 +16116,7 @@ fn zirTruncate(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai ); } - try sema.requireRuntimeBlock(block, src); + try sema.requireRuntimeBlock(block, src, operand_src); return block.addTyOp(.trunc, dest_ty, operand); } @@ -15855,6 +16159,7 @@ fn zirBitCount( comptimeOp: fn (val: Value, ty: Type, target: std.Target) u64, ) CompileError!Air.Inst.Ref { const inst_data = sema.code.instructions.items(.data)[inst].un_node; + const src = inst_data.src(); const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node }; const operand = try sema.resolveInst(inst_data.operand); const operand_ty = sema.typeOf(operand); @@ -15887,7 +16192,7 @@ fn zirBitCount( try Value.Tag.aggregate.create(sema.arena, elems), ); } else { - try sema.requireRuntimeBlock(block, operand_src); + try sema.requireRuntimeBlock(block, src, operand_src); return block.addTyOp(air_tag, result_ty, operand); } }, @@ -15896,7 +16201,7 @@ fn zirBitCount( if (val.isUndef()) return sema.addConstUndef(result_scalar_ty); return sema.addIntUnsigned(result_scalar_ty, comptimeOp(val, operand_ty, target)); } else { - try sema.requireRuntimeBlock(block, operand_src); + try sema.requireRuntimeBlock(block, src, operand_src); return block.addTyOp(air_tag, result_scalar_ty, operand); } }, @@ -15906,6 +16211,7 @@ fn zirBitCount( fn zirByteSwap(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { const inst_data = sema.code.instructions.items(.data)[inst].un_node; + const src = inst_data.src(); const ty_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node }; const operand = try sema.resolveInst(inst_data.operand); @@ -15934,7 +16240,7 @@ fn zirByteSwap(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai return sema.addConstant(operand_ty, result_val); } else operand_src; - try sema.requireRuntimeBlock(block, runtime_src); + try sema.requireRuntimeBlock(block, src, runtime_src); return block.addTyOp(.byte_swap, operand_ty, operand); }, .Vector => { @@ -15955,7 +16261,7 @@ fn zirByteSwap(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai ); } else operand_src; - try sema.requireRuntimeBlock(block, runtime_src); + try sema.requireRuntimeBlock(block, src, runtime_src); return block.addTyOp(.byte_swap, operand_ty, operand); }, else => unreachable, @@ -15964,6 +16270,7 @@ fn zirByteSwap(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai fn zirBitReverse(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { const inst_data = sema.code.instructions.items(.data)[inst].un_node; + const src = inst_data.src(); const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node }; const operand = try sema.resolveInst(inst_data.operand); const operand_ty = sema.typeOf(operand); @@ -15982,7 +16289,7 @@ fn zirBitReverse(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError! return sema.addConstant(operand_ty, result_val); } else operand_src; - try sema.requireRuntimeBlock(block, runtime_src); + try sema.requireRuntimeBlock(block, src, runtime_src); return block.addTyOp(.bit_reverse, operand_ty, operand); }, .Vector => { @@ -16003,7 +16310,7 @@ fn zirBitReverse(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError! ); } else operand_src; - try sema.requireRuntimeBlock(block, runtime_src); + try sema.requireRuntimeBlock(block, src, runtime_src); return block.addTyOp(.bit_reverse, operand_ty, operand); }, else => unreachable, @@ -16030,7 +16337,7 @@ fn bitOffsetOf(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!u6 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; const ty = try sema.resolveType(block, lhs_src, extra.lhs); - const field_name = try sema.resolveConstString(block, rhs_src, extra.rhs); + const field_name = try sema.resolveConstString(block, rhs_src, extra.rhs, "name of field must be comptime known"); const target = sema.mod.getTarget(); try sema.resolveTypeLayout(block, lhs_src, ty); @@ -16451,19 +16758,19 @@ fn resolveExportOptions( const options = try sema.coerce(block, export_options_ty, air_ref, src); const name_operand = try sema.fieldVal(block, src, options, "name", src); - const name_val = try sema.resolveConstValue(block, src, name_operand); + const name_val = try sema.resolveConstValue(block, src, name_operand, "name of exported value must be comptime known"); const name_ty = Type.initTag(.const_slice_u8); const name = try name_val.toAllocatedBytes(name_ty, sema.arena, sema.mod); const linkage_operand = try sema.fieldVal(block, src, options, "linkage", src); - const linkage_val = try sema.resolveConstValue(block, src, linkage_operand); + const linkage_val = try sema.resolveConstValue(block, src, linkage_operand, "linkage of exported value must be comptime known"); const linkage = linkage_val.toEnum(std.builtin.GlobalLinkage); const section = try sema.fieldVal(block, src, options, "section", src); - const section_val = try sema.resolveConstValue(block, src, section); + const section_val = try sema.resolveConstValue(block, src, section, "linksection of exported value must be comptime known"); const visibility_operand = try sema.fieldVal(block, src, options, "visibility", src); - const visibility_val = try sema.resolveConstValue(block, src, visibility_operand); + const visibility_val = try sema.resolveConstValue(block, src, visibility_operand, "visibility of exported value must be comptime known"); const visibility = visibility_val.toEnum(std.builtin.SymbolVisibility); if (name.len < 1) { @@ -16494,11 +16801,12 @@ fn resolveBuiltinEnum( src: LazySrcLoc, zir_ref: Zir.Inst.Ref, comptime name: []const u8, + reason: []const u8, ) CompileError!@field(std.builtin, name) { const ty = try sema.getBuiltinType(block, src, name); const air_ref = try sema.resolveInst(zir_ref); const coerced = try sema.coerce(block, ty, air_ref, src); - const val = try sema.resolveConstValue(block, src, coerced); + const val = try sema.resolveConstValue(block, src, coerced, reason); return val.toEnum(@field(std.builtin, name)); } @@ -16507,8 +16815,9 @@ fn resolveAtomicOrder( block: *Block, src: LazySrcLoc, zir_ref: Zir.Inst.Ref, + reason: []const u8, ) CompileError!std.builtin.AtomicOrder { - return resolveBuiltinEnum(sema, block, src, zir_ref, "AtomicOrder"); + return resolveBuiltinEnum(sema, block, src, zir_ref, "AtomicOrder", reason); } fn resolveAtomicRmwOp( @@ -16517,7 +16826,7 @@ fn resolveAtomicRmwOp( src: LazySrcLoc, zir_ref: Zir.Inst.Ref, ) CompileError!std.builtin.AtomicRmwOp { - return resolveBuiltinEnum(sema, block, src, zir_ref, "AtomicRmwOp"); + return resolveBuiltinEnum(sema, block, src, zir_ref, "AtomicRmwOp", "@atomicRmW operation must be comptime known"); } fn zirCmpxchg( @@ -16550,8 +16859,8 @@ fn zirCmpxchg( const uncasted_ptr = try sema.resolveInst(extra.ptr); const ptr = try sema.checkAtomicPtrOperand(block, elem_ty, elem_ty_src, uncasted_ptr, ptr_src, false); const new_value = try sema.coerce(block, elem_ty, try sema.resolveInst(extra.new_value), new_value_src); - const success_order = try sema.resolveAtomicOrder(block, success_order_src, extra.success_order); - const failure_order = try sema.resolveAtomicOrder(block, failure_order_src, extra.failure_order); + const success_order = try sema.resolveAtomicOrder(block, success_order_src, extra.success_order, "atomic order of cmpxchg success must be comptime known"); + const failure_order = try sema.resolveAtomicOrder(block, failure_order_src, extra.failure_order, "atomic order of cmpxchg failure must be comptime known"); if (@enumToInt(success_order) < @enumToInt(std.builtin.AtomicOrder.Monotonic)) { return sema.fail(block, success_order_src, "success atomic ordering must be Monotonic or stricter", .{}); @@ -16596,7 +16905,7 @@ fn zirCmpxchg( const flags: u32 = @as(u32, @enumToInt(success_order)) | (@as(u32, @enumToInt(failure_order)) << 3); - try sema.requireRuntimeBlock(block, runtime_src); + try sema.requireRuntimeBlock(block, src, runtime_src); return block.addInst(.{ .tag = air_tag, .data = .{ .ty_pl = .{ @@ -16616,7 +16925,7 @@ fn zirSplat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; const len_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node }; const scalar_src: LazySrcLoc = .{ .node_offset_bin_rhs = inst_data.src_node }; - const len = @intCast(u32, try sema.resolveInt(block, len_src, extra.lhs, Type.u32)); + const len = @intCast(u32, try sema.resolveInt(block, len_src, extra.lhs, Type.u32, "vector splat destination length must be comptime known")); const scalar = try sema.resolveInst(extra.rhs); const scalar_ty = sema.typeOf(scalar); try sema.checkVectorElemType(block, scalar_src, scalar_ty); @@ -16633,7 +16942,7 @@ fn zirSplat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I ); } - try sema.requireRuntimeBlock(block, scalar_src); + try sema.requireRuntimeBlock(block, inst_data.src(), scalar_src); return block.addTyOp(.splat, vector_ty, scalar); } @@ -16642,7 +16951,7 @@ fn zirReduce(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air. const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; const op_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node }; - const operation = try sema.resolveBuiltinEnum(block, op_src, extra.lhs, "ReduceOp"); + const operation = try sema.resolveBuiltinEnum(block, op_src, extra.lhs, "ReduceOp", "@reduce operation must be comptime known"); const operand = try sema.resolveInst(extra.rhs); const operand_ty = sema.typeOf(operand); const target = sema.mod.getTarget(); @@ -16697,7 +17006,7 @@ fn zirReduce(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air. return sema.addConstant(scalar_ty, accum); } - try sema.requireRuntimeBlock(block, operand_src); + try sema.requireRuntimeBlock(block, inst_data.src(), operand_src); return block.addInst(.{ .tag = .reduce, .data = .{ .reduce = .{ @@ -16729,7 +17038,7 @@ fn zirShuffle(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air .elem_type = Type.@"i32", }); mask = try sema.coerce(block, mask_ty, mask, mask_src); - const mask_val = try sema.resolveConstMaybeUndefVal(block, mask_src, mask); + const mask_val = try sema.resolveConstMaybeUndefVal(block, mask_src, mask, "shuffle mask must be comptime known"); return sema.analyzeShuffle(block, inst_data.src_node, elem_ty, a, b, mask_val, @intCast(u32, mask_len)); } @@ -16901,6 +17210,7 @@ fn analyzeShuffle( fn zirSelect(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref { const extra = sema.code.extraData(Zir.Inst.Select, extended.operand).data; + const src = LazySrcLoc.nodeOffset(extra.node); const elem_ty_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = extra.node }; const pred_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = extra.node }; const a_src: LazySrcLoc = .{ .node_offset_builtin_call_arg2 = extra.node }; @@ -16972,7 +17282,7 @@ fn zirSelect(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) C break :rs pred_src; }; - try sema.requireRuntimeBlock(block, runtime_src); + try sema.requireRuntimeBlock(block, src, runtime_src); return block.addInst(.{ .tag = .select, .data = .{ .pl_op = .{ @@ -16996,7 +17306,7 @@ fn zirAtomicLoad(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError! const elem_ty = try sema.resolveType(block, elem_ty_src, extra.elem_type); const uncasted_ptr = try sema.resolveInst(extra.ptr); const ptr = try sema.checkAtomicPtrOperand(block, elem_ty, elem_ty_src, uncasted_ptr, ptr_src, true); - const order = try sema.resolveAtomicOrder(block, order_src, extra.ordering); + const order = try sema.resolveAtomicOrder(block, order_src, extra.ordering, "atomic order of @atomicLoad must be comptime known"); switch (order) { .Release, .AcqRel => { @@ -17020,7 +17330,7 @@ fn zirAtomicLoad(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError! } } - try sema.requireRuntimeBlock(block, ptr_src); + try sema.requireRuntimeBlock(block, inst_data.src(), ptr_src); return block.addInst(.{ .tag = .atomic_load, .data = .{ .atomic_load = .{ @@ -17060,7 +17370,7 @@ fn zirAtomicRmw(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A }, else => {}, } - const order = try sema.resolveAtomicOrder(block, order_src, extra.ordering); + const order = try sema.resolveAtomicOrder(block, order_src, extra.ordering, "atomic order of @atomicRmW must be comptime known"); if (order == .Unordered) { return sema.fail(block, order_src, "@atomicRmw atomic ordering must not be Unordered", .{}); @@ -17101,7 +17411,7 @@ fn zirAtomicRmw(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A const flags: u32 = @as(u32, @enumToInt(order)) | (@as(u32, @enumToInt(op)) << 3); - try sema.requireRuntimeBlock(block, runtime_src); + try sema.requireRuntimeBlock(block, src, runtime_src); return block.addInst(.{ .tag = .atomic_rmw, .data = .{ .pl_op = .{ @@ -17128,7 +17438,7 @@ fn zirAtomicStore(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError const elem_ty = sema.typeOf(operand); const uncasted_ptr = try sema.resolveInst(extra.ptr); const ptr = try sema.checkAtomicPtrOperand(block, elem_ty, elem_ty_src, uncasted_ptr, ptr_src, false); - const order = try sema.resolveAtomicOrder(block, order_src, extra.ordering); + const order = try sema.resolveAtomicOrder(block, order_src, extra.ordering, "atomic order of @atomicStore must be comptime known"); const air_tag: Air.Inst.Tag = switch (order) { .Acquire, .AcqRel => { @@ -17200,7 +17510,7 @@ fn zirMulAdd(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air. break :rs mulend1_src; }; - try sema.requireRuntimeBlock(block, runtime_src); + try sema.requireRuntimeBlock(block, src, runtime_src); return block.addInst(.{ .tag = .mul_add, .data = .{ .pl_op = .{ @@ -17233,10 +17543,10 @@ fn zirBuiltinCall(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError const coerced_options = try sema.coerce(block, call_options_ty, options, options_src); const modifier = try sema.fieldVal(block, options_src, coerced_options, "modifier", options_src); - const modifier_val = try sema.resolveConstValue(block, options_src, modifier); + const modifier_val = try sema.resolveConstValue(block, options_src, modifier, "call modifier must be comptime known"); const stack = try sema.fieldVal(block, options_src, coerced_options, "stack", options_src); - const stack_val = try sema.resolveConstValue(block, options_src, stack); + const stack_val = try sema.resolveConstValue(block, options_src, stack, "call stack value must be comptime known"); if (!stack_val.isNull()) { return sema.fail(block, options_src, "TODO: implement @call with stack", .{}); @@ -17297,7 +17607,7 @@ fn zirBuiltinCall(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError // Desugar bound functions here if (sema.typeOf(func).tag() == .bound_fn) { - const bound_func = try sema.resolveValue(block, func_src, func); + const bound_func = try sema.resolveValue(block, .unneeded, func, undefined); 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); @@ -17324,7 +17634,7 @@ fn zirFieldParentPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileEr const ptr_src: LazySrcLoc = .{ .node_offset_builtin_call_arg2 = inst_data.src_node }; const struct_ty = try sema.resolveType(block, ty_src, extra.parent_type); - const field_name = try sema.resolveConstString(block, name_src, extra.field_name); + const field_name = try sema.resolveConstString(block, name_src, extra.field_name, "field name must be comptime known"); const field_ptr = try sema.resolveInst(extra.field_ptr); const field_ptr_ty = sema.typeOf(field_ptr); @@ -17389,7 +17699,7 @@ fn zirFieldParentPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileEr return sema.addConstant(result_ptr, payload.data.container_ptr); } - try sema.requireRuntimeBlock(block, src); + try sema.requireRuntimeBlock(block, src, ptr_src); return block.addInst(.{ .tag = .field_parent_ptr, .data = .{ .ty_pl = .{ @@ -17470,7 +17780,7 @@ fn analyzeMinMax( break :rs lhs_src; }; - try sema.requireRuntimeBlock(block, runtime_src); + try sema.requireRuntimeBlock(block, src, runtime_src); return block.addBinOp(air_tag, simd_op.lhs, simd_op.rhs); } @@ -17518,7 +17828,7 @@ fn zirMemcpy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void } else break :rs src_src; } else dest_src; - try sema.requireRuntimeBlock(block, runtime_src); + try sema.requireRuntimeBlock(block, src, runtime_src); _ = try block.addInst(.{ .tag = .memcpy, .data = .{ .pl_op = .{ @@ -17560,7 +17870,7 @@ fn zirMemset(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void } else break :rs len_src; } else dest_src; - try sema.requireRuntimeBlock(block, runtime_src); + try sema.requireRuntimeBlock(block, src, runtime_src); _ = try block.addInst(.{ .tag = .memset, .data = .{ .pl_op = .{ @@ -17656,7 +17966,7 @@ fn zirVarExtended( uncasted_init; break :blk (try sema.resolveMaybeUndefVal(block, init_src, init)) orelse - return sema.failWithNeededComptime(block, init_src); + return sema.failWithNeededComptime(block, init_src, "container level variable initializers must be comptime known"); } else Value.initTag(.unreachable_value); try sema.validateVarType(block, name_src, var_ty, small.is_extern); @@ -17694,15 +18004,15 @@ fn zirFuncFancy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A defer tracy.end(); const inst_data = sema.code.instructions.items(.data)[inst].pl_node; - const src = inst_data.src(); const extra = sema.code.extraData(Zir.Inst.FuncFancy, inst_data.payload_index); const target = sema.mod.getTarget(); - const align_src: LazySrcLoc = src; // TODO add a LazySrcLoc that points at align - const addrspace_src: LazySrcLoc = src; // TODO add a LazySrcLoc that points at addrspace - const section_src: LazySrcLoc = src; // TODO add a LazySrcLoc that points at section + const align_src: LazySrcLoc = .{ .node_offset_fn_type_align = inst_data.src_node }; + const addrspace_src: LazySrcLoc = .{ .node_offset_fn_type_addrspace = inst_data.src_node }; + const section_src: LazySrcLoc = .{ .node_offset_fn_type_section = inst_data.src_node }; const cc_src: LazySrcLoc = .{ .node_offset_fn_type_cc = inst_data.src_node }; - const ret_src: LazySrcLoc = src; // TODO add a LazySrcLoc that points at the return type + const ret_src: LazySrcLoc = .{ .node_offset_fn_type_ret_ty = inst_data.src_node }; + const has_body = extra.data.body_len != 0; var extra_index: usize = extra.end; @@ -17712,17 +18022,25 @@ fn zirFuncFancy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A break :blk lib_name; } else null; + if (has_body and + (extra.data.bits.has_align_body or extra.data.bits.has_align_ref) and + !target_util.supportsFunctionAlignment(target)) + { + return sema.fail(block, align_src, "target does not support function alignment", .{}); + } + const @"align": ?u32 = if (extra.data.bits.has_align_body) blk: { const body_len = sema.code.extra[extra_index]; extra_index += 1; const body = sema.code.extra[extra_index..][0..body_len]; extra_index += body.len; - const val = try sema.resolveGenericBody(block, align_src, body, inst, Type.u29); + const val = try sema.resolveGenericBody(block, align_src, body, inst, Type.u29, "alignment must be comptime known"); if (val.tag() == .generic_poison) { break :blk null; } const alignment = @intCast(u32, val.toUnsignedInt(target)); + try sema.validateAlign(block, align_src, alignment); if (alignment == target_util.defaultFunctionAlignment(target)) { break :blk 0; } else { @@ -17731,13 +18049,14 @@ fn zirFuncFancy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A } else if (extra.data.bits.has_align_ref) blk: { const align_ref = @intToEnum(Zir.Inst.Ref, sema.code.extra[extra_index]); extra_index += 1; - const align_tv = sema.resolveInstConst(block, align_src, align_ref) catch |err| switch (err) { + const align_tv = sema.resolveInstConst(block, align_src, align_ref, "alignment must be comptime known") catch |err| switch (err) { error.GenericPoison => { break :blk null; }, else => |e| return e, }; const alignment = @intCast(u32, align_tv.val.toUnsignedInt(target)); + try sema.validateAlign(block, align_src, alignment); if (alignment == target_util.defaultFunctionAlignment(target)) { break :blk 0; } else { @@ -17752,7 +18071,7 @@ fn zirFuncFancy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A extra_index += body.len; const addrspace_ty = try sema.getBuiltinType(block, addrspace_src, "AddressSpace"); - const val = try sema.resolveGenericBody(block, addrspace_src, body, inst, addrspace_ty); + const val = try sema.resolveGenericBody(block, addrspace_src, body, inst, addrspace_ty, "addrespace must be comptime known"); if (val.tag() == .generic_poison) { break :blk null; } @@ -17760,7 +18079,7 @@ fn zirFuncFancy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A } else if (extra.data.bits.has_addrspace_ref) blk: { const addrspace_ref = @intToEnum(Zir.Inst.Ref, sema.code.extra[extra_index]); extra_index += 1; - const addrspace_tv = sema.resolveInstConst(block, addrspace_src, addrspace_ref) catch |err| switch (err) { + const addrspace_tv = sema.resolveInstConst(block, addrspace_src, addrspace_ref, "addrespace must be comptime known") catch |err| switch (err) { error.GenericPoison => { break :blk null; }, @@ -17775,7 +18094,7 @@ fn zirFuncFancy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A const body = sema.code.extra[extra_index..][0..body_len]; extra_index += body.len; - const val = try sema.resolveGenericBody(block, section_src, body, inst, Type.initTag(.const_slice_u8)); + const val = try sema.resolveGenericBody(block, section_src, body, inst, Type.initTag(.const_slice_u8), "linksection must be comptime known"); if (val.tag() == .generic_poison) { break :blk FuncLinkSection{ .generic = {} }; } @@ -17784,7 +18103,7 @@ fn zirFuncFancy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A } else if (extra.data.bits.has_section_ref) blk: { const section_ref = @intToEnum(Zir.Inst.Ref, sema.code.extra[extra_index]); extra_index += 1; - const section_tv = sema.resolveInstConst(block, section_src, section_ref) catch |err| switch (err) { + const section_tv = sema.resolveInstConst(block, section_src, section_ref, "linksection must be comptime known") catch |err| switch (err) { error.GenericPoison => { break :blk FuncLinkSection{ .generic = {} }; }, @@ -17801,7 +18120,7 @@ fn zirFuncFancy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A extra_index += body.len; const cc_ty = try sema.getBuiltinType(block, addrspace_src, "CallingConvention"); - const val = try sema.resolveGenericBody(block, cc_src, body, inst, cc_ty); + const val = try sema.resolveGenericBody(block, cc_src, body, inst, cc_ty, "calling convention must be comptime known"); if (val.tag() == .generic_poison) { break :blk null; } @@ -17809,7 +18128,7 @@ fn zirFuncFancy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A } else if (extra.data.bits.has_cc_ref) blk: { const cc_ref = @intToEnum(Zir.Inst.Ref, sema.code.extra[extra_index]); extra_index += 1; - const cc_tv = sema.resolveInstConst(block, cc_src, cc_ref) catch |err| switch (err) { + const cc_tv = sema.resolveInstConst(block, cc_src, cc_ref, "calling convention must be comptime known") catch |err| switch (err) { error.GenericPoison => { break :blk null; }, @@ -17824,14 +18143,14 @@ fn zirFuncFancy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A const body = sema.code.extra[extra_index..][0..body_len]; extra_index += body.len; - const val = try sema.resolveGenericBody(block, ret_src, body, inst, Type.type); + const val = try sema.resolveGenericBody(block, ret_src, body, inst, Type.type, "return type must be comptime known"); var buffer: Value.ToTypeBuffer = undefined; const ty = try val.toType(&buffer).copy(sema.arena); break :blk ty; } else if (extra.data.bits.has_ret_ty_ref) blk: { const ret_ty_ref = @intToEnum(Zir.Inst.Ref, sema.code.extra[extra_index]); extra_index += 1; - const ret_ty_tv = sema.resolveInstConst(block, ret_src, ret_ty_ref) catch |err| switch (err) { + const ret_ty_tv = sema.resolveInstConst(block, ret_src, ret_ty_ref, "return type must be comptime known") catch |err| switch (err) { error.GenericPoison => { break :blk Type.initTag(.generic_poison); }, @@ -17849,7 +18168,6 @@ fn zirFuncFancy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A } else 0; var src_locs: Zir.Inst.Func.SrcLocs = undefined; - const has_body = extra.data.body_len != 0; if (has_body) { extra_index += extra.data.body_len; src_locs = sema.code.extraData(Zir.Inst.Func.SrcLocs, extra_index).data; @@ -17886,7 +18204,7 @@ fn zirCUndef( const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data; const src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = extra.node }; - const name = try sema.resolveConstString(block, src, extra.operand); + const name = try sema.resolveConstString(block, src, extra.operand, "name of macro being undefined must be comptime known"); try block.c_import_buf.?.writer().print("#undefine {s}\n", .{name}); return Air.Inst.Ref.void_value; } @@ -17899,7 +18217,7 @@ fn zirCInclude( const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data; const src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = extra.node }; - const name = try sema.resolveConstString(block, src, extra.operand); + const name = try sema.resolveConstString(block, src, extra.operand, "path being included must be comptime known"); try block.c_import_buf.?.writer().print("#include <{s}>\n", .{name}); return Air.Inst.Ref.void_value; } @@ -17913,10 +18231,10 @@ fn zirCDefine( const name_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = extra.node }; const val_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = extra.node }; - const name = try sema.resolveConstString(block, name_src, extra.lhs); + const name = try sema.resolveConstString(block, name_src, extra.lhs, "name of macro being undefined must be comptime known"); const rhs = try sema.resolveInst(extra.rhs); if (sema.typeOf(rhs).zigTypeTag() != .Void) { - const value = try sema.resolveConstString(block, val_src, extra.rhs); + const value = try sema.resolveConstString(block, val_src, extra.rhs, "value of macro being undefined must be comptime known"); try block.c_import_buf.?.writer().print("#define {s} {s}\n", .{ name, value }); } else { try block.c_import_buf.?.writer().print("#define {s}\n", .{name}); @@ -17937,8 +18255,8 @@ fn zirWasmMemorySize( return sema.fail(block, builtin_src, "builtin @wasmMemorySize is available when targeting WebAssembly; targeted CPU architecture is {s}", .{@tagName(target.cpu.arch)}); } - const index = @intCast(u32, try sema.resolveInt(block, index_src, extra.operand, Type.u32)); - try sema.requireRuntimeBlock(block, builtin_src); + const index = @intCast(u32, try sema.resolveInt(block, index_src, extra.operand, Type.u32, "wasm memory size index must be comptime known")); + try sema.requireRuntimeBlock(block, builtin_src, null); return block.addInst(.{ .tag = .wasm_memory_size, .data = .{ .pl_op = .{ @@ -17962,10 +18280,10 @@ fn zirWasmMemoryGrow( return sema.fail(block, builtin_src, "builtin @wasmMemoryGrow is available when targeting WebAssembly; targeted CPU architecture is {s}", .{@tagName(target.cpu.arch)}); } - const index = @intCast(u32, try sema.resolveInt(block, index_src, extra.lhs, Type.u32)); + const index = @intCast(u32, try sema.resolveInt(block, index_src, extra.lhs, Type.u32, "wasm memory size index must be comptime known")); const delta = try sema.coerce(block, Type.u32, try sema.resolveInst(extra.rhs), delta_src); - try sema.requireRuntimeBlock(block, builtin_src); + try sema.requireRuntimeBlock(block, builtin_src, null); return block.addInst(.{ .tag = .wasm_memory_grow, .data = .{ .pl_op = .{ @@ -17990,15 +18308,15 @@ fn zirPrefetch( const target = sema.mod.getTarget(); const rw = try sema.fieldVal(block, opts_src, options, "rw", opts_src); - const rw_val = try sema.resolveConstValue(block, opts_src, rw); + const rw_val = try sema.resolveConstValue(block, opts_src, rw, "prefetch read/write must be comptime known"); const rw_tag = rw_val.toEnum(std.builtin.PrefetchOptions.Rw); const locality = try sema.fieldVal(block, opts_src, options, "locality", opts_src); - const locality_val = try sema.resolveConstValue(block, opts_src, locality); + const locality_val = try sema.resolveConstValue(block, opts_src, locality, "prefetch locality must be comptime known"); const locality_int = @intCast(u2, locality_val.toUnsignedInt(target)); const cache = try sema.fieldVal(block, opts_src, options, "cache", opts_src); - const cache_val = try sema.resolveConstValue(block, opts_src, cache); + const cache_val = try sema.resolveConstValue(block, opts_src, cache, "prefetch cache must be comptime known"); const cache_tag = cache_val.toEnum(std.builtin.PrefetchOptions.Cache); if (!block.is_comptime) { @@ -18035,16 +18353,16 @@ fn zirBuiltinExtern( const options = try sema.coerce(block, extern_options_ty, options_inst, options_src); const name = try sema.fieldVal(block, options_src, options, "name", options_src); - const name_val = try sema.resolveConstValue(block, options_src, name); + const name_val = try sema.resolveConstValue(block, options_src, name, "name of the extern symbol must be comptime known"); const library_name_inst = try sema.fieldVal(block, options_src, options, "library_name", options_src); - const library_name_val = try sema.resolveConstValue(block, options_src, library_name_inst); + const library_name_val = try sema.resolveConstValue(block, options_src, library_name_inst, "library in which extern symbol is must be comptime known"); const linkage = try sema.fieldVal(block, options_src, options, "linkage", options_src); - const linkage_val = try sema.resolveConstValue(block, options_src, linkage); + const linkage_val = try sema.resolveConstValue(block, options_src, linkage, "linkage of the extern symbol must be comptime known"); const is_thread_local = try sema.fieldVal(block, options_src, options, "is_thread_local", options_src); - const is_thread_local_val = try sema.resolveConstValue(block, options_src, is_thread_local); + const is_thread_local_val = try sema.resolveConstValue(block, options_src, is_thread_local, "threadlocality of the extern symbol must be comptime known"); var library_name: ?[]const u8 = null; if (!library_name_val.isNull()) { @@ -18121,19 +18439,30 @@ fn zirBuiltinExtern( new_decl.value_arena = arena_state; const ref = try sema.analyzeDeclRef(new_decl_index); - try sema.requireRuntimeBlock(block, src); + try sema.requireRuntimeBlock(block, src, null); return block.addBitCast(ty, ref); } +/// Asserts that the block is not comptime. fn requireFunctionBlock(sema: *Sema, block: *Block, src: LazySrcLoc) !void { + assert(!block.is_comptime); if (sema.func == null and !block.is_typeof and !block.is_coerce_result_ptr) { return sema.fail(block, src, "instruction illegal outside function body", .{}); } } -fn requireRuntimeBlock(sema: *Sema, block: *Block, src: LazySrcLoc) !void { +fn requireRuntimeBlock(sema: *Sema, block: *Block, src: LazySrcLoc, runtime_src: ?LazySrcLoc) !void { if (block.is_comptime) { - return sema.failWithNeededComptime(block, src); + const msg = msg: { + const msg = try sema.errMsg(block, src, "unable to evalutate comptime expression", .{}); + errdefer msg.destroy(sema.gpa); + + if (runtime_src) |some| { + try sema.errNote(block, some, msg, "operation is runtime due to this operand", .{}); + } + break :msg msg; + }; + return sema.failWithOwnedErrorMsg(block, msg); } try sema.requireFunctionBlock(block, src); } @@ -18337,6 +18666,8 @@ const ExternPosition = enum { other, }; +/// Returns true if `ty` is allowed in extern types. +/// Does *NOT* require `ty` to be resolved in any way. fn validateExternType(sema: *Sema, ty: Type, position: ExternPosition) CompileError!bool { switch (ty.zigTypeTag()) { .Type, @@ -18350,7 +18681,7 @@ fn validateExternType(sema: *Sema, ty: Type, position: ExternPosition) CompileEr .BoundFn, .Frame, => return false, - .Void => return position == .union_field, + .Void => return position == .union_field or position == .ret_ty, .NoReturn => return position == .ret_ty, .Opaque, .Bool, @@ -18362,7 +18693,7 @@ fn validateExternType(sema: *Sema, ty: Type, position: ExternPosition) CompileEr 8, 16, 32, 64, 128 => return true, else => return false, }, - .Fn => return !ty.fnCallingConventionAllowsZigTypes(), + .Fn => return !Type.fnCallingConventionAllowsZigTypes(ty.fnCallingConvention()), .Enum => { var buf: Type.Payload.Bits = undefined; return sema.validateExternType(ty.intTagType(&buf), position); @@ -18433,9 +18764,9 @@ fn explainWhyTypeIsNotExtern( .Union => try mod.errNoteNonLazy(src_loc, msg, "only unions with packed or extern layout are extern compatible", .{}), .Array => { if (position == .ret_ty) { - try mod.errNoteNonLazy(src_loc, msg, "arrays are not allowed as a return type", .{}); + return mod.errNoteNonLazy(src_loc, msg, "arrays are not allowed as a return type", .{}); } else if (position == .param_ty) { - try mod.errNoteNonLazy(src_loc, msg, "arrays are not allowed as a parameter type", .{}); + return mod.errNoteNonLazy(src_loc, msg, "arrays are not allowed as a parameter type", .{}); } try sema.explainWhyTypeIsNotExtern(block, src, msg, src_loc, ty.elemType2(), position); }, @@ -18972,7 +19303,7 @@ fn fieldPtr( }), ); } - try sema.requireRuntimeBlock(block, src); + try sema.requireRuntimeBlock(block, src, null); return block.addTyOp(.ptr_slice_ptr_ptr, result_ty, inner_ptr); } else if (mem.eql(u8, field_name, "len")) { @@ -18992,7 +19323,7 @@ fn fieldPtr( }), ); } - try sema.requireRuntimeBlock(block, src); + try sema.requireRuntimeBlock(block, src, null); return block.addTyOp(.ptr_slice_len_ptr, result_ty, inner_ptr); } else { @@ -19005,7 +19336,7 @@ fn fieldPtr( } }, .Type => { - _ = try sema.resolveConstValue(block, object_ptr_src, object_ptr); + _ = try sema.resolveConstValue(block, .unneeded, object_ptr, undefined); 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) @@ -19238,7 +19569,7 @@ fn finishFieldCallBind( return sema.analyzeLoad(block, src, pointer, src); } - try sema.requireRuntimeBlock(block, src); + try sema.requireRuntimeBlock(block, src, null); const ptr_inst = try block.addStructFieldPtr(object_ptr, field_index, ptr_field_ty); return sema.analyzeLoad(block, src, ptr_inst, src); } @@ -19425,7 +19756,7 @@ fn structFieldPtrByIndex( ); } - try sema.requireRuntimeBlock(block, src); + try sema.requireRuntimeBlock(block, src, null); return block.addStructFieldPtr(struct_ptr, field_index, ptr_field_ty); } @@ -19469,7 +19800,7 @@ fn structFieldVal( return sema.addConstant(field.ty, field_values[field_index]); } - try sema.requireRuntimeBlock(block, src); + try sema.requireRuntimeBlock(block, src, null); return block.addStructFieldVal(struct_byval, field_index, field.ty); }, else => unreachable, @@ -19533,7 +19864,7 @@ fn tupleFieldValByIndex( return sema.addConstant(field_ty, field_values[field_index]); } - try sema.requireRuntimeBlock(block, src); + try sema.requireRuntimeBlock(block, src, null); return block.addStructFieldVal(tuple_byval, field_index, field_ty); } @@ -19597,7 +19928,7 @@ fn unionFieldPtr( ); } - try sema.requireRuntimeBlock(block, src); + try sema.requireRuntimeBlock(block, src, null); return block.addStructFieldPtr(union_ptr, field_index, ptr_field_ty); } @@ -19655,7 +19986,7 @@ fn unionFieldVal( } } - try sema.requireRuntimeBlock(block, src); + try sema.requireRuntimeBlock(block, src, null); return block.addStructFieldVal(union_byval, field_index, field.ty); } @@ -19684,7 +20015,7 @@ fn elemPtr( // In all below cases, we have to deref the ptr operand to get the actual indexable pointer. const indexable = try sema.analyzeLoad(block, indexable_ptr_src, indexable_ptr, indexable_ptr_src); switch (indexable_ty.ptrSize()) { - .Slice => return sema.elemPtrSlice(block, indexable_ptr_src, indexable, elem_index_src, elem_index), + .Slice => return sema.elemPtrSlice(block, src, indexable_ptr_src, indexable, elem_index_src, elem_index), .Many, .C => { const maybe_ptr_val = try sema.resolveDefinedValue(block, indexable_ptr_src, indexable); const maybe_index_val = try sema.resolveDefinedValue(block, elem_index_src, elem_index); @@ -19698,19 +20029,19 @@ fn elemPtr( }; const result_ty = try sema.elemPtrType(indexable_ty, null); - try sema.requireRuntimeBlock(block, runtime_src); + try sema.requireRuntimeBlock(block, src, runtime_src); return block.addPtrElemPtr(indexable, elem_index, result_ty); }, .One => { assert(indexable_ty.childType().zigTypeTag() == .Array); // Guaranteed by isIndexable - return sema.elemPtrArray(block, indexable_ptr_src, indexable, elem_index_src, elem_index, init); + return sema.elemPtrArray(block, src, indexable_ptr_src, indexable, elem_index_src, elem_index, init); }, } }, - .Array, .Vector => return sema.elemPtrArray(block, indexable_ptr_src, indexable_ptr, elem_index_src, elem_index, init), + .Array, .Vector => return sema.elemPtrArray(block, src, indexable_ptr_src, indexable_ptr, elem_index_src, elem_index, init), .Struct => { // Tuple field access. - const index_val = try sema.resolveConstValue(block, elem_index_src, elem_index); + const index_val = try sema.resolveConstValue(block, elem_index_src, elem_index, "tuple field access index must be comptime known"); const index = @intCast(u32, index_val.toUnsignedInt(target)); return sema.tupleFieldPtr(block, src, indexable_ptr, elem_index_src, index); }, @@ -19740,7 +20071,7 @@ fn elemVal( switch (indexable_ty.zigTypeTag()) { .Pointer => switch (indexable_ty.ptrSize()) { - .Slice => return sema.elemValSlice(block, indexable_src, indexable, elem_index_src, elem_index), + .Slice => return sema.elemValSlice(block, src, indexable_src, indexable, elem_index_src, elem_index), .Many, .C => { const maybe_indexable_val = try sema.resolveDefinedValue(block, indexable_src, indexable); const maybe_index_val = try sema.resolveDefinedValue(block, elem_index_src, elem_index); @@ -19756,7 +20087,7 @@ fn elemVal( break :rs indexable_src; }; - try sema.requireRuntimeBlock(block, runtime_src); + try sema.requireRuntimeBlock(block, src, runtime_src); return block.addBinOp(.ptr_elem_val, indexable, elem_index); }, .One => { @@ -19765,14 +20096,14 @@ fn elemVal( return sema.analyzeLoad(block, indexable_src, elem_ptr, elem_index_src); }, }, - .Array => return elemValArray(sema, block, indexable_src, indexable, elem_index_src, elem_index), + .Array => return sema.elemValArray(block, src, indexable_src, indexable, elem_index_src, elem_index), .Vector => { // TODO: If the index is a vector, the result should be a vector. - return elemValArray(sema, block, indexable_src, indexable, elem_index_src, elem_index); + return sema.elemValArray(block, src, indexable_src, indexable, elem_index_src, elem_index); }, .Struct => { // Tuple field access. - const index_val = try sema.resolveConstValue(block, elem_index_src, elem_index); + const index_val = try sema.resolveConstValue(block, elem_index_src, elem_index, "tuple field access index must be comptime known"); const index = @intCast(u32, index_val.toUnsignedInt(target)); return tupleField(sema, block, indexable_src, indexable, elem_index_src, index); }, @@ -19850,7 +20181,7 @@ fn tupleFieldPtr( try sema.validateRuntimeElemAccess(block, field_index_src, field_ty, tuple_ty, tuple_ptr_src); - try sema.requireRuntimeBlock(block, tuple_ptr_src); + try sema.requireRuntimeBlock(block, tuple_ptr_src, null); return block.addStructFieldPtr(tuple_ptr, field_index, ptr_field_ty); } @@ -19890,13 +20221,14 @@ fn tupleField( try sema.validateRuntimeElemAccess(block, field_index_src, field_ty, tuple_ty, tuple_src); - try sema.requireRuntimeBlock(block, tuple_src); + try sema.requireRuntimeBlock(block, tuple_src, null); return block.addStructFieldVal(tuple, field_index, field_ty); } fn elemValArray( sema: *Sema, block: *Block, + src: LazySrcLoc, array_src: LazySrcLoc, array: Air.Inst.Ref, elem_index_src: LazySrcLoc, @@ -19943,7 +20275,7 @@ fn elemValArray( try sema.validateRuntimeElemAccess(block, elem_index_src, elem_ty, array_ty, array_src); const runtime_src = if (maybe_undef_array_val != null) elem_index_src else array_src; - try sema.requireRuntimeBlock(block, runtime_src); + try sema.requireRuntimeBlock(block, src, runtime_src); if (block.wantSafety()) { // Runtime check is only needed if unable to comptime check if (maybe_index_val == null) { @@ -19958,6 +20290,7 @@ fn elemValArray( fn elemPtrArray( sema: *Sema, block: *Block, + src: LazySrcLoc, array_ptr_src: LazySrcLoc, array_ptr: Air.Inst.Ref, elem_index_src: LazySrcLoc, @@ -20003,7 +20336,7 @@ fn elemPtrArray( } const runtime_src = if (maybe_undef_array_ptr_val != null) elem_index_src else array_ptr_src; - try sema.requireRuntimeBlock(block, runtime_src); + try sema.requireRuntimeBlock(block, src, runtime_src); // Runtime check is only needed if unable to comptime check. if (block.wantSafety() and offset == null) { @@ -20018,6 +20351,7 @@ fn elemPtrArray( fn elemValSlice( sema: *Sema, block: *Block, + src: LazySrcLoc, slice_src: LazySrcLoc, slice: Air.Inst.Ref, elem_index_src: LazySrcLoc, @@ -20057,7 +20391,7 @@ fn elemValSlice( try sema.validateRuntimeElemAccess(block, elem_index_src, elem_ty, slice_ty, slice_src); - try sema.requireRuntimeBlock(block, runtime_src); + try sema.requireRuntimeBlock(block, src, runtime_src); if (block.wantSafety()) { const len_inst = if (maybe_slice_val) |slice_val| try sema.addIntUnsigned(Type.usize, slice_val.sliceLen(sema.mod)) @@ -20073,6 +20407,7 @@ fn elemValSlice( fn elemPtrSlice( sema: *Sema, block: *Block, + src: LazySrcLoc, slice_src: LazySrcLoc, slice: Air.Inst.Ref, elem_index_src: LazySrcLoc, @@ -20113,7 +20448,7 @@ fn elemPtrSlice( try sema.validateRuntimeElemAccess(block, elem_index_src, elem_ptr_ty, slice_ty, slice_src); const runtime_src = if (maybe_undef_slice_val != null) elem_index_src else slice_src; - try sema.requireRuntimeBlock(block, runtime_src); + try sema.requireRuntimeBlock(block, src, runtime_src); if (block.wantSafety()) { const len_inst = len: { if (maybe_undef_slice_val) |slice_val| @@ -20177,7 +20512,7 @@ fn coerceExtra( // Keep the comptime Value representation; take the new type. return sema.addConstant(dest_ty, val); } - try sema.requireRuntimeBlock(block, inst_src); + try sema.requireRuntimeBlock(block, inst_src, null); return block.addBitCast(dest_ty, inst); } @@ -20222,7 +20557,7 @@ fn coerceExtra( // Function body to function pointer. if (inst_ty.zigTypeTag() == .Fn) { - const fn_val = try sema.resolveConstValue(block, inst_src, inst); + const fn_val = try sema.resolveConstValue(block, .unneeded, inst, undefined); const fn_decl = fn_val.castTag(.function).?.data.owner_decl; const inst_as_ptr = try sema.analyzeDeclRef(fn_decl); return sema.coerce(block, dest_ty, inst_as_ptr, inst_src); @@ -20489,7 +20824,7 @@ fn coerceExtra( // small enough unsigned ints can get casted to large enough signed ints (dst_info.signedness == .signed and dst_info.bits > src_info.bits)) { - try sema.requireRuntimeBlock(block, inst_src); + try sema.requireRuntimeBlock(block, inst_src, null); return block.addTyOp(.intcast, dest_ty, inst); } }, @@ -20500,7 +20835,7 @@ fn coerceExtra( }, .Float, .ComptimeFloat => switch (inst_ty.zigTypeTag()) { .ComptimeFloat => { - const val = try sema.resolveConstValue(block, inst_src, inst); + const val = try sema.resolveConstValue(block, .unneeded, inst, undefined); const result_val = try val.floatCast(sema.arena, dest_ty, target); return try sema.addConstant(dest_ty, result_val); }, @@ -20516,13 +20851,15 @@ fn coerceExtra( ); } return try sema.addConstant(dest_ty, result_val); + } else if (dest_ty.zigTypeTag() == .ComptimeFloat) { + return sema.failWithNeededComptime(block, inst_src, "value being casted to 'comptime_float' must be comptime known"); } // float widening const src_bits = inst_ty.floatBits(target); const dst_bits = dest_ty.floatBits(target); if (dst_bits >= src_bits) { - try sema.requireRuntimeBlock(block, inst_src); + try sema.requireRuntimeBlock(block, inst_src, null); return block.addTyOp(.fpext, dest_ty, inst); } }, @@ -20549,7 +20886,7 @@ fn coerceExtra( .Enum => switch (inst_ty.zigTypeTag()) { .EnumLiteral => { // enum literal to enum - const val = try sema.resolveConstValue(block, inst_src, inst); + const val = try sema.resolveConstValue(block, .unneeded, inst, undefined); const bytes = val.castTag(.enum_literal).?.data; const field_index = dest_ty.enumFieldIndex(bytes) orelse { const msg = msg: { @@ -20956,17 +21293,17 @@ const InMemoryCoercionResult = union(enum) { } } if (!actual_noalias) { - try sema.errNote(block, src, msg, "regular paramter {d} cannot cast into a noalias paramter", .{index}); + try sema.errNote(block, src, msg, "regular parameter {d} cannot cast into a noalias parameter", .{index}); } else { - try sema.errNote(block, src, msg, "noalias paramter {d} cannot cast into a regular paramter", .{index}); + try sema.errNote(block, src, msg, "noalias parameter {d} cannot cast into a regular parameter", .{index}); } break; }, .fn_param_comptime => |param| { if (param.wanted) { - try sema.errNote(block, src, msg, "non-comptime paramter {d} cannot cast into a comptime paramter", .{param.index}); + try sema.errNote(block, src, msg, "non-comptime parameter {d} cannot cast into a comptime parameter", .{param.index}); } else { - try sema.errNote(block, src, msg, "comptime paramter {d} cannot cast into a non-comptime paramter", .{param.index}); + try sema.errNote(block, src, msg, "comptime parameter {d} cannot cast into a non-comptime parameter", .{param.index}); } break; }, @@ -21712,7 +22049,12 @@ fn storePtr2( return; } - try sema.requireRuntimeBlock(block, runtime_src); + if (block.is_comptime) { + // TODO ideally this would tell why the block is comptime + return sema.fail(block, ptr_src, "cannot store to runtime value in comptime block", .{}); + } + + try sema.requireRuntimeBlock(block, src, runtime_src); try sema.queueFullTypeResolution(elem_ty); if (is_ret) { _ = try block.addBinOp(.store, ptr, operand); @@ -22644,7 +22986,7 @@ fn bitCast( const result_val = try sema.bitCastVal(block, inst_src, val, old_ty, dest_ty, 0); return sema.addConstant(dest_ty, result_val); } - try sema.requireRuntimeBlock(block, inst_src); + try sema.requireRuntimeBlock(block, inst_src, null); return block.addBitCast(dest_ty, inst); } @@ -22727,7 +23069,7 @@ fn coerceArrayPtrToSlice( }); return sema.addConstant(dest_ty, slice_val); } - try sema.requireRuntimeBlock(block, inst_src); + try sema.requireRuntimeBlock(block, inst_src, null); return block.addTyOp(.array_to_slice, dest_ty, inst); } @@ -22743,7 +23085,7 @@ fn coerceCompatiblePtrs( // The comptime Value representation is compatible with both types. return sema.addConstant(dest_ty, val); } - try sema.requireRuntimeBlock(block, inst_src); + try sema.requireRuntimeBlock(block, inst_src, null); return sema.bitCast(block, dest_ty, inst, inst_src); } @@ -22807,7 +23149,7 @@ fn coerceEnumToUnion( })); } - try sema.requireRuntimeBlock(block, inst_src); + try sema.requireRuntimeBlock(block, inst_src, null); if (tag_ty.isNonexhaustiveEnum()) { const msg = msg: { @@ -22947,7 +23289,7 @@ fn coerceArrayLike( // These types share the same comptime value representation. return sema.addConstant(dest_ty, inst_val); } - try sema.requireRuntimeBlock(block, inst_src); + try sema.requireRuntimeBlock(block, inst_src, null); return block.addBitCast(dest_ty, inst); } @@ -22960,8 +23302,9 @@ fn coerceArrayLike( Type.usize, try Value.Tag.int_u64.create(sema.arena, i), ); + const src = inst_src; // TODO better source location const elem_src = inst_src; // TODO better source location - const elem_ref = try elemValArray(sema, block, inst_src, inst, elem_src, index_ref); + const elem_ref = try sema.elemValArray(block, src, inst_src, inst, elem_src, index_ref); const coerced = try sema.coerce(block, dest_elem_ty, elem_ref, elem_src); element_refs[i] = coerced; if (runtime_src == null) { @@ -22974,7 +23317,7 @@ fn coerceArrayLike( } if (runtime_src) |rs| { - try sema.requireRuntimeBlock(block, rs); + try sema.requireRuntimeBlock(block, inst_src, rs); return block.addAggregateInit(dest_ty, element_refs); } @@ -23037,7 +23380,7 @@ fn coerceTupleToArray( } if (runtime_src) |rs| { - try sema.requireRuntimeBlock(block, rs); + try sema.requireRuntimeBlock(block, inst_src, rs); return block.addAggregateInit(dest_ty, element_refs); } @@ -23168,7 +23511,7 @@ fn coerceTupleToStruct( } if (runtime_src) |rs| { - try sema.requireRuntimeBlock(block, rs); + try sema.requireRuntimeBlock(block, inst_src, rs); return block.addAggregateInit(struct_ty, field_refs); } @@ -23282,7 +23625,7 @@ fn analyzeRef( )); } - try sema.requireRuntimeBlock(block, src); + try sema.requireRuntimeBlock(block, src, null); const address_space = target_util.defaultAddressSpace(sema.mod.getTarget(), .local); const ptr_type = try Type.ptr(sema.arena, sema.mod, .{ .pointee_type = operand_ty, @@ -23326,10 +23669,12 @@ fn analyzeLoad( } } - const valid_rt = try sema.validateRunTimeType(block, src, elem_ty, false); - if (!valid_rt) return sema.failWithNeededComptime(block, src); + if (block.is_comptime) { + // TODO ideally this would tell why the block is comptime + return sema.fail(block, ptr_src, "cannot load runtime value in comptime block", .{}); + } - try sema.requireRuntimeBlock(block, src); + try sema.requireFunctionBlock(block, src); return block.addTyOp(.load, elem_ty, ptr); } @@ -23346,7 +23691,7 @@ fn analyzeSlicePtr( if (val.isUndef()) return sema.addConstUndef(result_ty); return sema.addConstant(result_ty, val.slicePtr()); } - try sema.requireRuntimeBlock(block, slice_src); + try sema.requireRuntimeBlock(block, slice_src, null); return block.addTyOp(.slice_ptr, result_ty, slice); } @@ -23362,7 +23707,7 @@ fn analyzeSliceLen( } return sema.addIntUnsigned(Type.usize, slice_val.sliceLen(sema.mod)); } - try sema.requireRuntimeBlock(block, src); + try sema.requireRuntimeBlock(block, src, null); return block.addTyOp(.slice_len, Type.usize, slice_inst); } @@ -23386,7 +23731,7 @@ fn analyzeIsNull( return Air.Inst.Ref.bool_false; } } - try sema.requireRuntimeBlock(block, src); + try sema.requireRuntimeBlock(block, src, null); const air_tag: Air.Inst.Tag = if (invert_logic) .is_non_null else .is_null; return block.addUnOp(air_tag, operand); } @@ -23476,7 +23821,7 @@ fn analyzeIsNonErr( ) CompileError!Air.Inst.Ref { const result = try sema.analyzeIsNonErrComptimeOnly(block, src, operand); if (result == .none) { - try sema.requireRuntimeBlock(block, src); + try sema.requireRuntimeBlock(block, src, null); return block.addUnOp(.is_non_err, operand); } else { return result; @@ -23661,7 +24006,7 @@ fn analyzeSlice( const sentinel = s: { if (sentinel_opt != .none) { const casted = try sema.coerce(block, elem_ty, sentinel_opt, sentinel_src); - break :s try sema.resolveConstValue(block, sentinel_src, casted); + break :s try sema.resolveConstValue(block, sentinel_src, casted, "slice sentinel must be comptime known"); } // If we are slicing to the end of something that is sentinel-terminated // then the resulting slice type is also sentinel-terminated. @@ -23738,7 +24083,14 @@ fn analyzeSlice( .size = .Slice, }); - try sema.requireRuntimeBlock(block, src); + const runtime_src = if ((try sema.resolveMaybeUndefVal(block, ptr_src, ptr_or_slice)) == null) + ptr_src + else if ((try sema.resolveMaybeUndefVal(block, src, start)) == null) + start_src + else + end_src; + + try sema.requireRuntimeBlock(block, src, runtime_src); if (block.wantSafety()) { // requirement: slicing C ptr is non-null if (ptr_ptr_child_ty.isCPtr()) { @@ -23846,7 +24198,7 @@ fn cmpNumeric( // a full resolution of their value, for example `@sizeOf(@Frame(function))` is known to // always be nonzero, and we benefit from not forcing the full evaluation and stack frame layout // of this function if we don't need to. - try sema.requireRuntimeBlock(block, runtime_src); + try sema.requireRuntimeBlock(block, src, runtime_src); // For floats, emit a float comparison instruction. const lhs_is_float = switch (lhs_ty_tag) { @@ -24034,7 +24386,7 @@ fn cmpVector( } }; - try sema.requireRuntimeBlock(block, runtime_src); + try sema.requireRuntimeBlock(block, src, runtime_src); const result_ty_inst = try sema.addType(result_ty); return block.addCmpVector(lhs, rhs, op, result_ty_inst); } @@ -24050,7 +24402,7 @@ fn wrapOptional( return sema.addConstant(dest_ty, try Value.Tag.opt_payload.create(sema.arena, val)); } - try sema.requireRuntimeBlock(block, inst_src); + try sema.requireRuntimeBlock(block, inst_src, null); return block.addTyOp(.wrap_optional, dest_ty, inst); } @@ -24066,7 +24418,7 @@ fn wrapErrorUnionPayload( if (try sema.resolveMaybeUndefVal(block, inst_src, coerced)) |val| { return sema.addConstant(dest_ty, try Value.Tag.eu_payload.create(sema.arena, val)); } - try sema.requireRuntimeBlock(block, inst_src); + try sema.requireRuntimeBlock(block, inst_src, null); try sema.queueFullTypeResolution(dest_payload_ty); return block.addTyOp(.wrap_errunion_payload, dest_ty, coerced); } @@ -24122,7 +24474,7 @@ fn wrapErrorUnionSet( return sema.addConstant(dest_ty, val); } - try sema.requireRuntimeBlock(block, inst_src); + try sema.requireRuntimeBlock(block, inst_src, null); const coerced = try sema.coerce(block, dest_err_set_ty, inst, inst_src); return block.addTyOp(.wrap_errunion_err, dest_ty, coerced); } @@ -24140,7 +24492,7 @@ fn unionToTag( if (try sema.resolveMaybeUndefVal(block, un_src, un)) |un_val| { return sema.addConstant(enum_ty, un_val.unionTag()); } - try sema.requireRuntimeBlock(block, un_src); + try sema.requireRuntimeBlock(block, un_src, null); return block.addTyOp(.get_union_tag, enum_ty, un); } @@ -24889,20 +25241,6 @@ fn resolveStructFully( struct_obj.status = .fully_resolved_wip; for (struct_obj.fields.values()) |field| { try sema.resolveTypeFully(block, src, field.ty); - - if (struct_obj.layout == .Extern and !(try sema.validateExternType(field.ty, .other))) { - const msg = msg: { - const msg = try sema.errMsg(block, src, "extern structs cannot contain fields of type '{}'", .{field.ty.fmt(sema.mod)}); - errdefer msg.destroy(sema.gpa); - - const src_decl = sema.mod.declPtr(block.src_decl); - try sema.explainWhyTypeIsNotExtern(block, src, msg, src.toSrcLoc(src_decl), field.ty, .other); - - try sema.addDeclaredHereNote(msg, field.ty); - break :msg msg; - }; - return sema.failWithOwnedErrorMsg(block, msg); - } } struct_obj.status = .fully_resolved; } @@ -24936,20 +25274,6 @@ fn resolveUnionFully( union_obj.status = .fully_resolved_wip; for (union_obj.fields.values()) |field| { try sema.resolveTypeFully(block, src, field.ty); - - if (union_obj.layout == .Extern and !(try sema.validateExternType(field.ty, .union_field))) { - const msg = msg: { - const msg = try sema.errMsg(block, src, "extern unions cannot contain fields of type '{}'", .{field.ty.fmt(sema.mod)}); - errdefer msg.destroy(sema.gpa); - - const src_decl = sema.mod.declPtr(block.src_decl); - try sema.explainWhyTypeIsNotExtern(block, src, msg, src.toSrcLoc(src_decl), field.ty, .union_field); - - try sema.addDeclaredHereNote(msg, field.ty); - break :msg msg; - }; - return sema.failWithOwnedErrorMsg(block, msg); - } } union_obj.status = .fully_resolved; } @@ -25033,7 +25357,7 @@ fn resolveTypeFieldsUnion( } union_obj.status = .field_types_wip; - try semaUnionFields(block, sema.mod, union_obj); + try semaUnionFields(sema.mod, union_obj); union_obj.status = .have_field_types; } @@ -25287,6 +25611,33 @@ fn semaStructFields(mod: *Module, struct_obj: *Module.Struct) CompileError!void const field = &struct_obj.fields.values()[i]; field.ty = try field_ty.copy(decl_arena_allocator); + if (struct_obj.layout == .Extern and !(try sema.validateExternType(field.ty, .other))) { + const msg = msg: { + const tree = try sema.getAstTree(&block_scope); + const fields_src = enumFieldSrcLoc(decl, tree.*, struct_obj.node_offset, 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(&block_scope, fields_src, msg, fields_src.toSrcLoc(decl), field.ty, .other); + + try sema.addDeclaredHereNote(msg, field.ty); + break :msg msg; + }; + return sema.failWithOwnedErrorMsg(&block_scope, msg); + } + if (field_ty.zigTypeTag() == .Opaque) { + const msg = msg: { + const tree = try sema.getAstTree(&block_scope); + const field_src = enumFieldSrcLoc(decl, tree.*, struct_obj.node_offset, i); + const msg = try sema.errMsg(&block_scope, field_src, "opaque types have unknown size and therefore cannot be directly embedded in structs", .{}); + errdefer msg.destroy(sema.gpa); + + try sema.addDeclaredHereNote(msg, field_ty); + break :msg msg; + }; + return sema.failWithOwnedErrorMsg(&block_scope, msg); + } + if (zir_field.align_body_len > 0) { const body = zir.extra[extra_index..][0..zir_field.align_body_len]; extra_index += body.len; @@ -25311,7 +25662,7 @@ fn semaStructFields(mod: *Module, struct_obj: *Module.Struct) CompileError!void const field = &struct_obj.fields.values()[i]; const coerced = try sema.coerce(&block_scope, field.ty, init, src); const default_val = (try sema.resolveMaybeUndefVal(&block_scope, src, coerced)) orelse - return sema.failWithNeededComptime(&block_scope, src); + return sema.failWithNeededComptime(&block_scope, src, "struct field default value must be comptime known"); field.default_val = try default_val.copy(decl_arena_allocator); } } @@ -25320,7 +25671,7 @@ fn semaStructFields(mod: *Module, struct_obj: *Module.Struct) CompileError!void struct_obj.have_field_inits = true; } -fn semaUnionFields(block: *Block, mod: *Module, union_obj: *Module.Union) CompileError!void { +fn semaUnionFields(mod: *Module, union_obj: *Module.Union) CompileError!void { const tracy = trace(@src()); defer tracy.end(); @@ -25425,10 +25776,14 @@ fn semaUnionFields(block: *Block, mod: *Module, union_obj: *Module.Union) Compil var enum_value_map: ?*Module.EnumNumbered.ValueMap = null; var tag_ty_field_names: ?Module.EnumFull.NameMap = null; if (tag_type_ref != .none) { - const provided_ty = try sema.resolveType(&block_scope, src, tag_type_ref); + const tag_ty_src: LazySrcLoc = .{ .node_offset_container_tag = src.node_offset.x }; + const provided_ty = try sema.resolveType(&block_scope, tag_ty_src, tag_type_ref); if (small.auto_enum_tag) { // The provided type is an integer type and we must construct the enum tag type here. int_tag_ty = provided_ty; + if (int_tag_ty.zigTypeTag() != .Int and int_tag_ty.zigTypeTag() != .ComptimeInt) { + return sema.fail(&block_scope, tag_ty_src, "expected integer tag type, found '{}'", .{int_tag_ty.fmt(sema.mod)}); + } union_obj.tag_ty = try sema.generateUnionTagTypeNumbered(&block_scope, fields_len, provided_ty, union_obj); const enum_obj = union_obj.tag_ty.castTag(.enum_numbered).?.data; enum_field_names = &enum_obj.fields; @@ -25437,8 +25792,7 @@ fn semaUnionFields(block: *Block, mod: *Module, union_obj: *Module.Union) Compil // The provided type is the enum tag type. union_obj.tag_ty = try provided_ty.copy(decl_arena_allocator); if (union_obj.tag_ty.zigTypeTag() != .Enum) { - const tag_ty_src = src; // TODO better source location - return sema.fail(block, tag_ty_src, "expected enum tag type, found '{}'", .{union_obj.tag_ty.fmt(sema.mod)}); + return sema.fail(&block_scope, tag_ty_src, "expected enum tag type, found '{}'", .{union_obj.tag_ty.fmt(sema.mod)}); } // The fields of the union must match the enum exactly. // Store a copy of the enum field names so we can check for @@ -25504,7 +25858,7 @@ fn semaUnionFields(block: *Block, mod: *Module, union_obj: *Module.Union) Compil if (tag_ref != .none) { const tag_src = src; // TODO better source location const coerced = try sema.coerce(&block_scope, int_tag_ty, tag_ref, tag_src); - const val = try sema.resolveConstValue(&block_scope, tag_src, coerced); + const val = try sema.resolveConstValue(&block_scope, tag_src, coerced, "enum tag value must be comptime known"); last_tag_val = val; // This puts the memory into the union arena, not the enum arena, but @@ -25516,7 +25870,7 @@ fn semaUnionFields(block: *Block, mod: *Module, union_obj: *Module.Union) Compil }); } else { const val = if (last_tag_val) |val| - try sema.intAdd(block, src, val, Value.one, int_tag_ty) + try sema.intAdd(&block_scope, src, val, Value.one, int_tag_ty) else Value.zero; last_tag_val = val; @@ -25570,15 +25924,44 @@ fn semaUnionFields(block: *Block, mod: *Module, union_obj: *Module.Union) Compil const enum_has_field = names.orderedRemove(field_name); if (!enum_has_field) { const msg = msg: { - const msg = try sema.errMsg(block, src, "enum '{}' has no field named '{s}'", .{ union_obj.tag_ty.fmt(sema.mod), field_name }); + const tree = try sema.getAstTree(&block_scope); + const field_src = enumFieldSrcLoc(decl, tree.*, union_obj.node_offset, field_i); + const msg = try sema.errMsg(&block_scope, field_src, "enum '{}' has no field named '{s}'", .{ union_obj.tag_ty.fmt(sema.mod), field_name }); errdefer msg.destroy(sema.gpa); try sema.addDeclaredHereNote(msg, union_obj.tag_ty); break :msg msg; }; - return sema.failWithOwnedErrorMsg(block, msg); + return sema.failWithOwnedErrorMsg(&block_scope, msg); } } + if (union_obj.layout == .Extern and !(try sema.validateExternType(field_ty, .union_field))) { + const msg = msg: { + const tree = try sema.getAstTree(&block_scope); + const field_src = enumFieldSrcLoc(decl, tree.*, union_obj.node_offset, field_i); + const msg = try sema.errMsg(&block_scope, field_src, "extern unions cannot contain fields of type '{}'", .{field_ty.fmt(sema.mod)}); + errdefer msg.destroy(sema.gpa); + + try sema.explainWhyTypeIsNotExtern(&block_scope, field_src, msg, field_src.toSrcLoc(decl), field_ty, .union_field); + + try sema.addDeclaredHereNote(msg, field_ty); + break :msg msg; + }; + return sema.failWithOwnedErrorMsg(&block_scope, msg); + } + if (field_ty.zigTypeTag() == .Opaque) { + const msg = msg: { + const tree = try sema.getAstTree(&block_scope); + const field_src = enumFieldSrcLoc(decl, tree.*, union_obj.node_offset, field_i); + const msg = try sema.errMsg(&block_scope, field_src, "opaque types have unknown size and therefore cannot be directly embedded in unions", .{}); + errdefer msg.destroy(sema.gpa); + + try sema.addDeclaredHereNote(msg, field_ty); + break :msg msg; + }; + return sema.failWithOwnedErrorMsg(&block_scope, msg); + } + gop.value_ptr.* = .{ .ty = try field_ty.copy(decl_arena_allocator), .abi_align = 0, @@ -25597,18 +25980,18 @@ fn semaUnionFields(block: *Block, mod: *Module, union_obj: *Module.Union) Compil if (tag_ty_field_names) |names| { if (names.count() > 0) { const msg = msg: { - const msg = try sema.errMsg(block, src, "enum field(s) missing in union", .{}); + const msg = try sema.errMsg(&block_scope, src, "enum field(s) missing in union", .{}); errdefer msg.destroy(sema.gpa); const enum_ty = union_obj.tag_ty; for (names.keys()) |field_name| { const field_index = enum_ty.enumFieldIndex(field_name).?; - try sema.addFieldErrNote(block, enum_ty, field_index, msg, "field '{s}' missing, declared here", .{field_name}); + try sema.addFieldErrNote(&block_scope, enum_ty, field_index, msg, "field '{s}' missing, declared here", .{field_name}); } try sema.addDeclaredHereNote(msg, union_obj.tag_ty); break :msg msg; }; - return sema.failWithOwnedErrorMsg(block, msg); + return sema.failWithOwnedErrorMsg(&block_scope, msg); } } } @@ -26247,7 +26630,7 @@ pub fn analyzeAddrspace( zir_ref: Zir.Inst.Ref, ctx: AddressSpaceContext, ) !std.builtin.AddressSpace { - const addrspace_tv = try sema.resolveInstConst(block, src, zir_ref); + const addrspace_tv = try sema.resolveInstConst(block, src, zir_ref, "addresspace must be comptime known"); const address_space = addrspace_tv.val.toEnum(std.builtin.AddressSpace); const target = sema.mod.getTarget(); const arch = target.cpu.arch; |
