diff --git a/src/Sema.zig b/src/Sema.zig index 22d0c572fc..71ab59d7f9 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -5049,13 +5049,13 @@ pub fn analyzeExport( try mod.ensureDeclAnalyzed(exported_decl_index); const exported_decl = mod.declPtr(exported_decl_index); - if (!(try sema.validateExternType(exported_decl.ty, .other))) { + if (!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.explainWhyTypeIsNotExtern(msg, src.toSrcLoc(src_decl), exported_decl.ty, .other); try sema.addDeclaredHereNote(msg, exported_decl.ty); break :msg msg; @@ -7634,7 +7634,7 @@ fn funcCommon( }; return sema.failWithOwnedErrorMsg(block, msg); } - if (!Type.fnCallingConventionAllowsZigTypes(cc_workaround) and !(try sema.validateExternType(return_type, .ret_ty))) { + if (!Type.fnCallingConventionAllowsZigTypes(cc_workaround) and !sema.validateExternType(return_type, .ret_ty)) { const msg = msg: { const msg = try sema.errMsg(block, ret_ty_src, "return type '{}' not allowed in function with calling convention '{s}'", .{ return_type.fmt(sema.mod), @tagName(cc_workaround), @@ -7642,7 +7642,7 @@ fn funcCommon( errdefer msg.destroy(sema.gpa); const src_decl = sema.mod.declPtr(block.src_decl); - try sema.explainWhyTypeIsNotExtern(block, ret_ty_src, msg, ret_ty_src.toSrcLoc(src_decl), return_type, .ret_ty); + try sema.explainWhyTypeIsNotExtern(msg, ret_ty_src.toSrcLoc(src_decl), return_type, .ret_ty); try sema.addDeclaredHereNote(msg, return_type); break :msg msg; @@ -7830,7 +7830,7 @@ fn analyzeParameter( }; return sema.failWithOwnedErrorMsg(block, msg); } - if (!Type.fnCallingConventionAllowsZigTypes(cc) and !(try sema.validateExternType(param.ty, .param_ty))) { + if (!Type.fnCallingConventionAllowsZigTypes(cc) and !sema.validateExternType(param.ty, .param_ty)) { const msg = msg: { const msg = try sema.errMsg(block, param_src, "parameter of type '{}' not allowed in function with calling convention '{s}'", .{ param.ty.fmt(sema.mod), @tagName(cc), @@ -7838,7 +7838,7 @@ fn analyzeParameter( errdefer msg.destroy(sema.gpa); const src_decl = sema.mod.declPtr(block.src_decl); - try sema.explainWhyTypeIsNotExtern(block, param_src, msg, param_src.toSrcLoc(src_decl), param.ty, .param_ty); + try sema.explainWhyTypeIsNotExtern(msg, param_src.toSrcLoc(src_decl), param.ty, .param_ty); try sema.addDeclaredHereNote(msg, param.ty); break :msg msg; @@ -14866,13 +14866,13 @@ fn zirPtrType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air } else if (inst_data.size == .Many and elem_ty.zigTypeTag() == .Opaque) { return sema.fail(block, elem_ty_src, "unknown-length pointer to opaque not allowed", .{}); } else if (inst_data.size == .C) { - if (!(try sema.validateExternType(elem_ty, .other))) { + if (!sema.validateExternType(elem_ty, .other)) { const msg = msg: { const msg = try sema.errMsg(block, elem_ty_src, "C pointers cannot point to non-C-ABI-compatible type '{}'", .{elem_ty.fmt(sema.mod)}); errdefer msg.destroy(sema.gpa); const src_decl = sema.mod.declPtr(block.src_decl); - try sema.explainWhyTypeIsNotExtern(block, elem_ty_src, msg, elem_ty_src.toSrcLoc(src_decl), elem_ty, .other); + try sema.explainWhyTypeIsNotExtern(msg, elem_ty_src.toSrcLoc(src_decl), elem_ty, .other); try sema.addDeclaredHereNote(msg, elem_ty); break :msg msg; @@ -15950,13 +15950,13 @@ fn zirReify(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I } else if (ptr_size == .Many and elem_ty.zigTypeTag() == .Opaque) { return sema.fail(block, src, "unknown-length pointer to opaque not allowed", .{}); } else if (ptr_size == .C) { - if (!(try sema.validateExternType(elem_ty, .other))) { + if (!sema.validateExternType(elem_ty, .other)) { const msg = msg: { const msg = try sema.errMsg(block, 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, src, msg, src.toSrcLoc(src_decl), elem_ty, .other); + try sema.explainWhyTypeIsNotExtern(msg, src.toSrcLoc(src_decl), elem_ty, .other); try sema.addDeclaredHereNote(msg, elem_ty); break :msg msg; @@ -19736,7 +19736,7 @@ const ExternPosition = enum { /// Returns true if `ty` is allowed in extern types. /// Does *NOT* require `ty` to be resolved in any way. -fn validateExternType(sema: *Sema, ty: Type, position: ExternPosition) CompileError!bool { +fn validateExternType(sema: *Sema, ty: Type, position: ExternPosition) bool { switch (ty.zigTypeTag()) { .Type, .ComptimeFloat, @@ -19781,8 +19781,6 @@ fn validateExternType(sema: *Sema, ty: Type, position: ExternPosition) CompileEr fn explainWhyTypeIsNotExtern( sema: *Sema, - block: *Block, - src: LazySrcLoc, msg: *Module.ErrorMsg, src_loc: Module.SrcLoc, ty: Type, @@ -19826,7 +19824,7 @@ fn explainWhyTypeIsNotExtern( 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); + try sema.explainWhyTypeIsNotExtern(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", .{}), @@ -19836,13 +19834,87 @@ fn explainWhyTypeIsNotExtern( } else if (position == .param_ty) { return mod.errNoteNonLazy(src_loc, msg, "arrays are not allowed as a parameter type", .{}); } - try sema.explainWhyTypeIsNotExtern(block, src, msg, src_loc, ty.elemType2(), position); + try sema.explainWhyTypeIsNotExtern(msg, src_loc, ty.elemType2(), position); }, - .Vector => try sema.explainWhyTypeIsNotExtern(block, src, msg, src_loc, ty.elemType2(), position), + .Vector => try sema.explainWhyTypeIsNotExtern(msg, src_loc, ty.elemType2(), position), .Optional => try mod.errNoteNonLazy(src_loc, msg, "only pointer like optionals are extern compatible", .{}), } } +/// Returns true if `ty` is allowed in packed types. +/// Does *NOT* require `ty` to be resolved in any way. +fn validatePackedType(ty: Type) bool { + switch (ty.zigTypeTag()) { + .Type, + .ComptimeFloat, + .ComptimeInt, + .EnumLiteral, + .Undefined, + .Null, + .ErrorUnion, + .ErrorSet, + .BoundFn, + .Frame, + .NoReturn, + .Opaque, + .AnyFrame, + .Fn, + .Array, + .Optional, + => return false, + .Void, + .Bool, + .Float, + .Pointer, + .Int, + .Vector, + .Enum, + => return true, + .Struct, .Union => return ty.containerLayout() == .Packed, + } +} + +fn explainWhyTypeIsNotPacked( + sema: *Sema, + msg: *Module.ErrorMsg, + src_loc: Module.SrcLoc, + ty: Type, +) CompileError!void { + const mod = sema.mod; + switch (ty.zigTypeTag()) { + .Void, + .Bool, + .Float, + .Pointer, + .Int, + .Vector, + .Enum, + => return, + .Type, + .ComptimeFloat, + .ComptimeInt, + .EnumLiteral, + .Undefined, + .Null, + .BoundFn, + .Frame, + .NoReturn, + .Opaque, + .ErrorUnion, + .ErrorSet, + .AnyFrame, + .Optional, + .Array, + => try mod.errNoteNonLazy(src_loc, msg, "type has no guaranteed in-memory representation", .{}), + .Fn => { + try mod.errNoteNonLazy(src_loc, msg, "type has no guaranteed in-memory representation", .{}); + try mod.errNoteNonLazy(src_loc, msg, "use '*const ' to make a function pointer type", .{}); + }, + .Struct => try mod.errNoteNonLazy(src_loc, msg, "only packed structs layout are allowed in packed types", .{}), + .Union => try mod.errNoteNonLazy(src_loc, msg, "only packed unions layout are allowed in packed types", .{}), + } +} + pub const PanicId = enum { unreach, unwrap_null, @@ -26919,20 +26991,6 @@ fn semaStructFields(mod: *Module, struct_obj: *Module.Struct) CompileError!void const field = &struct_obj.fields.values()[i]; field.ty = try field_ty.copy(decl_arena_allocator); - if (struct_obj.layout == .Extern and !(try sema.validateExternType(field.ty, .other))) { - const msg = msg: { - const tree = try sema.getAstTree(&block_scope); - const fields_src = enumFieldSrcLoc(decl, tree.*, struct_obj.node_offset, i); - const msg = try sema.errMsg(&block_scope, fields_src, "extern structs cannot contain fields of type '{}'", .{field.ty.fmt(sema.mod)}); - errdefer msg.destroy(sema.gpa); - - try sema.explainWhyTypeIsNotExtern(&block_scope, fields_src, msg, fields_src.toSrcLoc(decl), field.ty, .other); - - try sema.addDeclaredHereNote(msg, field.ty); - break :msg msg; - }; - return sema.failWithOwnedErrorMsg(&block_scope, msg); - } if (field_ty.zigTypeTag() == .Opaque) { const msg = msg: { const tree = try sema.getAstTree(&block_scope); @@ -26945,6 +27003,33 @@ fn semaStructFields(mod: *Module, struct_obj: *Module.Struct) CompileError!void }; return sema.failWithOwnedErrorMsg(&block_scope, msg); } + if (struct_obj.layout == .Extern and !sema.validateExternType(field.ty, .other)) { + const msg = msg: { + const tree = try sema.getAstTree(&block_scope); + const fields_src = enumFieldSrcLoc(decl, tree.*, struct_obj.node_offset, i); + const msg = try sema.errMsg(&block_scope, fields_src, "extern structs cannot contain fields of type '{}'", .{field.ty.fmt(sema.mod)}); + errdefer msg.destroy(sema.gpa); + + try sema.explainWhyTypeIsNotExtern(msg, fields_src.toSrcLoc(decl), field.ty, .other); + + try sema.addDeclaredHereNote(msg, field.ty); + break :msg msg; + }; + return sema.failWithOwnedErrorMsg(&block_scope, msg); + } else if (struct_obj.layout == .Packed and !(validatePackedType(field.ty))) { + const msg = msg: { + const tree = try sema.getAstTree(&block_scope); + const fields_src = enumFieldSrcLoc(decl, tree.*, struct_obj.node_offset, i); + const msg = try sema.errMsg(&block_scope, fields_src, "packed structs cannot contain fields of type '{}'", .{field.ty.fmt(sema.mod)}); + errdefer msg.destroy(sema.gpa); + + try sema.explainWhyTypeIsNotPacked(msg, fields_src.toSrcLoc(decl), field.ty); + + try sema.addDeclaredHereNote(msg, field.ty); + break :msg msg; + }; + return sema.failWithOwnedErrorMsg(&block_scope, msg); + } if (zir_field.align_body_len > 0) { const body = zir.extra[extra_index..][0..zir_field.align_body_len]; @@ -27243,20 +27328,6 @@ fn semaUnionFields(mod: *Module, union_obj: *Module.Union) CompileError!void { } } - if (union_obj.layout == .Extern and !(try sema.validateExternType(field_ty, .union_field))) { - const msg = msg: { - const tree = try sema.getAstTree(&block_scope); - const field_src = enumFieldSrcLoc(decl, tree.*, union_obj.node_offset, field_i); - const msg = try sema.errMsg(&block_scope, field_src, "extern unions cannot contain fields of type '{}'", .{field_ty.fmt(sema.mod)}); - errdefer msg.destroy(sema.gpa); - - try sema.explainWhyTypeIsNotExtern(&block_scope, field_src, msg, field_src.toSrcLoc(decl), field_ty, .union_field); - - try sema.addDeclaredHereNote(msg, field_ty); - break :msg msg; - }; - return sema.failWithOwnedErrorMsg(&block_scope, msg); - } if (field_ty.zigTypeTag() == .Opaque) { const msg = msg: { const tree = try sema.getAstTree(&block_scope); @@ -27269,6 +27340,33 @@ fn semaUnionFields(mod: *Module, union_obj: *Module.Union) CompileError!void { }; return sema.failWithOwnedErrorMsg(&block_scope, msg); } + if (union_obj.layout == .Extern and !sema.validateExternType(field_ty, .union_field)) { + const msg = msg: { + const tree = try sema.getAstTree(&block_scope); + const field_src = enumFieldSrcLoc(decl, tree.*, union_obj.node_offset, field_i); + const msg = try sema.errMsg(&block_scope, field_src, "extern unions cannot contain fields of type '{}'", .{field_ty.fmt(sema.mod)}); + errdefer msg.destroy(sema.gpa); + + try sema.explainWhyTypeIsNotExtern(msg, field_src.toSrcLoc(decl), field_ty, .union_field); + + try sema.addDeclaredHereNote(msg, field_ty); + break :msg msg; + }; + return sema.failWithOwnedErrorMsg(&block_scope, msg); + } else if (union_obj.layout == .Packed and !(validatePackedType(field_ty))) { + const msg = msg: { + const tree = try sema.getAstTree(&block_scope); + const fields_src = enumFieldSrcLoc(decl, tree.*, union_obj.node_offset, field_i); + const msg = try sema.errMsg(&block_scope, fields_src, "packed unions cannot contain fields of type '{}'", .{field_ty.fmt(sema.mod)}); + errdefer msg.destroy(sema.gpa); + + try sema.explainWhyTypeIsNotPacked(msg, fields_src.toSrcLoc(decl), field_ty); + + try sema.addDeclaredHereNote(msg, field_ty); + break :msg msg; + }; + return sema.failWithOwnedErrorMsg(&block_scope, msg); + } gop.value_ptr.* = .{ .ty = try field_ty.copy(decl_arena_allocator), diff --git a/test/behavior/packed-struct.zig b/test/behavior/packed-struct.zig index 2dea485bf5..8c34f5741b 100644 --- a/test/behavior/packed-struct.zig +++ b/test/behavior/packed-struct.zig @@ -6,6 +6,8 @@ const expectEqual = std.testing.expectEqual; const native_endian = builtin.cpu.arch.endian(); test "correct size of packed structs" { + // Stage2 has different packed struct semantics. + if (builtin.zig_backend != .stage1) return error.SkipZigTest; const T1 = packed struct { one: u8, three: [3]u8 }; try expectEqual(4, @sizeOf(T1)); @@ -118,18 +120,6 @@ test "flags in packed structs" { try expectEqual(32, @bitSizeOf(Flags3)); } -test "arrays in packed structs" { - if (builtin.zig_backend == .stage1) return error.SkipZigTest; - - const T1 = packed struct { array: [3][3]u8 }; - const T2 = packed struct { array: [9]u8 }; - - try expectEqual(@sizeOf(u72), @sizeOf(T1)); - try expectEqual(72, @bitSizeOf(T1)); - try expectEqual(@sizeOf(u72), @sizeOf(T2)); - try expectEqual(72, @bitSizeOf(T2)); -} - test "consistent size of packed structs" { if (builtin.zig_backend == .stage1) return error.SkipZigTest; @@ -145,23 +135,15 @@ test "consistent size of packed structs" { try expectEqual(register_size_bits, @bitSizeOf(TxData2)); try expectEqual(register_size_bytes, @sizeOf(TxData2)); - const TxData3 = packed struct { a: u32, b: [3]u8 }; const TxData4 = packed struct { a: u32, b: u24 }; - const TxData5 = packed struct { a: [3]u8, b: u32 }; const TxData6 = packed struct { a: u24, b: u32 }; const expectedBitSize = 56; const expectedByteSize = @sizeOf(u56); - try expectEqual(expectedBitSize, @bitSizeOf(TxData3)); - try expectEqual(expectedByteSize, @sizeOf(TxData3)); - try expectEqual(expectedBitSize, @bitSizeOf(TxData4)); try expectEqual(expectedByteSize, @sizeOf(TxData4)); - try expectEqual(expectedBitSize, @bitSizeOf(TxData5)); - try expectEqual(expectedByteSize, @sizeOf(TxData5)); - try expectEqual(expectedBitSize, @bitSizeOf(TxData6)); try expectEqual(expectedByteSize, @sizeOf(TxData6)); } @@ -234,12 +216,6 @@ test "correct sizeOf and offsets in packed structs" { try expectEqual(@as(u7, 0b1111010), s2.y); try expectEqual(@as(u24, 0xd5c71f), s2.z); } - - const S = packed struct { a: u32, pad: [3]u32, b: u32 }; - - try expectEqual(16, @offsetOf(S, "b")); - try expectEqual(128, @bitOffsetOf(S, "b")); - try expectEqual(@sizeOf(u160), @sizeOf(S)); } test "nested packed structs" { diff --git a/test/behavior/sizeof_and_typeof.zig b/test/behavior/sizeof_and_typeof.zig index 6c7e16b502..83c5d977be 100644 --- a/test/behavior/sizeof_and_typeof.zig +++ b/test/behavior/sizeof_and_typeof.zig @@ -105,6 +105,8 @@ test "@offsetOf" { } test "@offsetOf packed struct, array length not power of 2 or multiple of native pointer width in bytes" { + // Stage2 has different packed struct semantics. + if (builtin.zig_backend != .stage1) return error.SkipZigTest; const p3a_len = 3; const P3 = packed struct { a: [p3a_len]u8, diff --git a/test/behavior/struct.zig b/test/behavior/struct.zig index 8fac5697ec..377cbb56f4 100644 --- a/test/behavior/struct.zig +++ b/test/behavior/struct.zig @@ -704,10 +704,8 @@ const FooArray24Bits = packed struct { }; test "aligned array of packed struct" { - if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + // Stage2 has different packed struct semantics. + if (builtin.zig_backend != .stage1) return error.SkipZigTest; comptime { try expect(@sizeOf(FooStructAligned) == 2); diff --git a/test/cases/compile_errors/packed_struct_with_fields_of_not_allowed_types.zig b/test/cases/compile_errors/packed_struct_with_fields_of_not_allowed_types.zig new file mode 100644 index 0000000000..8fcd300629 --- /dev/null +++ b/test/cases/compile_errors/packed_struct_with_fields_of_not_allowed_types.zig @@ -0,0 +1,84 @@ +export fn entry1() void { + _ = @sizeOf(packed struct { + x: anyerror, + }); +} +export fn entry2() void { + _ = @sizeOf(packed struct { + x: [2]u24, + }); +} +export fn entry3() void { + _ = @sizeOf(packed struct { + x: anyerror!u32, + }); +} +export fn entry4() void { + _ = @sizeOf(packed struct { + x: S, + }); +} +export fn entry5() void { + _ = @sizeOf(packed struct { + x: U, + }); +} +export fn entry6() void { + _ = @sizeOf(packed struct { + x: ?anyerror, + }); +} +export fn entry7() void { + _ = @sizeOf(packed struct { + x: enum { A, B }, + }); +} +export fn entry8() void { + _ = @sizeOf(packed struct { + x: fn () void, + }); +} +export fn entry9() void { + _ = @sizeOf(packed struct { + x: *const fn () void, + }); +} +export fn entry10() void { + _ = @sizeOf(packed struct { + x: packed struct { x: i32 }, + }); +} +export fn entry11() void { + _ = @sizeOf(packed struct { + x: packed union { A: i32, B: u32 }, + }); +} +const S = struct { + x: i32, +}; +const U = extern union { + A: i32, + B: u32, +}; + +// error +// backend=llvm +// target=native +// +// :3:9: error: packed structs cannot contain fields of type 'anyerror' +// :3:9: note: type has no guaranteed in-memory representation +// :8:9: error: packed structs cannot contain fields of type '[2]u24' +// :8:9: note: type has no guaranteed in-memory representation +// :13:9: error: packed structs cannot contain fields of type 'anyerror!u32' +// :13:9: note: type has no guaranteed in-memory representation +// :18:9: error: packed structs cannot contain fields of type 'tmp.S' +// :18:9: note: only packed structs layout are allowed in packed types +// :56:11: note: struct declared here +// :23:9: error: packed structs cannot contain fields of type 'tmp.U' +// :23:9: note: only packed unions layout are allowed in packed types +// :59:18: note: union declared here +// :28:9: error: packed structs cannot contain fields of type '?anyerror' +// :28:9: note: type has no guaranteed in-memory representation +// :38:9: error: packed structs cannot contain fields of type 'fn() void' +// :38:9: note: type has no guaranteed in-memory representation +// :38:9: note: use '*const ' to make a function pointer type diff --git a/test/cases/compile_errors/stage1/test/packed_struct_with_fields_of_not_allowed_types.zig b/test/cases/compile_errors/stage1/test/packed_struct_with_fields_of_not_allowed_types.zig deleted file mode 100644 index 2951d26c52..0000000000 --- a/test/cases/compile_errors/stage1/test/packed_struct_with_fields_of_not_allowed_types.zig +++ /dev/null @@ -1,74 +0,0 @@ -const A = packed struct { - x: anyerror, -}; -const B = packed struct { - x: [2]u24, -}; -const C = packed struct { - x: [1]anyerror, -}; -const D = packed struct { - x: [1]S, -}; -const E = packed struct { - x: [1]U, -}; -const F = packed struct { - x: ?anyerror, -}; -const G = packed struct { - x: Enum, -}; -export fn entry1() void { - var a: A = undefined; - _ = a; -} -export fn entry2() void { - var b: B = undefined; - _ = b; -} -export fn entry3() void { - var r: C = undefined; - _ = r; -} -export fn entry4() void { - var d: D = undefined; - _ = d; -} -export fn entry5() void { - var e: E = undefined; - _ = e; -} -export fn entry6() void { - var f: F = undefined; - _ = f; -} -export fn entry7() void { - var g: G = undefined; - _ = g; -} -const S = struct { - x: i32, -}; -const U = struct { - A: i32, - B: u32, -}; -const Enum = enum { - A, - B, -}; - -// error -// backend=stage1 -// target=native -// is_test=1 -// -// tmp.zig:2:5: error: type 'anyerror' not allowed in packed struct; no guaranteed in-memory representation -// tmp.zig:5:5: error: array of 'u24' not allowed in packed struct due to padding bits (must be padded from 48 to 64 bits) -// tmp.zig:8:5: error: type 'anyerror' not allowed in packed struct; no guaranteed in-memory representation -// tmp.zig:11:5: error: non-packed, non-extern struct 'S' not allowed in packed struct; no guaranteed in-memory representation -// tmp.zig:14:5: error: non-packed, non-extern struct 'U' not allowed in packed struct; no guaranteed in-memory representation -// tmp.zig:17:5: error: type '?anyerror' not allowed in packed struct; no guaranteed in-memory representation -// tmp.zig:20:5: error: type 'Enum' not allowed in packed struct; no guaranteed in-memory representation -// tmp.zig:57:14: note: enum declaration does not specify an integer tag type