authorgravatar for jacobly@ziglang.orgJacob Young <jacobly@ziglang.org> 2026-10-18 17:49:31-04:00
committergravatar for jacobly@ziglang.orgJacob Young <jacobly@ziglang.org> 2026-10-20 15:47:57-04:00
logb2d0675f90cc4e26fdf7a81c1edda8f1b8a00f1f
treef0b2698b7dab4de1f1e50b9fbee01488de0036fb
parentd85c0ba08048d3e729d535a6df7d095fb72c6677

Type: make `c_longdouble` an invalid type without `x87`

Also invert `xclang_flags` loop nesting which will make it easier to translate features to something without `-Xclang` in the future.

8 files changed, 89 insertions(+), 43 deletions(-)

lib/std/Target.zig+9-9
......@@ -3212,7 +3212,7 @@ pub const CType = enum {
32123212 longdouble,
32133213};
32143214
3215/// Returns `null` if no C ABI is defined for this target.
3215/// Returns `null` if no C ABI for `c_type` is defined for this target.
32163216pub fn cTypeByteSize(t: *const Target, c_type: CType) ?u16 {
32173217 return switch (c_type) {
32183218 .char,
......@@ -3238,7 +3238,7 @@ pub fn cTypeByteSize(t: *const Target, c_type: CType) ?u16 {
32383238 };
32393239}
32403240
3241/// Returns `null` if no C ABI is defined for this target.
3241/// Returns `null` if no C ABI for `c_type` is defined for this target.
32423242pub fn cTypeBitSize(target: *const Target, c_type: CType) ?u16 {
32433243 return switch (target.os.tag) {
32443244 .freestanding,
......@@ -3285,7 +3285,7 @@ pub fn cTypeBitSize(target: *const Target, c_type: CType) ?u16 {
32853285
32863286 .x86,
32873287 .x86_64,
3288 => 80,
3288 => if (target.cpu.has(.x86, .x87)) 80 else null,
32893289
32903290 .alpha,
32913291 .riscv32,
......@@ -3345,7 +3345,7 @@ pub fn cTypeBitSize(target: *const Target, c_type: CType) ?u16 {
33453345 .x86_64,
33463346 => switch (target.abi) {
33473347 .android, .ohos => 64,
3348 else => 80,
3348 else => if (target.cpu.has(.x86, .x87)) 80 else null,
33493349 },
33503350
33513351 .powerpc,
......@@ -3409,7 +3409,7 @@ pub fn cTypeBitSize(target: *const Target, c_type: CType) ?u16 {
34093409 .long, .ulong => 32,
34103410 .longlong, .ulonglong, .double => 64,
34113411 .longdouble => switch (target.abi) {
3412 .gnu => 80,
3412 .gnu => if (target.cpu.has(.x86, .x87)) 80 else null,
34133413 else => 64,
34143414 },
34153415 },
......@@ -3420,7 +3420,7 @@ pub fn cTypeBitSize(target: *const Target, c_type: CType) ?u16 {
34203420 .long, .ulong => 32,
34213421 .longlong, .ulonglong, .double => 64,
34223422 .longdouble => switch (target.abi) {
3423 .gnu => 80,
3423 .gnu => if (target.cpu.has(.x86, .x87)) 80 else null,
34243424 else => 64,
34253425 },
34263426 },
......@@ -3454,7 +3454,7 @@ pub fn cTypeBitSize(target: *const Target, c_type: CType) ?u16 {
34543454 },
34553455 .longlong, .ulonglong, .double => 64,
34563456 .longdouble => switch (target.cpu.arch) {
3457 .x86_64 => 80,
3457 .x86_64 => if (target.cpu.has(.x86, .x87)) 80 else null,
34583458 else => 64,
34593459 },
34603460 },
......@@ -3492,14 +3492,14 @@ pub fn cTypeBitSize(target: *const Target, c_type: CType) ?u16 {
34923492 .short, .ushort => 16,
34933493 .int, .uint, .float => 32,
34943494 .long, .ulong, .longlong, .ulonglong, .double => 64,
3495 .longdouble => 80,
3495 .longdouble => if (target.cpu.has(.x86, .x87)) 80 else null,
34963496 },
34973497
34983498 .opengl => null,
34993499 };
35003500}
35013501
3502/// Returns `null` if no C ABI is defined for this target.
3502/// Returns `null` if no C ABI for `c_type` is defined for this target.
35033503pub fn cTypeAlignment(target: *const Target, c_type: CType) ?u16 {
35043504 // Overrides for unusual alignments
35053505 switch (target.cpu.arch) {
lib/std/math/float.zig+5-4
......@@ -12,10 +12,11 @@ pub const long_double = struct {
1212
1313 inline fn bitWidth(x: anytype) u16 {
1414 const T = @TypeOf(x);
15 return switch (T) {
16 f80, f128, c_longdouble => @typeInfo(T).float.bits,
17 else => @compileError("Unsupported type: " ++ @typeName(T) ++ "\nPass a `f80`, `f128`, or `c_longdouble`."),
18 };
15 switch (@typeInfo(T)) {
16 .float => |float| if (float.bits >= 80) return float.bits,
17 else => {},
18 }
19 @compileError("Unsupported type: " ++ @typeName(T) ++ "\nPass a `f80`, `f128`, or `c_longdouble`.");
1920 }
2021
2122 /// Returns the sign + exponent bits of a `long double`.
lib/std/zig/AstGen.zig+16
......@@ -8140,6 +8140,22 @@ fn identifier(
81408140 // if not @"" syntax, just use raw token slice
81418141 if (ident_name_raw[0] != '@') {
81428142 if (primitive_instrs.get(ident_name_raw)) |zir_const_ref| {
8143 const maybe_c_type: ?std.Target.CType = switch (zir_const_ref) {
8144 else => null,
8145 .c_char_type => .char,
8146 .c_short_type => .short,
8147 .c_ushort_type => .ushort,
8148 .c_int_type => .int,
8149 .c_uint_type => .uint,
8150 .c_long_type => .long,
8151 .c_ulong_type => .ulong,
8152 .c_longlong_type => .longlong,
8153 .c_ulonglong_type => .ulonglong,
8154 .c_longdouble_type => .longdouble,
8155 };
8156 if (maybe_c_type) |c_type| {
8157 _ = try gz.addExtendedNodeSmall(.validate_c_type, ident, @backingInt(c_type));
8158 }
81438159 return rvalue(gz, ri, zir_const_ref, ident);
81448160 }
81458161
lib/std/zig/Zir.zig+6
......@@ -2186,6 +2186,11 @@ pub const Inst = struct {
21862186 /// For example: Provided with `error{Foo}!?f64`, returns `f64`.
21872187 /// `operand` is payload index to `UnNode`.
21882188 float_op_result_ty,
2189 /// Validate that the given `CType` is valid for the current target.
2190 /// This can be folded into the `@CType` tag once that is implemented.
2191 /// `operand` is `src_node: Ast.Node.Offset`.
2192 /// `small` is a `std.Target.CType`.
2193 validate_c_type,
21892194
21902195 pub const InstData = struct {
21912196 opcode: Extended,
......@@ -4441,6 +4446,7 @@ fn findTrackableInner(
44414446 .astgen_error,
44424447 .float_op_result_ty,
44434448 .round_op_ty,
4449 .validate_c_type,
44444450 => return,
44454451
44464452 // `@TypeOf` has a body.
src/Compilation.zig+28-27
......@@ -6761,39 +6761,40 @@ pub fn addCCArgs(
67616761 else => &.{"-Xclang"},
67626762 };
67636763
6764 for (xclang_flags) |xclang| {
6765 if (target_util.clangSupportsTargetCpuArg(target)) {
6766 if (target.cpu.model.llvm_name) |llvm_name| {
6764 if (target_util.clangSupportsTargetCpuArg(target)) {
6765 if (target.cpu.model.llvm_name) |llvm_name| {
6766 for (xclang_flags) |xclang| {
67676767 try argv.appendSlice(&[_][]const u8{
67686768 xclang, "-target-cpu", xclang, llvm_name,
67696769 });
67706770 }
67716771 }
6772 }
6773
6774 // It would be really nice if there was a more compact way to communicate this info to Clang.
6775 const all_features_list = target.cpu.arch.allFeaturesList();
6776 try argv.ensureUnusedCapacity(all_features_list.len * xclang_flags.len * 4);
6777 for (all_features_list, 0..) |feature, index_usize| {
6778 const index = @as(std.Target.Cpu.Feature.Set.Index, @intCast(index_usize));
6779 const is_enabled = target.cpu.features.isEnabled(index);
67726780
6773 // It would be really nice if there was a more compact way to communicate this info to Clang.
6774 const all_features_list = target.cpu.arch.allFeaturesList();
6775 try argv.ensureUnusedCapacity(all_features_list.len * 4);
6776 for (all_features_list, 0..) |feature, index_usize| {
6777 const index = @as(std.Target.Cpu.Feature.Set.Index, @intCast(index_usize));
6778 const is_enabled = target.cpu.features.isEnabled(index);
6779
6780 if (feature.llvm_name) |llvm_name| {
6781 // We communicate these to Clang through the dedicated options.
6782 if (std.mem.startsWith(u8, llvm_name, "soft-float") or
6783 std.mem.startsWith(u8, llvm_name, "hard-float") or
6784 (target.cpu.arch.isPowerPC() and std.mem.startsWith(u8, llvm_name, "64bit")) or
6785 (target.cpu.arch.isX86() and std.mem.startsWith(u8, llvm_name, "x32")) or
6786 (target.cpu.arch == .s390x and std.mem.eql(u8, llvm_name, "backchain")))
6787 continue;
6788
6789 // Ignore these until we figure out how to handle the concept of omitting features.
6790 // See https://github.com/ziglang/zig/issues/23539
6791 if (target_util.isDynamicAMDGCNFeature(target, feature)) continue;
6792
6793 argv.appendSliceAssumeCapacity(&[_][]const u8{ xclang, "-target-feature", xclang });
6794 const plus_or_minus = "-+"[@intFromBool(is_enabled)];
6795 const arg = try std.fmt.allocPrint(arena, "{c}{s}", .{ plus_or_minus, llvm_name });
6796 argv.appendAssumeCapacity(arg);
6781 if (feature.llvm_name) |llvm_name| {
6782 // We communicate these to Clang through the dedicated options.
6783 if (std.mem.startsWith(u8, llvm_name, "soft-float") or
6784 std.mem.startsWith(u8, llvm_name, "hard-float") or
6785 (target.cpu.arch.isPowerPC() and std.mem.startsWith(u8, llvm_name, "64bit")) or
6786 (target.cpu.arch.isX86() and std.mem.startsWith(u8, llvm_name, "x32")) or
6787 (target.cpu.arch == .s390x and std.mem.eql(u8, llvm_name, "backchain")))
6788 continue;
6789
6790 // Ignore these until we figure out how to handle the concept of omitting features.
6791 // See https://github.com/ziglang/zig/issues/23539
6792 if (target_util.isDynamicAMDGCNFeature(target, feature)) continue;
6793
6794 const plus_or_minus = "-+"[@intFromBool(is_enabled)];
6795 const arg = try std.fmt.allocPrint(arena, "{c}{s}", .{ plus_or_minus, llvm_name });
6796 for (xclang_flags) |xclang| {
6797 argv.appendSliceAssumeCapacity(&[_][]const u8{ xclang, "-target-feature", xclang, arg });
67976798 }
67986799 }
67996800 }
src/Sema.zig+13
......@@ -1469,6 +1469,11 @@ fn analyzeBodyInner(
14691469 },
14701470 .astgen_error => return sema.failTransitive(.astgen_error),
14711471 .float_op_result_ty => try sema.zirFloatOpResultType(block, extended),
1472 .validate_c_type => {
1473 try sema.zirValidateCType(block, extended);
1474 i += 1;
1475 continue;
1476 },
14721477 };
14731478 },
14741479
......@@ -4315,6 +4320,14 @@ fn zirTryOperandTy(sema: *Sema, block: *Block, inst: Zir.Inst.Index, is_ref: boo
43154320 }
43164321}
43174322
4323fn zirValidateCType(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!void {
4324 const c_type: std.Target.CType = @fromBackingInt(@intCast(extended.small));
4325 if (sema.pt.zcu.getTarget().cTypeBitSize(c_type)) |_| return;
4326 const src_node: std.zig.Ast.Node.Offset = @fromBackingInt(@bitCast(extended.operand));
4327 const src = block.nodeOffset(src_node);
4328 return sema.fail(block, src, "'c_{t}' is not valid on this target", .{c_type});
4329}
4330
43184331fn zirValidateRefTy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void {
43194332 const pt = sema.pt;
43204333 const zcu = pt.zcu;
src/Type.zig+4-1
......@@ -3178,7 +3178,10 @@ pub fn validateExtern(ty: Type, position: ExternPosition, zcu: *const Zcu) bool
31783178 },
31793179 .float => switch (ty.floatBits(zcu.getTarget())) {
31803180 else => true,
3181 80 => |bits| std.zig.target.compilerRtFloatAbi(zcu.getTarget(), bits) == .hard,
3181 80 => |bits| switch (std.zig.target.compilerRtFloatAbi(zcu.getTarget(), bits)) {
3182 .soft => false,
3183 .hard => true,
3184 },
31823185 },
31833186 .@"fn" => {
31843187 if (position != .other) return false;
src/print_zir.zig+8-2
......@@ -712,8 +712,14 @@ const Writer = struct {
712712 .std_lang_value => try self.writeStdLangValue(stream, extended),
713713 .inplace_arith_result_ty => try self.writeInplaceArithResultTy(stream, extended),
714714
715 .dbg_empty_stmt => try stream.writeAll("))"),
716 .astgen_error => try stream.writeAll("))"),
715 .dbg_empty_stmt, .astgen_error => try stream.writeAll("))"),
716
717 .validate_c_type => {
718 const c_type: std.Target.CType = @fromBackingInt(@intCast(extended.small));
719 const src_node: Ast.Node.Offset = @fromBackingInt(@bitCast(extended.operand));
720 try stream.print("{t})) ", .{c_type});
721 try self.writeSrcNode(stream, src_node);
722 },
717723 }
718724 }
719725