diff options
| author | Veikka Tuominen <git@vexu.eu> | 2022-07-11 17:54:53 +0300 |
|---|---|---|
| committer | Veikka Tuominen <git@vexu.eu> | 2022-07-11 18:47:29 +0300 |
| commit | d00da05ecbeca36e8ca25dcb1238bf98b38081a5 (patch) | |
| tree | 3c47892fb4a3485ba7a3400a523729b16484646b /src | |
| parent | 3ceb27c8401df17e5a7f522d68104da79501b5b6 (diff) | |
| download | zig-d00da05ecbeca36e8ca25dcb1238bf98b38081a5.tar.gz zig-d00da05ecbeca36e8ca25dcb1238bf98b38081a5.zip | |
Sema: validate extern types
Diffstat (limited to 'src')
| -rw-r--r-- | src/Sema.zig | 175 | ||||
| -rw-r--r-- | src/type.zig | 9 |
2 files changed, 176 insertions, 8 deletions
diff --git a/src/Sema.zig b/src/Sema.zig index 953336ee39..48cab0f04c 100644 --- a/src/Sema.zig +++ b/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", .{}); } @@ -18169,6 +18189,119 @@ fn explainWhyTypeIsComptime( } } +const ExternPosition = enum { + ret_ty, + param_ty, + other, +}; + +fn validateExternType(sema: *Sema, ty: Type, position: ExternPosition) CompileError!bool { + switch (ty.zigTypeTag()) { + .Type, + .ComptimeFloat, + .ComptimeInt, + .EnumLiteral, + .Undefined, + .Null, + .ErrorUnion, + .ErrorSet, + .BoundFn, + .Void, + .Frame, + => return false, + .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, @@ -24012,6 +24145,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; } @@ -24045,6 +24192,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, .other))) { + 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, .other); + + try sema.addDeclaredHereNote(msg, field.ty); + break :msg msg; + }; + return sema.failWithOwnedErrorMsg(block, msg); + } } union_obj.status = .fully_resolved; } diff --git a/src/type.zig b/src/type.zig index f14ad3a85b..765f1da18c 100644 --- a/src/type.zig +++ b/src/type.zig @@ -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, @@ -4631,6 +4630,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()) { .fn_noreturn_no_args => false, |
