From b5d3db5fc6104f9af8ab7d3f5cf05a5d4f30f721 Mon Sep 17 00:00:00 2001 From: mlugg Date: Sun, 15 Dec 2024 11:07:22 +0000 Subject: [PATCH 1/2] Sema: do not allow coercing undefined to opaque types --- src/Sema.zig | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/src/Sema.zig b/src/Sema.zig index f31e71047f..7cd8e0e635 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -30675,6 +30675,18 @@ fn coerceExtra( else => {}, } + const can_coerce_to = switch (dest_ty.zigTypeTag(zcu)) { + .noreturn, .@"opaque" => false, + else => true, + }; + + if (can_coerce_to) { + // undefined to anything. We do this after the big switch above so that + // special logic has a chance to run first, such as `*[N]T` to `[]T` which + // should initialize the length field of the slice. + if (maybe_inst_val) |val| if (val.toIntern() == .undef) return pt.undefRef(dest_ty); + } + if (!opts.report_err) return error.NotCoercible; if (opts.is_ret and dest_ty.zigTypeTag(zcu) == .noreturn) { @@ -30692,15 +30704,14 @@ fn coerceExtra( return sema.failWithOwnedErrorMsg(block, msg); } - // undefined to anything. We do this after the big switch above so that - // special logic has a chance to run first, such as `*[N]T` to `[]T` which - // should initialize the length field of the slice. - if (maybe_inst_val) |val| if (val.toIntern() == .undef) return pt.undefRef(dest_ty); - const msg = msg: { const msg = try sema.errMsg(inst_src, "expected type '{}', found '{}'", .{ dest_ty.fmt(pt), inst_ty.fmt(pt) }); errdefer msg.destroy(sema.gpa); + if (!can_coerce_to) { + try sema.errNote(inst_src, msg, "cannot coerce to '{}'", .{dest_ty.fmt(pt)}); + } + // E!T to T if (inst_ty.zigTypeTag(zcu) == .error_union and (try sema.coerceInMemoryAllowed(block, inst_ty.errorUnionPayload(zcu), dest_ty, false, target, dest_ty_src, inst_src, maybe_inst_val)) == .ok) From 72ba7f7e98b8cd00787e2903641e752e15264522 Mon Sep 17 00:00:00 2001 From: mlugg Date: Sun, 15 Dec 2024 11:08:07 +0000 Subject: [PATCH 2/2] Sema: disallow runtime stores to pointers with comptime-only element types --- src/Sema.zig | 17 +++++++- ..._comptime_only_type_to_runtime_pointer.zig | 42 +++++++++++++++++++ 2 files changed, 58 insertions(+), 1 deletion(-) create mode 100644 test/cases/compile_errors/store_comptime_only_type_to_runtime_pointer.zig diff --git a/src/Sema.zig b/src/Sema.zig index 7cd8e0e635..aea749ddbd 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -3628,6 +3628,10 @@ fn zirAllocExtended( } } + if (small.has_type and try var_ty.comptimeOnlySema(pt)) { + return sema.analyzeComptimeAlloc(block, var_ty, alignment); + } + if (small.has_type) { if (!small.is_const) { try sema.validateVarType(block, ty_src, var_ty, false); @@ -4075,7 +4079,7 @@ fn zirAlloc(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I const ty_src = block.src(.{ .node_offset_var_decl_ty = inst_data.src_node }); const var_ty = try sema.resolveType(block, ty_src, inst_data.operand); - if (block.is_comptime) { + if (block.is_comptime or try var_ty.comptimeOnlySema(pt)) { return sema.analyzeComptimeAlloc(block, var_ty, .none); } if (sema.func_is_naked and try var_ty.hasRuntimeBitsSema(pt)) { @@ -31938,6 +31942,17 @@ fn storePtr2( } else break :rs ptr_src; } else ptr_src; + // We're performing the store at runtime; as such, we need to make sure the pointee type + // is not comptime-only. We can hit this case with a `@ptrFromInt` pointer. + if (try elem_ty.comptimeOnlySema(pt)) { + return sema.failWithOwnedErrorMsg(block, msg: { + const msg = try sema.errMsg(src, "cannot store comptime-only type '{}' at runtime", .{elem_ty.fmt(pt)}); + errdefer msg.destroy(sema.gpa); + try sema.errNote(ptr_src, msg, "operation is runtime due to this pointer", .{}); + break :msg msg; + }); + } + // We do this after the possible comptime store above, for the case of field_ptr stores // to unions because we want the comptime tag to be set, even if the field type is void. if ((try sema.typeHasOnePossibleValue(elem_ty)) != null) { diff --git a/test/cases/compile_errors/store_comptime_only_type_to_runtime_pointer.zig b/test/cases/compile_errors/store_comptime_only_type_to_runtime_pointer.zig new file mode 100644 index 0000000000..af30c80f70 --- /dev/null +++ b/test/cases/compile_errors/store_comptime_only_type_to_runtime_pointer.zig @@ -0,0 +1,42 @@ +export fn a() void { + const p: *fn () void = @ptrFromInt(4); + p.* = undefined; +} + +export fn b(p: *anyopaque) void { + p.* = undefined; +} + +export fn c(p: *anyopaque, q: *anyopaque) void { + p.* = q.*; +} + +const Opaque = opaque {}; +export fn d(p: *Opaque) void { + p.* = undefined; +} + +export fn e() void { + const p: *comptime_int = @ptrFromInt(16); + p.* = undefined; +} + +export fn f() void { + const p: **comptime_int = @ptrFromInt(16); // double pointer ('*comptime_int' is comptime-only) + p.* = undefined; +} + +// error +// +// :3:9: error: cannot store comptime-only type 'fn () void' at runtime +// :3:6: note: operation is runtime due to this pointer +// :7:11: error: expected type 'anyopaque', found '@TypeOf(undefined)' +// :7:11: note: cannot coerce to 'anyopaque' +// :11:12: error: cannot load opaque type 'anyopaque' +// :16:11: error: expected type 'tmp.Opaque', found '@TypeOf(undefined)' +// :16:11: note: cannot coerce to 'tmp.Opaque' +// :14:16: note: opaque declared here +// :21:9: error: cannot store comptime-only type 'comptime_int' at runtime +// :21:6: note: operation is runtime due to this pointer +// :26:9: error: cannot store comptime-only type '*comptime_int' at runtime +// :26:6: note: operation is runtime due to this pointer