mirror of
https://github.com/ziglang/zig.git
synced 2026-02-12 20:37:54 +00:00
Merge pull request #12075 from Vexu/stage2-validate-extern
Stage2 validate extern types
This commit is contained in:
commit
dc815e5e8f
@ -59,12 +59,12 @@ const twords = extern union {
|
||||
s: S,
|
||||
|
||||
const S = if (native_endian == .Little)
|
||||
struct {
|
||||
extern struct {
|
||||
low: u64,
|
||||
high: u64,
|
||||
}
|
||||
else
|
||||
struct {
|
||||
extern struct {
|
||||
high: u64,
|
||||
low: u64,
|
||||
};
|
||||
|
||||
@ -31,9 +31,9 @@ fn Dwords(comptime T: type, comptime signed_half: bool) type {
|
||||
|
||||
all: T,
|
||||
s: if (native_endian == .Little)
|
||||
struct { low: HalfT, high: HalfT }
|
||||
extern struct { low: HalfT, high: HalfT }
|
||||
else
|
||||
struct { high: HalfT, low: HalfT },
|
||||
extern struct { high: HalfT, low: HalfT },
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@ -30,8 +30,8 @@ pub extern "c" fn _kern_get_current_team() i32;
|
||||
pub const sem_t = extern struct {
|
||||
type: i32,
|
||||
u: extern union {
|
||||
named_sem_id: ?i32,
|
||||
unnamed_sem: ?i32,
|
||||
named_sem_id: i32,
|
||||
unnamed_sem: i32,
|
||||
},
|
||||
padding: [2]i32,
|
||||
};
|
||||
|
||||
@ -108,7 +108,7 @@ fn callMain2() noreturn {
|
||||
exit2(0);
|
||||
}
|
||||
|
||||
fn wasiMain2() noreturn {
|
||||
fn wasiMain2() callconv(.C) noreturn {
|
||||
switch (@typeInfo(@typeInfo(@TypeOf(root.main)).Fn.return_type.?)) {
|
||||
.Void => {
|
||||
root.main();
|
||||
|
||||
186
src/Sema.zig
186
src/Sema.zig
@ -4740,12 +4740,19 @@ pub fn analyzeExport(
|
||||
|
||||
try mod.ensureDeclAnalyzed(exported_decl_index);
|
||||
const exported_decl = mod.declPtr(exported_decl_index);
|
||||
// TODO run the same checks as we do for C ABI struct fields
|
||||
switch (exported_decl.ty.zigTypeTag()) {
|
||||
.Fn, .Int, .Enum, .Struct, .Union, .Array, .Float, .Pointer, .Optional => {},
|
||||
else => return sema.fail(block, src, "unable to export type '{}'", .{
|
||||
exported_decl.ty.fmt(sema.mod),
|
||||
}),
|
||||
|
||||
if (!(try sema.validateExternType(exported_decl.ty, .other))) {
|
||||
const msg = msg: {
|
||||
const msg = try sema.errMsg(block, src, "unable to export type '{}'", .{exported_decl.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), exported_decl.ty, .other);
|
||||
|
||||
try sema.addDeclaredHereNote(msg, exported_decl.ty);
|
||||
break :msg msg;
|
||||
};
|
||||
return sema.failWithOwnedErrorMsg(block, msg);
|
||||
}
|
||||
|
||||
const gpa = mod.gpa;
|
||||
@ -13799,7 +13806,20 @@ fn validatePtrTy(sema: *Sema, block: *Block, elem_src: LazySrcLoc, ty: Type) Com
|
||||
} 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) {
|
||||
// TODO check extern type
|
||||
const elem_ty = ptr_info.pointee_type;
|
||||
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)});
|
||||
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.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", .{});
|
||||
}
|
||||
@ -18098,10 +18118,12 @@ fn explainWhyTypeIsComptime(
|
||||
.NoReturn,
|
||||
.Undefined,
|
||||
.Null,
|
||||
.Opaque,
|
||||
.Optional,
|
||||
=> return,
|
||||
|
||||
.Opaque => {
|
||||
try mod.errNoteNonLazy(src_loc, msg, "opaque type '{}' has undefined size", .{ty.fmt(sema.mod)});
|
||||
},
|
||||
|
||||
.Array, .Vector => {
|
||||
try sema.explainWhyTypeIsComptime(block, src, msg, src_loc, ty.elemType());
|
||||
},
|
||||
@ -18124,6 +18146,10 @@ fn explainWhyTypeIsComptime(
|
||||
try sema.explainWhyTypeIsComptime(block, src, msg, src_loc, ty.elemType());
|
||||
},
|
||||
|
||||
.Optional => {
|
||||
var buf: Type.Payload.ElemType = undefined;
|
||||
try sema.explainWhyTypeIsComptime(block, src, msg, src_loc, ty.optionalChild(&buf));
|
||||
},
|
||||
.ErrorUnion => {
|
||||
try sema.explainWhyTypeIsComptime(block, src, msg, src_loc, ty.errorUnionPayload());
|
||||
},
|
||||
@ -18163,6 +18189,120 @@ fn explainWhyTypeIsComptime(
|
||||
}
|
||||
}
|
||||
|
||||
const ExternPosition = enum {
|
||||
ret_ty,
|
||||
param_ty,
|
||||
union_field,
|
||||
other,
|
||||
};
|
||||
|
||||
fn validateExternType(sema: *Sema, ty: Type, position: ExternPosition) CompileError!bool {
|
||||
switch (ty.zigTypeTag()) {
|
||||
.Type,
|
||||
.ComptimeFloat,
|
||||
.ComptimeInt,
|
||||
.EnumLiteral,
|
||||
.Undefined,
|
||||
.Null,
|
||||
.ErrorUnion,
|
||||
.ErrorSet,
|
||||
.BoundFn,
|
||||
.Frame,
|
||||
=> return false,
|
||||
.Void => return position == .union_field,
|
||||
.NoReturn => return position == .ret_ty,
|
||||
.Opaque,
|
||||
.Bool,
|
||||
.Float,
|
||||
.Pointer,
|
||||
.AnyFrame,
|
||||
=> return true,
|
||||
.Int => switch (ty.intInfo(sema.mod.getTarget()).bits) {
|
||||
8, 16, 32, 64, 128 => return true,
|
||||
else => return false,
|
||||
},
|
||||
.Fn => return !ty.fnCallingConventionAllowsZigTypes(),
|
||||
.Enum => {
|
||||
var buf: Type.Payload.Bits = undefined;
|
||||
return sema.validateExternType(ty.intTagType(&buf), position);
|
||||
},
|
||||
.Struct, .Union => switch (ty.containerLayout()) {
|
||||
.Extern, .Packed => return true,
|
||||
else => return false,
|
||||
},
|
||||
.Array => {
|
||||
if (position == .ret_ty or position == .param_ty) return false;
|
||||
return sema.validateExternType(ty.elemType2(), .other);
|
||||
},
|
||||
.Vector => return sema.validateExternType(ty.elemType2(), .other),
|
||||
.Optional => return ty.isPtrLikeOptional(),
|
||||
}
|
||||
}
|
||||
|
||||
fn explainWhyTypeIsNotExtern(
|
||||
sema: *Sema,
|
||||
block: *Block,
|
||||
src: LazySrcLoc,
|
||||
msg: *Module.ErrorMsg,
|
||||
src_loc: Module.SrcLoc,
|
||||
ty: Type,
|
||||
position: ExternPosition,
|
||||
) CompileError!void {
|
||||
const mod = sema.mod;
|
||||
switch (ty.zigTypeTag()) {
|
||||
.Opaque,
|
||||
.Bool,
|
||||
.Float,
|
||||
.Pointer,
|
||||
.AnyFrame,
|
||||
=> return,
|
||||
|
||||
.Type,
|
||||
.ComptimeFloat,
|
||||
.ComptimeInt,
|
||||
.EnumLiteral,
|
||||
.Undefined,
|
||||
.Null,
|
||||
.ErrorUnion,
|
||||
.ErrorSet,
|
||||
.BoundFn,
|
||||
.Frame,
|
||||
=> return,
|
||||
|
||||
.Void => try mod.errNoteNonLazy(src_loc, msg, "'void' is a zero bit type; for C 'void' use 'anyopaque'", .{}),
|
||||
.NoReturn => try mod.errNoteNonLazy(src_loc, msg, "'noreturn' is only allowed as a return type", .{}),
|
||||
.Int => if (ty.intInfo(sema.mod.getTarget()).bits > 128) {
|
||||
try mod.errNoteNonLazy(src_loc, msg, "only integers with less than 128 bits are extern compatible", .{});
|
||||
} else {
|
||||
try mod.errNoteNonLazy(src_loc, msg, "only integers with power of two bits are extern compatible", .{});
|
||||
},
|
||||
.Fn => switch (ty.fnCallingConvention()) {
|
||||
.Unspecified => try mod.errNoteNonLazy(src_loc, msg, "extern function must specify calling convention", .{}),
|
||||
.Async => try mod.errNoteNonLazy(src_loc, msg, "async function cannot be extern", .{}),
|
||||
.Inline => try mod.errNoteNonLazy(src_loc, msg, "inline function cannot be extern", .{}),
|
||||
else => return,
|
||||
},
|
||||
.Enum => {
|
||||
var buf: Type.Payload.Bits = undefined;
|
||||
const tag_ty = ty.intTagType(&buf);
|
||||
try mod.errNoteNonLazy(src_loc, msg, "enum tag type '{}' is not extern compatible", .{tag_ty.fmt(sema.mod)});
|
||||
try sema.explainWhyTypeIsNotExtern(block, src, msg, src_loc, tag_ty, position);
|
||||
},
|
||||
.Struct => try mod.errNoteNonLazy(src_loc, msg, "only structs with packed or extern layout are extern compatible", .{}),
|
||||
.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", .{});
|
||||
} else if (position == .param_ty) {
|
||||
try mod.errNoteNonLazy(src_loc, msg, "arrays are not allowed as a parameter type", .{});
|
||||
}
|
||||
try sema.explainWhyTypeIsNotExtern(block, src, msg, src_loc, ty.elemType2(), position);
|
||||
},
|
||||
.Vector => try sema.explainWhyTypeIsNotExtern(block, src, msg, src_loc, ty.elemType2(), position),
|
||||
.Optional => try mod.errNoteNonLazy(src_loc, msg, "only pointer like optionals are extern compatible", .{}),
|
||||
}
|
||||
}
|
||||
|
||||
pub const PanicId = enum {
|
||||
unreach,
|
||||
unwrap_null,
|
||||
@ -24006,6 +24146,20 @@ 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;
|
||||
}
|
||||
@ -24039,6 +24193,20 @@ 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;
|
||||
}
|
||||
|
||||
@ -3935,7 +3935,6 @@ pub const Type = extern union {
|
||||
|
||||
/// Returns true if the type is optional and would be lowered to a single pointer
|
||||
/// address value, using 0 for null. Note that this returns true for C pointers.
|
||||
/// See also `hasOptionalRepr`.
|
||||
pub fn isPtrLikeOptional(self: Type) bool {
|
||||
switch (self.tag()) {
|
||||
.optional_single_const_pointer,
|
||||
@ -4630,6 +4629,14 @@ pub const Type = extern union {
|
||||
};
|
||||
}
|
||||
|
||||
/// Asserts the type is a function.
|
||||
pub fn fnCallingConventionAllowsZigTypes(self: Type) bool {
|
||||
return switch (self.fnCallingConvention()) {
|
||||
.Unspecified, .Async, .Inline, .PtxKernel => true,
|
||||
else => false,
|
||||
};
|
||||
}
|
||||
|
||||
/// Asserts the type is a function.
|
||||
pub fn fnIsVarArgs(self: Type) bool {
|
||||
return switch (self.tag()) {
|
||||
|
||||
@ -84,18 +84,19 @@ test "comptime union field access" {
|
||||
|
||||
const FooExtern = extern union {
|
||||
int: i32,
|
||||
str: struct {
|
||||
slice: []const u8,
|
||||
str: extern struct {
|
||||
slice: [*:0]const u8,
|
||||
},
|
||||
};
|
||||
|
||||
test "basic extern unions" {
|
||||
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
|
||||
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
|
||||
|
||||
var foo = FooExtern{ .int = 1 };
|
||||
try expect(foo.int == 1);
|
||||
foo.str.slice = "Well";
|
||||
try expect(std.mem.eql(u8, foo.str.slice, "Well"));
|
||||
try expect(std.mem.eql(u8, std.mem.sliceTo(foo.str.slice, 0), "Well"));
|
||||
}
|
||||
|
||||
const ExternPtrOrInt = extern union {
|
||||
|
||||
11
test/cases/compile_errors/c_pointer_to_void.zig
Normal file
11
test/cases/compile_errors/c_pointer_to_void.zig
Normal file
@ -0,0 +1,11 @@
|
||||
export fn entry() void {
|
||||
var a: [*c]void = undefined;
|
||||
_ = a;
|
||||
}
|
||||
|
||||
// error
|
||||
// backend=stage2
|
||||
// target=native
|
||||
//
|
||||
// :1:1: error: C pointers cannot point to non-C-ABI-compatible type 'void'
|
||||
// :1:1: note: 'void' is a zero bit type; for C 'void' use 'anyopaque'
|
||||
@ -0,0 +1,18 @@
|
||||
const E = enum { one, two };
|
||||
comptime {
|
||||
@export(E, .{ .name = "E" });
|
||||
}
|
||||
const e: E = .two;
|
||||
comptime {
|
||||
@export(e, .{ .name = "e" });
|
||||
}
|
||||
|
||||
// error
|
||||
// backend=stage2
|
||||
// target=native
|
||||
//
|
||||
// :3:5: error: unable to export type 'type'
|
||||
// :7:5: error: unable to export type 'tmp.E'
|
||||
// :7:5: note: enum tag type 'u1' is not extern compatible
|
||||
// :7:5: note: only integers with power of two bits are extern compatible
|
||||
// :1:11: note: enum declared here
|
||||
@ -25,19 +25,21 @@ pub const E = enum {
|
||||
@"227",@"228",@"229",@"230",@"231",@"232",@"233",@"234",@"235",
|
||||
@"236",@"237",@"238",@"239",@"240",@"241",@"242",@"243",@"244",
|
||||
@"245",@"246",@"247",@"248",@"249",@"250",@"251",@"252",@"253",
|
||||
@"254",@"255"
|
||||
@"254",@"255", @"256"
|
||||
};
|
||||
pub const S = extern struct {
|
||||
e: E,
|
||||
};
|
||||
export fn entry() void {
|
||||
if (@typeInfo(E).Enum.tag_type != u8) @compileError("did not infer u8 tag type");
|
||||
const s: S = undefined;
|
||||
_ = s;
|
||||
}
|
||||
|
||||
// error
|
||||
// backend=stage1
|
||||
// backend=stage2
|
||||
// target=native
|
||||
//
|
||||
// tmp.zig:31:5: error: extern structs cannot contain fields of type 'E'
|
||||
// :33:8: error: extern structs cannot contain fields of type 'tmp.E'
|
||||
// :33:8: note: enum tag type 'u9' is not extern compatible
|
||||
// :33:8: note: only integers with power of two bits are extern compatible
|
||||
// :1:15: note: enum declared here
|
||||
@ -0,0 +1,17 @@
|
||||
pub const E = enum(u31) { A, B, C };
|
||||
pub const S = extern struct {
|
||||
e: E,
|
||||
};
|
||||
export fn entry() void {
|
||||
const s: S = undefined;
|
||||
_ = s;
|
||||
}
|
||||
|
||||
// error
|
||||
// backend=stage2
|
||||
// target=native
|
||||
//
|
||||
// :5:8: error: extern structs cannot contain fields of type 'tmp.E'
|
||||
// :5:8: note: enum tag type 'u31' is not extern compatible
|
||||
// :5:8: note: only integers with power of two bits are extern compatible
|
||||
// :1:15: note: enum declared here
|
||||
@ -0,0 +1,11 @@
|
||||
const stroo = extern struct {
|
||||
moo: ?[*c]u8,
|
||||
};
|
||||
export fn testf(fluff: *stroo) void { _ = fluff; }
|
||||
|
||||
// error
|
||||
// backend=stage2
|
||||
// target=native
|
||||
//
|
||||
// :4:8: error: extern structs cannot contain fields of type '?[*c]u8'
|
||||
// :4:8: note: only pointer like optionals are extern compatible
|
||||
@ -40,5 +40,6 @@ const Opaque = opaque {};
|
||||
// :14:8: note: to modify this variable at runtime, it must be given an explicit fixed-size number type
|
||||
// :18:8: error: variable of type '@TypeOf(null)' must be const or comptime
|
||||
// :22:19: error: values of type 'tmp.Opaque' must be comptime known, but operand value is runtime known
|
||||
// :22:19: note: opaque type 'tmp.Opaque' has undefined size
|
||||
// :26:8: error: variable of type 'type' must be const or comptime
|
||||
// :26:8: note: types are not available at runtime
|
||||
|
||||
@ -1,15 +0,0 @@
|
||||
const E = enum { one, two };
|
||||
comptime {
|
||||
@export(E, .{ .name = "E" });
|
||||
}
|
||||
const e: E = .two;
|
||||
comptime {
|
||||
@export(e, .{ .name = "e" });
|
||||
}
|
||||
|
||||
// error
|
||||
// backend=stage1
|
||||
// target=native
|
||||
//
|
||||
// tmp.zig:3:13: error: exported enum without explicit integer tag type
|
||||
// tmp.zig:7:13: error: exported enum value without explicit integer tag type
|
||||
@ -1,14 +0,0 @@
|
||||
pub const E = enum(u31) { A, B, C };
|
||||
pub const S = extern struct {
|
||||
e: E,
|
||||
};
|
||||
export fn entry() void {
|
||||
const s: S = undefined;
|
||||
_ = s;
|
||||
}
|
||||
|
||||
// error
|
||||
// backend=stage1
|
||||
// target=native
|
||||
//
|
||||
// tmp.zig:3:5: error: extern structs cannot contain fields of type 'E'
|
||||
@ -1,10 +0,0 @@
|
||||
const stroo = extern struct {
|
||||
moo: ?[*c]u8,
|
||||
};
|
||||
export fn testf(fluff: *stroo) void { _ = fluff; }
|
||||
|
||||
// error
|
||||
// backend=stage1
|
||||
// target=native
|
||||
//
|
||||
// tmp.zig:2:5: error: extern structs cannot contain fields of type '?[*c]u8'
|
||||
Loading…
x
Reference in New Issue
Block a user