Merge pull request #12044 from Vexu/stage2-compile-errors

Sema: add detailed error notes to `coerceInMemoryAllowed`
This commit is contained in:
Andrew Kelley
2022-07-11 20:45:27 -04:00
committed by GitHub
53 changed files with 966 additions and 295 deletions
+4
View File
@@ -49,6 +49,10 @@ pub const Loc = struct {
column: usize,
/// Does not include the trailing newline.
source_line: []const u8,
pub fn eql(a: Loc, b: Loc) bool {
return a.line == b.line and a.column == b.column and std.mem.eql(u8, a.source_line, b.source_line);
}
};
pub fn findLineColumn(source: []const u8, byte_offset: usize) Loc {
+23 -24
View File
@@ -526,6 +526,9 @@ pub const AllErrors = struct {
Message.HashContext,
std.hash_map.default_max_load_percentage,
).init(allocator);
const err_source = try module_err_msg.src_loc.file_scope.getSource(module.gpa);
const err_byte_offset = try module_err_msg.src_loc.byteOffset(module.gpa);
const err_loc = std.zig.findLineColumn(err_source.bytes, err_byte_offset);
for (module_err_msg.notes) |module_note| {
const source = try module_note.src_loc.file_scope.getSource(module.gpa);
@@ -540,7 +543,7 @@ pub const AllErrors = struct {
.byte_offset = byte_offset,
.line = @intCast(u32, loc.line),
.column = @intCast(u32, loc.column),
.source_line = try allocator.dupe(u8, loc.source_line),
.source_line = if (err_loc.eql(loc)) null else try allocator.dupe(u8, loc.source_line),
},
};
const gop = try seen_notes.getOrPut(note);
@@ -558,19 +561,16 @@ pub const AllErrors = struct {
});
return;
}
const source = try module_err_msg.src_loc.file_scope.getSource(module.gpa);
const byte_offset = try module_err_msg.src_loc.byteOffset(module.gpa);
const loc = std.zig.findLineColumn(source.bytes, byte_offset);
const file_path = try module_err_msg.src_loc.file_scope.fullPath(allocator);
try errors.append(.{
.src = .{
.src_path = file_path,
.msg = try allocator.dupe(u8, module_err_msg.msg),
.byte_offset = byte_offset,
.line = @intCast(u32, loc.line),
.column = @intCast(u32, loc.column),
.byte_offset = err_byte_offset,
.line = @intCast(u32, err_loc.line),
.column = @intCast(u32, err_loc.column),
.notes = notes_buf[0..note_i],
.source_line = try allocator.dupe(u8, loc.source_line),
.source_line = try allocator.dupe(u8, err_loc.source_line),
},
});
}
@@ -593,6 +593,16 @@ pub const AllErrors = struct {
while (item_i < items_len) : (item_i += 1) {
const item = file.zir.extraData(Zir.Inst.CompileErrors.Item, extra_index);
extra_index = item.end;
const err_byte_offset = blk: {
const token_starts = file.tree.tokens.items(.start);
if (item.data.node != 0) {
const main_tokens = file.tree.nodes.items(.main_token);
const main_token = main_tokens[item.data.node];
break :blk token_starts[main_token];
}
break :blk token_starts[item.data.token] + item.data.byte_offset;
};
const err_loc = std.zig.findLineColumn(file.source, err_byte_offset);
var notes: []Message = &[0]Message{};
if (item.data.notes != 0) {
@@ -621,33 +631,22 @@ pub const AllErrors = struct {
.line = @intCast(u32, loc.line),
.column = @intCast(u32, loc.column),
.notes = &.{}, // TODO rework this function to be recursive
.source_line = try arena.dupe(u8, loc.source_line),
.source_line = if (loc.eql(err_loc)) null else try arena.dupe(u8, loc.source_line),
},
};
}
}
const msg = file.zir.nullTerminatedString(item.data.msg);
const byte_offset = blk: {
const token_starts = file.tree.tokens.items(.start);
if (item.data.node != 0) {
const main_tokens = file.tree.nodes.items(.main_token);
const main_token = main_tokens[item.data.node];
break :blk token_starts[main_token];
}
break :blk token_starts[item.data.token] + item.data.byte_offset;
};
const loc = std.zig.findLineColumn(file.source, byte_offset);
try errors.append(.{
.src = .{
.src_path = try file.fullPath(arena),
.msg = try arena.dupe(u8, msg),
.byte_offset = byte_offset,
.line = @intCast(u32, loc.line),
.column = @intCast(u32, loc.column),
.byte_offset = err_byte_offset,
.line = @intCast(u32, err_loc.line),
.column = @intCast(u32, err_loc.column),
.notes = notes,
.source_line = try arena.dupe(u8, loc.source_line),
.source_line = try arena.dupe(u8, err_loc.source_line),
},
});
}
+684 -95
View File
@@ -2749,7 +2749,15 @@ fn ensureResultUsed(
const operand_ty = sema.typeOf(operand);
switch (operand_ty.zigTypeTag()) {
.Void, .NoReturn => return,
.ErrorSet, .ErrorUnion => return sema.fail(block, src, "error is ignored. consider using `try`, `catch`, or `if`", .{}),
.ErrorSet, .ErrorUnion => {
const msg = msg: {
const msg = try sema.errMsg(block, src, "error is ignored", .{});
errdefer msg.destroy(sema.gpa);
try sema.errNote(block, src, msg, "consider using `try`, `catch`, or `if`", .{});
break :msg msg;
};
return sema.failWithOwnedErrorMsg(block, msg);
},
else => return sema.fail(block, src, "expression value is ignored", .{}),
}
}
@@ -2763,7 +2771,15 @@ fn zirEnsureResultNonError(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Com
const src = inst_data.src();
const operand_ty = sema.typeOf(operand);
switch (operand_ty.zigTypeTag()) {
.ErrorSet, .ErrorUnion => return sema.fail(block, src, "error is discarded. consider using `try`, `catch`, or `if`", .{}),
.ErrorSet, .ErrorUnion => {
const msg = msg: {
const msg = try sema.errMsg(block, src, "error is discarded", .{});
errdefer msg.destroy(sema.gpa);
try sema.errNote(block, src, msg, "consider using `try`, `catch`, or `if`", .{});
break :msg msg;
};
return sema.failWithOwnedErrorMsg(block, msg);
},
else => return,
}
}
@@ -4119,23 +4135,24 @@ fn zirStoreNode(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!v
const ptr = try sema.resolveInst(extra.lhs);
const operand = try sema.resolveInst(extra.rhs);
const is_ret = if (Zir.refToIndex(extra.lhs)) |ptr_index|
zir_tags[ptr_index] == .ret_ptr
else
false;
// Check for the possibility of this pattern:
// %a = ret_ptr
// %b = store(%a, %c)
// Where %c is an error union or error set. In such case we need to add
// to the current function's inferred error set, if any.
if ((sema.typeOf(operand).zigTypeTag() == .ErrorUnion or
if (is_ret and (sema.typeOf(operand).zigTypeTag() == .ErrorUnion or
sema.typeOf(operand).zigTypeTag() == .ErrorSet) and
sema.fn_ret_ty.zigTypeTag() == .ErrorUnion)
{
if (Zir.refToIndex(extra.lhs)) |ptr_index| {
if (zir_tags[ptr_index] == .ret_ptr) {
try sema.addToInferredErrorSet(operand);
}
}
try sema.addToInferredErrorSet(operand);
}
return sema.storePtr(block, src, ptr, operand);
return sema.storePtr2(block, src, ptr, src, operand, src, if (is_ret) .ret_ptr else .store);
}
fn zirParamType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
@@ -5534,7 +5551,7 @@ fn analyzeCall(
try sema.resolveBody(&child_block, fn_info.ret_ty_body, module_fn.zir_body_inst)
else
try sema.resolveInst(fn_info.ret_ty_ref);
const ret_ty_src = func_src; // TODO better source location
const ret_ty_src: LazySrcLoc = .{ .node_offset_fn_type_ret_ty = 0 };
const bare_return_type = try sema.analyzeAsType(&child_block, ret_ty_src, ret_ty_inst);
// Create a fresh inferred error set type for inline/comptime calls.
const fn_ret_ty = blk: {
@@ -6876,7 +6893,7 @@ fn zirFunc(
const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
const extra = sema.code.extraData(Zir.Inst.Func, inst_data.payload_index);
const target = sema.mod.getTarget();
const ret_ty_src = inst_data.src(); // TODO better source location
const ret_ty_src: LazySrcLoc = .{ .node_offset_fn_type_ret_ty = inst_data.src_node };
var extra_index = extra.end;
@@ -7458,13 +7475,20 @@ fn analyzeAs(
zir_dest_type: Zir.Inst.Ref,
zir_operand: Zir.Inst.Ref,
) CompileError!Air.Inst.Ref {
const is_ret = if (Zir.refToIndex(zir_dest_type)) |ptr_index|
sema.code.instructions.items(.tag)[ptr_index] == .ret_type
else
false;
const dest_ty = try sema.resolveType(block, src, zir_dest_type);
const operand = try sema.resolveInst(zir_operand);
if (dest_ty.tag() == .var_args_param) return operand;
if (dest_ty.zigTypeTag() == .NoReturn) {
return sema.fail(block, src, "cannot cast to noreturn", .{});
}
return sema.coerce(block, dest_ty, operand, src);
return sema.coerceExtra(block, dest_ty, operand, src, true, is_ret) catch |err| switch (err) {
error.NotCoercible => unreachable,
else => |e| return e,
};
}
fn zirPtrToInt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
@@ -13647,7 +13671,10 @@ fn analyzeRet(
if (sema.fn_ret_ty.zigTypeTag() == .ErrorUnion) {
try sema.addToInferredErrorSet(uncasted_operand);
}
const operand = try sema.coerce(block, sema.fn_ret_ty, uncasted_operand, src);
const operand = sema.coerceExtra(block, sema.fn_ret_ty, uncasted_operand, src, true, true) catch |err| switch (err) {
error.NotCoercible => unreachable,
else => |e| return e,
};
if (block.inlining) |inlining| {
if (block.is_comptime) {
@@ -19993,6 +20020,27 @@ fn coerce(
inst: Air.Inst.Ref,
inst_src: LazySrcLoc,
) CompileError!Air.Inst.Ref {
return sema.coerceExtra(block, dest_ty_unresolved, inst, inst_src, true, false) catch |err| switch (err) {
error.NotCoercible => unreachable,
else => |e| return e,
};
}
const CoersionError = CompileError || error{
/// When coerce is called recursively, this error should be returned instead of using `fail`
/// to ensure correct types in compile errors.
NotCoercible,
};
fn coerceExtra(
sema: *Sema,
block: *Block,
dest_ty_unresolved: Type,
inst: Air.Inst.Ref,
inst_src: LazySrcLoc,
report_err: bool,
is_ret: bool,
) CoersionError!Air.Inst.Ref {
switch (dest_ty_unresolved.tag()) {
.var_args_param => return sema.coerceVarArgParam(block, inst, inst_src),
.generic_poison => return inst,
@@ -20009,7 +20057,7 @@ fn coerce(
const arena = sema.arena;
const maybe_inst_val = try sema.resolveMaybeUndefVal(block, inst_src, inst);
const in_memory_result = try sema.coerceInMemoryAllowed(block, dest_ty, inst_ty, false, target, dest_ty_src, inst_src);
var in_memory_result = try sema.coerceInMemoryAllowed(block, dest_ty, inst_ty, false, target, dest_ty_src, inst_src);
if (in_memory_result == .ok) {
if (maybe_inst_val) |val| {
// Keep the comptime Value representation; take the new type.
@@ -20022,7 +20070,7 @@ fn coerce(
const is_undef = if (maybe_inst_val) |val| val.isUndef() else false;
switch (dest_ty.zigTypeTag()) {
.Optional => {
.Optional => optional: {
// undefined sets the optional bit also to undefined.
if (is_undef) {
return sema.addConstUndef(dest_ty);
@@ -20043,10 +20091,19 @@ fn coerce(
// T to ?T
const child_type = try dest_ty.optionalChildAlloc(sema.arena);
const intermediate = try sema.coerce(block, child_type, inst, inst_src);
return sema.wrapOptional(block, dest_ty, intermediate, inst_src);
const intermediate = sema.coerceExtra(block, child_type, inst, inst_src, false, is_ret) catch |err| switch (err) {
error.NotCoercible => {
if (in_memory_result == .no_match) {
// Try to give more useful notes
in_memory_result = try sema.coerceInMemoryAllowed(block, child_type, inst_ty, false, target, dest_ty_src, inst_src);
}
break :optional;
},
else => |e| return e,
};
return try sema.wrapOptional(block, dest_ty, intermediate, inst_src);
},
.Pointer => {
.Pointer => pointer: {
const dest_info = dest_ty.ptrInfo().data;
// Function body to function pointer.
@@ -20071,7 +20128,7 @@ fn coerce(
if (inst_ty.ptrAddressSpace() != dest_info.@"addrspace") break :single_item;
switch (try sema.coerceInMemoryAllowed(block, array_elem_ty, ptr_elem_ty, dest_is_mut, target, dest_ty_src, inst_src)) {
.ok => {},
.no_match => break :single_item,
else => break :single_item,
}
return sema.coerceCompatiblePtrs(block, dest_ty, inst, inst_src);
}
@@ -20091,7 +20148,7 @@ fn coerce(
const dst_elem_type = dest_info.pointee_type;
switch (try sema.coerceInMemoryAllowed(block, dst_elem_type, array_elem_type, dest_is_mut, target, dest_ty_src, inst_src)) {
.ok => {},
.no_match => break :src_array_ptr,
else => break :src_array_ptr,
}
switch (dest_info.size) {
@@ -20130,7 +20187,7 @@ fn coerce(
const dst_elem_type = dest_info.pointee_type;
switch (try sema.coerceInMemoryAllowed(block, dst_elem_type, src_elem_ty, dest_is_mut, target, dest_ty_src, inst_src)) {
.ok => {},
.no_match => break :src_c_ptr,
else => break :src_c_ptr,
}
// TODO add safety check for null pointer
return sema.coerceCompatiblePtrs(block, dest_ty, inst, inst_src);
@@ -20151,16 +20208,26 @@ fn coerce(
return sema.addConstant(dest_ty, Value.@"null");
},
.ComptimeInt => {
const addr = try sema.coerce(block, Type.usize, inst, inst_src);
return sema.coerceCompatiblePtrs(block, dest_ty, addr, inst_src);
const addr = sema.coerceExtra(block, Type.usize, inst, inst_src, false, is_ret) catch |err| switch (err) {
error.NotCoercible => break :pointer,
else => |e| return e,
};
return try sema.coerceCompatiblePtrs(block, dest_ty, addr, inst_src);
},
.Int => {
const ptr_size_ty = switch (inst_ty.intInfo(target).signedness) {
.signed => Type.isize,
.unsigned => Type.usize,
};
const addr = try sema.coerce(block, ptr_size_ty, inst, inst_src);
return sema.coerceCompatiblePtrs(block, dest_ty, addr, inst_src);
const addr = sema.coerceExtra(block, ptr_size_ty, inst, inst_src, false, is_ret) catch |err| switch (err) {
error.NotCoercible => {
// Try to give more useful notes
in_memory_result = try sema.coerceInMemoryAllowed(block, ptr_size_ty, inst_ty, false, target, dest_ty_src, inst_src);
break :pointer;
},
else => |e| return e,
};
return try sema.coerceCompatiblePtrs(block, dest_ty, addr, inst_src);
},
.Pointer => p: {
const inst_info = inst_ty.ptrInfo().data;
@@ -20174,7 +20241,7 @@ fn coerce(
inst_src,
)) {
.ok => {},
.no_match => break :p,
else => break :p,
}
if (inst_info.size == .Slice) {
if (dest_info.sentinel == null or inst_info.sentinel == null or
@@ -20264,7 +20331,7 @@ fn coerce(
inst_src,
)) {
.ok => {},
.no_match => break :p,
else => break :p,
}
if (dest_info.sentinel == null or inst_info.sentinel == null or
@@ -20295,6 +20362,7 @@ fn coerce(
if (try sema.resolveDefinedValue(block, inst_src, inst)) |val| {
// comptime known integer to other number
if (!(try sema.intFitsInType(block, inst_src, val, dest_ty, null))) {
if (!report_err) return error.NotCoercible;
return sema.fail(block, inst_src, "type '{}' cannot represent integer value '{}'", .{ dest_ty.fmt(sema.mod), val.fmtValue(inst_ty, sema.mod) });
}
return try sema.addConstant(dest_ty, val);
@@ -20496,14 +20564,396 @@ fn coerce(
return sema.addConstUndef(dest_ty);
}
return sema.fail(block, inst_src, "expected type '{}', found '{}'", .{ dest_ty.fmt(sema.mod), inst_ty.fmt(sema.mod) });
if (!report_err) return error.NotCoercible;
if (is_ret and dest_ty.zigTypeTag() == .NoReturn) {
const msg = msg: {
const msg = try sema.errMsg(block, inst_src, "function declared 'noreturn' returns", .{});
errdefer msg.destroy(sema.gpa);
const ret_ty_src: LazySrcLoc = .{ .node_offset_fn_type_ret_ty = 0 };
const src_decl = sema.mod.declPtr(sema.func.?.owner_decl);
try sema.mod.errNoteNonLazy(ret_ty_src.toSrcLoc(src_decl), msg, "'noreturn' declared here", .{});
break :msg msg;
};
return sema.failWithOwnedErrorMsg(block, msg);
}
const msg = msg: {
const msg = try sema.errMsg(block, inst_src, "expected type '{}', found '{}'", .{ dest_ty.fmt(sema.mod), inst_ty.fmt(sema.mod) });
errdefer msg.destroy(sema.gpa);
// E!T to T
if (inst_ty.zigTypeTag() == .ErrorUnion and
(try sema.coerceInMemoryAllowed(block, inst_ty.errorUnionPayload(), dest_ty, false, target, dest_ty_src, inst_src)) == .ok)
{
try sema.errNote(block, inst_src, msg, "cannot convert error union to payload type", .{});
try sema.errNote(block, inst_src, msg, "consider using `try`, `catch`, or `if`", .{});
}
// ?T to T
var buf: Type.Payload.ElemType = undefined;
if (inst_ty.zigTypeTag() == .Optional and
(try sema.coerceInMemoryAllowed(block, inst_ty.optionalChild(&buf), dest_ty, false, target, dest_ty_src, inst_src)) == .ok)
{
try sema.errNote(block, inst_src, msg, "cannot convert optional to payload type", .{});
try sema.errNote(block, inst_src, msg, "consider using `.?`, `orelse`, or `if`", .{});
}
try in_memory_result.report(sema, block, inst_src, msg);
// Add notes about function return type
if (is_ret and sema.mod.test_functions.get(sema.func.?.owner_decl) == null) {
const ret_ty_src: LazySrcLoc = .{ .node_offset_fn_type_ret_ty = 0 };
const src_decl = sema.mod.declPtr(sema.func.?.owner_decl);
if (inst_ty.isError() and !dest_ty.isError()) {
try sema.mod.errNoteNonLazy(ret_ty_src.toSrcLoc(src_decl), msg, "function cannot return an error", .{});
} else {
try sema.mod.errNoteNonLazy(ret_ty_src.toSrcLoc(src_decl), msg, "function return type declared here", .{});
}
}
// TODO maybe add "cannot store an error in type '{}'" note
break :msg msg;
};
return sema.failWithOwnedErrorMsg(block, msg);
}
const InMemoryCoercionResult = enum {
const InMemoryCoercionResult = union(enum) {
ok,
no_match,
no_match: Pair,
int_not_coercible: Int,
error_union_payload: PairAndChild,
array_len: IntPair,
array_sentinel: Sentinel,
array_elem: PairAndChild,
vector_len: IntPair,
vector_elem: PairAndChild,
optional_shape: Pair,
optional_child: PairAndChild,
from_anyerror,
missing_error: []const []const u8,
/// true if wanted is var args
fn_var_args: bool,
/// true if wanted is generic
fn_generic: bool,
fn_param_count: IntPair,
fn_param_noalias: IntPair,
fn_param_comptime: ComptimeParam,
fn_param: Param,
fn_cc: CC,
fn_return_type: PairAndChild,
ptr_child: PairAndChild,
ptr_addrspace: AddressSpace,
ptr_sentinel: Sentinel,
ptr_size: Size,
ptr_qualifiers: Qualifiers,
ptr_allowzero: Pair,
ptr_bit_range: BitRange,
ptr_alignment: IntPair,
const Pair = struct {
actual: Type,
wanted: Type,
};
const PairAndChild = struct {
child: *InMemoryCoercionResult,
actual: Type,
wanted: Type,
};
const Param = struct {
child: *InMemoryCoercionResult,
actual: Type,
wanted: Type,
index: u64,
};
const ComptimeParam = struct {
index: u64,
wanted: bool,
};
const Sentinel = struct {
// unreachable_value indicates no sentinel
actual: Value,
wanted: Value,
ty: Type,
};
const Int = struct {
actual_signedness: std.builtin.Signedness,
wanted_signedness: std.builtin.Signedness,
actual_bits: u16,
wanted_bits: u16,
};
const IntPair = struct {
actual: u64,
wanted: u64,
};
const Size = struct {
actual: std.builtin.Type.Pointer.Size,
wanted: std.builtin.Type.Pointer.Size,
};
const Qualifiers = struct {
actual_const: bool,
wanted_const: bool,
actual_volatile: bool,
wanted_volatile: bool,
};
const AddressSpace = struct {
actual: std.builtin.AddressSpace,
wanted: std.builtin.AddressSpace,
};
const CC = struct {
actual: std.builtin.CallingConvention,
wanted: std.builtin.CallingConvention,
};
const BitRange = struct {
actual_host: u16,
wanted_host: u16,
actual_offset: u16,
wanted_offset: u16,
};
fn dupe(child: *const InMemoryCoercionResult, arena: Allocator) !*InMemoryCoercionResult {
const res = try arena.create(InMemoryCoercionResult);
res.* = child.*;
return res;
}
fn report(res: *const InMemoryCoercionResult, sema: *Sema, block: *Block, src: LazySrcLoc, msg: *Module.ErrorMsg) !void {
var cur = res;
while (true) switch (cur.*) {
.ok => unreachable,
.no_match => |types| {
try sema.addDeclaredHereNote(msg, types.wanted);
try sema.addDeclaredHereNote(msg, types.actual);
break;
},
.int_not_coercible => |int| {
try sema.errNote(block, src, msg, "{s} {d}-bit int cannot represent all possible {s} {d}-bit values", .{
@tagName(int.wanted_signedness), int.wanted_bits, @tagName(int.actual_signedness), int.actual_bits,
});
break;
},
.error_union_payload => |pair| {
try sema.errNote(block, src, msg, "error union payload '{}' cannot cast into error union payload '{}'", .{
pair.actual.fmt(sema.mod), pair.wanted.fmt(sema.mod),
});
cur = pair.child;
},
.array_len => |lens| {
try sema.errNote(block, src, msg, "array of length {d} cannot cast into an array of length {d}", .{
lens.actual, lens.wanted,
});
break;
},
.array_sentinel => |sentinel| {
if (sentinel.actual.tag() != .unreachable_value) {
try sema.errNote(block, src, msg, "array sentinel '{}' cannot cast into array sentinel '{}'", .{
sentinel.actual.fmtValue(sentinel.ty, sema.mod), sentinel.wanted.fmtValue(sentinel.ty, sema.mod),
});
} else {
try sema.errNote(block, src, msg, "destination array requires '{}' sentinel", .{
sentinel.wanted.fmtValue(sentinel.ty, sema.mod),
});
}
break;
},
.array_elem => |pair| {
try sema.errNote(block, src, msg, "array element type '{}' cannot cast into array element type '{}'", .{
pair.actual.fmt(sema.mod), pair.wanted.fmt(sema.mod),
});
cur = pair.child;
},
.vector_len => |lens| {
try sema.errNote(block, src, msg, "vector of length {d} cannot cast into a vector of length {d}", .{
lens.actual, lens.wanted,
});
break;
},
.vector_elem => |pair| {
try sema.errNote(block, src, msg, "vector element type '{}' cannot cast into vector element type '{}'", .{
pair.actual.fmt(sema.mod), pair.wanted.fmt(sema.mod),
});
cur = pair.child;
},
.optional_shape => |pair| {
var buf_actual: Type.Payload.ElemType = undefined;
var buf_wanted: Type.Payload.ElemType = undefined;
try sema.errNote(block, src, msg, "optional type child '{}' cannot cast into optional type child '{}'", .{
pair.actual.optionalChild(&buf_actual).fmt(sema.mod), pair.wanted.optionalChild(&buf_wanted).fmt(sema.mod),
});
break;
},
.optional_child => |pair| {
try sema.errNote(block, src, msg, "optional type child '{}' cannot cast into optional type child '{}'", .{
pair.actual.fmt(sema.mod), pair.wanted.fmt(sema.mod),
});
cur = pair.child;
},
.from_anyerror => {
try sema.errNote(block, src, msg, "global error set cannot cast into a smaller set", .{});
break;
},
.missing_error => |missing_errors| {
for (missing_errors) |err| {
try sema.errNote(block, src, msg, "'error.{s}' not a member of destination error set", .{err});
}
break;
},
.fn_var_args => |wanted_var_args| {
if (wanted_var_args) {
try sema.errNote(block, src, msg, "non-variadic function cannot cast into a variadic function", .{});
} else {
try sema.errNote(block, src, msg, "variadic function cannot cast into a non-variadic function", .{});
}
break;
},
.fn_generic => |wanted_generic| {
if (wanted_generic) {
try sema.errNote(block, src, msg, "non-generic function cannot cast into a generic function", .{});
} else {
try sema.errNote(block, src, msg, "generic function cannot cast into a non-generic function", .{});
}
break;
},
.fn_param_count => |lens| {
try sema.errNote(block, src, msg, "function with {d} parameters cannot cast into a function with {d} parameters", .{
lens.actual, lens.wanted,
});
break;
},
.fn_param_noalias => |param| {
var index: u6 = 0;
var actual_noalias = false;
while (true) : (index += 1) {
if (param.actual << index != param.wanted << index) {
actual_noalias = (param.actual << index) == (1 << 31);
}
}
if (!actual_noalias) {
try sema.errNote(block, src, msg, "regular paramter {d} cannot cast into a noalias paramter", .{index});
} else {
try sema.errNote(block, src, msg, "noalias paramter {d} cannot cast into a regular paramter", .{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});
} else {
try sema.errNote(block, src, msg, "comptime paramter {d} cannot cast into a non-comptime paramter", .{param.index});
}
break;
},
.fn_param => |param| {
try sema.errNote(block, src, msg, "parameter {d} '{}' cannot cast into '{}'", .{
param.index, param.actual.fmt(sema.mod), param.wanted.fmt(sema.mod),
});
cur = param.child;
},
.fn_cc => |cc| {
try sema.errNote(block, src, msg, "calling convention {s} cannot cast into calling convention {s}", .{ @tagName(cc.actual), @tagName(cc.wanted) });
break;
},
.fn_return_type => |pair| {
try sema.errNote(block, src, msg, "return type '{}' cannot cast into return type '{}'", .{
pair.actual.fmt(sema.mod), pair.wanted.fmt(sema.mod),
});
cur = pair.child;
},
.ptr_child => |pair| {
try sema.errNote(block, src, msg, "pointer type child '{}' cannot cast into pointer type child '{}'", .{
pair.actual.fmt(sema.mod), pair.wanted.fmt(sema.mod),
});
cur = pair.child;
},
.ptr_addrspace => |@"addrspace"| {
try sema.errNote(block, src, msg, "address space '{s}' cannot cast into address space '{s}'", .{ @tagName(@"addrspace".actual), @tagName(@"addrspace".wanted) });
break;
},
.ptr_sentinel => |sentinel| {
if (sentinel.actual.tag() != .unreachable_value) {
try sema.errNote(block, src, msg, "pointer sentinel '{}' cannot cast into pointer sentinel '{}'", .{
sentinel.actual.fmtValue(sentinel.ty, sema.mod), sentinel.wanted.fmtValue(sentinel.ty, sema.mod),
});
} else {
try sema.errNote(block, src, msg, "destination pointer requires '{}' sentinel", .{
sentinel.wanted.fmtValue(sentinel.ty, sema.mod),
});
}
break;
},
.ptr_size => |size| {
try sema.errNote(block, src, msg, "a {s} pointer cannot cast into a {s} pointer", .{ pointerSizeString(size.actual), pointerSizeString(size.wanted) });
break;
},
.ptr_qualifiers => |qualifiers| {
const ok_const = !qualifiers.actual_const or qualifiers.wanted_const;
const ok_volatile = !qualifiers.actual_volatile or qualifiers.wanted_volatile;
if (!ok_const) {
try sema.errNote(block, src, msg, "cast discards const qualifier", .{});
} else if (!ok_volatile) {
try sema.errNote(block, src, msg, "cast discards volatile qualifier", .{});
}
break;
},
.ptr_allowzero => |pair| {
const wanted_allow_zero = pair.wanted.ptrAllowsZero();
const actual_allow_zero = pair.actual.ptrAllowsZero();
if (actual_allow_zero and !wanted_allow_zero) {
try sema.errNote(block, src, msg, "'{}' could have null values which are illegal in type '{}'", .{
pair.actual.fmt(sema.mod), pair.wanted.fmt(sema.mod),
});
} else {
try sema.errNote(block, src, msg, "mutable '{}' allows illegal null values stored to type '{}'", .{
pair.actual.fmt(sema.mod), pair.wanted.fmt(sema.mod),
});
}
break;
},
.ptr_bit_range => |bit_range| {
if (bit_range.actual_host != bit_range.wanted_host) {
try sema.errNote(block, src, msg, "pointer host size '{}' cannot cast into pointer host size '{}'", .{
bit_range.actual_host, bit_range.wanted_host,
});
}
if (bit_range.actual_offset != bit_range.wanted_offset) {
try sema.errNote(block, src, msg, "pointer bit offset '{}' cannot cast into pointer bit offset '{}'", .{
bit_range.actual_offset, bit_range.wanted_offset,
});
}
break;
},
.ptr_alignment => |pair| {
try sema.errNote(block, src, msg, "pointer alignment '{}' cannot cast into pointer alignment '{}'", .{
pair.actual, pair.wanted,
});
break;
},
};
}
};
fn pointerSizeString(size: std.builtin.Type.Pointer.Size) []const u8 {
return switch (size) {
.One => "single",
.Many => "many",
.C => "C",
.Slice => unreachable,
};
}
/// If pointers have the same representation in runtime memory, a bitcast AIR instruction
/// may be used for the coercion.
/// * `const` attribute can be gained
@@ -20513,8 +20963,6 @@ const InMemoryCoercionResult = enum {
/// * bit offset attributes must match exactly
/// * `*`/`[*]` must match exactly, but `[*c]` matches either one
/// * sentinel-terminated pointers can coerce into `[*]`
/// TODO improve this function to report recursive compile errors like it does in stage1.
/// look at the function types_match_const_cast_only
fn coerceInMemoryAllowed(
sema: *Sema,
block: *Block,
@@ -20532,11 +20980,25 @@ fn coerceInMemoryAllowed(
if (dest_ty.zigTypeTag() == .Int and src_ty.zigTypeTag() == .Int) {
const dest_info = dest_ty.intInfo(target);
const src_info = src_ty.intInfo(target);
if (dest_info.signedness == src_info.signedness and
dest_info.bits == src_info.bits)
{
return .ok;
}
if ((src_info.signedness == dest_info.signedness and dest_info.bits < src_info.bits) or
// small enough unsigned ints can get casted to large enough signed ints
(dest_info.signedness == .signed and (src_info.signedness == .unsigned or dest_info.bits <= src_info.bits)) or
(dest_info.signedness == .unsigned and src_info.signedness == .signed))
{
return InMemoryCoercionResult{ .int_not_coercible = .{
.actual_signedness = src_info.signedness,
.wanted_signedness = dest_info.signedness,
.actual_bits = src_info.bits,
.wanted_bits = dest_info.bits,
} };
}
}
// Differently-named floats with the same number of bits.
@@ -20574,9 +21036,15 @@ fn coerceInMemoryAllowed(
// Error Unions
if (dest_tag == .ErrorUnion and src_tag == .ErrorUnion) {
const child = try sema.coerceInMemoryAllowed(block, dest_ty.errorUnionPayload(), src_ty.errorUnionPayload(), dest_is_mut, target, dest_src, src_src);
if (child == .no_match) {
return child;
const dest_payload = dest_ty.errorUnionPayload();
const src_payload = src_ty.errorUnionPayload();
const child = try sema.coerceInMemoryAllowed(block, dest_payload, src_payload, dest_is_mut, target, dest_src, src_src);
if (child != .ok) {
return InMemoryCoercionResult{ .error_union_payload = .{
.child = try child.dupe(sema.arena),
.actual = src_payload,
.wanted = dest_payload,
} };
}
return try sema.coerceInMemoryAllowed(block, dest_ty.errorUnionSet(), src_ty.errorUnionSet(), dest_is_mut, target, dest_src, src_src);
}
@@ -20587,57 +21055,89 @@ fn coerceInMemoryAllowed(
}
// Arrays
if (dest_tag == .Array and src_tag == .Array) arrays: {
if (dest_tag == .Array and src_tag == .Array) {
const dest_info = dest_ty.arrayInfo();
const src_info = src_ty.arrayInfo();
if (dest_info.len != src_info.len) break :arrays;
if (dest_info.len != src_info.len) {
return InMemoryCoercionResult{ .array_len = .{
.actual = src_info.len,
.wanted = dest_info.len,
} };
}
const child = try sema.coerceInMemoryAllowed(block, dest_info.elem_type, src_info.elem_type, dest_is_mut, target, dest_src, src_src);
if (child == .no_match) {
return child;
if (child != .ok) {
return InMemoryCoercionResult{ .array_elem = .{
.child = try child.dupe(sema.arena),
.actual = src_info.elem_type,
.wanted = dest_info.elem_type,
} };
}
const ok_sent = dest_info.sentinel == null or
(src_info.sentinel != null and
dest_info.sentinel.?.eql(src_info.sentinel.?, dest_info.elem_type, sema.mod));
if (!ok_sent) {
return .no_match;
return InMemoryCoercionResult{ .array_sentinel = .{
.actual = src_info.sentinel orelse Value.initTag(.unreachable_value),
.wanted = dest_info.sentinel orelse Value.initTag(.unreachable_value),
.ty = dest_info.elem_type,
} };
}
return .ok;
}
// Vectors
if (dest_tag == .Vector and src_tag == .Vector) vectors: {
if (dest_tag == .Vector and src_tag == .Vector) {
const dest_len = dest_ty.vectorLen();
const src_len = src_ty.vectorLen();
if (dest_len != src_len) break :vectors;
if (dest_len != src_len) {
return InMemoryCoercionResult{ .vector_len = .{
.actual = src_len,
.wanted = dest_len,
} };
}
const dest_elem_ty = dest_ty.scalarType();
const src_elem_ty = src_ty.scalarType();
const child = try sema.coerceInMemoryAllowed(block, dest_elem_ty, src_elem_ty, dest_is_mut, target, dest_src, src_src);
if (child == .no_match) break :vectors;
if (child != .ok) {
return InMemoryCoercionResult{ .vector_elem = .{
.child = try child.dupe(sema.arena),
.actual = src_elem_ty,
.wanted = dest_elem_ty,
} };
}
return .ok;
}
// Optionals
if (dest_tag == .Optional and src_tag == .Optional) optionals: {
if (dest_tag == .Optional and src_tag == .Optional) {
if ((maybe_dest_ptr_ty != null) != (maybe_src_ptr_ty != null)) {
// TODO "optional type child '{}' cannot cast into optional type '{}'"
return .no_match;
return InMemoryCoercionResult{ .optional_shape = .{
.actual = src_ty,
.wanted = dest_ty,
} };
}
const dest_child_type = dest_ty.optionalChild(&dest_buf);
const src_child_type = src_ty.optionalChild(&src_buf);
const child = try sema.coerceInMemoryAllowed(block, dest_child_type, src_child_type, dest_is_mut, target, dest_src, src_src);
if (child == .no_match) {
// TODO "optional type child '{}' cannot cast into optional type child '{}'"
break :optionals;
if (child != .ok) {
return InMemoryCoercionResult{ .optional_child = .{
.child = try child.dupe(sema.arena),
.actual = src_child_type,
.wanted = dest_child_type,
} };
}
return .ok;
}
return .no_match;
return InMemoryCoercionResult{ .no_match = .{
.actual = dest_ty,
.wanted = src_ty,
} };
}
fn coerceInMemoryAllowedErrorSets(
@@ -20704,6 +21204,9 @@ fn coerceInMemoryAllowedErrorSets(
}
}
var missing_error_buf = std.ArrayList([]const u8).init(sema.gpa);
defer missing_error_buf.deinit();
switch (src_ty.tag()) {
.error_set_inferred => {
const src_data = src_ty.castTag(.error_set_inferred).?.data;
@@ -20712,15 +21215,21 @@ fn coerceInMemoryAllowedErrorSets(
// src anyerror status might have changed after the resolution.
if (src_ty.isAnyError()) {
// dest_ty.isAnyError() == true is already checked for at this point.
return .no_match;
return .from_anyerror;
}
for (src_data.errors.keys()) |key| {
if (!dest_ty.errorSetHasField(key)) {
return .no_match;
try missing_error_buf.append(key);
}
}
if (missing_error_buf.items.len != 0) {
return InMemoryCoercionResult{
.missing_error = try sema.arena.dupe([]const u8, missing_error_buf.items),
};
}
return .ok;
},
.error_set_single => {
@@ -20728,37 +21237,52 @@ fn coerceInMemoryAllowedErrorSets(
if (dest_ty.errorSetHasField(name)) {
return .ok;
}
const list = try sema.arena.alloc([]const u8, 1);
list[0] = name;
return InMemoryCoercionResult{ .missing_error = list };
},
.error_set_merged => {
const names = src_ty.castTag(.error_set_merged).?.data.keys();
for (names) |name| {
if (!dest_ty.errorSetHasField(name)) {
return .no_match;
try missing_error_buf.append(name);
}
}
if (missing_error_buf.items.len != 0) {
return InMemoryCoercionResult{
.missing_error = try sema.arena.dupe([]const u8, missing_error_buf.items),
};
}
return .ok;
},
.error_set => {
const names = src_ty.castTag(.error_set).?.data.names.keys();
for (names) |name| {
if (!dest_ty.errorSetHasField(name)) {
return .no_match;
try missing_error_buf.append(name);
}
}
if (missing_error_buf.items.len != 0) {
return InMemoryCoercionResult{
.missing_error = try sema.arena.dupe([]const u8, missing_error_buf.items),
};
}
return .ok;
},
.anyerror => switch (dest_ty.tag()) {
.error_set_inferred => return .no_match, // Caught by dest.isAnyError() above.
.error_set_single, .error_set_merged, .error_set => {},
.error_set_inferred => unreachable, // Caught by dest_ty.isAnyError() above.
.error_set_single, .error_set_merged, .error_set => return .from_anyerror,
.anyerror => unreachable, // Filtered out above.
else => unreachable,
},
else => unreachable,
}
return .no_match;
unreachable;
}
fn coerceInMemoryAllowedFns(
@@ -20774,44 +21298,67 @@ fn coerceInMemoryAllowedFns(
const src_info = src_ty.fnInfo();
if (dest_info.is_var_args != src_info.is_var_args) {
return .no_match;
return InMemoryCoercionResult{ .fn_var_args = dest_info.is_var_args };
}
if (dest_info.is_generic != src_info.is_generic) {
return .no_match;
return InMemoryCoercionResult{ .fn_generic = dest_info.is_generic };
}
if (dest_info.cc != src_info.cc) {
return InMemoryCoercionResult{ .fn_cc = .{
.actual = src_info.cc,
.wanted = dest_info.cc,
} };
}
if (!src_info.return_type.isNoReturn()) {
const rt = try sema.coerceInMemoryAllowed(block, dest_info.return_type, src_info.return_type, false, target, dest_src, src_src);
if (rt == .no_match) {
return rt;
if (rt != .ok) {
return InMemoryCoercionResult{ .fn_return_type = .{
.child = try rt.dupe(sema.arena),
.actual = src_info.return_type,
.wanted = dest_info.return_type,
} };
}
}
if (dest_info.param_types.len != src_info.param_types.len) {
return .no_match;
return InMemoryCoercionResult{ .fn_param_count = .{
.actual = dest_info.param_types.len,
.wanted = dest_info.param_types.len,
} };
}
if (dest_info.noalias_bits != src_info.noalias_bits) {
return InMemoryCoercionResult{ .fn_param_noalias = .{
.actual = dest_info.noalias_bits,
.wanted = dest_info.noalias_bits,
} };
}
for (dest_info.param_types) |dest_param_ty, i| {
const src_param_ty = src_info.param_types[i];
if (dest_info.comptime_params[i] != src_info.comptime_params[i]) {
return .no_match;
return InMemoryCoercionResult{ .fn_param_comptime = .{
.index = i,
.wanted = dest_info.comptime_params[i],
} };
}
// TODO: noalias
// Note: Cast direction is reversed here.
const param = try sema.coerceInMemoryAllowed(block, src_param_ty, dest_param_ty, false, target, dest_src, src_src);
if (param == .no_match) {
return param;
if (param != .ok) {
return InMemoryCoercionResult{ .fn_param = .{
.child = try param.dupe(sema.arena),
.actual = src_param_ty,
.wanted = dest_param_ty,
.index = i,
} };
}
}
if (dest_info.cc != src_info.cc) {
return .no_match;
}
return .ok;
}
@@ -20830,26 +21377,13 @@ fn coerceInMemoryAllowedPtrs(
const dest_info = dest_ptr_ty.ptrInfo().data;
const src_info = src_ptr_ty.ptrInfo().data;
const child = try sema.coerceInMemoryAllowed(block, dest_info.pointee_type, src_info.pointee_type, dest_info.mutable, target, dest_src, src_src);
if (child == .no_match) {
return child;
}
if (dest_info.@"addrspace" != src_info.@"addrspace") {
return .no_match;
}
const ok_sent = dest_info.sentinel == null or src_info.size == .C or
(src_info.sentinel != null and
dest_info.sentinel.?.eql(src_info.sentinel.?, dest_info.pointee_type, sema.mod));
if (!ok_sent) {
return .no_match;
}
const ok_ptr_size = src_info.size == dest_info.size or
src_info.size == .C or dest_info.size == .C;
if (!ok_ptr_size) {
return .no_match;
return InMemoryCoercionResult{ .ptr_size = .{
.actual = src_info.size,
.wanted = dest_info.size,
} };
}
const ok_cv_qualifiers =
@@ -20857,7 +21391,28 @@ fn coerceInMemoryAllowedPtrs(
(!src_info.@"volatile" or dest_info.@"volatile");
if (!ok_cv_qualifiers) {
return .no_match;
return InMemoryCoercionResult{ .ptr_qualifiers = .{
.actual_const = !src_info.mutable,
.wanted_const = !dest_info.mutable,
.actual_volatile = src_info.@"volatile",
.wanted_volatile = dest_info.@"volatile",
} };
}
if (dest_info.@"addrspace" != src_info.@"addrspace") {
return InMemoryCoercionResult{ .ptr_addrspace = .{
.actual = src_info.@"addrspace",
.wanted = dest_info.@"addrspace",
} };
}
const child = try sema.coerceInMemoryAllowed(block, dest_info.pointee_type, src_info.pointee_type, dest_info.mutable, target, dest_src, src_src);
if (child != .ok) {
return InMemoryCoercionResult{ .ptr_child = .{
.child = try child.dupe(sema.arena),
.actual = src_info.pointee_type,
.wanted = dest_info.pointee_type,
} };
}
const dest_allow_zero = dest_ty.ptrAllowsZero();
@@ -20867,13 +21422,32 @@ fn coerceInMemoryAllowedPtrs(
(src_allow_zero or !dest_is_mut)) or
(!dest_allow_zero and !src_allow_zero);
if (!ok_allows_zero) {
return .no_match;
return InMemoryCoercionResult{ .ptr_allowzero = .{
.actual = src_ty,
.wanted = dest_ty,
} };
}
if (src_info.host_size != dest_info.host_size or
src_info.bit_offset != dest_info.bit_offset)
{
return .no_match;
return InMemoryCoercionResult{ .ptr_bit_range = .{
.actual_host = src_info.host_size,
.wanted_host = dest_info.host_size,
.actual_offset = src_info.bit_offset,
.wanted_offset = dest_info.bit_offset,
} };
}
const ok_sent = dest_info.sentinel == null or src_info.size == .C or
(src_info.sentinel != null and
dest_info.sentinel.?.eql(src_info.sentinel.?, dest_info.pointee_type, sema.mod));
if (!ok_sent) {
return InMemoryCoercionResult{ .ptr_sentinel = .{
.actual = src_info.sentinel orelse Value.initTag(.unreachable_value),
.wanted = dest_info.sentinel orelse Value.initTag(.unreachable_value),
.ty = dest_info.pointee_type,
} };
}
// If both pointers have alignment 0, it means they both want ABI alignment.
@@ -20898,7 +21472,10 @@ fn coerceInMemoryAllowedPtrs(
dest_info.pointee_type.abiAlignment(target);
if (dest_align > src_align) {
return .no_match;
return InMemoryCoercionResult{ .ptr_alignment = .{
.actual = src_align,
.wanted = dest_align,
} };
}
break :alignment;
@@ -20974,6 +21551,8 @@ fn storePtr2(
// TODO do the same thing for anon structs as for tuples above.
// However, beware of the need to handle missing/extra fields.
const is_ret = air_tag == .ret_ptr;
// Detect if we are storing an array operand to a bitcasted vector pointer.
// If so, we instead reach through the bitcasted pointer to the vector pointer,
// bitcast the array operand to a vector, and then lower this as a store of
@@ -20982,12 +21561,18 @@ fn storePtr2(
// https://github.com/ziglang/zig/issues/11154
if (sema.obtainBitCastedVectorPtr(ptr)) |vector_ptr| {
const vector_ty = sema.typeOf(vector_ptr).childType();
const vector = try sema.coerce(block, vector_ty, uncasted_operand, operand_src);
const vector = sema.coerceExtra(block, vector_ty, uncasted_operand, operand_src, true, is_ret) catch |err| switch (err) {
error.NotCoercible => unreachable,
else => |e| return e,
};
try sema.storePtr2(block, src, vector_ptr, ptr_src, vector, operand_src, .store);
return;
}
const operand = try sema.coerce(block, elem_ty, uncasted_operand, operand_src);
const operand = sema.coerceExtra(block, elem_ty, uncasted_operand, operand_src, true, is_ret) catch |err| switch (err) {
error.NotCoercible => unreachable,
else => |e| return e,
};
const maybe_operand_val = try sema.resolveMaybeUndefVal(block, operand_src, operand);
const runtime_src = if (try sema.resolveDefinedValue(block, ptr_src, ptr)) |ptr_val| rs: {
@@ -21017,7 +21602,11 @@ fn storePtr2(
try sema.requireRuntimeBlock(block, runtime_src);
try sema.queueFullTypeResolution(elem_ty);
_ = try block.addBinOp(air_tag, ptr, operand);
if (is_ret) {
_ = try block.addBinOp(.store, ptr, operand);
} else {
_ = try block.addBinOp(air_tag, ptr, operand);
}
}
/// Traverse an arbitrary number of bitcasted pointers and return the underyling vector
@@ -2,4 +2,5 @@ pub export fn main() noreturn {}
// error
//
// :1:32: error: expected type 'noreturn', found 'void'
// :1:32: error: function declared 'noreturn' returns
// :1:22: note: 'noreturn' declared here
@@ -8,3 +8,5 @@ export fn entry() usize { return @sizeOf(@TypeOf(&foo)); }
// target=native
//
// :3:30: error: expected type '*const i32', found '*const comptime_int'
// :3:30: note: pointer type child 'comptime_int' cannot cast into pointer type child 'i32'
// :3:10: note: function return type declared here
@@ -1,11 +1,11 @@
pub fn main() void {
pub export fn entry() void {
var a: ?*anyopaque = undefined;
a = @as(?usize, null);
}
// error
// output_mode=Exe
// backend=stage2,llvm
// target=x86_64-linux,x86_64-macos
// backend=stage2
// target=native
//
// :3:21: error: expected type '*anyopaque', found '?usize'
// :3:21: error: expected type '?*anyopaque', found '?usize'
// :3:21: note: optional type child 'usize' cannot cast into optional type child '*anyopaque'
@@ -15,7 +15,9 @@ export fn entry() void {
}
// error
// backend=stage1
// backend=stage2
// target=native
//
// tmp.zig:11:27: error: cannot convert optional to payload type. consider using `.?`, `orelse`, or `if`. expected type 'u8', found '?u8'
// :11:27: error: expected type 'u8', found '?u8'
// :11:27: note: cannot convert optional to payload type
// :11:27: note: consider using `.?`, `orelse`, or `if`
@@ -7,8 +7,8 @@ export fn entry(byte: u8) void {
}
// error
// backend=stage1
// backend=stage2
// target=native
//
// tmp.zig:4:22: error: expected type '*[1]i32', found '*const i32'
// tmp.zig:4:22: note: cast discards const qualifier
// :4:22: error: expected type '*[1]i32', found '*const i32'
// :4:22: note: cast discards const qualifier
@@ -0,0 +1,26 @@
pub const fnty1 = ?*const fn (i8) void;
pub const fnty2 = ?*const fn (u64) void;
export fn entry1() void {
var a: fnty1 = undefined;
var b: fnty2 = undefined;
a = b;
}
pub const fnty3 = ?*const fn (u63) void;
export fn entry2() void {
var a: fnty3 = undefined;
var b: fnty2 = undefined;
a = b;
}
// error
// backend=stage2
// target=native
//
// :6:9: error: expected type '?*const fn(i8) void', found '?*const fn(u64) void'
// :6:9: note: pointer type child 'fn(u64) void' cannot cast into pointer type child 'fn(i8) void'
// :6:9: note: parameter 0 'u64' cannot cast into 'i8'
// :6:9: note: unsigned 64-bit int cannot represent all possible signed 8-bit values
// :13:9: error: expected type '?*const fn(u63) void', found '?*const fn(u64) void'
// :13:9: note: pointer type child 'fn(u64) void' cannot cast into pointer type child 'fn(u63) void'
// :13:9: note: parameter 0 'u64' cannot cast into 'u63'
@@ -0,0 +1,15 @@
const SmallErrorSet = error{A};
export fn entry() void {
var x: SmallErrorSet!i32 = foo();
_ = x;
}
fn foo() anyerror!i32 {
return error.B;
}
// error
// backend=stage2
// target=native
//
// :3:35: error: expected type 'error{A}!i32', found 'anyerror!i32'
// :3:35: note: global error set cannot cast into a smaller set
@@ -8,8 +8,8 @@ fn foo() anyerror {
}
// error
// backend=stage1
// backend=stage2
// target=native
//
// tmp.zig:3:31: error: expected type 'SmallErrorSet', found 'anyerror'
// tmp.zig:3:31: note: cannot cast global error set into smaller set
// :3:31: error: expected type 'error{A}', found 'anyerror'
// :3:31: note: global error set cannot cast into a smaller set
@@ -19,3 +19,5 @@ export fn entry() usize { return @sizeOf(@TypeOf(&foo)); }
// target=native
//
// :8:15: error: expected type '*const u3', found '*align(0:3:1) const u3'
// :8:15: note: pointer host size '1' cannot cast into pointer host size '0'
// :8:15: note: pointer bit offset '3' cannot cast into pointer bit offset '0'
@@ -9,4 +9,5 @@ fn foo() !void {
// backend=stage2
// target=native
//
// :2:12: error: error is discarded. consider using `try`, `catch`, or `if`
// :2:12: error: error is discarded
// :2:12: note: consider using `try`, `catch`, or `if`
@@ -11,3 +11,4 @@ export fn entry() void {
// target=native
//
// :5:28: error: expected type '*anyopaque', found '**u32'
// :5:28: note: pointer type child '*u32' cannot cast into pointer type child 'anyopaque'
@@ -0,0 +1,13 @@
fn do_the_thing(func: *const fn (arg: i32) void) void { _ = func; }
fn bar(arg: bool) void { _ = arg; }
export fn entry() void {
do_the_thing(bar);
}
// error
// backend=stage2
// target=native
//
// :4:17: error: expected type '*const fn(i32) void', found '*const fn(bool) void'
// :4:17: note: pointer type child 'fn(bool) void' cannot cast into pointer type child 'fn(i32) void'
// :4:17: note: parameter 0 'bool' cannot cast into 'i32'
@@ -7,4 +7,5 @@ fn bar() anyerror!i32 { return 0; }
// backend=stage2
// target=native
//
// :2:14: error: error is ignored. consider using `try`, `catch`, or `if`
// :2:14: error: error is ignored
// :2:14: note: consider using `try`, `catch`, or `if`
@@ -17,6 +17,9 @@ fn bad() anyerror!void {
// backend=stage2
// target=native
//
// :2:24: error: error is ignored. consider using `try`, `catch`, or `if`
// :6:25: error: error is ignored. consider using `try`, `catch`, or `if`
// :10:25: error: error is ignored. consider using `try`, `catch`, or `if`
// :2:24: error: error is ignored
// :2:24: note: consider using `try`, `catch`, or `if`
// :6:25: error: error is ignored
// :6:25: note: consider using `try`, `catch`, or `if`
// :10:25: error: error is ignored
// :10:25: note: consider using `try`, `catch`, or `if`
@@ -9,8 +9,8 @@ fn foo(set1: Set1) void {
}
// error
// backend=stage1
// backend=stage2
// target=native
//
// tmp.zig:7:19: error: expected type 'Set2', found 'Set1'
// tmp.zig:1:23: note: 'error.B' not a member of destination error set
// :7:19: error: expected type 'error{A,C}', found 'error{A,B}'
// :7:19: note: 'error.B' not a member of destination error set
@@ -0,0 +1,15 @@
const std = @import("std");
export fn entry1() void {
_ = @as([*c]u8, @as(u65, std.math.maxInt(u65)));
}
export fn entry2() void {
_ = @as([*c]u8, std.math.maxInt(u65));
}
// error
// backend=stage2
// target=native
//
// :3:21: error: expected type '[*c]u8', found 'u65'
// :3:21: note: unsigned 64-bit int cannot represent all possible unsigned 65-bit values
// :6:36: error: expected type '[*c]u8', found 'comptime_int'
@@ -0,0 +1,26 @@
export fn entry() void {
var slice: []const u8 = "aoeu";
const opt_many_ptr: [*]const u8 = slice.ptr;
var ptr_opt_many_ptr = &opt_many_ptr;
var c_ptr: [*c]const [*c]const u8 = ptr_opt_many_ptr;
ptr_opt_many_ptr = c_ptr;
}
export fn entry2() void {
var buf: [4]u8 = "aoeu".*;
var slice: []u8 = &buf;
var opt_many_ptr: [*]u8 = slice.ptr;
var ptr_opt_many_ptr = &opt_many_ptr;
var c_ptr: [*c][*c]const u8 = ptr_opt_many_ptr;
_ = c_ptr;
}
// error
// backend=stage2
// target=native
//
// :6:24: error: expected type '*const [*]const u8', found '[*c]const [*c]const u8'
// :6:24: note: pointer type child '[*c]const u8' cannot cast into pointer type child '[*]const u8'
// :6:24: note: '[*c]const u8' could have null values which are illegal in type '[*]const u8'
// :13:35: error: expected type '[*c][*c]const u8', found '*[*]u8'
// :13:35: note: pointer type child '[*]u8' cannot cast into pointer type child '[*c]const u8'
// :13:35: note: mutable '[*]u8' allows illegal null values stored to type '[*c]const u8'
@@ -15,3 +15,4 @@ export fn entry() void {
// target=native
//
// :9:22: error: expected type 'u2', found 'tmp.Small'
// :1:15: note: enum declared here
@@ -0,0 +1,31 @@
// Note: One of the error messages here is backwards. It would be nice to fix, but that's not
// going to stop me from merging this branch which fixes a bunch of other stuff.
export fn entry1(ptr: [*:255]u8) [*:0]u8 {
return ptr;
}
export fn entry2(ptr: [*]u8) [*:0]u8 {
return ptr;
}
export fn entry3() void {
var array: [2:0]u8 = [_:255]u8{ 1, 2 };
_ = array;
}
export fn entry4() void {
var array: [2:0]u8 = [_]u8{ 1, 2 };
_ = array;
}
// error
// backend=stage2
// target=native
//
// :4:12: error: expected type '[*:0]u8', found '[*:255]u8'
// :4:12: note: pointer sentinel '255' cannot cast into pointer sentinel '0'
// :3:35: note: function return type declared here
// :7:12: error: expected type '[*:0]u8', found '[*]u8'
// :7:12: note: destination pointer requires '0' sentinel
// :6:31: note: function return type declared here
// :10:35: error: expected type '[2:0]u8', found '[2:255]u8'
// :10:35: note: array sentinel '255' cannot cast into array sentinel '0'
// :14:31: error: expected type '[2:0]u8', found '[2]u8'
// :14:31: note: destination array requires '0' sentinel
@@ -19,3 +19,6 @@
// target=native
//
// :8:16: error: expected type 'tmp.A', found 'tmp.B'
// :10:12: note: struct declared here
// :4:12: note: struct declared here
// :7:11: note: function return type declared here
@@ -11,3 +11,5 @@ pub fn main() void {
// target=x86_64-linux,x86_64-macos
//
// :2:12: error: expected type '*i32', found '*addrspace(.gs) i32'
// :2:12: note: address space 'gs' cannot cast into address space 'generic'
// :1:34: note: function return type declared here
@@ -15,3 +15,4 @@ fn foo(x: usize) void {
// target=native
//
// :9:10: error: expected type 'usize', found 'tmp.E'
// :1:11: note: enum declared here
@@ -11,3 +11,5 @@ pub fn main() void {
// target=x86_64-linux,x86_64-macos
//
// :2:12: error: expected type '*i32', found '*addrspace(.gs) i32'
// :2:12: note: address space 'gs' cannot cast into address space 'generic'
// :1:34: note: function return type declared here
@@ -0,0 +1,14 @@
export fn foo() void {
var u: ?*anyopaque = null;
var v: *anyopaque = undefined;
v = u;
}
// error
// backend=stage2
// target=native
//
// :4:9: error: expected type '*anyopaque', found '?*anyopaque'
// :4:9: note: cannot convert optional to payload type
// :4:9: note: consider using `.?`, `orelse`, or `if`
// :4:9: note: '?*anyopaque' could have null values which are illegal in type '*anyopaque'
@@ -17,3 +17,4 @@ const ExpectedVarDeclOrFn = struct {};
// target=native
//
// :4:9: error: expected type '@typeInfo(tmp.Error).Union.tag_type.?', found 'type'
// :8:1: note: enum declared here
@@ -10,3 +10,4 @@ export fn entry() bool {
// target=native
//
// :4:31: error: expected type '*i32', found '*align(1) i32'
// :4:31: note: pointer alignment '1' cannot cast into pointer alignment '4'
@@ -11,3 +11,5 @@ export fn entry2() void {
// target=x86_64-linux,x86_64-macos
//
// :2:12: error: expected type '*addrspace(.fs) i32', found '*addrspace(.gs) i32'
// :2:12: note: address space 'gs' cannot cast into address space 'fs'
// :1:34: note: function return type declared here
@@ -11,3 +11,5 @@ pub fn main() void {
// target=x86_64-linux,x86_64-macos
//
// :2:13: error: expected type '*i32', found '*addrspace(.gs) i32'
// :2:13: note: address space 'gs' cannot cast into address space 'generic'
// :1:35: note: function return type declared here
@@ -15,7 +15,9 @@ export fn entry() void {
}
// error
// backend=stage1
// backend=stage2
// target=native
//
// tmp.zig:12:25: error: cannot convert error union to payload type. consider using `try`, `catch`, or `if`. expected type 'u32', found '@typeInfo(@typeInfo(@TypeOf(get_uval)).Fn.return_type.?).ErrorUnion.error_set!u32'
// :12:25: error: expected type 'u32', found '@typeInfo(@typeInfo(@TypeOf(tmp.get_uval)).Fn.return_type.?).ErrorUnion.error_set!u32'
// :12:25: note: cannot convert error union to payload type
// :12:25: note: consider using `try`, `catch`, or `if`
@@ -12,7 +12,9 @@ pub const Container = struct {
};
// error
// backend=stage1
// backend=stage2
// target=native
//
// tmp.zig:3:36: error: cannot convert optional to payload type. consider using `.?`, `orelse`, or `if`. expected type 'i32', found '?i32'
// :3:36: error: expected type 'i32', found '?i32'
// :3:36: note: cannot convert optional to payload type
// :3:36: note: consider using `.?`, `orelse`, or `if`
@@ -12,7 +12,9 @@ pub const Container = struct {
};
// error
// backend=stage1
// backend=stage2
// target=native
//
// tmp.zig:3:36: error: cannot convert optional to payload type. consider using `.?`, `orelse`, or `if`. expected type 'i32', found '?i32'
// :3:36: error: expected type 'i32', found '?i32'
// :3:36: note: cannot convert optional to payload type
// :3:36: note: consider using `.?`, `orelse`, or `if`
@@ -7,3 +7,4 @@ export fn entry(x: u8, y: u8) u8 {
// target=native
//
// :2:17: error: expected type 'u3', found 'u8'
// :2:17: note: unsigned 3-bit int cannot represent all possible unsigned 8-bit values
@@ -0,0 +1,13 @@
fn foo() [:0]u8 {
var x: []u8 = undefined;
return x;
}
comptime { _ = foo; }
// error
// backend=stage2
// target=native
//
// :3:12: error: expected type '[:0]u8', found '[]u8'
// :3:12: note: destination pointer requires '0' sentinel
// :1:10: note: function return type declared here
@@ -1,16 +0,0 @@
const SmallErrorSet = error{A};
export fn entry() void {
var x: SmallErrorSet!i32 = foo();
_ = x;
}
fn foo() anyerror!i32 {
return error.B;
}
// error
// backend=stage1
// target=native
//
// tmp.zig:3:35: error: expected type 'SmallErrorSet!i32', found 'anyerror!i32'
// tmp.zig:3:35: note: error set 'anyerror' cannot cast into error set 'SmallErrorSet'
// tmp.zig:3:35: note: cannot cast global error set into smaller set
@@ -1,12 +0,0 @@
fn do_the_thing(func: fn (arg: i32) void) void { _ = func; }
fn bar(arg: bool) void { _ = arg; }
export fn entry() void {
do_the_thing(bar);
}
// error
// backend=stage1
// target=native
//
// tmp.zig:4:18: error: expected type 'fn(i32) void', found 'fn(bool) void
// tmp.zig:4:18: note: parameter 0: 'bool' cannot cast into 'i32'
@@ -1,26 +0,0 @@
export fn entry() void {
var slice: []const u8 = "aoeu";
const opt_many_ptr: [*]const u8 = slice.ptr;
var ptr_opt_many_ptr = &opt_many_ptr;
var c_ptr: [*c]const [*c]const u8 = ptr_opt_many_ptr;
ptr_opt_many_ptr = c_ptr;
}
export fn entry2() void {
var buf: [4]u8 = "aoeu".*;
var slice: []u8 = &buf;
var opt_many_ptr: [*]u8 = slice.ptr;
var ptr_opt_many_ptr = &opt_many_ptr;
var c_ptr: [*c][*c]const u8 = ptr_opt_many_ptr;
_ = c_ptr;
}
// error
// backend=stage1
// target=native
//
// tmp.zig:6:24: error: expected type '*const [*]const u8', found '[*c]const [*c]const u8'
// tmp.zig:6:24: note: pointer type child '[*c]const u8' cannot cast into pointer type child '[*]const u8'
// tmp.zig:6:24: note: '[*c]const u8' could have null values which are illegal in type '[*]const u8'
// tmp.zig:13:35: error: expected type '[*c][*c]const u8', found '*[*]u8'
// tmp.zig:13:35: note: pointer type child '[*]u8' cannot cast into pointer type child '[*c]const u8'
// tmp.zig:13:35: note: mutable '[*c]const u8' allows illegal null values stored to type '[*]u8'
@@ -1,29 +0,0 @@
// Note: One of the error messages here is backwards. It would be nice to fix, but that's not
// going to stop me from merging this branch which fixes a bunch of other stuff.
export fn entry1(ptr: [*:255]u8) [*:0]u8 {
return ptr;
}
export fn entry2(ptr: [*]u8) [*:0]u8 {
return ptr;
}
export fn entry3() void {
var array: [2:0]u8 = [_:255]u8{ 1, 2 };
_ = array;
}
export fn entry4() void {
var array: [2:0]u8 = [_]u8{ 1, 2 };
_ = array;
}
// error
// backend=stage1
// target=native
//
// tmp.zig:4:12: error: expected type '[*:0]u8', found '[*:255]u8'
// tmp.zig:4:12: note: destination pointer requires a terminating '0' sentinel, but source pointer has a terminating '255' sentinel
// tmp.zig:7:12: error: expected type '[*:0]u8', found '[*]u8'
// tmp.zig:7:12: note: destination pointer requires a terminating '0' sentinel
// tmp.zig:10:35: error: expected type '[2:255]u8', found '[2:0]u8'
// tmp.zig:10:35: note: destination array requires a terminating '255' sentinel, but source array has a terminating '0' sentinel
// tmp.zig:14:31: error: expected type '[2:0]u8', found '[2]u8'
// tmp.zig:14:31: note: destination array requires a terminating '0' sentinel
@@ -1,11 +0,0 @@
export fn foo() void {
var u: ?*anyopaque = null;
var v: *anyopaque = undefined;
v = u;
}
// error
// backend=stage1
// target=native
//
// tmp.zig:4:9: error: cannot convert optional to payload type. consider using `.?`, `orelse`, or `if`. expected type '*anyopaque', found '?*anyopaque'
@@ -1,12 +0,0 @@
fn foo() [:0]u8 {
var x: []u8 = undefined;
return x;
}
comptime { _ = foo; }
// error
// backend=stage1
// target=native
//
// tmp.zig:3:12: error: expected type '[:0]u8', found '[]u8'
// tmp.zig:3:12: note: destination pointer requires a terminating '0' sentinel
@@ -1,13 +0,0 @@
fn a(b: fn (*const u8) void) void {
b('a');
}
fn c(d: u8) void {_ = d;}
export fn entry() void {
a(c);
}
// error
// backend=stage1
// target=native
//
// tmp.zig:6:7: error: expected type 'fn(*const u8) void', found 'fn(u8) void'
@@ -1,15 +0,0 @@
pub const fnty1 = ?fn (i8) void;
pub const fnty2 = ?fn (u64) void;
export fn entry() void {
var a: fnty1 = undefined;
var b: fnty2 = undefined;
a = b;
}
// error
// backend=stage1
// target=native
// is_test=1
//
// tmp.zig:6:9: error: expected type '?fn(i8) void', found '?fn(u64) void'
// tmp.zig:6:9: note: optional type child 'fn(u64) void' cannot cast into optional type child 'fn(i8) void'
@@ -16,15 +16,17 @@ export fn quux() u32 {
}
// error
// backend=stage1
// backend=stage2
// target=native
// is_test=1
//
// tmp.zig:2:17: error: expected type 'u32', found 'error{Ohno}'
// tmp.zig:1:17: note: function cannot return an error
// tmp.zig:8:5: error: expected type 'void', found '@typeInfo(@typeInfo(@TypeOf(bar)).Fn.return_type.?).ErrorUnion.error_set'
// tmp.zig:7:17: note: function cannot return an error
// tmp.zig:11:15: error: cannot convert error union to payload type. consider using `try`, `catch`, or `if`. expected type 'u32', found '@typeInfo(@typeInfo(@TypeOf(bar)).Fn.return_type.?).ErrorUnion.error_set!u32'
// tmp.zig:10:17: note: function cannot return an error
// tmp.zig:15:14: error: cannot convert error union to payload type. consider using `try`, `catch`, or `if`. expected type 'u32', found '@typeInfo(@typeInfo(@TypeOf(bar)).Fn.return_type.?).ErrorUnion.error_set!u32'
// tmp.zig:14:5: note: cannot store an error in type 'u32'
// :2:18: error: expected type 'u32', found 'error{Ohno}'
// :1:17: note: function cannot return an error
// :8:5: error: expected type 'void', found '@typeInfo(@typeInfo(@TypeOf(tmp.bar)).Fn.return_type.?).ErrorUnion.error_set'
// :7:17: note: function cannot return an error
// :11:15: error: expected type 'u32', found '@typeInfo(@typeInfo(@TypeOf(tmp.bar)).Fn.return_type.?).ErrorUnion.error_set!u32'
// :10:17: note: function cannot return an error
// :11:15: note: cannot convert error union to payload type
// :11:15: note: consider using `try`, `catch`, or `if`
// :15:14: error: expected type 'u32', found '@typeInfo(@typeInfo(@TypeOf(tmp.bar)).Fn.return_type.?).ErrorUnion.error_set!u32'
// :15:14: note: cannot convert error union to payload type
// :15:14: note: consider using `try`, `catch`, or `if`
@@ -8,3 +8,4 @@ fn something() anyerror!void { }
// target=native
//
// :2:5: error: expected type 'void', found 'anyerror'
// :1:15: note: function cannot return an error
@@ -0,0 +1,15 @@
fn a(b: *const fn (*const u8) void) void {
_ = b;
}
fn c(d: u8) void {_ = d;}
export fn entry() void {
a(c);
}
// error
// backend=stage2
// target=native
//
// :6:6: error: expected type '*const fn(*const u8) void', found '*const fn(u8) void'
// :6:6: note: pointer type child 'fn(u8) void' cannot cast into pointer type child 'fn(*const u8) void'
// :6:6: note: parameter 0 'u8' cannot cast into '*const u8'
@@ -10,4 +10,6 @@ export fn main() void {
// backend=stage2
// target=native
//
// :5:22: error: expected type 'fn([*c]u8, ...) callconv(.C) void', found 'fn([*:0]u8, ...) callconv(.C) void'
// :5:22: error: expected type '?fn([*c]u8, ...) callconv(.C) void', found 'fn([*:0]u8, ...) callconv(.C) void'
// :5:22: note: parameter 0 '[*:0]u8' cannot cast into '[*c]u8'
// :5:22: note: '[*c]u8' could have null values which are illegal in type '[*:0]u8'
@@ -5,4 +5,5 @@ export fn entry() void { a(); }
// backend=stage2
// target=native
//
// :1:18: error: expected type 'noreturn', found 'void'
// :1:18: error: function declared 'noreturn' returns
// :1:8: note: 'noreturn' declared here
@@ -8,3 +8,4 @@ export fn f() i32 {
// target=native
//
// :3:12: error: expected type 'i32', found '*const [1:0]u8'
// :1:15: note: function return type declared here
@@ -7,3 +7,4 @@ export fn entry() void {
// target=native
//
// :2:15: error: expected type 'builtin.Type', found 'comptime_int'
// :?:?: note: union declared here
@@ -2,4 +2,5 @@ pub export fn _start() noreturn {}
// error
//
// :1:34: error: expected type 'noreturn', found 'void'
// :1:34: error: function declared 'noreturn' returns
// :1:24: note: 'noreturn' declared here
@@ -2,4 +2,5 @@ pub export fn main() noreturn {}
// error
//
// :1:32: error: expected type 'noreturn', found 'void'
// :1:32: error: function declared 'noreturn' returns
// :1:22: note: 'noreturn' declared here