Commit de8cece6e7
Changed files (35)
lib
compiler
aro
aro
Attribute
Builtins
Diagnostics
Driver
toolchains
backend
resinator
lib/compiler/aro/aro/Attribute/names.zig
@@ -69,6 +69,7 @@ pub const longest_name = 30;
/// If found, returns the index of the node within the `dafsa` array.
/// Otherwise, returns `null`.
pub fn findInList(first_child_index: u16, char: u8) ?u16 {
+ @setEvalBranchQuota(206);
var index = first_child_index;
while (true) {
if (dafsa[index].char == char) return index;
@@ -787,7 +788,7 @@ const dafsa = [_]Node{
.{ .char = 'i', .end_of_word = false, .end_of_list = true, .number = 1, .child_index = 215 },
};
pub const data = blk: {
- @setEvalBranchQuota(103);
+ @setEvalBranchQuota(721);
break :blk [_]@This(){
// access
.{ .tag = @enumFromInt(0), .properties = .{ .tag = .access, .gnu = true } },
lib/compiler/aro/aro/Builtins/Builtin.zig
@@ -71,6 +71,7 @@ pub const longest_name = 43;
/// If found, returns the index of the node within the `dafsa` array.
/// Otherwise, returns `null`.
pub fn findInList(first_child_index: u16, char: u8) ?u16 {
+ @setEvalBranchQuota(7972);
var index = first_child_index;
while (true) {
if (dafsa[index].char == char) return index;
@@ -5165,7 +5166,7 @@ const dafsa = [_]Node{
.{ .char = 'e', .end_of_word = false, .end_of_list = true, .number = 1, .child_index = 4913 },
};
pub const data = blk: {
- @setEvalBranchQuota(30_000);
+ @setEvalBranchQuota(27902);
break :blk [_]@This(){
// _Block_object_assign
.{ .tag = @enumFromInt(0), .properties = .{ .param_str = "vv*vC*iC", .header = .blocks, .attributes = .{ .lib_function_without_prefix = true } } },
lib/compiler/aro/aro/Builtins/eval.zig
@@ -0,0 +1,86 @@
+const std = @import("std");
+const backend = @import("../../backend.zig");
+const Interner = backend.Interner;
+const Builtins = @import("../Builtins.zig");
+const Builtin = Builtins.Builtin;
+const Parser = @import("../Parser.zig");
+const Tree = @import("../Tree.zig");
+const NodeIndex = Tree.NodeIndex;
+const Type = @import("../Type.zig");
+const Value = @import("../Value.zig");
+
+fn makeNan(comptime T: type, str: []const u8) T {
+ const UnsignedSameSize = std.meta.Int(.unsigned, @bitSizeOf(T));
+ const parsed = std.fmt.parseUnsigned(UnsignedSameSize, str[0 .. str.len - 1], 0) catch 0;
+ const bits: switch (T) {
+ f32 => u23,
+ f64 => u52,
+ f80 => u63,
+ f128 => u112,
+ else => @compileError("Invalid type for makeNan"),
+ } = @truncate(parsed);
+ return @bitCast(@as(UnsignedSameSize, bits) | @as(UnsignedSameSize, @bitCast(std.math.nan(T))));
+}
+
+pub fn eval(tag: Builtin.Tag, p: *Parser, args: []const NodeIndex) !Value {
+ const builtin = Builtin.fromTag(tag);
+ if (!builtin.properties.attributes.const_evaluable) return .{};
+
+ switch (tag) {
+ Builtin.tagFromName("__builtin_inff").?,
+ Builtin.tagFromName("__builtin_inf").?,
+ Builtin.tagFromName("__builtin_infl").?,
+ => {
+ const ty: Type = switch (tag) {
+ Builtin.tagFromName("__builtin_inff").? => .{ .specifier = .float },
+ Builtin.tagFromName("__builtin_inf").? => .{ .specifier = .double },
+ Builtin.tagFromName("__builtin_infl").? => .{ .specifier = .long_double },
+ else => unreachable,
+ };
+ const f: Interner.Key.Float = switch (ty.bitSizeof(p.comp).?) {
+ 32 => .{ .f32 = std.math.inf(f32) },
+ 64 => .{ .f64 = std.math.inf(f64) },
+ 80 => .{ .f80 = std.math.inf(f80) },
+ 128 => .{ .f128 = std.math.inf(f128) },
+ else => unreachable,
+ };
+ return Value.intern(p.comp, .{ .float = f });
+ },
+ Builtin.tagFromName("__builtin_isinf").? => blk: {
+ if (args.len == 0) break :blk;
+ const val = p.value_map.get(args[0]) orelse break :blk;
+ return Value.fromBool(val.isInf(p.comp));
+ },
+ Builtin.tagFromName("__builtin_isinf_sign").? => blk: {
+ if (args.len == 0) break :blk;
+ const val = p.value_map.get(args[0]) orelse break :blk;
+ switch (val.isInfSign(p.comp)) {
+ .unknown => {},
+ .finite => return Value.zero,
+ .positive => return Value.one,
+ .negative => return Value.int(@as(i64, -1), p.comp),
+ }
+ },
+ Builtin.tagFromName("__builtin_isnan").? => blk: {
+ if (args.len == 0) break :blk;
+ const val = p.value_map.get(args[0]) orelse break :blk;
+ return Value.fromBool(val.isNan(p.comp));
+ },
+ Builtin.tagFromName("__builtin_nan").? => blk: {
+ if (args.len == 0) break :blk;
+ const val = p.getDecayedStringLiteral(args[0]) orelse break :blk;
+ const bytes = p.comp.interner.get(val.ref()).bytes;
+
+ const f: Interner.Key.Float = switch ((Type{ .specifier = .double }).bitSizeof(p.comp).?) {
+ 32 => .{ .f32 = makeNan(f32, bytes) },
+ 64 => .{ .f64 = makeNan(f64, bytes) },
+ 80 => .{ .f80 = makeNan(f80, bytes) },
+ 128 => .{ .f128 = makeNan(f128, bytes) },
+ else => unreachable,
+ };
+ return Value.intern(p.comp, .{ .float = f });
+ },
+ else => {},
+ }
+ return .{};
+}
lib/compiler/aro/aro/Diagnostics/messages.zig
@@ -107,6 +107,9 @@ pub const Tag = enum {
multiple_default,
previous_case,
expected_arguments,
+ callee_with_static_array,
+ array_argument_too_small,
+ non_null_argument,
expected_arguments_old,
expected_at_least_arguments,
invalid_static_star,
@@ -214,6 +217,7 @@ pub const Tag = enum {
pre_c23_compat,
unbound_vla,
array_too_large,
+ record_too_large,
incompatible_ptr_init,
incompatible_ptr_init_sign,
incompatible_ptr_assign,
@@ -349,6 +353,8 @@ pub const Tag = enum {
non_standard_escape_char,
invalid_pp_stringify_escape,
vla,
+ int_value_changed,
+ sign_conversion,
float_overflow_conversion,
float_out_of_range,
float_zero_conversion,
@@ -425,7 +431,8 @@ pub const Tag = enum {
bit_int,
unsigned_bit_int_too_small,
signed_bit_int_too_small,
- bit_int_too_big,
+ unsigned_bit_int_too_big,
+ signed_bit_int_too_big,
keyword_macro,
ptr_arithmetic_incomplete,
callconv_not_supported,
@@ -509,6 +516,9 @@ pub const Tag = enum {
complex_conj,
overflow_builtin_requires_int,
overflow_result_requires_ptr,
+ attribute_todo,
+ invalid_type_underlying_enum,
+ auto_type_self_initialized,
pub fn property(tag: Tag) Properties {
return named_data[@intFromEnum(tag)];
@@ -613,6 +623,9 @@ pub const Tag = enum {
.{ .msg = "multiple default cases in the same switch", .kind = .@"error" },
.{ .msg = "previous case defined here", .kind = .note },
.{ .msg = expected_arguments, .extra = .arguments, .kind = .@"error" },
+ .{ .msg = "callee declares array parameter as static here", .kind = .note },
+ .{ .msg = "array argument is too small; contains {d} elements, callee requires at least {d}", .extra = .arguments, .kind = .warning, .opt = W("array-bounds") },
+ .{ .msg = "null passed to a callee that requires a non-null argument", .kind = .warning, .opt = W("nonnull") },
.{ .msg = expected_arguments, .extra = .arguments, .kind = .warning },
.{ .msg = "expected at least {d} argument(s) got {d}", .extra = .arguments, .kind = .warning },
.{ .msg = "'static' may not be used with an unspecified variable length array size", .kind = .@"error" },
@@ -720,6 +733,7 @@ pub const Tag = enum {
.{ .msg = "{s} is incompatible with C standards before C23", .extra = .str, .kind = .off, .suppress_unless_version = .c23, .opt = W("pre-c23-compat") },
.{ .msg = "variable length array must be bound in function definition", .kind = .@"error" },
.{ .msg = "array is too large", .kind = .@"error" },
+ .{ .msg = "type '{s}' is too large", .kind = .@"error", .extra = .str },
.{ .msg = "incompatible pointer types initializing {s}", .extra = .str, .opt = W("incompatible-pointer-types"), .kind = .warning },
.{ .msg = "incompatible pointer types initializing {s}" ++ pointer_sign_message, .extra = .str, .opt = W("pointer-sign"), .kind = .warning },
.{ .msg = "incompatible pointer types assigning to {s}", .extra = .str, .opt = W("incompatible-pointer-types"), .kind = .warning },
@@ -855,6 +869,8 @@ pub const Tag = enum {
.{ .msg = "use of non-standard escape character '\\{s}'", .kind = .off, .opt = W("pedantic"), .extra = .invalid_escape },
.{ .msg = "invalid string literal, ignoring final '\\'", .kind = .warning },
.{ .msg = "variable length array used", .kind = .off, .opt = W("vla") },
+ .{ .msg = "implicit conversion from {s}", .extra = .str, .kind = .warning, .opt = W("constant-conversion") },
+ .{ .msg = "implicit conversion changes signedness: {s}", .extra = .str, .kind = .off, .opt = W("sign-conversion") },
.{ .msg = "implicit conversion of non-finite value from {s} is undefined", .extra = .str, .kind = .off, .opt = W("float-overflow-conversion") },
.{ .msg = "implicit conversion of out of range value from {s} is undefined", .extra = .str, .kind = .warning, .opt = W("literal-conversion") },
.{ .msg = "implicit conversion from {s}", .extra = .str, .kind = .off, .opt = W("float-zero-conversion") },
@@ -929,9 +945,10 @@ pub const Tag = enum {
.{ .msg = "this declarator", .kind = .note },
.{ .msg = "{s} is not supported on this target", .extra = .str, .kind = .@"error" },
.{ .msg = "'_BitInt' in C17 and earlier is a Clang extension'", .kind = .off, .pedantic = true, .opt = W("bit-int-extension"), .suppress_version = .c23 },
- .{ .msg = "{s} must have a bit size of at least 1", .extra = .str, .kind = .@"error" },
- .{ .msg = "{s} must have a bit size of at least 2", .extra = .str, .kind = .@"error" },
- .{ .msg = "{s} of bit sizes greater than " ++ std.fmt.comptimePrint("{d}", .{Properties.max_bits}) ++ " not supported", .extra = .str, .kind = .@"error" },
+ .{ .msg = "{s}unsigned _BitInt must have a bit size of at least 1", .extra = .str, .kind = .@"error" },
+ .{ .msg = "{s}signed _BitInt must have a bit size of at least 2", .extra = .str, .kind = .@"error" },
+ .{ .msg = "{s}unsigned _BitInt of bit sizes greater than " ++ std.fmt.comptimePrint("{d}", .{Properties.max_bits}) ++ " not supported", .extra = .str, .kind = .@"error" },
+ .{ .msg = "{s}signed _BitInt of bit sizes greater than " ++ std.fmt.comptimePrint("{d}", .{Properties.max_bits}) ++ " not supported", .extra = .str, .kind = .@"error" },
.{ .msg = "keyword is hidden by macro definition", .kind = .off, .pedantic = true, .opt = W("keyword-macro") },
.{ .msg = "arithmetic on a pointer to an incomplete type '{s}'", .extra = .str, .kind = .@"error" },
.{ .msg = "'{s}' calling convention is not supported for this target", .extra = .str, .opt = W("ignored-attributes"), .kind = .warning },
@@ -1015,6 +1032,9 @@ pub const Tag = enum {
.{ .msg = "ISO C does not support '~' for complex conjugation of '{s}'", .opt = W("pedantic"), .extra = .str, .kind = .off },
.{ .msg = "operand argument to overflow builtin must be an integer ('{s}' invalid)", .extra = .str, .kind = .@"error" },
.{ .msg = "result argument to overflow builtin must be a pointer to a non-const integer ('{s}' invalid)", .extra = .str, .kind = .@"error" },
+ .{ .msg = "TODO: implement '{s}' attribute for {s}", .extra = .attribute_todo, .kind = .@"error" },
+ .{ .msg = "non-integral type '{s}' is an invalid underlying type", .extra = .str, .kind = .@"error" },
+ .{ .msg = "variable '{s}' declared with deduced type '__auto_type' cannot appear in its own initializer", .extra = .str, .kind = .@"error" },
};
};
};
lib/compiler/aro/aro/Driver/Filesystem.zig
@@ -56,7 +56,7 @@ fn existsFake(entries: []const Filesystem.Entry, path: []const u8) bool {
}
fn canExecutePosix(path: []const u8) bool {
- std.os.access(path, std.os.X_OK) catch return false;
+ std.posix.access(path, std.posix.X_OK) catch return false;
// Todo: ensure path is not a directory
return true;
}
@@ -173,7 +173,7 @@ pub const Filesystem = union(enum) {
pub fn exists(fs: Filesystem, path: []const u8) bool {
switch (fs) {
.real => {
- std.os.access(path, std.os.F_OK) catch return false;
+ std.fs.cwd().access(path, .{}) catch return false;
return true;
},
.fake => |paths| return existsFake(paths, path),
lib/compiler/aro/aro/toolchains/Linux.zig
@@ -423,7 +423,7 @@ test Linux {
defer arena_instance.deinit();
const arena = arena_instance.allocator();
- var comp = Compilation.init(std.testing.allocator);
+ var comp = Compilation.init(std.testing.allocator, std.fs.cwd());
defer comp.deinit();
comp.environment = .{
.path = "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
lib/compiler/aro/aro/Tree/number_affixes.zig
@@ -74,8 +74,8 @@ pub const Suffix = enum {
// float and imaginary float
F, IF,
- // _Float16
- F16,
+ // _Float16 and imaginary _Float16
+ F16, IF16,
// __float80
W,
@@ -129,6 +129,7 @@ pub const Suffix = enum {
.{ .I, &.{"I"} },
.{ .IL, &.{ "I", "L" } },
+ .{ .IF16, &.{ "I", "F16" } },
.{ .IF, &.{ "I", "F" } },
.{ .IW, &.{ "I", "W" } },
.{ .IF128, &.{ "I", "F128" } },
@@ -161,7 +162,7 @@ pub const Suffix = enum {
pub fn isImaginary(suffix: Suffix) bool {
return switch (suffix) {
- .I, .IL, .IF, .IU, .IUL, .ILL, .IULL, .IWB, .IUWB, .IF128, .IQ, .IW => true,
+ .I, .IL, .IF, .IU, .IUL, .ILL, .IULL, .IWB, .IUWB, .IF128, .IQ, .IW, .IF16 => true,
.None, .L, .F16, .F, .U, .UL, .LL, .ULL, .WB, .UWB, .F128, .Q, .W => false,
};
}
@@ -170,7 +171,7 @@ pub const Suffix = enum {
return switch (suffix) {
.None, .L, .LL, .I, .IL, .ILL, .WB, .IWB => true,
.U, .UL, .ULL, .IU, .IUL, .IULL, .UWB, .IUWB => false,
- .F, .IF, .F16, .F128, .IF128, .Q, .IQ, .W, .IW => unreachable,
+ .F, .IF, .F16, .F128, .IF128, .Q, .IQ, .W, .IW, .IF16 => unreachable,
};
}
@@ -184,4 +185,8 @@ pub const Suffix = enum {
else => false,
};
}
+
+ pub fn isFloat80(suffix: Suffix) bool {
+ return suffix == .W or suffix == .IW;
+ }
};
lib/compiler/aro/aro/annex_g.zig
@@ -0,0 +1,118 @@
+//! Complex arithmetic algorithms from C99 Annex G
+
+const std = @import("std");
+const copysign = std.math.copysign;
+const ilogb = std.math.ilogb;
+const inf = std.math.inf;
+const isFinite = std.math.isFinite;
+const isInf = std.math.isInf;
+const isNan = std.math.isNan;
+const isPositiveZero = std.math.isPositiveZero;
+const scalbn = std.math.scalbn;
+
+/// computes floating point z*w where a_param, b_param are real, imaginary parts of z and c_param, d_param are real, imaginary parts of w
+pub fn complexFloatMul(comptime T: type, a_param: T, b_param: T, c_param: T, d_param: T) [2]T {
+ var a = a_param;
+ var b = b_param;
+ var c = c_param;
+ var d = d_param;
+
+ const ac = a * c;
+ const bd = b * d;
+ const ad = a * d;
+ const bc = b * c;
+ var x = ac - bd;
+ var y = ad + bc;
+ if (isNan(x) and isNan(y)) {
+ var recalc = false;
+ if (isInf(a) or isInf(b)) {
+ // lhs infinite
+ // Box the infinity and change NaNs in the other factor to 0
+ a = copysign(if (isInf(a)) @as(T, 1.0) else @as(T, 0.0), a);
+ b = copysign(if (isInf(b)) @as(T, 1.0) else @as(T, 0.0), b);
+ if (isNan(c)) c = copysign(@as(T, 0.0), c);
+ if (isNan(d)) d = copysign(@as(T, 0.0), d);
+ recalc = true;
+ }
+ if (isInf(c) or isInf(d)) {
+ // rhs infinite
+ // Box the infinity and change NaNs in the other factor to 0
+ c = copysign(if (isInf(c)) @as(T, 1.0) else @as(T, 0.0), c);
+ d = copysign(if (isInf(d)) @as(T, 1.0) else @as(T, 0.0), d);
+ if (isNan(a)) a = copysign(@as(T, 0.0), a);
+ if (isNan(b)) b = copysign(@as(T, 0.0), b);
+ recalc = true;
+ }
+ if (!recalc and (isInf(ac) or isInf(bd) or isInf(ad) or isInf(bc))) {
+ // Recover infinities from overflow by changing NaN's to 0
+ if (isNan(a)) a = copysign(@as(T, 0.0), a);
+ if (isNan(b)) b = copysign(@as(T, 0.0), b);
+ if (isNan(c)) c = copysign(@as(T, 0.0), c);
+ if (isNan(d)) d = copysign(@as(T, 0.0), d);
+ }
+ if (recalc) {
+ x = inf(T) * (a * c - b * d);
+ y = inf(T) * (a * d + b * c);
+ }
+ }
+ return .{ x, y };
+}
+
+/// computes floating point z / w where a_param, b_param are real, imaginary parts of z and c_param, d_param are real, imaginary parts of w
+pub fn complexFloatDiv(comptime T: type, a_param: T, b_param: T, c_param: T, d_param: T) [2]T {
+ var a = a_param;
+ var b = b_param;
+ var c = c_param;
+ var d = d_param;
+ var denom_logb: i32 = 0;
+ const max_cd = @max(@abs(c), @abs(d));
+ if (isFinite(max_cd)) {
+ if (max_cd == 0) {
+ denom_logb = std.math.minInt(i32) + 1;
+ c = 0;
+ d = 0;
+ } else {
+ denom_logb = ilogb(max_cd);
+ c = scalbn(c, -denom_logb);
+ d = scalbn(d, -denom_logb);
+ }
+ }
+ const denom = c * c + d * d;
+ var x = scalbn((a * c + b * d) / denom, -denom_logb);
+ var y = scalbn((b * c - a * d) / denom, -denom_logb);
+ if (isNan(x) and isNan(y)) {
+ if (isPositiveZero(denom) and (!isNan(a) or !isNan(b))) {
+ x = copysign(inf(T), c) * a;
+ y = copysign(inf(T), c) * b;
+ } else if ((isInf(a) or isInf(b)) and isFinite(c) and isFinite(d)) {
+ a = copysign(if (isInf(a)) @as(T, 1.0) else @as(T, 0.0), a);
+ b = copysign(if (isInf(b)) @as(T, 1.0) else @as(T, 0.0), b);
+ x = inf(T) * (a * c + b * d);
+ y = inf(T) * (b * c - a * d);
+ } else if (isInf(max_cd) and isFinite(a) and isFinite(b)) {
+ c = copysign(if (isInf(c)) @as(T, 1.0) else @as(T, 0.0), c);
+ d = copysign(if (isInf(d)) @as(T, 1.0) else @as(T, 0.0), d);
+ x = 0.0 * (a * c + b * d);
+ y = 0.0 * (b * c - a * d);
+ }
+ }
+ return .{ x, y };
+}
+
+test complexFloatMul {
+ // Naive algorithm would produce NaN + NaNi instead of inf + NaNi
+ const result = complexFloatMul(f64, inf(f64), std.math.nan(f64), 2, 0);
+ try std.testing.expect(isInf(result[0]));
+ try std.testing.expect(isNan(result[1]));
+}
+
+test complexFloatDiv {
+ // Naive algorithm would produce NaN + NaNi instead of inf + NaNi
+ var result = complexFloatDiv(f64, inf(f64), std.math.nan(f64), 2, 0);
+ try std.testing.expect(isInf(result[0]));
+ try std.testing.expect(isNan(result[1]));
+
+ result = complexFloatDiv(f64, 2.0, 2.0, 0.0, 0.0);
+ try std.testing.expect(isInf(result[0]));
+ try std.testing.expect(isInf(result[1]));
+}
lib/compiler/aro/aro/Attribute.zig
@@ -38,12 +38,64 @@ pub const Kind = enum {
}
};
+pub const Iterator = struct {
+ source: union(enum) {
+ ty: Type,
+ slice: []const Attribute,
+ },
+ index: usize,
+
+ pub fn initSlice(slice: ?[]const Attribute) Iterator {
+ return .{ .source = .{ .slice = slice orelse &.{} }, .index = 0 };
+ }
+
+ pub fn initType(ty: Type) Iterator {
+ return .{ .source = .{ .ty = ty }, .index = 0 };
+ }
+
+ /// returns the next attribute as well as its index within the slice or current type
+ /// The index can be used to determine when a nested type has been recursed into
+ pub fn next(self: *Iterator) ?struct { Attribute, usize } {
+ switch (self.source) {
+ .slice => |slice| {
+ if (self.index < slice.len) {
+ defer self.index += 1;
+ return .{ slice[self.index], self.index };
+ }
+ },
+ .ty => |ty| {
+ switch (ty.specifier) {
+ .typeof_type => {
+ self.* = .{ .source = .{ .ty = ty.data.sub_type.* }, .index = 0 };
+ return self.next();
+ },
+ .typeof_expr => {
+ self.* = .{ .source = .{ .ty = ty.data.expr.ty }, .index = 0 };
+ return self.next();
+ },
+ .attributed => {
+ if (self.index < ty.data.attributed.attributes.len) {
+ defer self.index += 1;
+ return .{ ty.data.attributed.attributes[self.index], self.index };
+ }
+ self.* = .{ .source = .{ .ty = ty.data.attributed.base }, .index = 0 };
+ return self.next();
+ },
+ else => {},
+ }
+ },
+ }
+ return null;
+ }
+};
+
pub const ArgumentType = enum {
string,
identifier,
int,
alignment,
float,
+ complex_float,
expression,
nullptr_t,
@@ -54,6 +106,7 @@ pub const ArgumentType = enum {
.int, .alignment => "an integer constant",
.nullptr_t => "nullptr",
.float => "a floating point number",
+ .complex_float => "a complex floating point number",
.expression => "an expression",
};
}
@@ -65,7 +118,7 @@ pub fn requiredArgCount(attr: Tag) u32 {
inline else => |tag| {
comptime var needed = 0;
comptime {
- const fields = std.meta.fields(@field(attributes, @tagName(tag)));
+ const fields = @typeInfo(@field(attributes, @tagName(tag))).@"struct".fields;
for (fields) |arg_field| {
if (!mem.eql(u8, arg_field.name, "__name_tok") and @typeInfo(arg_field.type) != .optional) needed += 1;
}
@@ -81,7 +134,7 @@ pub fn maxArgCount(attr: Tag) u32 {
inline else => |tag| {
comptime var max = 0;
comptime {
- const fields = std.meta.fields(@field(attributes, @tagName(tag)));
+ const fields = @typeInfo(@field(attributes, @tagName(tag))).@"struct".fields;
for (fields) |arg_field| {
if (!mem.eql(u8, arg_field.name, "__name_tok")) max += 1;
}
@@ -106,7 +159,7 @@ pub const Formatting = struct {
switch (attr) {
.calling_convention => unreachable,
inline else => |tag| {
- const fields = std.meta.fields(@field(attributes, @tagName(tag)));
+ const fields = @typeInfo(@field(attributes, @tagName(tag))).@"struct".fields;
if (fields.len == 0) unreachable;
const Unwrapped = UnwrapOptional(fields[0].type);
@@ -123,14 +176,13 @@ pub const Formatting = struct {
switch (attr) {
.calling_convention => unreachable,
inline else => |tag| {
- const fields = std.meta.fields(@field(attributes, @tagName(tag)));
+ const fields = @typeInfo(@field(attributes, @tagName(tag))).@"struct".fields;
if (fields.len == 0) unreachable;
const Unwrapped = UnwrapOptional(fields[0].type);
if (@typeInfo(Unwrapped) != .@"enum") unreachable;
const enum_fields = @typeInfo(Unwrapped).@"enum".fields;
- @setEvalBranchQuota(3000);
const quote = comptime quoteChar(@enumFromInt(@intFromEnum(tag)));
comptime var values: []const u8 = quote ++ enum_fields[0].name ++ quote;
inline for (enum_fields[1..]) |enum_field| {
@@ -148,7 +200,7 @@ pub fn wantsIdentEnum(attr: Tag) bool {
switch (attr) {
.calling_convention => return false,
inline else => |tag| {
- const fields = std.meta.fields(@field(attributes, @tagName(tag)));
+ const fields = @typeInfo(@field(attributes, @tagName(tag))).@"struct".fields;
if (fields.len == 0) return false;
const Unwrapped = UnwrapOptional(fields[0].type);
@@ -162,7 +214,7 @@ pub fn wantsIdentEnum(attr: Tag) bool {
pub fn diagnoseIdent(attr: Tag, arguments: *Arguments, ident: []const u8) ?Diagnostics.Message {
switch (attr) {
inline else => |tag| {
- const fields = std.meta.fields(@field(attributes, @tagName(tag)));
+ const fields = @typeInfo(@field(attributes, @tagName(tag))).@"struct".fields;
if (fields.len == 0) unreachable;
const Unwrapped = UnwrapOptional(fields[0].type);
if (@typeInfo(Unwrapped) != .@"enum") unreachable;
@@ -181,7 +233,7 @@ pub fn diagnoseIdent(attr: Tag, arguments: *Arguments, ident: []const u8) ?Diagn
pub fn wantsAlignment(attr: Tag, idx: usize) bool {
switch (attr) {
inline else => |tag| {
- const fields = std.meta.fields(@field(attributes, @tagName(tag)));
+ const fields = @typeInfo(@field(attributes, @tagName(tag))).@"struct".fields;
if (fields.len == 0) return false;
return switch (idx) {
@@ -195,7 +247,7 @@ pub fn wantsAlignment(attr: Tag, idx: usize) bool {
pub fn diagnoseAlignment(attr: Tag, arguments: *Arguments, arg_idx: u32, res: Parser.Result, p: *Parser) !?Diagnostics.Message {
switch (attr) {
inline else => |tag| {
- const arg_fields = std.meta.fields(@field(attributes, @tagName(tag)));
+ const arg_fields = @typeInfo(@field(attributes, @tagName(tag))).@"struct".fields;
if (arg_fields.len == 0) unreachable;
switch (arg_idx) {
@@ -249,8 +301,7 @@ fn diagnoseField(
},
.bytes => |bytes| {
if (Wanted == Value) {
- std.debug.assert(node.tag == .string_literal_expr);
- if (!node.ty.elemType().is(.char) and !node.ty.elemType().is(.uchar)) {
+ if (node.tag != .string_literal_expr or (!node.ty.elemType().is(.char) and !node.ty.elemType().is(.uchar))) {
return .{
.tag = .attribute_requires_string,
.extra = .{ .str = decl.name },
@@ -264,7 +315,6 @@ fn diagnoseField(
@field(@field(arguments, decl.name), field.name) = enum_val;
return null;
} else {
- @setEvalBranchQuota(3000);
return .{
.tag = .unknown_attr_enum,
.extra = .{ .attr_enum = .{ .tag = std.meta.stringToEnum(Tag, decl.name).? } },
@@ -278,8 +328,19 @@ fn diagnoseField(
.int => .int,
.bytes => .string,
.float => .float,
+ .complex => .complex_float,
.null => .nullptr_t,
- else => unreachable,
+ .int_ty,
+ .float_ty,
+ .complex_ty,
+ .ptr_ty,
+ .noreturn_ty,
+ .void_ty,
+ .func_ty,
+ .array_ty,
+ .vector_ty,
+ .record_ty,
+ => unreachable,
});
}
@@ -309,7 +370,7 @@ pub fn diagnose(attr: Tag, arguments: *Arguments, arg_idx: u32, res: Parser.Resu
.tag = .attribute_too_many_args,
.extra = .{ .attr_arg_count = .{ .attribute = attr, .expected = max_arg_count } },
};
- const arg_fields = std.meta.fields(@field(attributes, decl.name));
+ const arg_fields = @typeInfo(@field(attributes, decl.name)).@"struct".fields;
switch (arg_idx) {
inline 0...arg_fields.len - 1 => |arg_i| {
return diagnoseField(decl, arg_fields[arg_i], UnwrapOptional(arg_fields[arg_i].type), arguments, res, node, p);
@@ -645,7 +706,7 @@ pub const Arguments = blk: {
var union_fields: [decls.len]ZigType.UnionField = undefined;
for (decls, &union_fields) |decl, *field| {
field.* = .{
- .name = decl.name ++ "",
+ .name = decl.name,
.type = @field(attributes, decl.name),
.alignment = 0,
};
@@ -730,7 +791,6 @@ pub fn applyVariableAttributes(p: *Parser, ty: Type, attr_buf_start: usize, tag:
const toks = p.attr_buf.items(.tok)[attr_buf_start..];
p.attr_application_buf.items.len = 0;
var base_ty = ty;
- if (base_ty.specifier == .attributed) base_ty = base_ty.data.attributed.base;
var common = false;
var nocommon = false;
for (attrs, toks) |attr, tok| switch (attr.tag) {
@@ -772,15 +832,10 @@ pub fn applyVariableAttributes(p: *Parser, ty: Type, attr_buf_start: usize, tag:
.copy,
.tls_model,
.visibility,
- => std.debug.panic("apply variable attribute {s}", .{@tagName(attr.tag)}),
+ => |t| try p.errExtra(.attribute_todo, tok, .{ .attribute_todo = .{ .tag = t, .kind = .variables } }),
else => try ignoredAttrErr(p, tok, attr.tag, "variables"),
};
- const existing = ty.getAttributes();
- if (existing.len == 0 and p.attr_application_buf.items.len == 0) return base_ty;
- if (existing.len == 0) return base_ty.withAttributes(p.arena, p.attr_application_buf.items);
-
- const attributed_type = try Type.Attributed.create(p.arena, base_ty, existing, p.attr_application_buf.items);
- return Type{ .specifier = .attributed, .data = .{ .attributed = attributed_type } };
+ return base_ty.withAttributes(p.arena, p.attr_application_buf.items);
}
pub fn applyFieldAttributes(p: *Parser, field_ty: *Type, attr_buf_start: usize) ![]const Attribute {
@@ -789,7 +844,7 @@ pub fn applyFieldAttributes(p: *Parser, field_ty: *Type, attr_buf_start: usize)
p.attr_application_buf.items.len = 0;
for (attrs, toks) |attr, tok| switch (attr.tag) {
// zig fmt: off
- .@"packed", .may_alias, .deprecated, .unavailable, .unused, .warn_if_not_aligned, .mode,
+ .@"packed", .may_alias, .deprecated, .unavailable, .unused, .warn_if_not_aligned, .mode, .warn_unused_result, .nodiscard,
=> try p.attr_application_buf.append(p.gpa, attr),
// zig fmt: on
.vector_size => try attr.applyVectorSize(p, tok, field_ty),
@@ -805,7 +860,6 @@ pub fn applyTypeAttributes(p: *Parser, ty: Type, attr_buf_start: usize, tag: ?Di
const toks = p.attr_buf.items(.tok)[attr_buf_start..];
p.attr_application_buf.items.len = 0;
var base_ty = ty;
- if (base_ty.specifier == .attributed) base_ty = base_ty.data.attributed.base;
for (attrs, toks) |attr, tok| switch (attr.tag) {
// zig fmt: off
.@"packed", .may_alias, .deprecated, .unavailable, .unused, .warn_if_not_aligned, .mode,
@@ -823,22 +877,10 @@ pub fn applyTypeAttributes(p: *Parser, ty: Type, attr_buf_start: usize, tag: ?Di
.copy,
.scalar_storage_order,
.nonstring,
- => std.debug.panic("apply type attribute {s}", .{@tagName(attr.tag)}),
+ => |t| try p.errExtra(.attribute_todo, tok, .{ .attribute_todo = .{ .tag = t, .kind = .types } }),
else => try ignoredAttrErr(p, tok, attr.tag, "types"),
};
-
- const existing = ty.getAttributes();
- // TODO: the alignment annotation on a type should override
- // the decl it refers to. This might not be true for others. Maybe bug.
-
- // if there are annotations on this type def use those.
- if (p.attr_application_buf.items.len > 0) {
- return try base_ty.withAttributes(p.arena, p.attr_application_buf.items);
- } else if (existing.len > 0) {
- // else use the ones on the typedef decl we were refering to.
- return try base_ty.withAttributes(p.arena, existing);
- }
- return base_ty;
+ return base_ty.withAttributes(p.arena, p.attr_application_buf.items);
}
pub fn applyFunctionAttributes(p: *Parser, ty: Type, attr_buf_start: usize) !Type {
@@ -846,7 +888,6 @@ pub fn applyFunctionAttributes(p: *Parser, ty: Type, attr_buf_start: usize) !Typ
const toks = p.attr_buf.items(.tok)[attr_buf_start..];
p.attr_application_buf.items.len = 0;
var base_ty = ty;
- if (base_ty.specifier == .attributed) base_ty = base_ty.data.attributed.base;
var hot = false;
var cold = false;
var @"noinline" = false;
@@ -896,6 +937,13 @@ pub fn applyFunctionAttributes(p: *Parser, ty: Type, attr_buf_start: usize) !Typ
else => try p.errStr(.callconv_not_supported, tok, p.tok_ids[tok].lexeme().?),
},
},
+ .malloc => {
+ if (base_ty.returnType().isPtr()) {
+ try p.attr_application_buf.append(p.gpa, attr);
+ } else {
+ try ignoredAttrErr(p, tok, attr.tag, "functions that do not return pointers");
+ }
+ },
.access,
.alloc_align,
.alloc_size,
@@ -908,7 +956,6 @@ pub fn applyFunctionAttributes(p: *Parser, ty: Type, attr_buf_start: usize) !Typ
.ifunc,
.interrupt,
.interrupt_handler,
- .malloc,
.no_address_safety_analysis,
.no_icf,
.no_instrument_function,
@@ -937,7 +984,7 @@ pub fn applyFunctionAttributes(p: *Parser, ty: Type, attr_buf_start: usize) !Typ
.visibility,
.weakref,
.zero_call_used_regs,
- => std.debug.panic("apply type attribute {s}", .{@tagName(attr.tag)}),
+ => |t| try p.errExtra(.attribute_todo, tok, .{ .attribute_todo = .{ .tag = t, .kind = .functions } }),
else => try ignoredAttrErr(p, tok, attr.tag, "functions"),
};
return ty.withAttributes(p.arena, p.attr_application_buf.items);
@@ -1043,11 +1090,14 @@ fn applyTransparentUnion(attr: Attribute, p: *Parser, tok: TokenIndex, ty: Type)
}
fn applyVectorSize(attr: Attribute, p: *Parser, tok: TokenIndex, ty: *Type) !void {
- if (!(ty.isInt() or ty.isFloat()) or !ty.isReal()) {
- const orig_ty = try p.typeStr(ty.*);
- ty.* = Type.invalid;
- return p.errStr(.invalid_vec_elem_ty, tok, orig_ty);
+ const base = ty.base();
+ const is_enum = ty.is(.@"enum");
+ if (!(ty.isInt() or ty.isFloat()) or !ty.isReal() or (is_enum and p.comp.langopts.emulate == .gcc)) {
+ try p.errStr(.invalid_vec_elem_ty, tok, try p.typeStr(ty.*));
+ return error.ParsingFailed;
}
+ if (is_enum) return;
+
const vec_bytes = attr.args.vector_size.bytes;
const ty_size = ty.sizeof(p.comp).?;
if (vec_bytes % ty_size != 0) {
@@ -1057,7 +1107,7 @@ fn applyVectorSize(attr: Attribute, p: *Parser, tok: TokenIndex, ty: *Type) !voi
const arr_ty = try p.arena.create(Type.Array);
arr_ty.* = .{ .elem = ty.*, .len = vec_size };
- ty.* = Type{
+ base.* = .{
.specifier = .vector,
.data = .{ .array = arr_ty },
};
lib/compiler/aro/aro/Builtins.zig
@@ -350,7 +350,7 @@ test Iterator {
}
test "All builtins" {
- var comp = Compilation.init(std.testing.allocator);
+ var comp = Compilation.init(std.testing.allocator, std.fs.cwd());
defer comp.deinit();
_ = try comp.generateBuiltinMacros(.include_system_defines);
var arena = std.heap.ArenaAllocator.init(std.testing.allocator);
@@ -373,7 +373,7 @@ test "All builtins" {
test "Allocation failures" {
const Test = struct {
fn testOne(allocator: std.mem.Allocator) !void {
- var comp = Compilation.init(allocator);
+ var comp = Compilation.init(allocator, std.fs.cwd());
defer comp.deinit();
_ = try comp.generateBuiltinMacros(.include_system_defines);
var arena = std.heap.ArenaAllocator.init(comp.gpa);
lib/compiler/aro/aro/Compilation.zig
@@ -127,22 +127,27 @@ types: struct {
} = .{},
string_interner: StrInt = .{},
interner: Interner = .{},
+/// If this is not null, the directory containing the specified Source will be searched for includes
+/// Used by MS extensions which allow searching for includes relative to the directory of the main source file.
ms_cwd_source_id: ?Source.Id = null,
+cwd: std.fs.Dir,
-pub fn init(gpa: Allocator) Compilation {
+pub fn init(gpa: Allocator, cwd: std.fs.Dir) Compilation {
return .{
.gpa = gpa,
.diagnostics = Diagnostics.init(gpa),
+ .cwd = cwd,
};
}
/// Initialize Compilation with default environment,
/// pragma handlers and emulation mode set to target.
-pub fn initDefault(gpa: Allocator) !Compilation {
+pub fn initDefault(gpa: Allocator, cwd: std.fs.Dir) !Compilation {
var comp: Compilation = .{
.gpa = gpa,
.environment = try Environment.loadAll(gpa),
.diagnostics = Diagnostics.init(gpa),
+ .cwd = cwd,
};
errdefer comp.deinit();
try comp.addDefaultPragmaHandlers();
@@ -534,7 +539,7 @@ pub fn generateBuiltinMacros(comp: *Compilation, system_defines_mode: SystemDefi
if (system_defines_mode == .include_system_defines) {
try buf.appendSlice(
\\#define __VERSION__ "Aro
- ++ @import("../backend.zig").version_str ++ "\"\n" ++
+ ++ " " ++ @import("../backend.zig").version_str ++ "\"\n" ++
\\#define __Aro__
\\
);
@@ -550,6 +555,9 @@ pub fn generateBuiltinMacros(comp: *Compilation, system_defines_mode: SystemDefi
\\#define __STDC_NO_VLA__ 1
\\#define __STDC_UTF_16__ 1
\\#define __STDC_UTF_32__ 1
+ \\#define __STDC_EMBED_NOT_FOUND__ 0
+ \\#define __STDC_EMBED_FOUND__ 1
+ \\#define __STDC_EMBED_EMPTY__ 2
\\
);
if (comp.langopts.standard.StdCVersionMacro()) |stdc_version| {
@@ -719,8 +727,13 @@ fn generateBuiltinTypes(comp: *Compilation) !void {
try comp.generateNsConstantStringType();
}
+pub fn float80Type(comp: *const Compilation) ?Type {
+ if (comp.langopts.emulate != .gcc) return null;
+ return target_util.float80Type(comp.target);
+}
+
/// Smallest integer type with at least N bits
-fn intLeastN(comp: *const Compilation, bits: usize, signedness: std.builtin.Signedness) Type {
+pub fn intLeastN(comp: *const Compilation, bits: usize, signedness: std.builtin.Signedness) Type {
if (bits == 64 and (comp.target.isDarwin() or comp.target.isWasm())) {
// WebAssembly and Darwin use `long long` for `int_least64_t` and `int_fast64_t`.
return .{ .specifier = if (signedness == .signed) .long_long else .ulong_long };
@@ -903,7 +916,7 @@ fn generateNsConstantStringType(comp: *Compilation) !void {
comp.types.ns_constant_string.fields[2] = .{ .name = try StrInt.intern(comp, "str"), .ty = const_char_ptr };
comp.types.ns_constant_string.fields[3] = .{ .name = try StrInt.intern(comp, "length"), .ty = .{ .specifier = .long } };
comp.types.ns_constant_string.ty = .{ .specifier = .@"struct", .data = .{ .record = &comp.types.ns_constant_string.record } };
- record_layout.compute(&comp.types.ns_constant_string.record, comp.types.ns_constant_string.ty, comp, null);
+ record_layout.compute(&comp.types.ns_constant_string.record, comp.types.ns_constant_string.ty, comp, null) catch unreachable;
}
fn generateVaListType(comp: *Compilation) !Type {
@@ -911,12 +924,12 @@ fn generateVaListType(comp: *Compilation) !Type {
const kind: Kind = switch (comp.target.cpu.arch) {
.aarch64 => switch (comp.target.os.tag) {
.windows => @as(Kind, .char_ptr),
- .ios, .macos, .tvos, .watchos, .visionos => .char_ptr,
+ .ios, .macos, .tvos, .watchos => .char_ptr,
else => .aarch64_va_list,
},
.sparc, .wasm32, .wasm64, .bpfel, .bpfeb, .riscv32, .riscv64, .avr, .spirv32, .spirv64 => .void_ptr,
.powerpc => switch (comp.target.os.tag) {
- .ios, .macos, .tvos, .watchos, .visionos, .aix => @as(Kind, .char_ptr),
+ .ios, .macos, .tvos, .watchos, .aix => @as(Kind, .char_ptr),
else => return Type{ .specifier = .void }, // unknown
},
.x86, .msp430 => .char_ptr,
@@ -951,7 +964,7 @@ fn generateVaListType(comp: *Compilation) !Type {
record_ty.fields[3] = .{ .name = try StrInt.intern(comp, "__gr_offs"), .ty = .{ .specifier = .int } };
record_ty.fields[4] = .{ .name = try StrInt.intern(comp, "__vr_offs"), .ty = .{ .specifier = .int } };
ty = .{ .specifier = .@"struct", .data = .{ .record = record_ty } };
- record_layout.compute(record_ty, ty, comp, null);
+ record_layout.compute(record_ty, ty, comp, null) catch unreachable;
},
.x86_64_va_list => {
const record_ty = try arena.create(Type.Record);
@@ -969,7 +982,7 @@ fn generateVaListType(comp: *Compilation) !Type {
record_ty.fields[2] = .{ .name = try StrInt.intern(comp, "overflow_arg_area"), .ty = void_ptr };
record_ty.fields[3] = .{ .name = try StrInt.intern(comp, "reg_save_area"), .ty = void_ptr };
ty = .{ .specifier = .@"struct", .data = .{ .record = record_ty } };
- record_layout.compute(record_ty, ty, comp, null);
+ record_layout.compute(record_ty, ty, comp, null) catch unreachable;
},
}
if (kind == .char_ptr or kind == .void_ptr) {
@@ -988,13 +1001,28 @@ fn generateVaListType(comp: *Compilation) !Type {
fn generateIntMax(comp: *const Compilation, w: anytype, name: []const u8, ty: Type) !void {
const bit_count: u8 = @intCast(ty.sizeof(comp).? * 8);
const unsigned = ty.isUnsignedInt(comp);
- const max = if (bit_count == 128)
- @as(u128, if (unsigned) std.math.maxInt(u128) else std.math.maxInt(u128))
- else
- ty.maxInt(comp);
+ const max: u128 = switch (bit_count) {
+ 8 => if (unsigned) std.math.maxInt(u8) else std.math.maxInt(i8),
+ 16 => if (unsigned) std.math.maxInt(u16) else std.math.maxInt(i16),
+ 32 => if (unsigned) std.math.maxInt(u32) else std.math.maxInt(i32),
+ 64 => if (unsigned) std.math.maxInt(u64) else std.math.maxInt(i64),
+ 128 => if (unsigned) std.math.maxInt(u128) else std.math.maxInt(i128),
+ else => unreachable,
+ };
try w.print("#define __{s}_MAX__ {d}{s}\n", .{ name, max, ty.intValueSuffix(comp) });
}
+/// Largest value that can be stored in wchar_t
+pub fn wcharMax(comp: *const Compilation) u32 {
+ const unsigned = comp.types.wchar.isUnsignedInt(comp);
+ return switch (comp.types.wchar.bitSizeof(comp).?) {
+ 8 => if (unsigned) std.math.maxInt(u8) else std.math.maxInt(i8),
+ 16 => if (unsigned) std.math.maxInt(u16) else std.math.maxInt(i16),
+ 32 => if (unsigned) std.math.maxInt(u32) else std.math.maxInt(i32),
+ else => unreachable,
+ };
+}
+
fn generateExactWidthIntMax(comp: *const Compilation, w: anytype, specifier: Type.Specifier) !void {
var ty = Type{ .specifier = specifier };
const bit_count: u8 = @intCast(ty.sizeof(comp).? * 8);
@@ -1039,6 +1067,12 @@ pub fn nextLargestIntSameSign(comp: *const Compilation, ty: Type) ?Type {
return null;
}
+/// Maximum size of an array, in bytes
+pub fn maxArrayBytes(comp: *const Compilation) u64 {
+ const max_bits = @min(61, comp.target.ptrBitWidth());
+ return (@as(u64, 1) << @truncate(max_bits)) - 1;
+}
+
/// If `enum E { ... }` syntax has a fixed underlying integer type regardless of the presence of
/// __attribute__((packed)) or the range of values of the corresponding enumerator constants,
/// specify it here.
@@ -1060,7 +1094,7 @@ pub fn getCharSignedness(comp: *const Compilation) std.builtin.Signedness {
pub fn addBuiltinIncludeDir(comp: *Compilation, aro_dir: []const u8) !void {
var search_path = aro_dir;
while (std.fs.path.dirname(search_path)) |dirname| : (search_path = dirname) {
- var base_dir = std.fs.cwd().openDir(dirname, .{}) catch continue;
+ var base_dir = comp.cwd.openDir(dirname, .{}) catch continue;
defer base_dir.close();
base_dir.access("include/stddef.h", .{}) catch continue;
@@ -1266,7 +1300,7 @@ fn addSourceFromPathExtra(comp: *Compilation, path: []const u8, kind: Source.Kin
return error.FileNotFound;
}
- const file = try std.fs.cwd().openFile(path, .{});
+ const file = try comp.cwd.openFile(path, .{});
defer file.close();
const contents = file.readToEndAlloc(comp.gpa, std.math.maxInt(u32)) catch |err| switch (err) {
@@ -1349,10 +1383,9 @@ pub fn hasInclude(
return false;
}
- const cwd = std.fs.cwd();
if (std.fs.path.isAbsolute(filename)) {
if (which == .next) return false;
- return !std.meta.isError(cwd.access(filename, .{}));
+ return !std.meta.isError(comp.cwd.access(filename, .{}));
}
const cwd_source_id = switch (include_type) {
@@ -1372,7 +1405,7 @@ pub fn hasInclude(
while (try it.nextWithFile(filename, sf_allocator)) |found| {
defer sf_allocator.free(found.path);
- if (!std.meta.isError(cwd.access(found.path, .{}))) return true;
+ if (!std.meta.isError(comp.cwd.access(found.path, .{}))) return true;
}
return false;
}
@@ -1392,7 +1425,7 @@ fn getFileContents(comp: *Compilation, path: []const u8, limit: ?u32) ![]const u
return error.FileNotFound;
}
- const file = try std.fs.cwd().openFile(path, .{});
+ const file = try comp.cwd.openFile(path, .{});
defer file.close();
var buf = std.ArrayList(u8).init(comp.gpa);
@@ -1571,6 +1604,17 @@ pub fn hasBuiltinFunction(comp: *const Compilation, builtin: Builtin) bool {
}
}
+pub fn locSlice(comp: *const Compilation, loc: Source.Location) []const u8 {
+ var tmp_tokenizer = Tokenizer{
+ .buf = comp.getSource(loc.id).buf,
+ .langopts = comp.langopts,
+ .index = loc.byte_offset,
+ .source = .generated,
+ };
+ const tok = tmp_tokenizer.next();
+ return tmp_tokenizer.buf[tok.start..tok.end];
+}
+
pub const CharUnitSize = enum(u32) {
@"1" = 1,
@"2" = 2,
@@ -1590,7 +1634,7 @@ pub const addDiagnostic = Diagnostics.add;
test "addSourceFromReader" {
const Test = struct {
fn addSourceFromReader(str: []const u8, expected: []const u8, warning_count: u32, splices: []const u32) !void {
- var comp = Compilation.init(std.testing.allocator);
+ var comp = Compilation.init(std.testing.allocator, std.fs.cwd());
defer comp.deinit();
var buf_reader = std.io.fixedBufferStream(str);
@@ -1602,7 +1646,7 @@ test "addSourceFromReader" {
}
fn withAllocationFailures(allocator: std.mem.Allocator) !void {
- var comp = Compilation.init(allocator);
+ var comp = Compilation.init(allocator, std.fs.cwd());
defer comp.deinit();
_ = try comp.addSourceFromBuffer("path", "spliced\\\nbuffer\n");
@@ -1644,7 +1688,7 @@ test "addSourceFromReader - exhaustive check for carriage return elimination" {
const alen = alphabet.len;
var buf: [alphabet.len]u8 = [1]u8{alphabet[0]} ** alen;
- var comp = Compilation.init(std.testing.allocator);
+ var comp = Compilation.init(std.testing.allocator, std.fs.cwd());
defer comp.deinit();
var source_count: u32 = 0;
@@ -1672,7 +1716,7 @@ test "ignore BOM at beginning of file" {
const Test = struct {
fn run(buf: []const u8) !void {
- var comp = Compilation.init(std.testing.allocator);
+ var comp = Compilation.init(std.testing.allocator, std.fs.cwd());
defer comp.deinit();
var buf_reader = std.io.fixedBufferStream(buf);
lib/compiler/aro/aro/Diagnostics.zig
@@ -47,6 +47,10 @@ pub const Message = struct {
tag: Attribute.Tag,
specifier: enum { @"struct", @"union", @"enum" },
},
+ attribute_todo: struct {
+ tag: Attribute.Tag,
+ kind: enum { variables, fields, types, functions },
+ },
builtin_with_header: struct {
builtin: Builtin.Tag,
header: Header,
@@ -210,6 +214,9 @@ pub const Options = struct {
normalized: Kind = .default,
@"shift-count-negative": Kind = .default,
@"shift-count-overflow": Kind = .default,
+ @"constant-conversion": Kind = .default,
+ @"sign-conversion": Kind = .default,
+ nonnull: Kind = .default,
};
const Diagnostics = @This();
@@ -222,14 +229,14 @@ errors: u32 = 0,
macro_backtrace_limit: u32 = 6,
pub fn warningExists(name: []const u8) bool {
- inline for (std.meta.fields(Options)) |f| {
+ inline for (@typeInfo(Options).@"struct".fields) |f| {
if (mem.eql(u8, f.name, name)) return true;
}
return false;
}
pub fn set(d: *Diagnostics, name: []const u8, to: Kind) !void {
- inline for (std.meta.fields(Options)) |f| {
+ inline for (@typeInfo(Options).@"struct".fields) |f| {
if (mem.eql(u8, f.name, name)) {
@field(d.options, f.name) = to;
return;
@@ -422,6 +429,10 @@ pub fn renderMessage(comp: *Compilation, m: anytype, msg: Message) void {
@tagName(msg.extra.ignored_record_attr.tag),
@tagName(msg.extra.ignored_record_attr.specifier),
}),
+ .attribute_todo => printRt(m, prop.msg, .{ "{s}", "{s}" }, .{
+ @tagName(msg.extra.attribute_todo.tag),
+ @tagName(msg.extra.attribute_todo.kind),
+ }),
.builtin_with_header => printRt(m, prop.msg, .{ "{s}", "{s}" }, .{
@tagName(msg.extra.builtin_with_header.header),
Builtin.nameFromTag(msg.extra.builtin_with_header.builtin).span(),
lib/compiler/aro/aro/Driver.zig
@@ -47,6 +47,20 @@ color: ?bool = null,
nobuiltininc: bool = false,
nostdinc: bool = false,
nostdlibinc: bool = false,
+debug_dump_letters: packed struct(u3) {
+ d: bool = false,
+ m: bool = false,
+ n: bool = false,
+
+ /// According to GCC, specifying letters whose behavior conflicts is undefined.
+ /// We follow clang in that `-dM` always takes precedence over `-dD`
+ pub fn getPreprocessorDumpMode(self: @This()) Preprocessor.DumpMode {
+ if (self.m) return .macros_only;
+ if (self.d) return .macros_and_result;
+ if (self.n) return .macro_names_and_result;
+ return .result_only;
+ }
+} = .{},
/// Full path to the aro executable
aro_name: []const u8 = "",
@@ -92,6 +106,9 @@ pub const usage =
\\
\\Compile options:
\\ -c, --compile Only run preprocess, compile, and assemble steps
+ \\ -dM Output #define directives for all the macros defined during the execution of the preprocessor
+ \\ -dD Like -dM except that it outputs both the #define directives and the result of preprocessing
+ \\ -dN Like -dD, but emit only the macro names, not their expansions.
\\ -D <macro>=<value> Define <macro> to <value> (defaults to 1)
\\ -E Only run the preprocessor
\\ -fchar8_t Enable char8_t (enabled by default in C23 and later)
@@ -234,6 +251,12 @@ pub fn parseArgs(
d.system_defines = .no_system_defines;
} else if (mem.eql(u8, arg, "-c") or mem.eql(u8, arg, "--compile")) {
d.only_compile = true;
+ } else if (mem.eql(u8, arg, "-dD")) {
+ d.debug_dump_letters.d = true;
+ } else if (mem.eql(u8, arg, "-dM")) {
+ d.debug_dump_letters.m = true;
+ } else if (mem.eql(u8, arg, "-dN")) {
+ d.debug_dump_letters.n = true;
} else if (mem.eql(u8, arg, "-E")) {
d.only_preprocess = true;
} else if (mem.eql(u8, arg, "-P") or mem.eql(u8, arg, "--no-line-commands")) {
@@ -636,13 +659,17 @@ fn processSource(
if (d.comp.langopts.ms_extensions) {
d.comp.ms_cwd_source_id = source.id;
}
-
+ const dump_mode = d.debug_dump_letters.getPreprocessorDumpMode();
if (d.verbose_pp) pp.verbose = true;
if (d.only_preprocess) {
pp.preserve_whitespace = true;
if (d.line_commands) {
pp.linemarkers = if (d.use_line_directives) .line_directives else .numeric_directives;
}
+ switch (dump_mode) {
+ .macros_and_result, .macro_names_and_result => pp.store_macro_tokens = true,
+ .result_only, .macros_only => {},
+ }
}
try pp.preprocessSources(&.{ source, builtin, user_macros });
@@ -663,7 +690,8 @@ fn processSource(
defer if (d.output_name != null) file.close();
var buf_w = std.io.bufferedWriter(file.writer());
- pp.prettyPrintTokens(buf_w.writer()) catch |er|
+
+ pp.prettyPrintTokens(buf_w.writer(), dump_mode) catch |er|
return d.fatal("unable to write result: {s}", .{errorDescription(er)});
buf_w.flush() catch |er|
lib/compiler/aro/aro/features.zig
@@ -45,7 +45,7 @@ pub fn hasFeature(comp: *Compilation, ext: []const u8) bool {
.c_static_assert = comp.langopts.standard.atLeast(.c11),
.c_thread_local = comp.langopts.standard.atLeast(.c11) and target_util.isTlsSupported(comp.target),
};
- inline for (std.meta.fields(@TypeOf(list))) |f| {
+ inline for (@typeInfo(@TypeOf(list)).@"struct".fields) |f| {
if (std.mem.eql(u8, f.name, ext)) return @field(list, f.name);
}
return false;
@@ -69,7 +69,7 @@ pub fn hasExtension(comp: *Compilation, ext: []const u8) bool {
.matrix_types = false, // TODO
.matrix_types_scalar_division = false, // TODO
};
- inline for (std.meta.fields(@TypeOf(list))) |f| {
+ inline for (@typeInfo(@TypeOf(list)).@"struct".fields) |f| {
if (std.mem.eql(u8, f.name, ext)) return @field(list, f.name);
}
return false;
lib/compiler/aro/aro/Hideset.zig
@@ -46,15 +46,15 @@ const Item = struct {
const List = std.MultiArrayList(Item);
};
-const Index = enum(u32) {
+pub const Index = enum(u32) {
none = std.math.maxInt(u32),
_,
};
map: std.AutoHashMapUnmanaged(Identifier, Index) = .{},
-/// Used for computing intersection of two lists; stored here so that allocations can be retained
+/// Used for computing union/intersection of two lists; stored here so that allocations can be retained
/// until hideset is deinit'ed
-intersection_map: std.AutoHashMapUnmanaged(Identifier, void) = .{},
+tmp_map: std.AutoHashMapUnmanaged(Identifier, void) = .{},
linked_list: Item.List = .{},
comp: *const Compilation,
@@ -72,7 +72,7 @@ const Iterator = struct {
pub fn deinit(self: *Hideset) void {
self.map.deinit(self.comp.gpa);
- self.intersection_map.deinit(self.comp.gpa);
+ self.tmp_map.deinit(self.comp.gpa);
self.linked_list.deinit(self.comp.gpa);
}
@@ -83,7 +83,7 @@ pub fn clearRetainingCapacity(self: *Hideset) void {
pub fn clearAndFree(self: *Hideset) void {
self.map.clearAndFree(self.comp.gpa);
- self.intersection_map.clearAndFree(self.comp.gpa);
+ self.tmp_map.clearAndFree(self.comp.gpa);
self.linked_list.shrinkAndFree(self.comp.gpa, 0);
}
@@ -109,8 +109,13 @@ fn ensureUnusedCapacity(self: *Hideset, new_size: usize) !void {
/// Creates a one-item list with contents `identifier`
fn createNodeAssumeCapacity(self: *Hideset, identifier: Identifier) Index {
+ return self.createNodeAssumeCapacityExtra(identifier, .none);
+}
+
+/// Creates a one-item list with contents `identifier`
+fn createNodeAssumeCapacityExtra(self: *Hideset, identifier: Identifier, next: Index) Index {
const next_idx = self.linked_list.len;
- self.linked_list.appendAssumeCapacity(.{ .identifier = identifier });
+ self.linked_list.appendAssumeCapacity(.{ .identifier = identifier, .next = next });
return @enumFromInt(next_idx);
}
@@ -121,24 +126,24 @@ pub fn prepend(self: *Hideset, loc: Source.Location, tail: Index) !Index {
return @enumFromInt(new_idx);
}
-/// Copy a, then attach b at the end
+/// Attach elements of `b` to the front of `a` (if they're not in `a`)
pub fn @"union"(self: *Hideset, a: Index, b: Index) !Index {
- var cur: Index = .none;
+ if (a == .none) return b;
+ if (b == .none) return a;
+ self.tmp_map.clearRetainingCapacity();
+
+ var it = self.iterator(b);
+ while (it.next()) |identifier| {
+ try self.tmp_map.put(self.comp.gpa, identifier, {});
+ }
+
var head: Index = b;
try self.ensureUnusedCapacity(self.len(a));
- var it = self.iterator(a);
+ it = self.iterator(a);
while (it.next()) |identifier| {
- const new_idx = self.createNodeAssumeCapacity(identifier);
- if (head == b) {
- head = new_idx;
+ if (!self.tmp_map.contains(identifier)) {
+ head = self.createNodeAssumeCapacityExtra(identifier, head);
}
- if (cur != .none) {
- self.linked_list.items(.next)[@intFromEnum(cur)] = new_idx;
- }
- cur = new_idx;
- }
- if (cur != .none) {
- self.linked_list.items(.next)[@intFromEnum(cur)] = b;
}
return head;
}
@@ -163,20 +168,20 @@ fn len(self: *const Hideset, list: Index) usize {
pub fn intersection(self: *Hideset, a: Index, b: Index) !Index {
if (a == .none or b == .none) return .none;
- self.intersection_map.clearRetainingCapacity();
+ self.tmp_map.clearRetainingCapacity();
var cur: Index = .none;
var head: Index = .none;
var it = self.iterator(a);
var a_len: usize = 0;
while (it.next()) |identifier| : (a_len += 1) {
- try self.intersection_map.put(self.comp.gpa, identifier, {});
+ try self.tmp_map.put(self.comp.gpa, identifier, {});
}
try self.ensureUnusedCapacity(@min(a_len, self.len(b)));
it = self.iterator(b);
while (it.next()) |identifier| {
- if (self.intersection_map.contains(identifier)) {
+ if (self.tmp_map.contains(identifier)) {
const new_idx = self.createNodeAssumeCapacity(identifier);
if (head == .none) {
head = new_idx;
lib/compiler/aro/aro/Parser.zig
@@ -28,6 +28,7 @@ const StrInt = @import("StringInterner.zig");
const StringId = StrInt.StringId;
const Builtins = @import("Builtins.zig");
const Builtin = Builtins.Builtin;
+const evalBuiltin = @import("Builtins/eval.zig").eval;
const target_util = @import("target.zig");
const Switch = struct {
@@ -100,7 +101,7 @@ value_map: Tree.ValueMap,
// buffers used during compilation
syms: SymbolStack = .{},
-strings: std.ArrayList(u8),
+strings: std.ArrayListAligned(u8, 4),
labels: std.ArrayList(Label),
list_buf: NodeList,
decl_buf: NodeList,
@@ -130,6 +131,10 @@ const_decl_folding: ConstDeclFoldingMode = .fold_const_decls,
/// address-of-label expression (tracked with contains_address_of_label)
computed_goto_tok: ?TokenIndex = null,
+/// __auto_type may only be used with a single declarator. Keep track of the name
+/// so that it is not used in its own initializer.
+auto_type_decl_name: StringId = .empty,
+
/// Various variables that are different for each function.
func: struct {
/// null if not in function, will always be plain func, var_args_func or old_style_func
@@ -160,7 +165,7 @@ record: struct {
}
fn addFieldsFromAnonymous(r: @This(), p: *Parser, ty: Type) Error!void {
- for (ty.data.record.fields) |f| {
+ for (ty.getRecord().?.fields) |f| {
if (f.isAnonymousRecord()) {
try r.addFieldsFromAnonymous(p, f.ty.canonicalize(.standard));
} else if (f.name_tok != 0) {
@@ -470,7 +475,7 @@ pub fn typePairStrExtra(p: *Parser, a: Type, msg: []const u8, b: Type) ![]const
return try p.comp.diagnostics.arena.allocator().dupe(u8, p.strings.items[strings_top..]);
}
-pub fn floatValueChangedStr(p: *Parser, res: *Result, old_value: Value, int_ty: Type) ![]const u8 {
+pub fn valueChangedStr(p: *Parser, res: *Result, old_value: Value, int_ty: Type) ![]const u8 {
const strings_top = p.strings.items.len;
defer p.strings.items.len = strings_top;
@@ -572,6 +577,14 @@ fn nodeIs(p: *Parser, node: NodeIndex, tag: Tree.Tag) bool {
return p.getNode(node, tag) != null;
}
+pub fn getDecayedStringLiteral(p: *Parser, node: NodeIndex) ?Value {
+ const cast_node = p.getNode(node, .implicit_cast) orelse return null;
+ const data = p.nodes.items(.data)[@intFromEnum(cast_node)];
+ if (data.cast.kind != .array_to_pointer) return null;
+ const literal_node = p.getNode(data.cast.operand, .string_literal_expr) orelse return null;
+ return p.value_map.get(literal_node);
+}
+
fn getNode(p: *Parser, node: NodeIndex, tag: Tree.Tag) ?NodeIndex {
var cur = node;
const tags = p.nodes.items(.tag);
@@ -680,7 +693,7 @@ pub fn parse(pp: *Preprocessor) Compilation.Error!Tree {
.gpa = pp.comp.gpa,
.arena = arena.allocator(),
.tok_ids = pp.tokens.items(.id),
- .strings = std.ArrayList(u8).init(pp.comp.gpa),
+ .strings = std.ArrayListAligned(u8, 4).init(pp.comp.gpa),
.value_map = Tree.ValueMap.init(pp.comp.gpa),
.data = NodeList.init(pp.comp.gpa),
.labels = std.ArrayList(Label).init(pp.comp.gpa),
@@ -725,7 +738,7 @@ pub fn parse(pp: *Preprocessor) Compilation.Error!Tree {
defer p.syms.popScope();
// NodeIndex 0 must be invalid
- _ = try p.addNode(.{ .tag = .invalid, .ty = undefined, .data = undefined });
+ _ = try p.addNode(.{ .tag = .invalid, .ty = undefined, .data = undefined, .loc = undefined });
{
if (p.comp.langopts.hasChar8_T()) {
@@ -747,6 +760,10 @@ pub fn parse(pp: *Preprocessor) Compilation.Error!Tree {
if (ty.isArray()) ty.decayArray();
try p.syms.defineTypedef(&p, try StrInt.intern(p.comp, "__NSConstantString"), pp.comp.types.ns_constant_string.ty, 0, .none);
+
+ if (p.comp.float80Type()) |float80_ty| {
+ try p.syms.defineTypedef(&p, try StrInt.intern(p.comp, "__float80"), float80_ty, 0, .none);
+ }
}
while (p.eatToken(.eof) == null) {
@@ -862,6 +879,8 @@ fn nextExternDecl(p: *Parser) void {
.keyword_int,
.keyword_long,
.keyword_signed,
+ .keyword_signed1,
+ .keyword_signed2,
.keyword_unsigned,
.keyword_float,
.keyword_double,
@@ -1018,10 +1037,8 @@ fn decl(p: *Parser) Error!bool {
// Collect old style parameter declarations.
if (init_d.d.old_style_func != null) {
- const attrs = init_d.d.ty.getAttributes();
- var base_ty = if (init_d.d.ty.specifier == .attributed) init_d.d.ty.data.attributed.base else init_d.d.ty;
+ var base_ty = init_d.d.ty.base();
base_ty.specifier = .func;
- init_d.d.ty = try base_ty.withAttributes(p.arena, attrs);
const param_buf_top = p.param_buf.items.len;
defer p.param_buf.items.len = param_buf_top;
@@ -1116,6 +1133,7 @@ fn decl(p: *Parser) Error!bool {
.ty = init_d.d.ty,
.tag = try decl_spec.validateFnDef(p),
.data = .{ .decl = .{ .name = init_d.d.name, .node = body } },
+ .loc = @enumFromInt(init_d.d.name),
});
try p.decl_buf.append(node);
@@ -1142,9 +1160,18 @@ fn decl(p: *Parser) Error!bool {
if (init_d.d.old_style_func) |tok_i| try p.errTok(.invalid_old_style_params, tok_i);
const tag = try decl_spec.validate(p, &init_d.d.ty, init_d.initializer.node != .none);
- const node = try p.addNode(.{ .ty = init_d.d.ty, .tag = tag, .data = .{
- .decl = .{ .name = init_d.d.name, .node = init_d.initializer.node },
- } });
+ const tok = switch (decl_spec.storage_class) {
+ .auto, .@"extern", .register, .static, .typedef => |tok| tok,
+ .none => init_d.d.name,
+ };
+ const node = try p.addNode(.{
+ .ty = init_d.d.ty,
+ .tag = tag,
+ .data = .{
+ .decl = .{ .name = init_d.d.name, .node = init_d.initializer.node },
+ },
+ .loc = @enumFromInt(tok),
+ });
try p.decl_buf.append(node);
const interned_name = try StrInt.intern(p.comp, p.tokSlice(init_d.d.name));
@@ -1287,6 +1314,7 @@ fn staticAssert(p: *Parser) Error!bool {
.lhs = res.node,
.rhs = str.node,
} },
+ .loc = @enumFromInt(static_assert),
});
try p.decl_buf.append(node);
return true;
@@ -1407,6 +1435,8 @@ fn typeof(p: *Parser) Error!?Type {
const l_paren = try p.expectToken(.l_paren);
if (try p.typeName()) |ty| {
try p.expectClosing(l_paren, .r_paren);
+ if (ty.is(.invalid)) return null;
+
const typeof_ty = try p.arena.create(Type);
typeof_ty.* = .{
.data = ty.data,
@@ -1428,6 +1458,8 @@ fn typeof(p: *Parser) Error!?Type {
.specifier = .nullptr_t,
.qual = if (unqual) .{} else typeof_expr.ty.qual.inheritFromTypeof(),
};
+ } else if (typeof_expr.ty.is(.invalid)) {
+ return null;
}
const inner = try p.arena.create(Type.Expr);
@@ -1774,6 +1806,8 @@ fn initDeclarator(p: *Parser, decl_spec: *DeclSpec, attr_buf_top: usize) Error!?
} else {
apply_var_attributes = true;
}
+ const c23_auto = init_d.d.ty.is(.c23_auto);
+ const auto_type = init_d.d.ty.is(.auto_type);
if (p.eatToken(.equal)) |eq| init: {
if (decl_spec.storage_class == .typedef or
@@ -1801,19 +1835,21 @@ fn initDeclarator(p: *Parser, decl_spec: *DeclSpec, attr_buf_top: usize) Error!?
const interned_name = try StrInt.intern(p.comp, p.tokSlice(init_d.d.name));
try p.syms.declareSymbol(p, interned_name, init_d.d.ty, init_d.d.name, .none);
+ if (c23_auto or auto_type) {
+ p.auto_type_decl_name = interned_name;
+ }
+ defer p.auto_type_decl_name = .empty;
+
var init_list_expr = try p.initializer(init_d.d.ty);
init_d.initializer = init_list_expr;
if (!init_list_expr.ty.isArray()) break :init;
- if (init_d.d.ty.specifier == .incomplete_array) {
- // Modifying .data is exceptionally allowed for .incomplete_array.
- init_d.d.ty.data.array.len = init_list_expr.ty.arrayLen() orelse break :init;
- init_d.d.ty.specifier = .array;
+ if (init_d.d.ty.is(.incomplete_array)) {
+ init_d.d.ty.setIncompleteArrayLen(init_list_expr.ty.arrayLen() orelse break :init);
}
}
const name = init_d.d.name;
- const c23_auto = init_d.d.ty.is(.c23_auto);
- if (init_d.d.ty.is(.auto_type) or c23_auto) {
+ if (auto_type or c23_auto) {
if (init_d.initializer.node == .none) {
init_d.d.ty = Type.invalid;
if (c23_auto) {
@@ -1872,6 +1908,8 @@ fn initDeclarator(p: *Parser, decl_spec: *DeclSpec, attr_buf_top: usize) Error!?
/// | keyword_float
/// | keyword_double
/// | keyword_signed
+/// | keyword_signed1
+/// | keyword_signed2
/// | keyword_unsigned
/// | keyword_bool
/// | keyword_c23_bool
@@ -1911,14 +1949,13 @@ fn typeSpec(p: *Parser, ty: *Type.Builder) Error!bool {
.keyword_long => try ty.combine(p, .long, p.tok_i),
.keyword_int64, .keyword_int64_2 => try ty.combine(p, .long_long, p.tok_i),
.keyword_int128 => try ty.combine(p, .int128, p.tok_i),
- .keyword_signed => try ty.combine(p, .signed, p.tok_i),
+ .keyword_signed, .keyword_signed1, .keyword_signed2 => try ty.combine(p, .signed, p.tok_i),
.keyword_unsigned => try ty.combine(p, .unsigned, p.tok_i),
.keyword_fp16 => try ty.combine(p, .fp16, p.tok_i),
.keyword_float16 => try ty.combine(p, .float16, p.tok_i),
.keyword_float => try ty.combine(p, .float, p.tok_i),
.keyword_double => try ty.combine(p, .double, p.tok_i),
.keyword_complex => try ty.combine(p, .complex, p.tok_i),
- .keyword_float80 => try ty.combine(p, .float80, p.tok_i),
.keyword_float128_1, .keyword_float128_2 => {
if (!p.comp.hasFloat128()) {
try p.errStr(.type_not_supported_on_target, p.tok_i, p.tok_ids[p.tok_i].lexeme().?);
@@ -2128,6 +2165,7 @@ fn recordSpec(p: *Parser) Error!Type {
.tag = if (is_struct) .struct_forward_decl else .union_forward_decl,
.ty = ty,
.data = .{ .decl_ref = ident },
+ .loc = @enumFromInt(ident),
}));
return ty;
}
@@ -2248,19 +2286,22 @@ fn recordSpec(p: *Parser) Error!Type {
// TODO: msvc considers `#pragma pack` on a per-field basis
.msvc => p.pragma_pack,
};
- record_layout.compute(record_ty, ty, p.comp, pragma_pack_value);
+ record_layout.compute(record_ty, ty, p.comp, pragma_pack_value) catch |er| switch (er) {
+ error.Overflow => try p.errStr(.record_too_large, maybe_ident orelse kind_tok, try p.typeStr(ty)),
+ };
}
// finish by creating a node
var node: Tree.Node = .{
.tag = if (is_struct) .struct_decl_two else .union_decl_two,
.ty = ty,
- .data = .{ .bin = .{ .lhs = .none, .rhs = .none } },
+ .data = .{ .two = .{ .none, .none } },
+ .loc = @enumFromInt(maybe_ident orelse kind_tok),
};
switch (record_decls.len) {
0 => {},
- 1 => node.data = .{ .bin = .{ .lhs = record_decls[0], .rhs = .none } },
- 2 => node.data = .{ .bin = .{ .lhs = record_decls[0], .rhs = record_decls[1] } },
+ 1 => node.data = .{ .two = .{ record_decls[0], .none } },
+ 2 => node.data = .{ .two = .{ record_decls[0], record_decls[1] } },
else => {
node.tag = if (is_struct) .struct_decl else .union_decl;
node.data = .{ .range = try p.addList(record_decls) };
@@ -2383,6 +2424,7 @@ fn recordDeclarator(p: *Parser) Error!bool {
.tag = .indirect_record_field_decl,
.ty = ty,
.data = undefined,
+ .loc = @enumFromInt(first_tok),
});
try p.decl_buf.append(node);
try p.record.addFieldsFromAnonymous(p, ty);
@@ -2402,6 +2444,7 @@ fn recordDeclarator(p: *Parser) Error!bool {
.tag = .record_field_decl,
.ty = ty,
.data = .{ .decl = .{ .name = name_tok, .node = bits_node } },
+ .loc = @enumFromInt(if (name_tok != 0) name_tok else first_tok),
});
try p.decl_buf.append(node);
}
@@ -2461,7 +2504,8 @@ fn enumSpec(p: *Parser) Error!Type {
const maybe_ident = try p.eatIdentifier();
const fixed_ty = if (p.eatToken(.colon)) |colon| fixed: {
- const fixed = (try p.typeName()) orelse {
+ const ty_start = p.tok_i;
+ const fixed = (try p.specQual()) orelse {
if (p.record.kind != .invalid) {
// This is a bit field.
p.tok_i -= 1;
@@ -2471,6 +2515,12 @@ fn enumSpec(p: *Parser) Error!Type {
try p.errTok(.enum_fixed, colon);
break :fixed null;
};
+
+ if (!fixed.isInt() or fixed.is(.@"enum")) {
+ try p.errStr(.invalid_type_underlying_enum, ty_start, try p.typeStr(fixed));
+ break :fixed Type.int;
+ }
+
try p.errTok(.enum_fixed, colon);
break :fixed fixed;
} else null;
@@ -2505,6 +2555,7 @@ fn enumSpec(p: *Parser) Error!Type {
.tag = .enum_forward_decl,
.ty = ty,
.data = .{ .decl_ref = ident },
+ .loc = @enumFromInt(ident),
}));
return ty;
}
@@ -2587,7 +2638,7 @@ fn enumSpec(p: *Parser) Error!Type {
continue;
const symbol = p.syms.getPtr(field.name, .vars);
- try symbol.val.intCast(dest_ty, p.comp);
+ _ = try symbol.val.intCast(dest_ty, p.comp);
symbol.ty = dest_ty;
p.nodes.items(.ty)[@intFromEnum(field_nodes[i])] = dest_ty;
field.ty = dest_ty;
@@ -2615,13 +2666,18 @@ fn enumSpec(p: *Parser) Error!Type {
}
// finish by creating a node
- var node: Tree.Node = .{ .tag = .enum_decl_two, .ty = ty, .data = .{
- .bin = .{ .lhs = .none, .rhs = .none },
- } };
+ var node: Tree.Node = .{
+ .tag = .enum_decl_two,
+ .ty = ty,
+ .data = .{
+ .two = .{ .none, .none },
+ },
+ .loc = @enumFromInt(maybe_ident orelse enum_tok),
+ };
switch (field_nodes.len) {
0 => {},
- 1 => node.data = .{ .bin = .{ .lhs = field_nodes[0], .rhs = .none } },
- 2 => node.data = .{ .bin = .{ .lhs = field_nodes[0], .rhs = field_nodes[1] } },
+ 1 => node.data = .{ .two = .{ field_nodes[0], .none } },
+ 2 => node.data = .{ .two = .{ field_nodes[0], field_nodes[1] } },
else => {
node.tag = .enum_decl;
node.data = .{ .range = try p.addList(field_nodes) };
@@ -2679,8 +2735,6 @@ const Enumerator = struct {
return;
}
if (try e.res.val.add(e.res.val, Value.one, e.res.ty, p.comp)) {
- const byte_size = e.res.ty.sizeof(p.comp).?;
- const bit_size: u8 = @intCast(if (e.res.ty.isUnsignedInt(p.comp)) byte_size * 8 else byte_size * 8 - 1);
if (e.fixed) {
try p.errStr(.enum_not_representable_fixed, tok, try p.typeStr(e.res.ty));
return;
@@ -2689,6 +2743,8 @@ const Enumerator = struct {
try p.errTok(.enumerator_overflow, tok);
break :blk larger;
} else blk: {
+ const signed = !e.res.ty.isUnsignedInt(p.comp);
+ const bit_size: u8 = @intCast(e.res.ty.bitSizeof(p.comp).? - @intFromBool(signed));
try p.errExtra(.enum_not_representable, tok, .{ .pow_2_as_string = bit_size });
break :blk Type{ .specifier = .ulong_long };
};
@@ -2792,14 +2848,12 @@ fn enumerator(p: *Parser, e: *Enumerator) Error!?EnumFieldAndNode {
if (err_start == p.comp.diagnostics.list.items.len) {
// only do these warnings if we didn't already warn about overflow or non-representable values
if (e.res.val.compare(.lt, Value.zero, p.comp)) {
- const min_int = (Type{ .specifier = .int }).minInt(p.comp);
- const min_val = try Value.int(min_int, p.comp);
+ const min_val = try Value.minInt(Type.int, p.comp);
if (e.res.val.compare(.lt, min_val, p.comp)) {
try p.errStr(.enumerator_too_small, name_tok, try e.res.str(p));
}
} else {
- const max_int = (Type{ .specifier = .int }).maxInt(p.comp);
- const max_val = try Value.int(max_int, p.comp);
+ const max_val = try Value.maxInt(Type.int, p.comp);
if (e.res.val.compare(.gt, max_val, p.comp)) {
try p.errStr(.enumerator_too_large, name_tok, try e.res.str(p));
}
@@ -2815,6 +2869,7 @@ fn enumerator(p: *Parser, e: *Enumerator) Error!?EnumFieldAndNode {
.name = name_tok,
.node = res.node,
} },
+ .loc = @enumFromInt(name_tok),
});
try p.value_map.put(node, e.res.val);
return EnumFieldAndNode{ .field = .{
@@ -2991,15 +3046,12 @@ fn directDeclarator(p: *Parser, base_type: Type, d: *Declarator, kind: Declarato
}
const outer = try p.directDeclarator(base_type, d, kind);
- var max_bits = p.comp.target.ptrBitWidth();
- if (max_bits > 61) max_bits = 61;
- const max_bytes = (@as(u64, 1) << @truncate(max_bits)) - 1;
if (!size.ty.isInt()) {
try p.errStr(.array_size_non_int, size_tok, try p.typeStr(size.ty));
return error.ParsingFailed;
}
- if (base_type.is(.c23_auto)) {
+ if (base_type.is(.c23_auto) or outer.is(.invalid)) {
// issue error later
return Type.invalid;
} else if (size.val.opt_ref == .none) {
@@ -3030,7 +3082,7 @@ fn directDeclarator(p: *Parser, base_type: Type, d: *Declarator, kind: Declarato
} else {
// `outer` is validated later so it may be invalid here
const outer_size = outer.sizeof(p.comp);
- const max_elems = max_bytes / @max(1, outer_size orelse 1);
+ const max_elems = p.comp.maxArrayBytes() / @max(1, outer_size orelse 1);
var size_val = size.val;
if (size_val.isZero(p.comp)) {
@@ -3047,7 +3099,7 @@ fn directDeclarator(p: *Parser, base_type: Type, d: *Declarator, kind: Declarato
arr_ty.len = max_elems;
}
res_ty.data = .{ .array = arr_ty };
- res_ty.specifier = .array;
+ res_ty.specifier = if (static != null) .static_array else .array;
}
try res_ty.combine(outer);
@@ -3120,12 +3172,14 @@ fn directDeclarator(p: *Parser, base_type: Type, d: *Declarator, kind: Declarato
fn pointer(p: *Parser, base_ty: Type) Error!Type {
var ty = base_ty;
while (p.eatToken(.asterisk)) |_| {
- const elem_ty = try p.arena.create(Type);
- elem_ty.* = ty;
- ty = Type{
- .specifier = .pointer,
- .data = .{ .sub_type = elem_ty },
- };
+ if (!ty.is(.invalid)) {
+ const elem_ty = try p.arena.create(Type);
+ elem_ty.* = ty;
+ ty = Type{
+ .specifier = .pointer,
+ .data = .{ .sub_type = elem_ty },
+ };
+ }
var quals = Type.Qualifiers.Builder{};
_ = try p.typeQual(&quals);
try quals.finish(p, &ty);
@@ -3237,6 +3291,75 @@ fn typeName(p: *Parser) Error!?Type {
return try Attribute.applyTypeAttributes(p, ty, attr_buf_top, .align_ignored);
}
+fn complexInitializer(p: *Parser, init_ty: Type) Error!Result {
+ assert(p.tok_ids[p.tok_i] == .l_brace);
+ assert(init_ty.isComplex());
+
+ const real_ty = init_ty.makeReal();
+ if (real_ty.isInt()) {
+ return p.todo("Complex integer initializers");
+ }
+ const l_brace = p.tok_i;
+ p.tok_i += 1;
+ try p.errTok(.complex_component_init, l_brace);
+
+ const first_tok = p.tok_i;
+ var first = try p.assignExpr();
+ try first.expect(p);
+ try p.coerceInit(&first, first_tok, real_ty);
+
+ var second: Result = .{
+ .ty = real_ty,
+ .val = Value.zero,
+ };
+ if (p.eatToken(.comma)) |_| {
+ const second_tok = p.tok_i;
+ const maybe_second = try p.assignExpr();
+ if (!maybe_second.empty(p)) {
+ second = maybe_second;
+ try p.coerceInit(&second, second_tok, real_ty);
+ }
+ }
+
+ // Eat excess initializers
+ var extra_tok: ?TokenIndex = null;
+ while (p.eatToken(.comma)) |_| {
+ if (p.tok_ids[p.tok_i] == .r_brace) break;
+ extra_tok = p.tok_i;
+ const extra = try p.assignExpr();
+ if (extra.empty(p)) {
+ try p.errTok(.expected_expr, p.tok_i);
+ p.skipTo(.r_brace);
+ return error.ParsingFailed;
+ }
+ }
+ try p.expectClosing(l_brace, .r_brace);
+ if (extra_tok) |tok| {
+ try p.errTok(.excess_scalar_init, tok);
+ }
+
+ const arr_init_node: Tree.Node = .{
+ .tag = .array_init_expr_two,
+ .ty = init_ty,
+ .data = .{ .two = .{ first.node, second.node } },
+ .loc = @enumFromInt(l_brace),
+ };
+ var res: Result = .{
+ .node = try p.addNode(arr_init_node),
+ .ty = init_ty,
+ };
+ if (first.val.opt_ref != .none and second.val.opt_ref != .none) {
+ res.val = try Value.intern(p.comp, switch (real_ty.bitSizeof(p.comp).?) {
+ 32 => .{ .complex = .{ .cf32 = .{ first.val.toFloat(f32, p.comp), second.val.toFloat(f32, p.comp) } } },
+ 64 => .{ .complex = .{ .cf64 = .{ first.val.toFloat(f64, p.comp), second.val.toFloat(f64, p.comp) } } },
+ 80 => .{ .complex = .{ .cf80 = .{ first.val.toFloat(f80, p.comp), second.val.toFloat(f80, p.comp) } } },
+ 128 => .{ .complex = .{ .cf128 = .{ first.val.toFloat(f128, p.comp), second.val.toFloat(f128, p.comp) } } },
+ else => unreachable,
+ });
+ }
+ return res;
+}
+
/// initializer
/// : assignExpr
/// | '{' initializerItems '}'
@@ -3255,6 +3378,9 @@ fn initializer(p: *Parser, init_ty: Type) Error!Result {
return error.ParsingFailed;
}
+ if (init_ty.isComplex()) {
+ return p.complexInitializer(init_ty);
+ }
var il: InitList = .{};
defer il.deinit(p.gpa);
@@ -3754,9 +3880,15 @@ fn convertInitList(p: *Parser, il: InitList, init_ty: Type) Error!NodeIndex {
var arr_init_node: Tree.Node = .{
.tag = .array_init_expr_two,
.ty = init_ty,
- .data = .{ .bin = .{ .lhs = .none, .rhs = .none } },
+ .data = .{ .two = .{ .none, .none } },
};
+ const max_elems = p.comp.maxArrayBytes() / (@max(1, elem_ty.sizeof(p.comp) orelse 1));
+ if (start > max_elems) {
+ try p.errTok(.array_too_large, il.tok);
+ start = max_elems;
+ }
+
if (init_ty.specifier == .incomplete_array) {
arr_init_node.ty.specifier = .array;
arr_init_node.ty.data.array.len = start;
@@ -3767,8 +3899,6 @@ fn convertInitList(p: *Parser, il: InitList, init_ty: Type) Error!NodeIndex {
.specifier = .array,
.data = .{ .array = arr_ty },
};
- const attrs = init_ty.getAttributes();
- arr_init_node.ty = try arr_init_node.ty.withAttributes(p.arena, attrs);
} else if (start < max_items) {
const elem = try p.addNode(.{
.tag = .array_filler_expr,
@@ -3781,8 +3911,8 @@ fn convertInitList(p: *Parser, il: InitList, init_ty: Type) Error!NodeIndex {
const items = p.list_buf.items[list_buf_top..];
switch (items.len) {
0 => {},
- 1 => arr_init_node.data.bin.lhs = items[0],
- 2 => arr_init_node.data.bin = .{ .lhs = items[0], .rhs = items[1] },
+ 1 => arr_init_node.data.two[0] = items[0],
+ 2 => arr_init_node.data.two = .{ items[0], items[1] },
else => {
arr_init_node.tag = .array_init_expr;
arr_init_node.data = .{ .range = try p.addList(items) };
@@ -3813,13 +3943,13 @@ fn convertInitList(p: *Parser, il: InitList, init_ty: Type) Error!NodeIndex {
var struct_init_node: Tree.Node = .{
.tag = .struct_init_expr_two,
.ty = init_ty,
- .data = .{ .bin = .{ .lhs = .none, .rhs = .none } },
+ .data = .{ .two = .{ .none, .none } },
};
const items = p.list_buf.items[list_buf_top..];
switch (items.len) {
0 => {},
- 1 => struct_init_node.data.bin.lhs = items[0],
- 2 => struct_init_node.data.bin = .{ .lhs = items[0], .rhs = items[1] },
+ 1 => struct_init_node.data.two[0] = items[0],
+ 2 => struct_init_node.data.two = .{ items[0], items[1] },
else => {
struct_init_node.tag = .struct_init_expr;
struct_init_node.data = .{ .range = try p.addList(items) };
@@ -3894,7 +4024,7 @@ fn asmOperand(p: *Parser, names: *std.ArrayList(?TokenIndex), constraints: *Node
/// | asmStr ':' asmOperand* ':' asmOperand*
/// | asmStr ':' asmOperand* ':' asmOperand* : asmStr? (',' asmStr)*
/// | asmStr ':' asmOperand* ':' asmOperand* : asmStr? (',' asmStr)* : IDENTIFIER (',' IDENTIFIER)*
-fn gnuAsmStmt(p: *Parser, quals: Tree.GNUAssemblyQualifiers, l_paren: TokenIndex) Error!NodeIndex {
+fn gnuAsmStmt(p: *Parser, quals: Tree.GNUAssemblyQualifiers, asm_tok: TokenIndex, l_paren: TokenIndex) Error!NodeIndex {
const asm_str = try p.asmStr();
try p.checkAsmStr(asm_str.val, l_paren);
@@ -3903,6 +4033,7 @@ fn gnuAsmStmt(p: *Parser, quals: Tree.GNUAssemblyQualifiers, l_paren: TokenIndex
.tag = .gnu_asm_simple,
.ty = .{ .specifier = .void },
.data = .{ .un = asm_str.node },
+ .loc = @enumFromInt(asm_tok),
});
}
@@ -4007,6 +4138,7 @@ fn gnuAsmStmt(p: *Parser, quals: Tree.GNUAssemblyQualifiers, l_paren: TokenIndex
.tag = .addr_of_label,
.data = .{ .decl_ref = label },
.ty = result_ty,
+ .loc = @enumFromInt(ident),
});
try exprs.append(label_addr_node);
@@ -4088,9 +4220,10 @@ fn assembly(p: *Parser, kind: enum { global, decl_label, stmt }) Error!?NodeInde
.tag = .file_scope_asm,
.ty = .{ .specifier = .void },
.data = .{ .decl = .{ .name = asm_tok, .node = asm_str.node } },
+ .loc = @enumFromInt(asm_tok),
});
},
- .stmt => result_node = try p.gnuAsmStmt(quals, l_paren),
+ .stmt => result_node = try p.gnuAsmStmt(quals, asm_tok, l_paren),
}
try p.expectClosing(l_paren, .r_paren);
@@ -4141,7 +4274,7 @@ fn asmStr(p: *Parser) Error!Result {
fn stmt(p: *Parser) Error!NodeIndex {
if (try p.labeledStmt()) |some| return some;
if (try p.compoundStmt(false, null)) |some| return some;
- if (p.eatToken(.keyword_if)) |_| {
+ if (p.eatToken(.keyword_if)) |kw_if| {
const l_paren = try p.expectToken(.l_paren);
const cond_tok = p.tok_i;
var cond = try p.expr();
@@ -4160,14 +4293,16 @@ fn stmt(p: *Parser) Error!NodeIndex {
return try p.addNode(.{
.tag = .if_then_else_stmt,
.data = .{ .if3 = .{ .cond = cond.node, .body = (try p.addList(&.{ then, @"else" })).start } },
+ .loc = @enumFromInt(kw_if),
})
else
return try p.addNode(.{
.tag = .if_then_stmt,
.data = .{ .bin = .{ .lhs = cond.node, .rhs = then } },
+ .loc = @enumFromInt(kw_if),
});
}
- if (p.eatToken(.keyword_switch)) |_| {
+ if (p.eatToken(.keyword_switch)) |kw_switch| {
const l_paren = try p.expectToken(.l_paren);
const cond_tok = p.tok_i;
var cond = try p.expr();
@@ -4197,9 +4332,10 @@ fn stmt(p: *Parser) Error!NodeIndex {
return try p.addNode(.{
.tag = .switch_stmt,
.data = .{ .bin = .{ .lhs = cond.node, .rhs = body } },
+ .loc = @enumFromInt(kw_switch),
});
}
- if (p.eatToken(.keyword_while)) |_| {
+ if (p.eatToken(.keyword_while)) |kw_while| {
const l_paren = try p.expectToken(.l_paren);
const cond_tok = p.tok_i;
var cond = try p.expr();
@@ -4221,9 +4357,10 @@ fn stmt(p: *Parser) Error!NodeIndex {
return try p.addNode(.{
.tag = .while_stmt,
.data = .{ .bin = .{ .lhs = cond.node, .rhs = body } },
+ .loc = @enumFromInt(kw_while),
});
}
- if (p.eatToken(.keyword_do)) |_| {
+ if (p.eatToken(.keyword_do)) |kw_do| {
const body = body: {
const old_loop = p.in_loop;
p.in_loop = true;
@@ -4248,9 +4385,10 @@ fn stmt(p: *Parser) Error!NodeIndex {
return try p.addNode(.{
.tag = .do_while_stmt,
.data = .{ .bin = .{ .lhs = cond.node, .rhs = body } },
+ .loc = @enumFromInt(kw_do),
});
}
- if (p.eatToken(.keyword_for)) |_| {
+ if (p.eatToken(.keyword_for)) |kw_for| {
try p.syms.pushScope(p);
defer p.syms.popScope();
const decl_buf_top = p.decl_buf.items.len;
@@ -4301,16 +4439,22 @@ fn stmt(p: *Parser) Error!NodeIndex {
return try p.addNode(.{
.tag = .for_decl_stmt,
.data = .{ .range = .{ .start = start, .end = end } },
+ .loc = @enumFromInt(kw_for),
});
} else if (init.node == .none and cond.node == .none and incr.node == .none) {
return try p.addNode(.{
.tag = .forever_stmt,
.data = .{ .un = body },
+ .loc = @enumFromInt(kw_for),
});
- } else return try p.addNode(.{ .tag = .for_stmt, .data = .{ .if3 = .{
- .cond = body,
- .body = (try p.addList(&.{ init.node, cond.node, incr.node })).start,
- } } });
+ } else return try p.addNode(.{
+ .tag = .for_stmt,
+ .data = .{ .if3 = .{
+ .cond = body,
+ .body = (try p.addList(&.{ init.node, cond.node, incr.node })).start,
+ } },
+ .loc = @enumFromInt(kw_for),
+ });
}
if (p.eatToken(.keyword_goto)) |goto_tok| {
if (p.eatToken(.asterisk)) |_| {
@@ -4338,7 +4482,7 @@ fn stmt(p: *Parser) Error!NodeIndex {
}
}
- try e.un(p, .computed_goto_stmt);
+ try e.un(p, .computed_goto_stmt, goto_tok);
_ = try p.expectToken(.semicolon);
return e.node;
}
@@ -4351,17 +4495,18 @@ fn stmt(p: *Parser) Error!NodeIndex {
return try p.addNode(.{
.tag = .goto_stmt,
.data = .{ .decl_ref = name_tok },
+ .loc = @enumFromInt(goto_tok),
});
}
if (p.eatToken(.keyword_continue)) |cont| {
if (!p.in_loop) try p.errTok(.continue_not_in_loop, cont);
_ = try p.expectToken(.semicolon);
- return try p.addNode(.{ .tag = .continue_stmt, .data = undefined });
+ return try p.addNode(.{ .tag = .continue_stmt, .data = undefined, .loc = @enumFromInt(cont) });
}
if (p.eatToken(.keyword_break)) |br| {
if (!p.in_loop and p.@"switch" == null) try p.errTok(.break_not_in_loop_or_switch, br);
_ = try p.expectToken(.semicolon);
- return try p.addNode(.{ .tag = .break_stmt, .data = undefined });
+ return try p.addNode(.{ .tag = .break_stmt, .data = undefined, .loc = @enumFromInt(br) });
}
if (try p.returnStmt()) |some| return some;
if (try p.assembly(.stmt)) |some| return some;
@@ -4380,8 +4525,8 @@ fn stmt(p: *Parser) Error!NodeIndex {
defer p.attr_buf.len = attr_buf_top;
try p.attributeSpecifier();
- if (p.eatToken(.semicolon)) |_| {
- var null_node: Tree.Node = .{ .tag = .null_stmt, .data = undefined };
+ if (p.eatToken(.semicolon)) |semicolon| {
+ var null_node: Tree.Node = .{ .tag = .null_stmt, .data = undefined, .loc = @enumFromInt(semicolon) };
null_node.ty = try Attribute.applyStatementAttributes(p, null_node.ty, expr_start, attr_buf_top);
return p.addNode(null_node);
}
@@ -4422,6 +4567,7 @@ fn labeledStmt(p: *Parser) Error!?NodeIndex {
var labeled_stmt = Tree.Node{
.tag = .labeled_stmt,
.data = .{ .decl = .{ .name = name_tok, .node = try p.labelableStmt() } },
+ .loc = @enumFromInt(name_tok),
};
labeled_stmt.ty = try Attribute.applyLabelAttributes(p, labeled_stmt.ty, attr_buf_top);
return try p.addNode(labeled_stmt);
@@ -4464,9 +4610,11 @@ fn labeledStmt(p: *Parser) Error!?NodeIndex {
if (second_item) |some| return try p.addNode(.{
.tag = .case_range_stmt,
.data = .{ .if3 = .{ .cond = s, .body = (try p.addList(&.{ first_item.node, some.node })).start } },
+ .loc = @enumFromInt(case),
}) else return try p.addNode(.{
.tag = .case_stmt,
.data = .{ .bin = .{ .lhs = first_item.node, .rhs = s } },
+ .loc = @enumFromInt(case),
});
} else if (p.eatToken(.keyword_default)) |default| {
_ = try p.expectToken(.colon);
@@ -4474,6 +4622,7 @@ fn labeledStmt(p: *Parser) Error!?NodeIndex {
const node = try p.addNode(.{
.tag = .default_stmt,
.data = .{ .un = s },
+ .loc = @enumFromInt(default),
});
const @"switch" = p.@"switch" orelse {
try p.errStr(.case_not_in_switch, default, "default");
@@ -4492,7 +4641,7 @@ fn labeledStmt(p: *Parser) Error!?NodeIndex {
fn labelableStmt(p: *Parser) Error!NodeIndex {
if (p.tok_ids[p.tok_i] == .r_brace) {
try p.err(.label_compound_end);
- return p.addNode(.{ .tag = .null_stmt, .data = undefined });
+ return p.addNode(.{ .tag = .null_stmt, .data = undefined, .loc = @enumFromInt(p.tok_i) });
}
return p.stmt();
}
@@ -4557,6 +4706,7 @@ fn compoundStmt(p: *Parser, is_fn_body: bool, stmt_expr_state: ?*StmtExprState)
else => {},
}
}
+ const r_brace = p.tok_i - 1;
if (noreturn_index) |some| {
// if new labels were defined we cannot be certain that the code is unreachable
@@ -4580,7 +4730,7 @@ fn compoundStmt(p: *Parser, is_fn_body: bool, stmt_expr_state: ?*StmtExprState)
try p.errStr(.func_does_not_return, p.tok_i - 1, func_name);
}
}
- try p.decl_buf.append(try p.addNode(.{ .tag = .implicit_return, .ty = p.func.ty.?.returnType(), .data = .{ .return_zero = return_zero } }));
+ try p.decl_buf.append(try p.addNode(.{ .tag = .implicit_return, .ty = p.func.ty.?.returnType(), .data = .{ .return_zero = return_zero }, .loc = @enumFromInt(r_brace) }));
}
if (p.func.ident) |some| try p.decl_buf.insert(decl_buf_top, some.node);
if (p.func.pretty_ident) |some| try p.decl_buf.insert(decl_buf_top, some.node);
@@ -4588,13 +4738,14 @@ fn compoundStmt(p: *Parser, is_fn_body: bool, stmt_expr_state: ?*StmtExprState)
var node: Tree.Node = .{
.tag = .compound_stmt_two,
- .data = .{ .bin = .{ .lhs = .none, .rhs = .none } },
+ .data = .{ .two = .{ .none, .none } },
+ .loc = @enumFromInt(l_brace),
};
const statements = p.decl_buf.items[decl_buf_top..];
switch (statements.len) {
0 => {},
- 1 => node.data = .{ .bin = .{ .lhs = statements[0], .rhs = .none } },
- 2 => node.data = .{ .bin = .{ .lhs = statements[0], .rhs = statements[1] } },
+ 1 => node.data = .{ .two = .{ statements[0], .none } },
+ 2 => node.data = .{ .two = .{ statements[0], statements[1] } },
else => {
node.tag = .compound_stmt;
node.data = .{ .range = try p.addList(statements) };
@@ -4618,8 +4769,8 @@ fn nodeIsNoreturn(p: *Parser, node: NodeIndex) NoreturnKind {
},
.compound_stmt_two => {
const data = p.nodes.items(.data)[@intFromEnum(node)];
- const lhs_type = if (data.bin.lhs != .none) p.nodeIsNoreturn(data.bin.lhs) else .no;
- const rhs_type = if (data.bin.rhs != .none) p.nodeIsNoreturn(data.bin.rhs) else .no;
+ const lhs_type = if (data.two[0] != .none) p.nodeIsNoreturn(data.two[0]) else .no;
+ const rhs_type = if (data.two[1] != .none) p.nodeIsNoreturn(data.two[1]) else .no;
if (lhs_type == .complex or rhs_type == .complex) return .complex;
if (lhs_type == .yes or rhs_type == .yes) return .yes;
return .no;
@@ -4704,6 +4855,8 @@ fn nextStmt(p: *Parser, l_brace: TokenIndex) !void {
.keyword_int,
.keyword_long,
.keyword_signed,
+ .keyword_signed1,
+ .keyword_signed2,
.keyword_unsigned,
.keyword_float,
.keyword_double,
@@ -4743,17 +4896,17 @@ fn returnStmt(p: *Parser) Error!?NodeIndex {
if (e.node == .none) {
if (!ret_ty.is(.void)) try p.errStr(.func_should_return, ret_tok, p.tokSlice(p.func.name));
- return try p.addNode(.{ .tag = .return_stmt, .data = .{ .un = e.node } });
+ return try p.addNode(.{ .tag = .return_stmt, .data = .{ .un = e.node }, .loc = @enumFromInt(ret_tok) });
} else if (ret_ty.is(.void)) {
try p.errStr(.void_func_returns_value, e_tok, p.tokSlice(p.func.name));
- return try p.addNode(.{ .tag = .return_stmt, .data = .{ .un = e.node } });
+ return try p.addNode(.{ .tag = .return_stmt, .data = .{ .un = e.node }, .loc = @enumFromInt(ret_tok) });
}
try e.lvalConversion(p);
try e.coerce(p, ret_ty, e_tok, .ret);
try e.saveValue(p);
- return try p.addNode(.{ .tag = .return_stmt, .data = .{ .un = e.node } });
+ return try p.addNode(.{ .tag = .return_stmt, .data = .{ .un = e.node }, .loc = @enumFromInt(ret_tok) });
}
// ====== expressions ======
@@ -4802,7 +4955,6 @@ const CallExpr = union(enum) {
}
fn shouldPromoteVarArg(self: CallExpr, arg_idx: u32) bool {
- @setEvalBranchQuota(2000);
return switch (self) {
.standard => true,
.builtin => |builtin| switch (builtin.tag) {
@@ -4810,10 +4962,13 @@ const CallExpr = union(enum) {
Builtin.tagFromName("__va_start").?,
Builtin.tagFromName("va_start").?,
=> arg_idx != 1,
- Builtin.tagFromName("__builtin_complex").?,
Builtin.tagFromName("__builtin_add_overflow").?,
- Builtin.tagFromName("__builtin_sub_overflow").?,
+ Builtin.tagFromName("__builtin_complex").?,
+ Builtin.tagFromName("__builtin_isinf").?,
+ Builtin.tagFromName("__builtin_isinf_sign").?,
Builtin.tagFromName("__builtin_mul_overflow").?,
+ Builtin.tagFromName("__builtin_isnan").?,
+ Builtin.tagFromName("__builtin_sub_overflow").?,
=> false,
else => true,
},
@@ -4827,7 +4982,6 @@ const CallExpr = union(enum) {
}
fn checkVarArg(self: CallExpr, p: *Parser, first_after: TokenIndex, param_tok: TokenIndex, arg: *Result, arg_idx: u32) !void {
- @setEvalBranchQuota(10_000);
if (self == .standard) return;
const builtin_tok = p.nodes.items(.data)[@intFromEnum(self.builtin.node)].decl.name;
@@ -4852,13 +5006,15 @@ const CallExpr = union(enum) {
/// of arguments, `paramCountOverride` is used to tell us how many arguments we should actually expect to see for
/// these custom-typechecked functions.
fn paramCountOverride(self: CallExpr) ?u32 {
- @setEvalBranchQuota(10_000);
return switch (self) {
.standard => null,
.builtin => |builtin| switch (builtin.tag) {
Builtin.tagFromName("__c11_atomic_thread_fence").?,
Builtin.tagFromName("__c11_atomic_signal_fence").?,
Builtin.tagFromName("__c11_atomic_is_lock_free").?,
+ Builtin.tagFromName("__builtin_isinf").?,
+ Builtin.tagFromName("__builtin_isinf_sign").?,
+ Builtin.tagFromName("__builtin_isnan").?,
=> 1,
Builtin.tagFromName("__builtin_complex").?,
@@ -4903,7 +5059,6 @@ const CallExpr = union(enum) {
}
fn returnType(self: CallExpr, p: *Parser, callable_ty: Type) Type {
- @setEvalBranchQuota(6000);
return switch (self) {
.standard => callable_ty.returnType(),
.builtin => |builtin| switch (builtin.tag) {
@@ -4977,12 +5132,12 @@ const CallExpr = union(enum) {
var call_node: Tree.Node = .{
.tag = .call_expr_one,
.ty = ret_ty,
- .data = .{ .bin = .{ .lhs = func_node, .rhs = .none } },
+ .data = .{ .two = .{ func_node, .none } },
};
const args = p.list_buf.items[list_buf_top..];
switch (arg_count) {
0 => {},
- 1 => call_node.data.bin.rhs = args[1], // args[0] == func.node
+ 1 => call_node.data.two[1] = args[1], // args[0] == func.node
else => {
call_node.tag = .call_expr;
call_node.data = .{ .range = try p.addList(args) };
@@ -5005,7 +5160,8 @@ const CallExpr = union(enum) {
call_node.data = .{ .range = try p.addList(args) };
},
}
- return Result{ .node = builtin.node, .ty = ret_ty };
+ const val = try evalBuiltin(builtin.tag, p, args[1..]);
+ return Result{ .node = builtin.node, .ty = ret_ty, .val = val };
},
}
}
@@ -5016,6 +5172,8 @@ pub const Result = struct {
ty: Type = .{ .specifier = .int },
val: Value = .{},
+ const invalid: Result = .{ .ty = Type.invalid };
+
pub fn str(res: Result, p: *Parser) ![]const u8 {
switch (res.val.opt_ref) {
.none => return "(none)",
@@ -5073,30 +5231,21 @@ pub const Result = struct {
.post_inc_expr,
.post_dec_expr,
=> return,
- .call_expr_one => {
- const fn_ptr = p.nodes.items(.data)[@intFromEnum(cur_node)].bin.lhs;
- const fn_ty = p.nodes.items(.ty)[@intFromEnum(fn_ptr)].elemType();
- const cast_info = p.nodes.items(.data)[@intFromEnum(fn_ptr)].cast.operand;
- const decl_ref = p.nodes.items(.data)[@intFromEnum(cast_info)].decl_ref;
- if (fn_ty.hasAttribute(.nodiscard)) try p.errStr(.nodiscard_unused, expr_start, p.tokSlice(decl_ref));
- if (fn_ty.hasAttribute(.warn_unused_result)) try p.errStr(.warn_unused_result, expr_start, p.tokSlice(decl_ref));
- return;
- },
- .call_expr => {
- const fn_ptr = p.data.items[p.nodes.items(.data)[@intFromEnum(cur_node)].range.start];
- const fn_ty = p.nodes.items(.ty)[@intFromEnum(fn_ptr)].elemType();
- const cast_info = p.nodes.items(.data)[@intFromEnum(fn_ptr)].cast.operand;
- const decl_ref = p.nodes.items(.data)[@intFromEnum(cast_info)].decl_ref;
- if (fn_ty.hasAttribute(.nodiscard)) try p.errStr(.nodiscard_unused, expr_start, p.tokSlice(decl_ref));
- if (fn_ty.hasAttribute(.warn_unused_result)) try p.errStr(.warn_unused_result, expr_start, p.tokSlice(decl_ref));
+ .call_expr, .call_expr_one => {
+ const tmp_tree = p.tmpTree();
+ const child_nodes = tmp_tree.childNodes(cur_node);
+ const fn_ptr = child_nodes[0];
+ const call_info = tmp_tree.callableResultUsage(fn_ptr) orelse return;
+ if (call_info.nodiscard) try p.errStr(.nodiscard_unused, expr_start, p.tokSlice(call_info.tok));
+ if (call_info.warn_unused_result) try p.errStr(.warn_unused_result, expr_start, p.tokSlice(call_info.tok));
return;
},
.stmt_expr => {
const body = p.nodes.items(.data)[@intFromEnum(cur_node)].un;
switch (p.nodes.items(.tag)[@intFromEnum(body)]) {
.compound_stmt_two => {
- const body_stmt = p.nodes.items(.data)[@intFromEnum(body)].bin;
- cur_node = if (body_stmt.rhs != .none) body_stmt.rhs else body_stmt.lhs;
+ const body_stmt = p.nodes.items(.data)[@intFromEnum(body)].two;
+ cur_node = if (body_stmt[1] != .none) body_stmt[1] else body_stmt[0];
},
.compound_stmt => {
const data = p.nodes.items(.data)[@intFromEnum(body)];
@@ -5112,29 +5261,31 @@ pub const Result = struct {
try p.errTok(.unused_value, expr_start);
}
- fn boolRes(lhs: *Result, p: *Parser, tag: Tree.Tag, rhs: Result) !void {
+ fn boolRes(lhs: *Result, p: *Parser, tag: Tree.Tag, rhs: Result, tok_i: TokenIndex) !void {
if (lhs.val.opt_ref == .null) {
lhs.val = Value.zero;
}
if (lhs.ty.specifier != .invalid) {
lhs.ty = Type.int;
}
- return lhs.bin(p, tag, rhs);
+ return lhs.bin(p, tag, rhs, tok_i);
}
- fn bin(lhs: *Result, p: *Parser, tag: Tree.Tag, rhs: Result) !void {
+ fn bin(lhs: *Result, p: *Parser, tag: Tree.Tag, rhs: Result, tok_i: TokenIndex) !void {
lhs.node = try p.addNode(.{
.tag = tag,
.ty = lhs.ty,
.data = .{ .bin = .{ .lhs = lhs.node, .rhs = rhs.node } },
+ .loc = @enumFromInt(tok_i),
});
}
- fn un(operand: *Result, p: *Parser, tag: Tree.Tag) Error!void {
+ fn un(operand: *Result, p: *Parser, tag: Tree.Tag, tok_i: TokenIndex) Error!void {
operand.node = try p.addNode(.{
.tag = tag,
.ty = operand.ty,
.data = .{ .un = operand.node },
+ .loc = @enumFromInt(tok_i),
});
}
@@ -5368,10 +5519,14 @@ pub const Result = struct {
fn lvalConversion(res: *Result, p: *Parser) Error!void {
if (res.ty.isFunc()) {
- const elem_ty = try p.arena.create(Type);
- elem_ty.* = res.ty;
- res.ty.specifier = .pointer;
- res.ty.data = .{ .sub_type = elem_ty };
+ if (res.ty.isInvalidFunc()) {
+ res.ty = .{ .specifier = .invalid };
+ } else {
+ const elem_ty = try p.arena.create(Type);
+ elem_ty.* = res.ty;
+ res.ty.specifier = .pointer;
+ res.ty.data = .{ .sub_type = elem_ty };
+ }
try res.implicitCast(p, .function_to_pointer);
} else if (res.ty.isArray()) {
res.val = .{};
@@ -5455,7 +5610,14 @@ pub const Result = struct {
try res.implicitCast(p, .complex_float_to_complex_int);
}
} else if (!res.ty.eql(int_ty, p.comp, true)) {
- try res.val.intCast(int_ty, p.comp);
+ const old_val = res.val;
+ const value_change_kind = try res.val.intCast(int_ty, p.comp);
+ switch (value_change_kind) {
+ .none => {},
+ .truncated => try p.errStr(.int_value_changed, tok, try p.valueChangedStr(res, old_val, int_ty)),
+ .sign_changed => try p.errStr(.sign_conversion, tok, try p.typePairStrExtra(res.ty, " to ", int_ty)),
+ }
+
const old_real = res.ty.isReal();
const new_real = int_ty.isReal();
if (old_real and new_real) {
@@ -5486,8 +5648,8 @@ pub const Result = struct {
.none => return p.errStr(.float_to_int, tok, try p.typePairStrExtra(res.ty, " to ", int_ty)),
.out_of_range => return p.errStr(.float_out_of_range, tok, try p.typePairStrExtra(res.ty, " to ", int_ty)),
.overflow => return p.errStr(.float_overflow_conversion, tok, try p.typePairStrExtra(res.ty, " to ", int_ty)),
- .nonzero_to_zero => return p.errStr(.float_zero_conversion, tok, try p.floatValueChangedStr(res, old_value, int_ty)),
- .value_changed => return p.errStr(.float_value_changed, tok, try p.floatValueChangedStr(res, old_value, int_ty)),
+ .nonzero_to_zero => return p.errStr(.float_zero_conversion, tok, try p.valueChangedStr(res, old_value, int_ty)),
+ .value_changed => return p.errStr(.float_value_changed, tok, try p.valueChangedStr(res, old_value, int_ty)),
}
}
@@ -5555,7 +5717,7 @@ pub const Result = struct {
res.ty = ptr_ty;
try res.implicitCast(p, .bool_to_pointer);
} else if (res.ty.isInt()) {
- try res.val.intCast(ptr_ty, p.comp);
+ _ = try res.val.intCast(ptr_ty, p.comp);
res.ty = ptr_ty;
try res.implicitCast(p, .int_to_pointer);
}
@@ -5620,16 +5782,14 @@ pub const Result = struct {
// if either is a float cast to that type
if (a.ty.isFloat() or b.ty.isFloat()) {
- const float_types = [7][2]Type.Specifier{
+ const float_types = [6][2]Type.Specifier{
.{ .complex_long_double, .long_double },
.{ .complex_float128, .float128 },
- .{ .complex_float80, .float80 },
.{ .complex_double, .double },
.{ .complex_float, .float },
// No `_Complex __fp16` type
.{ .invalid, .fp16 },
- // No `_Complex _Float16`
- .{ .invalid, .float16 },
+ .{ .complex_float16, .float16 },
};
const a_spec = a.ty.canonicalize(.standard).specifier;
const b_spec = b.ty.canonicalize(.standard).specifier;
@@ -5647,7 +5807,7 @@ pub const Result = struct {
if (try a.floatConversion(b, a_spec, b_spec, p, float_types[3])) return;
if (try a.floatConversion(b, a_spec, b_spec, p, float_types[4])) return;
if (try a.floatConversion(b, a_spec, b_spec, p, float_types[5])) return;
- if (try a.floatConversion(b, a_spec, b_spec, p, float_types[6])) return;
+ unreachable;
}
if (a.ty.eql(b.ty, p.comp, true)) {
@@ -5875,6 +6035,10 @@ pub const Result = struct {
if (to.is(.bool)) {
res.val.boolCast(p.comp);
} else if (old_float and new_int) {
+ if (to.hasIncompleteSize()) {
+ try p.errStr(.cast_to_incomplete_type, l_paren, try p.typeStr(to));
+ return error.ParsingFailed;
+ }
// Explicit cast, no conversion warning
_ = try res.val.floatToInt(to, p.comp);
} else if (new_float and old_int) {
@@ -5886,7 +6050,7 @@ pub const Result = struct {
try p.errStr(.cast_to_incomplete_type, l_paren, try p.typeStr(to));
return error.ParsingFailed;
}
- try res.val.intCast(to, p.comp);
+ _ = try res.val.intCast(to, p.comp);
}
} else if (to.get(.@"union")) |union_ty| {
if (union_ty.data.record.hasFieldOfType(res.ty, p.comp)) {
@@ -5918,12 +6082,13 @@ pub const Result = struct {
.tag = .explicit_cast,
.ty = res.ty,
.data = .{ .cast = .{ .operand = res.node, .kind = cast_kind } },
+ .loc = @enumFromInt(l_paren),
});
}
fn intFitsInType(res: Result, p: *Parser, ty: Type) !bool {
- const max_int = try Value.int(ty.maxInt(p.comp), p.comp);
- const min_int = try Value.int(ty.minInt(p.comp), p.comp);
+ const max_int = try Value.maxInt(ty, p.comp);
+ const min_int = try Value.minInt(ty, p.comp);
return res.val.compare(.lte, max_int, p.comp) and
(res.ty.isUnsignedInt(p.comp) or res.val.compare(.gte, min_int, p.comp));
}
@@ -6091,7 +6256,7 @@ fn expr(p: *Parser) Error!Result {
var err_start = p.comp.diagnostics.list.items.len;
var lhs = try p.assignExpr();
if (p.tok_ids[p.tok_i] == .comma) try lhs.expect(p);
- while (p.eatToken(.comma)) |_| {
+ while (p.eatToken(.comma)) |comma| {
try lhs.maybeWarnUnused(p, expr_start, err_start);
expr_start = p.tok_i;
err_start = p.comp.diagnostics.list.items.len;
@@ -6101,7 +6266,7 @@ fn expr(p: *Parser) Error!Result {
try rhs.lvalConversion(p);
lhs.val = rhs.val;
lhs.ty = rhs.ty;
- try lhs.bin(p, .comma_expr, rhs);
+ try lhs.bin(p, .comma_expr, rhs, comma);
}
return lhs;
}
@@ -6183,7 +6348,7 @@ fn assignExpr(p: *Parser) Error!Result {
}
}
_ = try lhs_copy.adjustTypes(tok, &rhs, p, if (tag == .mod_assign_expr) .integer else .arithmetic);
- try lhs.bin(p, tag, rhs);
+ try lhs.bin(p, tag, rhs, bit_or.?);
return lhs;
},
.sub_assign_expr,
@@ -6194,7 +6359,7 @@ fn assignExpr(p: *Parser) Error!Result {
} else {
_ = try lhs_copy.adjustTypes(tok, &rhs, p, .arithmetic);
}
- try lhs.bin(p, tag, rhs);
+ try lhs.bin(p, tag, rhs, bit_or.?);
return lhs;
},
.shl_assign_expr,
@@ -6204,7 +6369,7 @@ fn assignExpr(p: *Parser) Error!Result {
.bit_or_assign_expr,
=> {
_ = try lhs_copy.adjustTypes(tok, &rhs, p, .integer);
- try lhs.bin(p, tag, rhs);
+ try lhs.bin(p, tag, rhs, bit_or.?);
return lhs;
},
else => unreachable,
@@ -6212,7 +6377,7 @@ fn assignExpr(p: *Parser) Error!Result {
try rhs.coerce(p, lhs.ty, tok, .assign);
- try lhs.bin(p, tag, rhs);
+ try lhs.bin(p, tag, rhs, bit_or.?);
return lhs;
}
@@ -6280,6 +6445,7 @@ fn condExpr(p: *Parser) Error!Result {
.tag = .binary_cond_expr,
.ty = cond.ty,
.data = .{ .if3 = .{ .cond = cond.node, .body = (try p.addList(&.{ cond_then.node, then_expr.node })).start } },
+ .loc = @enumFromInt(cond_tok),
});
return cond;
}
@@ -6305,6 +6471,7 @@ fn condExpr(p: *Parser) Error!Result {
.tag = .cond_expr,
.ty = cond.ty,
.data = .{ .if3 = .{ .cond = cond.node, .body = (try p.addList(&.{ then_expr.node, else_expr.node })).start } },
+ .loc = @enumFromInt(cond_tok),
});
return cond;
}
@@ -6324,8 +6491,10 @@ fn lorExpr(p: *Parser) Error!Result {
if (try lhs.adjustTypes(tok, &rhs, p, .boolean_logic)) {
const res = lhs.val.toBool(p.comp) or rhs.val.toBool(p.comp);
lhs.val = Value.fromBool(res);
+ } else {
+ lhs.val.boolCast(p.comp);
}
- try lhs.boolRes(p, .bool_or_expr, rhs);
+ try lhs.boolRes(p, .bool_or_expr, rhs, tok);
}
return lhs;
}
@@ -6345,8 +6514,10 @@ fn landExpr(p: *Parser) Error!Result {
if (try lhs.adjustTypes(tok, &rhs, p, .boolean_logic)) {
const res = lhs.val.toBool(p.comp) and rhs.val.toBool(p.comp);
lhs.val = Value.fromBool(res);
+ } else {
+ lhs.val.boolCast(p.comp);
}
- try lhs.boolRes(p, .bool_and_expr, rhs);
+ try lhs.boolRes(p, .bool_and_expr, rhs, tok);
}
return lhs;
}
@@ -6362,7 +6533,7 @@ fn orExpr(p: *Parser) Error!Result {
if (try lhs.adjustTypes(tok, &rhs, p, .integer)) {
lhs.val = try lhs.val.bitOr(rhs.val, p.comp);
}
- try lhs.bin(p, .bit_or_expr, rhs);
+ try lhs.bin(p, .bit_or_expr, rhs, tok);
}
return lhs;
}
@@ -6378,7 +6549,7 @@ fn xorExpr(p: *Parser) Error!Result {
if (try lhs.adjustTypes(tok, &rhs, p, .integer)) {
lhs.val = try lhs.val.bitXor(rhs.val, p.comp);
}
- try lhs.bin(p, .bit_xor_expr, rhs);
+ try lhs.bin(p, .bit_xor_expr, rhs, tok);
}
return lhs;
}
@@ -6394,7 +6565,7 @@ fn andExpr(p: *Parser) Error!Result {
if (try lhs.adjustTypes(tok, &rhs, p, .integer)) {
lhs.val = try lhs.val.bitAnd(rhs.val, p.comp);
}
- try lhs.bin(p, .bit_and_expr, rhs);
+ try lhs.bin(p, .bit_and_expr, rhs, tok);
}
return lhs;
}
@@ -6414,8 +6585,10 @@ fn eqExpr(p: *Parser) Error!Result {
const op: std.math.CompareOperator = if (tag == .equal_expr) .eq else .neq;
const res = lhs.val.compare(op, rhs.val, p.comp);
lhs.val = Value.fromBool(res);
+ } else {
+ lhs.val.boolCast(p.comp);
}
- try lhs.boolRes(p, tag, rhs);
+ try lhs.boolRes(p, tag, rhs, ne.?);
}
return lhs;
}
@@ -6443,8 +6616,10 @@ fn compExpr(p: *Parser) Error!Result {
};
const res = lhs.val.compare(op, rhs.val, p.comp);
lhs.val = Value.fromBool(res);
+ } else {
+ lhs.val.boolCast(p.comp);
}
- try lhs.boolRes(p, tag, rhs);
+ try lhs.boolRes(p, tag, rhs, ge.?);
}
return lhs;
}
@@ -6474,7 +6649,7 @@ fn shiftExpr(p: *Parser) Error!Result {
lhs.val = try lhs.val.shr(rhs.val, lhs.ty, p.comp);
}
}
- try lhs.bin(p, tag, rhs);
+ try lhs.bin(p, tag, rhs, shr.?);
}
return lhs;
}
@@ -6504,7 +6679,7 @@ fn addExpr(p: *Parser) Error!Result {
try p.errStr(.ptr_arithmetic_incomplete, minus.?, try p.typeStr(lhs_ty.elemType()));
lhs.ty = Type.invalid;
}
- try lhs.bin(p, tag, rhs);
+ try lhs.bin(p, tag, rhs, minus.?);
}
return lhs;
}
@@ -6538,7 +6713,7 @@ fn mulExpr(p: *Parser) Error!Result {
lhs.ty.signedness(p.comp) != .unsigned) try p.errOverflow(mul.?, lhs);
} else if (div != null) {
if (try lhs.val.div(lhs.val, rhs.val, lhs.ty, p.comp) and
- lhs.ty.signedness(p.comp) != .unsigned) try p.errOverflow(mul.?, lhs);
+ lhs.ty.signedness(p.comp) != .unsigned) try p.errOverflow(div.?, lhs);
} else {
var res = try Value.rem(lhs.val, rhs.val, lhs.ty, p.comp);
if (res.opt_ref == .none) {
@@ -6554,7 +6729,7 @@ fn mulExpr(p: *Parser) Error!Result {
}
}
- try lhs.bin(p, tag, rhs);
+ try lhs.bin(p, tag, rhs, percent.?);
}
return lhs;
}
@@ -6573,7 +6748,7 @@ fn removeUnusedWarningForTok(p: *Parser, last_expr_tok: TokenIndex) void {
}
/// castExpr
-/// : '(' compoundStmt ')'
+/// : '(' compoundStmt ')' suffixExpr*
/// | '(' typeName ')' castExpr
/// | '(' typeName ')' '{' initializerItems '}'
/// | __builtin_choose_expr '(' integerConstExpr ',' assignExpr ',' assignExpr ')'
@@ -6584,6 +6759,7 @@ fn removeUnusedWarningForTok(p: *Parser, last_expr_tok: TokenIndex) void {
fn castExpr(p: *Parser) Error!Result {
if (p.eatToken(.l_paren)) |l_paren| cast_expr: {
if (p.tok_ids[p.tok_i] == .l_brace) {
+ const tok = p.tok_i;
try p.err(.gnu_statement_expression);
if (p.func.ty == null) {
try p.err(.stmt_expr_not_allowed_file_scope);
@@ -6599,7 +6775,12 @@ fn castExpr(p: *Parser) Error!Result {
.val = stmt_expr_state.last_expr_res.val,
};
try p.expectClosing(l_paren, .r_paren);
- try res.un(p, .stmt_expr);
+ try res.un(p, .stmt_expr, tok);
+ while (true) {
+ const suffix = try p.suffixExpr(res);
+ if (suffix.empty(p)) break;
+ res = suffix;
+ }
return res;
}
const ty = (try p.typeName()) orelse {
@@ -6634,23 +6815,26 @@ fn castExpr(p: *Parser) Error!Result {
}
fn typesCompatible(p: *Parser) Error!Result {
+ const builtin_tok = p.tok_i;
p.tok_i += 1;
const l_paren = try p.expectToken(.l_paren);
+ const first_tok = p.tok_i;
const first = (try p.typeName()) orelse {
try p.err(.expected_type);
p.skipTo(.r_paren);
return error.ParsingFailed;
};
- const lhs = try p.addNode(.{ .tag = .invalid, .ty = first, .data = undefined });
+ const lhs = try p.addNode(.{ .tag = .invalid, .ty = first, .data = undefined, .loc = @enumFromInt(first_tok) });
_ = try p.expectToken(.comma);
+ const second_tok = p.tok_i;
const second = (try p.typeName()) orelse {
try p.err(.expected_type);
p.skipTo(.r_paren);
return error.ParsingFailed;
};
- const rhs = try p.addNode(.{ .tag = .invalid, .ty = second, .data = undefined });
+ const rhs = try p.addNode(.{ .tag = .invalid, .ty = second, .data = undefined, .loc = @enumFromInt(second_tok) });
try p.expectClosing(l_paren, .r_paren);
@@ -6665,10 +6849,15 @@ fn typesCompatible(p: *Parser) Error!Result {
const res = Result{
.val = Value.fromBool(compatible),
- .node = try p.addNode(.{ .tag = .builtin_types_compatible_p, .ty = Type.int, .data = .{ .bin = .{
- .lhs = lhs,
- .rhs = rhs,
- } } }),
+ .node = try p.addNode(.{
+ .tag = .builtin_types_compatible_p,
+ .ty = Type.int,
+ .data = .{ .bin = .{
+ .lhs = lhs,
+ .rhs = rhs,
+ } },
+ .loc = @enumFromInt(builtin_tok),
+ }),
};
try p.value_map.put(res.node, res.val);
return res;
@@ -6786,11 +6975,11 @@ fn offsetofMemberDesignator(p: *Parser, base_ty: Type, want_bits: bool) Error!Re
errdefer p.skipTo(.r_paren);
const base_field_name_tok = try p.expectIdentifier();
const base_field_name = try StrInt.intern(p.comp, p.tokSlice(base_field_name_tok));
- try p.validateFieldAccess(base_ty, base_ty, base_field_name_tok, base_field_name);
+ const base_record_ty = base_ty.getRecord().?;
+ try p.validateFieldAccess(base_record_ty, base_ty, base_field_name_tok, base_field_name);
const base_node = try p.addNode(.{ .tag = .default_init_expr, .ty = base_ty, .data = undefined });
var cur_offset: u64 = 0;
- const base_record_ty = base_ty.canonicalize(.standard);
var lhs = try p.fieldAccessExtra(base_node, base_record_ty, base_field_name, false, &cur_offset);
var total_offset = cur_offset;
@@ -6800,13 +6989,12 @@ fn offsetofMemberDesignator(p: *Parser, base_ty: Type, want_bits: bool) Error!Re
const field_name_tok = try p.expectIdentifier();
const field_name = try StrInt.intern(p.comp, p.tokSlice(field_name_tok));
- if (!lhs.ty.isRecord()) {
+ const lhs_record_ty = lhs.ty.getRecord() orelse {
try p.errStr(.offsetof_ty, field_name_tok, try p.typeStr(lhs.ty));
return error.ParsingFailed;
- }
- try p.validateFieldAccess(lhs.ty, lhs.ty, field_name_tok, field_name);
- const record_ty = lhs.ty.canonicalize(.standard);
- lhs = try p.fieldAccessExtra(lhs.node, record_ty, field_name, false, &cur_offset);
+ };
+ try p.validateFieldAccess(lhs_record_ty, lhs.ty, field_name_tok, field_name);
+ lhs = try p.fieldAccessExtra(lhs.node, lhs_record_ty, field_name, false, &cur_offset);
total_offset += cur_offset;
},
.l_bracket => {
@@ -6824,11 +7012,14 @@ fn offsetofMemberDesignator(p: *Parser, base_ty: Type, want_bits: bool) Error!Re
try ptr.lvalConversion(p);
try index.lvalConversion(p);
- if (!index.ty.isInt()) try p.errTok(.invalid_index, l_bracket_tok);
- try p.checkArrayBounds(index, lhs, l_bracket_tok);
+ if (index.ty.isInt()) {
+ try p.checkArrayBounds(index, lhs, l_bracket_tok);
+ } else {
+ try p.errTok(.invalid_index, l_bracket_tok);
+ }
try index.saveValue(p);
- try ptr.bin(p, .array_access_expr, index);
+ try ptr.bin(p, .array_access_expr, index, l_bracket_tok);
lhs = ptr;
},
else => break,
@@ -6867,6 +7058,7 @@ fn unExpr(p: *Parser) Error!Result {
.tag = .addr_of_label,
.data = .{ .decl_ref = name_tok },
.ty = result_ty,
+ .loc = @enumFromInt(address_tok),
}),
.ty = result_ty,
};
@@ -6886,19 +7078,21 @@ fn unExpr(p: *Parser) Error!Result {
{
if (tree.isBitfield(member_node)) try p.errTok(.addr_of_bitfield, tok);
}
- if (!tree.isLval(operand.node)) {
+ if (!tree.isLval(operand.node) and !operand.ty.is(.invalid)) {
try p.errTok(.addr_of_rvalue, tok);
}
if (operand.ty.qual.register) try p.errTok(.addr_of_register, tok);
- const elem_ty = try p.arena.create(Type);
- elem_ty.* = operand.ty;
- operand.ty = Type{
- .specifier = .pointer,
- .data = .{ .sub_type = elem_ty },
- };
+ if (!operand.ty.is(.invalid)) {
+ const elem_ty = try p.arena.create(Type);
+ elem_ty.* = operand.ty;
+ operand.ty = Type{
+ .specifier = .pointer,
+ .data = .{ .sub_type = elem_ty },
+ };
+ }
try operand.saveValue(p);
- try operand.un(p, .addr_of_expr);
+ try operand.un(p, .addr_of_expr, tok);
return operand;
},
.asterisk => {
@@ -6917,7 +7111,7 @@ fn unExpr(p: *Parser) Error!Result {
try p.errStr(.deref_incomplete_ty_ptr, asterisk_loc, try p.typeStr(operand.ty));
}
operand.ty.qual = .{};
- try operand.un(p, .deref_expr);
+ try operand.un(p, .deref_expr, tok);
return operand;
},
.plus => {
@@ -6943,12 +7137,12 @@ fn unExpr(p: *Parser) Error!Result {
try p.errStr(.invalid_argument_un, tok, try p.typeStr(operand.ty));
try operand.usualUnaryConversion(p, tok);
- if (operand.val.is(.int, p.comp) or operand.val.is(.float, p.comp)) {
+ if (operand.val.isArithmetic(p.comp)) {
_ = try operand.val.sub(Value.zero, operand.val, operand.ty, p.comp);
} else {
operand.val = .{};
}
- try operand.un(p, .negate_expr);
+ try operand.un(p, .negate_expr, tok);
return operand;
},
.plus_plus => {
@@ -6974,7 +7168,7 @@ fn unExpr(p: *Parser) Error!Result {
operand.val = .{};
}
- try operand.un(p, .pre_inc_expr);
+ try operand.un(p, .pre_inc_expr, tok);
return operand;
},
.minus_minus => {
@@ -7000,7 +7194,7 @@ fn unExpr(p: *Parser) Error!Result {
operand.val = .{};
}
- try operand.un(p, .pre_dec_expr);
+ try operand.un(p, .pre_dec_expr, tok);
return operand;
},
.tilde => {
@@ -7016,11 +7210,14 @@ fn unExpr(p: *Parser) Error!Result {
}
} else if (operand.ty.isComplex()) {
try p.errStr(.complex_conj, tok, try p.typeStr(operand.ty));
+ if (operand.val.is(.complex, p.comp)) {
+ operand.val = try operand.val.complexConj(operand.ty, p.comp);
+ }
} else {
try p.errStr(.invalid_argument_un, tok, try p.typeStr(operand.ty));
operand.val = .{};
}
- try operand.un(p, .bit_not_expr);
+ try operand.un(p, .bit_not_expr, tok);
return operand;
},
.bang => {
@@ -7045,7 +7242,7 @@ fn unExpr(p: *Parser) Error!Result {
}
}
operand.ty = .{ .specifier = .int };
- try operand.un(p, .bool_not_expr);
+ try operand.un(p, .bool_not_expr, tok);
return operand;
},
.keyword_sizeof => {
@@ -7089,7 +7286,7 @@ fn unExpr(p: *Parser) Error!Result {
res.ty = p.comp.types.size;
}
}
- try res.un(p, .sizeof_expr);
+ try res.un(p, .sizeof_expr, tok);
return res;
},
.keyword_alignof,
@@ -7127,7 +7324,7 @@ fn unExpr(p: *Parser) Error!Result {
try p.errStr(.invalid_alignof, expected_paren, try p.typeStr(res.ty));
res.ty = Type.invalid;
}
- try res.un(p, .alignof_expr);
+ try res.un(p, .alignof_expr, tok);
return res;
},
.keyword_extension => {
@@ -7147,15 +7344,18 @@ fn unExpr(p: *Parser) Error!Result {
var operand = try p.castExpr();
try operand.expect(p);
try operand.lvalConversion(p);
+ if (operand.ty.is(.invalid)) return Result.invalid;
if (!operand.ty.isInt() and !operand.ty.isFloat()) {
try p.errStr(.invalid_imag, imag_tok, try p.typeStr(operand.ty));
}
- if (operand.ty.isReal()) {
+ if (operand.ty.isComplex()) {
+ operand.val = try operand.val.imaginaryPart(p.comp);
+ } else if (operand.ty.isReal()) {
switch (p.comp.langopts.emulate) {
.msvc => {}, // Doesn't support `_Complex` or `__imag` in the first place
.gcc => operand.val = Value.zero,
.clang => {
- if (operand.val.is(.int, p.comp)) {
+ if (operand.val.is(.int, p.comp) or operand.val.is(.float, p.comp)) {
operand.val = Value.zero;
} else {
operand.val = .{};
@@ -7165,7 +7365,7 @@ fn unExpr(p: *Parser) Error!Result {
}
// convert _Complex T to T
operand.ty = operand.ty.makeReal();
- try operand.un(p, .imag_expr);
+ try operand.un(p, .imag_expr, tok);
return operand;
},
.keyword_real1, .keyword_real2 => {
@@ -7175,12 +7375,14 @@ fn unExpr(p: *Parser) Error!Result {
var operand = try p.castExpr();
try operand.expect(p);
try operand.lvalConversion(p);
+ if (operand.ty.is(.invalid)) return Result.invalid;
if (!operand.ty.isInt() and !operand.ty.isFloat()) {
try p.errStr(.invalid_real, real_tok, try p.typeStr(operand.ty));
}
// convert _Complex T to T
operand.ty = operand.ty.makeReal();
- try operand.un(p, .real_expr);
+ operand.val = try operand.val.realPart(p.comp);
+ try operand.un(p, .real_expr, tok);
return operand;
},
else => {
@@ -7253,7 +7455,7 @@ fn compoundLiteral(p: *Parser) Error!Result {
if (d.constexpr) |_| {
// TODO error if not constexpr
}
- try init_list_expr.un(p, tag);
+ try init_list_expr.un(p, tag, l_paren);
return init_list_expr;
}
@@ -7284,7 +7486,7 @@ fn suffixExpr(p: *Parser, lhs: Result) Error!Result {
}
try operand.usualUnaryConversion(p, p.tok_i);
- try operand.un(p, .post_inc_expr);
+ try operand.un(p, .post_inc_expr, p.tok_i);
return operand;
},
.minus_minus => {
@@ -7302,7 +7504,7 @@ fn suffixExpr(p: *Parser, lhs: Result) Error!Result {
}
try operand.usualUnaryConversion(p, p.tok_i);
- try operand.un(p, .post_dec_expr);
+ try operand.un(p, .post_dec_expr, p.tok_i);
return operand;
},
.l_bracket => {
@@ -7319,12 +7521,18 @@ fn suffixExpr(p: *Parser, lhs: Result) Error!Result {
try index.lvalConversion(p);
if (ptr.ty.isPtr()) {
ptr.ty = ptr.ty.elemType();
- if (!index.ty.isInt()) try p.errTok(.invalid_index, l_bracket);
- try p.checkArrayBounds(index_before_conversion, array_before_conversion, l_bracket);
+ if (index.ty.isInt()) {
+ try p.checkArrayBounds(index_before_conversion, array_before_conversion, l_bracket);
+ } else {
+ try p.errTok(.invalid_index, l_bracket);
+ }
} else if (index.ty.isPtr()) {
index.ty = index.ty.elemType();
- if (!ptr.ty.isInt()) try p.errTok(.invalid_index, l_bracket);
- try p.checkArrayBounds(array_before_conversion, index_before_conversion, l_bracket);
+ if (ptr.ty.isInt()) {
+ try p.checkArrayBounds(array_before_conversion, index_before_conversion, l_bracket);
+ } else {
+ try p.errTok(.invalid_index, l_bracket);
+ }
std.mem.swap(Result, &ptr, &index);
} else {
try p.errTok(.invalid_subscript, l_bracket);
@@ -7332,7 +7540,7 @@ fn suffixExpr(p: *Parser, lhs: Result) Error!Result {
try ptr.saveValue(p);
try index.saveValue(p);
- try ptr.bin(p, .array_access_expr, index);
+ try ptr.bin(p, .array_access_expr, index, l_bracket);
return ptr;
},
.period => {
@@ -7364,16 +7572,12 @@ fn fieldAccess(
const expr_ty = lhs.ty;
const is_ptr = expr_ty.isPtr();
const expr_base_ty = if (is_ptr) expr_ty.elemType() else expr_ty;
- const record_ty = expr_base_ty.canonicalize(.standard);
+ const record_ty = expr_base_ty.getRecord() orelse {
+ try p.errStr(.expected_record_ty, field_name_tok, try p.typeStr(expr_ty));
+ return error.ParsingFailed;
+ };
- switch (record_ty.specifier) {
- .@"struct", .@"union" => {},
- else => {
- try p.errStr(.expected_record_ty, field_name_tok, try p.typeStr(expr_ty));
- return error.ParsingFailed;
- },
- }
- if (record_ty.hasIncompleteSize()) {
+ if (record_ty.isIncomplete()) {
try p.errStr(.deref_incomplete_ty_ptr, field_name_tok - 2, try p.typeStr(expr_base_ty));
return error.ParsingFailed;
}
@@ -7386,7 +7590,7 @@ fn fieldAccess(
return p.fieldAccessExtra(lhs.node, record_ty, field_name, is_arrow, &discard);
}
-fn validateFieldAccess(p: *Parser, record_ty: Type, expr_ty: Type, field_name_tok: TokenIndex, field_name: StringId) Error!void {
+fn validateFieldAccess(p: *Parser, record_ty: *const Type.Record, expr_ty: Type, field_name_tok: TokenIndex, field_name: StringId) Error!void {
if (record_ty.hasField(field_name)) return;
p.strings.items.len = 0;
@@ -7401,8 +7605,8 @@ fn validateFieldAccess(p: *Parser, record_ty: Type, expr_ty: Type, field_name_to
return error.ParsingFailed;
}
-fn fieldAccessExtra(p: *Parser, lhs: NodeIndex, record_ty: Type, field_name: StringId, is_arrow: bool, offset_bits: *u64) Error!Result {
- for (record_ty.data.record.fields, 0..) |f, i| {
+fn fieldAccessExtra(p: *Parser, lhs: NodeIndex, record_ty: *const Type.Record, field_name: StringId, is_arrow: bool, offset_bits: *u64) Error!Result {
+ for (record_ty.fields, 0..) |f, i| {
if (f.isAnonymousRecord()) {
if (!f.ty.hasField(field_name)) continue;
const inner = try p.addNode(.{
@@ -7410,7 +7614,7 @@ fn fieldAccessExtra(p: *Parser, lhs: NodeIndex, record_ty: Type, field_name: Str
.ty = f.ty,
.data = .{ .member = .{ .lhs = lhs, .index = @intCast(i) } },
});
- const ret = p.fieldAccessExtra(inner, f.ty, field_name, false, offset_bits);
+ const ret = p.fieldAccessExtra(inner, f.ty.getRecord().?, field_name, false, offset_bits);
offset_bits.* = f.layout.offset_bits;
return ret;
}
@@ -7527,6 +7731,23 @@ fn callExpr(p: *Parser, lhs: Result) Error!Result {
continue;
}
const p_ty = params[arg_count].ty;
+ if (p_ty.specifier == .static_array) {
+ const arg_array_len: u64 = arg.ty.arrayLen() orelse std.math.maxInt(u64);
+ const param_array_len: u64 = p_ty.arrayLen().?;
+ if (arg_array_len < param_array_len) {
+ const extra = Diagnostics.Message.Extra{ .arguments = .{
+ .expected = @intCast(arg_array_len),
+ .actual = @intCast(param_array_len),
+ } };
+ try p.errExtra(.array_argument_too_small, param_tok, extra);
+ try p.errTok(.callee_with_static_array, params[arg_count].name_tok);
+ }
+ if (arg.val.isZero(p.comp)) {
+ try p.errTok(.non_null_argument, param_tok);
+ try p.errTok(.callee_with_static_array, params[arg_count].name_tok);
+ }
+ }
+
if (call_expr.shouldCoerceArg(arg_count)) {
try arg.coerce(p, p_ty, param_tok, .{ .arg = params[arg_count].name_tok });
}
@@ -7618,7 +7839,7 @@ fn primaryExpr(p: *Parser) Error!Result {
var e = try p.expr();
try e.expect(p);
try p.expectClosing(l_paren, .r_paren);
- try e.un(p, .paren_expr);
+ try e.un(p, .paren_expr, l_paren);
return e;
}
switch (p.tok_ids[p.tok_i]) {
@@ -7626,6 +7847,10 @@ fn primaryExpr(p: *Parser) Error!Result {
const name_tok = try p.expectIdentifier();
const name = p.tokSlice(name_tok);
const interned_name = try StrInt.intern(p.comp, name);
+ if (interned_name == p.auto_type_decl_name) {
+ try p.errStr(.auto_type_self_initialized, name_tok, name);
+ return error.ParsingFailed;
+ }
if (p.syms.findSymbol(interned_name)) |sym| {
try p.checkDeprecatedUnavailable(sym.ty, name_tok, sym.tok);
if (sym.kind == .constexpr) {
@@ -7636,6 +7861,7 @@ fn primaryExpr(p: *Parser) Error!Result {
.tag = .decl_ref_expr,
.ty = sym.ty,
.data = .{ .decl_ref = name_tok },
+ .loc = @enumFromInt(name_tok),
}),
};
}
@@ -7653,6 +7879,7 @@ fn primaryExpr(p: *Parser) Error!Result {
.tag = if (sym.kind == .enumeration) .enumeration_ref else .decl_ref_expr,
.ty = sym.ty,
.data = .{ .decl_ref = name_tok },
+ .loc = @enumFromInt(name_tok),
}),
};
}
@@ -7679,6 +7906,7 @@ fn primaryExpr(p: *Parser) Error!Result {
.tag = .builtin_call_expr_one,
.ty = some.ty,
.data = .{ .decl = .{ .name = name_tok, .node = .none } },
+ .loc = @enumFromInt(name_tok),
}),
};
}
@@ -7696,6 +7924,7 @@ fn primaryExpr(p: *Parser) Error!Result {
.ty = ty,
.tag = .fn_proto,
.data = .{ .decl = .{ .name = name_tok } },
+ .loc = @enumFromInt(name_tok),
});
try p.decl_buf.append(node);
@@ -7707,6 +7936,7 @@ fn primaryExpr(p: *Parser) Error!Result {
.tag = .decl_ref_expr,
.ty = ty,
.data = .{ .decl_ref = name_tok },
+ .loc = @enumFromInt(name_tok),
}),
};
}
@@ -7714,11 +7944,12 @@ fn primaryExpr(p: *Parser) Error!Result {
return error.ParsingFailed;
},
.keyword_true, .keyword_false => |id| {
+ const tok_i = p.tok_i;
p.tok_i += 1;
const res = Result{
.val = Value.fromBool(id == .keyword_true),
.ty = .{ .specifier = .bool },
- .node = try p.addNode(.{ .tag = .bool_literal, .ty = .{ .specifier = .bool }, .data = undefined }),
+ .node = try p.addNode(.{ .tag = .bool_literal, .ty = .{ .specifier = .bool }, .data = undefined, .loc = @enumFromInt(tok_i) }),
};
std.debug.assert(!p.in_macro); // Should have been replaced with .one / .zero
try p.value_map.put(res.node, res.val);
@@ -7734,6 +7965,7 @@ fn primaryExpr(p: *Parser) Error!Result {
.tag = .nullptr_literal,
.ty = .{ .specifier = .nullptr_t },
.data = undefined,
+ .loc = @enumFromInt(p.tok_i),
}),
};
},
@@ -7770,6 +8002,7 @@ fn primaryExpr(p: *Parser) Error!Result {
.tag = .decl_ref_expr,
.ty = ty,
.data = .{ .decl_ref = tok },
+ .loc = @enumFromInt(tok),
}),
};
},
@@ -7805,6 +8038,7 @@ fn primaryExpr(p: *Parser) Error!Result {
.tag = .decl_ref_expr,
.ty = ty,
.data = .{ .decl_ref = p.tok_i },
+ .loc = @enumFromInt(p.tok_i),
}),
};
},
@@ -7824,16 +8058,16 @@ fn primaryExpr(p: *Parser) Error!Result {
.unterminated_char_literal,
=> return p.charLiteral(),
.zero => {
- p.tok_i += 1;
+ defer p.tok_i += 1;
var res: Result = .{ .val = Value.zero, .ty = if (p.in_macro) p.comp.types.intmax else Type.int };
- res.node = try p.addNode(.{ .tag = .int_literal, .ty = res.ty, .data = undefined });
+ res.node = try p.addNode(.{ .tag = .int_literal, .ty = res.ty, .data = undefined, .loc = @enumFromInt(p.tok_i) });
if (!p.in_macro) try p.value_map.put(res.node, res.val);
return res;
},
.one => {
- p.tok_i += 1;
+ defer p.tok_i += 1;
var res: Result = .{ .val = Value.one, .ty = if (p.in_macro) p.comp.types.intmax else Type.int };
- res.node = try p.addNode(.{ .tag = .int_literal, .ty = res.ty, .data = undefined });
+ res.node = try p.addNode(.{ .tag = .int_literal, .ty = res.ty, .data = undefined, .loc = @enumFromInt(p.tok_i) });
if (!p.in_macro) try p.value_map.put(res.node, res.val);
return res;
},
@@ -7841,7 +8075,7 @@ fn primaryExpr(p: *Parser) Error!Result {
.embed_byte => {
assert(!p.in_macro);
const loc = p.pp.tokens.items(.loc)[p.tok_i];
- p.tok_i += 1;
+ defer p.tok_i += 1;
const buf = p.comp.getSource(.generated).buf[loc.byte_offset..];
var byte: u8 = buf[0] - '0';
for (buf[1..]) |c| {
@@ -7850,7 +8084,7 @@ fn primaryExpr(p: *Parser) Error!Result {
byte += c - '0';
}
var res: Result = .{ .val = try Value.int(byte, p.comp) };
- res.node = try p.addNode(.{ .tag = .int_literal, .ty = res.ty, .data = undefined });
+ res.node = try p.addNode(.{ .tag = .int_literal, .ty = res.ty, .data = undefined, .loc = @enumFromInt(p.tok_i) });
try p.value_map.put(res.node, res.val);
return res;
},
@@ -7869,17 +8103,19 @@ fn makePredefinedIdentifier(p: *Parser, strings_top: usize) !Result {
const slice = p.strings.items[strings_top..];
const val = try Value.intern(p.comp, .{ .bytes = slice });
- const str_lit = try p.addNode(.{ .tag = .string_literal_expr, .ty = ty, .data = undefined });
+ const str_lit = try p.addNode(.{ .tag = .string_literal_expr, .ty = ty, .data = undefined, .loc = @enumFromInt(p.tok_i) });
if (!p.in_macro) try p.value_map.put(str_lit, val);
return Result{ .ty = ty, .node = try p.addNode(.{
.tag = .implicit_static_var,
.ty = ty,
.data = .{ .decl = .{ .name = p.tok_i, .node = str_lit } },
+ .loc = @enumFromInt(p.tok_i),
}) };
}
fn stringLiteral(p: *Parser) Error!Result {
+ const string_start = p.tok_i;
var string_end = p.tok_i;
var string_kind: text_literal.Kind = .char;
while (text_literal.Kind.classify(p.tok_ids[string_end], .string_literal)) |next| : (string_end += 1) {
@@ -7894,13 +8130,17 @@ fn stringLiteral(p: *Parser) Error!Result {
return error.ParsingFailed;
}
}
- assert(string_end > p.tok_i);
+ const count = string_end - p.tok_i;
+ assert(count > 0);
const char_width = string_kind.charUnitSize(p.comp);
const strings_top = p.strings.items.len;
defer p.strings.items.len = strings_top;
+ const literal_start = mem.alignForward(usize, strings_top, @intFromEnum(char_width));
+ try p.strings.resize(literal_start);
+
while (p.tok_i < string_end) : (p.tok_i += 1) {
const this_kind = text_literal.Kind.classify(p.tok_ids[p.tok_i], .string_literal).?;
const slice = this_kind.contentSlice(p.tokSlice(p.tok_i));
@@ -7940,12 +8180,18 @@ fn stringLiteral(p: *Parser) Error!Result {
},
}
},
- .improperly_encoded => |bytes| p.strings.appendSliceAssumeCapacity(bytes),
+ .improperly_encoded => |bytes| {
+ if (count > 1) {
+ try p.errTok(.illegal_char_encoding_error, p.tok_i);
+ return error.ParsingFailed;
+ }
+ p.strings.appendSliceAssumeCapacity(bytes);
+ },
.utf8_text => |view| {
switch (char_width) {
.@"1" => p.strings.appendSliceAssumeCapacity(view.bytes),
.@"2" => {
- const capacity_slice: []align(@alignOf(u16)) u8 = @alignCast(p.strings.unusedCapacitySlice());
+ const capacity_slice: []align(@alignOf(u16)) u8 = @alignCast(p.strings.allocatedSlice()[literal_start..]);
const dest_len = std.mem.alignBackward(usize, capacity_slice.len, 2);
const dest = std.mem.bytesAsSlice(u16, capacity_slice[0..dest_len]);
const words_written = std.unicode.utf8ToUtf16Le(dest, view.bytes) catch unreachable;
@@ -7966,7 +8212,7 @@ fn stringLiteral(p: *Parser) Error!Result {
}
}
p.strings.appendNTimesAssumeCapacity(0, @intFromEnum(char_width));
- const slice = p.strings.items[strings_top..];
+ const slice = p.strings.items[literal_start..];
// TODO this won't do anything if there is a cache hit
const interned_align = mem.alignForward(
@@ -7987,7 +8233,7 @@ fn stringLiteral(p: *Parser) Error!Result {
},
.val = val,
};
- res.node = try p.addNode(.{ .tag = .string_literal_expr, .ty = res.ty, .data = undefined });
+ res.node = try p.addNode(.{ .tag = .string_literal_expr, .ty = res.ty, .data = undefined, .loc = @enumFromInt(string_start) });
if (!p.in_macro) try p.value_map.put(res.node, res.val);
return res;
}
@@ -8004,7 +8250,7 @@ fn charLiteral(p: *Parser) Error!Result {
return .{
.ty = Type.int,
.val = Value.zero,
- .node = try p.addNode(.{ .tag = .char_literal, .ty = Type.int, .data = undefined }),
+ .node = try p.addNode(.{ .tag = .char_literal, .ty = Type.int, .data = undefined, .loc = @enumFromInt(p.tok_i) }),
};
};
if (char_kind == .utf_8) try p.err(.u8_char_lit);
@@ -8013,7 +8259,7 @@ fn charLiteral(p: *Parser) Error!Result {
const slice = char_kind.contentSlice(p.tokSlice(p.tok_i));
var is_multichar = false;
- if (slice.len == 1 and std.ascii.isAscii(slice[0])) {
+ if (slice.len == 1 and std.ascii.isASCII(slice[0])) {
// fast path: single unescaped ASCII char
val = slice[0];
} else {
@@ -8096,25 +8342,25 @@ fn charLiteral(p: *Parser) Error!Result {
// > that of the single character or escape sequence is converted to type int.
// This conversion only matters if `char` is signed and has a high-order bit of `1`
if (char_kind == .char and !is_multichar and val > 0x7F and p.comp.getCharSignedness() == .signed) {
- try value.intCast(.{ .specifier = .char }, p.comp);
+ _ = try value.intCast(.{ .specifier = .char }, p.comp);
}
const res = Result{
.ty = if (p.in_macro) macro_ty else ty,
.val = value,
- .node = try p.addNode(.{ .tag = .char_literal, .ty = ty, .data = undefined }),
+ .node = try p.addNode(.{ .tag = .char_literal, .ty = ty, .data = undefined, .loc = @enumFromInt(p.tok_i) }),
};
if (!p.in_macro) try p.value_map.put(res.node, res.val);
return res;
}
-fn parseFloat(p: *Parser, buf: []const u8, suffix: NumberSuffix) !Result {
+fn parseFloat(p: *Parser, buf: []const u8, suffix: NumberSuffix, tok_i: TokenIndex) !Result {
const ty = Type{ .specifier = switch (suffix) {
.None, .I => .double,
.F, .IF => .float,
- .F16 => .float16,
+ .F16, .IF16 => .float16,
.L, .IL => .long_double,
- .W, .IW => .float80,
+ .W, .IW => p.comp.float80Type().?.specifier,
.Q, .IQ, .F128, .IF128 => .float128,
else => unreachable,
} };
@@ -8140,21 +8386,29 @@ fn parseFloat(p: *Parser, buf: []const u8, suffix: NumberSuffix) !Result {
});
var res = Result{
.ty = ty,
- .node = try p.addNode(.{ .tag = .float_literal, .ty = ty, .data = undefined }),
+ .node = try p.addNode(.{ .tag = .float_literal, .ty = ty, .data = undefined, .loc = @enumFromInt(tok_i) }),
.val = val,
};
if (suffix.isImaginary()) {
try p.err(.gnu_imaginary_constant);
res.ty = .{ .specifier = switch (suffix) {
.I => .complex_double,
+ .IF16 => .complex_float16,
.IF => .complex_float,
.IL => .complex_long_double,
- .IW => .complex_float80,
+ .IW => p.comp.float80Type().?.makeComplex().specifier,
.IQ, .IF128 => .complex_float128,
else => unreachable,
} };
- res.val = .{}; // TODO add complex values
- try res.un(p, .imaginary_literal);
+ res.val = try Value.intern(p.comp, switch (res.ty.bitSizeof(p.comp).?) {
+ 32 => .{ .complex = .{ .cf16 = .{ 0.0, val.toFloat(f16, p.comp) } } },
+ 64 => .{ .complex = .{ .cf32 = .{ 0.0, val.toFloat(f32, p.comp) } } },
+ 128 => .{ .complex = .{ .cf64 = .{ 0.0, val.toFloat(f64, p.comp) } } },
+ 160 => .{ .complex = .{ .cf80 = .{ 0.0, val.toFloat(f80, p.comp) } } },
+ 256 => .{ .complex = .{ .cf128 = .{ 0.0, val.toFloat(f128, p.comp) } } },
+ else => unreachable,
+ });
+ try res.un(p, .imaginary_literal, tok_i);
}
return res;
}
@@ -8233,12 +8487,14 @@ fn fixedSizeInt(p: *Parser, base: u8, buf: []const u8, suffix: NumberSuffix, tok
if (overflow) {
try p.errTok(.int_literal_too_big, tok_i);
res.ty = .{ .specifier = .ulong_long };
- res.node = try p.addNode(.{ .tag = .int_literal, .ty = res.ty, .data = undefined });
+ res.node = try p.addNode(.{ .tag = .int_literal, .ty = res.ty, .data = undefined, .loc = @enumFromInt(tok_i) });
if (!p.in_macro) try p.value_map.put(res.node, res.val);
return res;
}
+ const interned_val = try Value.int(val, p.comp);
if (suffix.isSignedInteger()) {
- if (val > p.comp.types.intmax.maxInt(p.comp)) {
+ const max_int = try Value.maxInt(p.comp.types.intmax, p.comp);
+ if (interned_val.compare(.gt, max_int, p.comp)) {
try p.errTok(.implicitly_unsigned_literal, tok_i);
}
}
@@ -8266,13 +8522,23 @@ fn fixedSizeInt(p: *Parser, base: u8, buf: []const u8, suffix: NumberSuffix, tok
for (specs) |spec| {
res.ty = Type{ .specifier = spec };
if (res.ty.compareIntegerRanks(suffix_ty, p.comp).compare(.lt)) continue;
- const max_int = res.ty.maxInt(p.comp);
- if (val <= max_int) break;
+ const max_int = try Value.maxInt(res.ty, p.comp);
+ if (interned_val.compare(.lte, max_int, p.comp)) break;
} else {
- res.ty = .{ .specifier = .ulong_long };
+ res.ty = .{ .specifier = spec: {
+ if (p.comp.langopts.emulate == .gcc) {
+ if (target_util.hasInt128(p.comp.target)) {
+ break :spec .int128;
+ } else {
+ break :spec .long_long;
+ }
+ } else {
+ break :spec .ulong_long;
+ }
+ } };
}
- res.node = try p.addNode(.{ .tag = .int_literal, .ty = res.ty, .data = undefined });
+ res.node = try p.addNode(.{ .tag = .int_literal, .ty = res.ty, .data = undefined, .loc = @enumFromInt(tok_i) });
if (!p.in_macro) try p.value_map.put(res.node, res.val);
return res;
}
@@ -8291,7 +8557,7 @@ fn parseInt(p: *Parser, prefix: NumberPrefix, buf: []const u8, suffix: NumberSuf
try p.errTok(.gnu_imaginary_constant, tok_i);
res.ty = res.ty.makeComplex();
res.val = .{};
- try res.un(p, .imaginary_literal);
+ try res.un(p, .imaginary_literal, tok_i);
}
return res;
}
@@ -8326,17 +8592,6 @@ fn bitInt(p: *Parser, base: u8, buf: []const u8, suffix: NumberSuffix, tok_i: To
// value of the constant is positive or was specified in hexadecimal or octal notation.
const sign_bits = @intFromBool(suffix.isSignedInteger());
const bits_needed = count + sign_bits;
- if (bits_needed > Compilation.bit_int_max_bits) {
- const specifier: Type.Builder.Specifier = switch (suffix) {
- .WB => .{ .bit_int = 0 },
- .UWB => .{ .ubit_int = 0 },
- .IWB => .{ .complex_bit_int = 0 },
- .IUWB => .{ .complex_ubit_int = 0 },
- else => unreachable,
- };
- try p.errStr(.bit_int_too_big, tok_i, specifier.str(p.comp.langopts).?);
- return error.ParsingFailed;
- }
break :blk @intCast(bits_needed);
};
@@ -8347,7 +8602,7 @@ fn bitInt(p: *Parser, base: u8, buf: []const u8, suffix: NumberSuffix, tok_i: To
.data = .{ .int = .{ .bits = bits_needed, .signedness = suffix.signedness() } },
},
};
- res.node = try p.addNode(.{ .tag = .int_literal, .ty = res.ty, .data = undefined });
+ res.node = try p.addNode(.{ .tag = .int_literal, .ty = res.ty, .data = undefined, .loc = @enumFromInt(tok_i) });
if (!p.in_macro) try p.value_map.put(res.node, res.val);
return res;
}
@@ -8420,6 +8675,10 @@ pub fn parseNumberToken(p: *Parser, tok_i: TokenIndex) !Result {
}
return error.ParsingFailed;
};
+ if (suffix.isFloat80() and p.comp.float80Type() == null) {
+ try p.errStr(.invalid_float_suffix, tok_i, suffix_str);
+ return error.ParsingFailed;
+ }
if (is_float) {
assert(prefix == .hex or prefix == .decimal);
@@ -8428,7 +8687,7 @@ pub fn parseNumberToken(p: *Parser, tok_i: TokenIndex) !Result {
return error.ParsingFailed;
}
const number = buf[0 .. buf.len - suffix_str.len];
- return p.parseFloat(number, suffix);
+ return p.parseFloat(number, suffix, tok_i);
} else {
return p.parseInt(prefix, int_part, suffix, tok_i);
}
@@ -8444,7 +8703,6 @@ fn ppNum(p: *Parser) Error!Result {
}
res.ty = if (res.ty.isUnsignedInt(p.comp)) p.comp.types.intmax.makeIntegerUnsigned() else p.comp.types.intmax;
} else if (res.val.opt_ref != .none) {
- // TODO add complex values
try p.value_map.put(res.node, res.val);
}
return res;
@@ -8465,6 +8723,7 @@ fn parseNoEval(p: *Parser, comptime func: fn (*Parser) Error!Result) Error!Resul
/// : typeName ':' assignExpr
/// | keyword_default ':' assignExpr
fn genericSelection(p: *Parser) Error!Result {
+ const kw_generic = p.tok_i;
p.tok_i += 1;
const l_paren = try p.expectToken(.l_paren);
const controlling_tok = p.tok_i;
@@ -8508,17 +8767,23 @@ fn genericSelection(p: *Parser) Error!Result {
try p.errStr(.generic_duplicate, start, try p.typeStr(ty));
try p.errStr(.generic_duplicate_here, chosen_tok, try p.typeStr(ty));
}
- for (p.list_buf.items[list_buf_top + 1 ..], p.decl_buf.items[decl_buf_top..]) |item, prev_tok| {
- const prev_ty = p.nodes.items(.ty)[@intFromEnum(item)];
- if (prev_ty.eql(ty, p.comp, true)) {
- try p.errStr(.generic_duplicate, start, try p.typeStr(ty));
- try p.errStr(.generic_duplicate_here, @intFromEnum(prev_tok), try p.typeStr(ty));
+ const list_buf = p.list_buf.items[list_buf_top + 1 ..];
+ const decl_buf = p.decl_buf.items[decl_buf_top..];
+ if (list_buf.len == decl_buf.len) {
+ // If these do not have the same length, there is already an error
+ for (list_buf, decl_buf) |item, prev_tok| {
+ const prev_ty = p.nodes.items(.ty)[@intFromEnum(item)];
+ if (prev_ty.eql(ty, p.comp, true)) {
+ try p.errStr(.generic_duplicate, start, try p.typeStr(ty));
+ try p.errStr(.generic_duplicate_here, @intFromEnum(prev_tok), try p.typeStr(ty));
+ }
}
}
try p.list_buf.append(try p.addNode(.{
.tag = .generic_association_expr,
.ty = ty,
.data = .{ .un = node.node },
+ .loc = @enumFromInt(start),
}));
try p.decl_buf.append(@enumFromInt(start));
} else if (p.eatToken(.keyword_default)) |tok| {
@@ -8542,10 +8807,12 @@ fn genericSelection(p: *Parser) Error!Result {
try p.expectClosing(l_paren, .r_paren);
if (chosen.node == .none) {
- if (default_tok != null) {
+ if (default_tok) |tok| {
try p.list_buf.insert(list_buf_top + 1, try p.addNode(.{
.tag = .generic_default_expr,
.data = .{ .un = default.node },
+ .ty = default.ty,
+ .loc = @enumFromInt(tok),
}));
chosen = default;
} else {
@@ -8556,11 +8823,15 @@ fn genericSelection(p: *Parser) Error!Result {
try p.list_buf.insert(list_buf_top + 1, try p.addNode(.{
.tag = .generic_association_expr,
.data = .{ .un = chosen.node },
+ .ty = chosen.ty,
+ .loc = @enumFromInt(chosen_tok),
}));
- if (default_tok != null) {
+ if (default_tok) |tok| {
try p.list_buf.append(try p.addNode(.{
.tag = .generic_default_expr,
- .data = .{ .un = chosen.node },
+ .data = .{ .un = default.node },
+ .ty = default.ty,
+ .loc = @enumFromInt(tok),
}));
}
}
@@ -8568,7 +8839,8 @@ fn genericSelection(p: *Parser) Error!Result {
var generic_node: Tree.Node = .{
.tag = .generic_expr_one,
.ty = chosen.ty,
- .data = .{ .bin = .{ .lhs = controlling.node, .rhs = chosen.node } },
+ .data = .{ .two = .{ controlling.node, chosen.node } },
+ .loc = @enumFromInt(kw_generic),
};
const associations = p.list_buf.items[list_buf_top..];
if (associations.len > 2) { // associations[0] == controlling.node
@@ -8578,3 +8850,42 @@ fn genericSelection(p: *Parser) Error!Result {
chosen.node = try p.addNode(generic_node);
return chosen;
}
+
+test "Node locations" {
+ var comp = Compilation.init(std.testing.allocator, std.fs.cwd());
+ defer comp.deinit();
+
+ const file = try comp.addSourceFromBuffer("file.c",
+ \\int foo = 5;
+ \\int bar = 10;
+ \\int main(void) {}
+ \\
+ );
+
+ const builtin_macros = try comp.generateBuiltinMacros(.no_system_defines);
+
+ var pp = Preprocessor.init(&comp);
+ defer pp.deinit();
+ try pp.addBuiltinMacros();
+
+ _ = try pp.preprocess(builtin_macros);
+
+ const eof = try pp.preprocess(file);
+ try pp.addToken(eof);
+
+ var tree = try Parser.parse(&pp);
+ defer tree.deinit();
+
+ try std.testing.expectEqual(0, comp.diagnostics.list.items.len);
+ for (tree.root_decls, 0..) |node, i| {
+ const tok_i = tree.nodeTok(node).?;
+ const slice = tree.tokSlice(tok_i);
+ const expected = switch (i) {
+ 0 => "foo",
+ 1 => "bar",
+ 2 => "main",
+ else => unreachable,
+ };
+ try std.testing.expectEqualStrings(expected, slice);
+ }
+}
lib/compiler/aro/aro/Preprocessor.zig
@@ -97,6 +97,11 @@ poisoned_identifiers: std.StringHashMap(void),
/// Map from Source.Id to macro name in the `#ifndef` condition which guards the source, if any
include_guards: std.AutoHashMapUnmanaged(Source.Id, []const u8) = .{},
+/// Store `keyword_define` and `keyword_undef` tokens.
+/// Used to implement preprocessor debug dump options
+/// Must be false unless in -E mode (parser does not handle those token types)
+store_macro_tokens: bool = false,
+
/// Memory is retained to avoid allocation on every single token.
top_expansion_buf: ExpandBuf,
@@ -622,9 +627,12 @@ fn preprocessExtra(pp: *Preprocessor, source: Source) MacroError!TokenWithExpans
}
if_level -= 1;
},
- .keyword_define => try pp.define(&tokenizer),
+ .keyword_define => try pp.define(&tokenizer, directive),
.keyword_undef => {
const macro_name = (try pp.expectMacroName(&tokenizer)) orelse continue;
+ if (pp.store_macro_tokens) {
+ try pp.addToken(tokFromRaw(directive));
+ }
_ = pp.defines.remove(macro_name);
try pp.expectNl(&tokenizer);
@@ -975,7 +983,7 @@ fn expr(pp: *Preprocessor, tokenizer: *Tokenizer) MacroError!bool {
.tok_i = @intCast(token_state.tokens_len),
.arena = pp.arena.allocator(),
.in_macro = true,
- .strings = std.ArrayList(u8).init(pp.comp.gpa),
+ .strings = std.ArrayListAligned(u8, 4).init(pp.comp.gpa),
.data = undefined,
.value_map = undefined,
@@ -1328,19 +1336,41 @@ fn stringify(pp: *Preprocessor, tokens: []const TokenWithExpansionLocs) !void {
try pp.char_buf.append(c);
}
}
- if (pp.char_buf.items[pp.char_buf.items.len - 1] == '\\') {
+ try pp.char_buf.ensureUnusedCapacity(2);
+ if (pp.char_buf.items[pp.char_buf.items.len - 1] != '\\') {
+ pp.char_buf.appendSliceAssumeCapacity("\"\n");
+ return;
+ }
+ pp.char_buf.appendAssumeCapacity('"');
+ var tokenizer: Tokenizer = .{
+ .buf = pp.char_buf.items,
+ .index = 0,
+ .source = .generated,
+ .langopts = pp.comp.langopts,
+ .line = 0,
+ };
+ const item = tokenizer.next();
+ if (item.id == .unterminated_string_literal) {
const tok = tokens[tokens.len - 1];
try pp.comp.addDiagnostic(.{
.tag = .invalid_pp_stringify_escape,
.loc = tok.loc,
}, tok.expansionSlice());
- pp.char_buf.items.len -= 1;
+ pp.char_buf.items.len -= 2; // erase unpaired backslash and appended end quote
+ pp.char_buf.appendAssumeCapacity('"');
}
- try pp.char_buf.appendSlice("\"\n");
+ pp.char_buf.appendAssumeCapacity('\n');
}
fn reconstructIncludeString(pp: *Preprocessor, param_toks: []const TokenWithExpansionLocs, embed_args: ?*[]const TokenWithExpansionLocs, first: TokenWithExpansionLocs) !?[]const u8 {
- assert(param_toks.len != 0);
+ if (param_toks.len == 0) {
+ try pp.comp.addDiagnostic(.{
+ .tag = .expected_filename,
+ .loc = first.loc,
+ }, first.expansionSlice());
+ return null;
+ }
+
const char_top = pp.char_buf.items.len;
defer pp.char_buf.items.len = char_top;
@@ -1539,11 +1569,13 @@ fn getPasteArgs(args: []const TokenWithExpansionLocs) []const TokenWithExpansion
fn expandFuncMacro(
pp: *Preprocessor,
- loc: Source.Location,
+ macro_tok: TokenWithExpansionLocs,
func_macro: *const Macro,
args: *const MacroArguments,
expanded_args: *const MacroArguments,
+ hideset_arg: Hideset.Index,
) MacroError!ExpandBuf {
+ var hideset = hideset_arg;
var buf = ExpandBuf.init(pp.gpa);
try buf.ensureTotalCapacity(func_macro.tokens.len);
errdefer buf.deinit();
@@ -1594,16 +1626,21 @@ fn expandFuncMacro(
},
else => &[1]TokenWithExpansionLocs{tokFromRaw(raw_next)},
};
-
try pp.pasteTokens(&buf, next);
if (next.len != 0) break;
},
.macro_param_no_expand => {
+ if (tok_i + 1 < func_macro.tokens.len and func_macro.tokens[tok_i + 1].id == .hash_hash) {
+ hideset = pp.hideset.get(tokFromRaw(func_macro.tokens[tok_i + 1]).loc);
+ }
const slice = getPasteArgs(args.items[raw.end]);
const raw_loc = Source.Location{ .id = raw.source, .byte_offset = raw.start, .line = raw.line };
try bufCopyTokens(&buf, slice, &.{raw_loc});
},
.macro_param => {
+ if (tok_i + 1 < func_macro.tokens.len and func_macro.tokens[tok_i + 1].id == .hash_hash) {
+ hideset = pp.hideset.get(tokFromRaw(func_macro.tokens[tok_i + 1]).loc);
+ }
const arg = expanded_args.items[raw.end];
const raw_loc = Source.Location{ .id = raw.source, .byte_offset = raw.start, .line = raw.line };
try bufCopyTokens(&buf, arg, &.{raw_loc});
@@ -1642,9 +1679,9 @@ fn expandFuncMacro(
const arg = expanded_args.items[0];
const result = if (arg.len == 0) blk: {
const extra = Diagnostics.Message.Extra{ .arguments = .{ .expected = 1, .actual = 0 } };
- try pp.comp.addDiagnostic(.{ .tag = .expected_arguments, .loc = loc, .extra = extra }, &.{});
+ try pp.comp.addDiagnostic(.{ .tag = .expected_arguments, .loc = macro_tok.loc, .extra = extra }, &.{});
break :blk false;
- } else try pp.handleBuiltinMacro(raw.id, arg, loc);
+ } else try pp.handleBuiltinMacro(raw.id, arg, macro_tok.loc);
const start = pp.comp.generated_buf.items.len;
const w = pp.comp.generated_buf.writer(pp.gpa);
try w.print("{}\n", .{@intFromBool(result)});
@@ -1655,7 +1692,7 @@ fn expandFuncMacro(
const not_found = "0\n";
const result = if (arg.len == 0) blk: {
const extra = Diagnostics.Message.Extra{ .arguments = .{ .expected = 1, .actual = 0 } };
- try pp.comp.addDiagnostic(.{ .tag = .expected_arguments, .loc = loc, .extra = extra }, &.{});
+ try pp.comp.addDiagnostic(.{ .tag = .expected_arguments, .loc = macro_tok.loc, .extra = extra }, &.{});
break :blk not_found;
} else res: {
var invalid: ?TokenWithExpansionLocs = null;
@@ -1687,7 +1724,7 @@ fn expandFuncMacro(
if (vendor_ident != null and attr_ident == null) {
invalid = vendor_ident;
} else if (attr_ident == null and invalid == null) {
- invalid = .{ .id = .eof, .loc = loc };
+ invalid = .{ .id = .eof, .loc = macro_tok.loc };
}
if (invalid) |some| {
try pp.comp.addDiagnostic(
@@ -1731,7 +1768,7 @@ fn expandFuncMacro(
const not_found = "0\n";
const result = if (arg.len == 0) blk: {
const extra = Diagnostics.Message.Extra{ .arguments = .{ .expected = 1, .actual = 0 } };
- try pp.comp.addDiagnostic(.{ .tag = .expected_arguments, .loc = loc, .extra = extra }, &.{});
+ try pp.comp.addDiagnostic(.{ .tag = .expected_arguments, .loc = macro_tok.loc, .extra = extra }, &.{});
break :blk not_found;
} else res: {
var embed_args: []const TokenWithExpansionLocs = &.{};
@@ -1877,11 +1914,11 @@ fn expandFuncMacro(
break;
},
};
- if (string == null and invalid == null) invalid = .{ .loc = loc, .id = .eof };
+ if (string == null and invalid == null) invalid = .{ .loc = macro_tok.loc, .id = .eof };
if (invalid) |some| try pp.comp.addDiagnostic(
.{ .tag = .pragma_operator_string_literal, .loc = some.loc },
some.expansionSlice(),
- ) else try pp.pragmaOperator(string.?, loc);
+ ) else try pp.pragmaOperator(string.?, macro_tok.loc);
},
.comma => {
if (tok_i + 2 < func_macro.tokens.len and func_macro.tokens[tok_i + 1].id == .hash_hash) {
@@ -1930,6 +1967,15 @@ fn expandFuncMacro(
}
removePlacemarkers(&buf);
+ const macro_expansion_locs = macro_tok.expansionSlice();
+ for (buf.items) |*tok| {
+ try tok.addExpansionLocation(pp.gpa, &.{macro_tok.loc});
+ try tok.addExpansionLocation(pp.gpa, macro_expansion_locs);
+ const tok_hidelist = pp.hideset.get(tok.loc);
+ const new_hidelist = try pp.hideset.@"union"(tok_hidelist, hideset);
+ try pp.hideset.put(tok.loc, new_hidelist);
+ }
+
return buf;
}
@@ -2207,8 +2253,10 @@ fn expandMacroExhaustive(
else => |e| return e,
};
assert(r_paren.id == .r_paren);
+ var free_arg_expansion_locs = false;
defer {
for (args.items) |item| {
+ if (free_arg_expansion_locs) for (item) |tok| TokenWithExpansionLocs.free(tok.expansion_locs, pp.gpa);
pp.gpa.free(item);
}
args.deinit();
@@ -2234,6 +2282,7 @@ fn expandMacroExhaustive(
.arguments = .{ .expected = @intCast(macro.params.len), .actual = args_count },
};
if (macro.var_args and args_count < macro.params.len) {
+ free_arg_expansion_locs = true;
try pp.comp.addDiagnostic(
.{ .tag = .expected_at_least_arguments, .loc = buf.items[idx].loc, .extra = extra },
buf.items[idx].expansionSlice(),
@@ -2243,6 +2292,7 @@ fn expandMacroExhaustive(
continue;
}
if (!macro.var_args and args_count != macro.params.len) {
+ free_arg_expansion_locs = true;
try pp.comp.addDiagnostic(
.{ .tag = .expected_arguments, .loc = buf.items[idx].loc, .extra = extra },
buf.items[idx].expansionSlice(),
@@ -2264,19 +2314,9 @@ fn expandMacroExhaustive(
expanded_args.appendAssumeCapacity(try expand_buf.toOwnedSlice());
}
- var res = try pp.expandFuncMacro(macro_tok.loc, macro, &args, &expanded_args);
+ var res = try pp.expandFuncMacro(macro_tok, macro, &args, &expanded_args, hs);
defer res.deinit();
const tokens_added = res.items.len;
-
- const macro_expansion_locs = macro_tok.expansionSlice();
- for (res.items) |*tok| {
- try tok.addExpansionLocation(pp.gpa, &.{macro_tok.loc});
- try tok.addExpansionLocation(pp.gpa, macro_expansion_locs);
- const tok_hidelist = pp.hideset.get(tok.loc);
- const new_hidelist = try pp.hideset.@"union"(tok_hidelist, hs);
- try pp.hideset.put(tok.loc, new_hidelist);
- }
-
const tokens_removed = macro_scan_idx - idx + 1;
for (buf.items[idx .. idx + tokens_removed]) |tok| TokenWithExpansionLocs.free(tok.expansion_locs, pp.gpa);
try buf.replaceRange(idx, tokens_removed, res.items);
@@ -2476,7 +2516,7 @@ fn makeGeneratedToken(pp: *Preprocessor, start: usize, id: Token.Id, source: Tok
}
/// Defines a new macro and warns if it is a duplicate
-fn defineMacro(pp: *Preprocessor, name_tok: RawToken, macro: Macro) Error!void {
+fn defineMacro(pp: *Preprocessor, define_tok: RawToken, name_tok: RawToken, macro: Macro) Error!void {
const name_str = pp.tokSlice(name_tok);
const gop = try pp.defines.getOrPut(pp.gpa, name_str);
if (gop.found_existing and !gop.value_ptr.eql(macro, pp)) {
@@ -2497,11 +2537,14 @@ fn defineMacro(pp: *Preprocessor, name_tok: RawToken, macro: Macro) Error!void {
if (pp.verbose) {
pp.verboseLog(name_tok, "macro {s} defined", .{name_str});
}
+ if (pp.store_macro_tokens) {
+ try pp.addToken(tokFromRaw(define_tok));
+ }
gop.value_ptr.* = macro;
}
/// Handle a #define directive.
-fn define(pp: *Preprocessor, tokenizer: *Tokenizer) Error!void {
+fn define(pp: *Preprocessor, tokenizer: *Tokenizer, define_tok: RawToken) Error!void {
// Get macro name and validate it.
const macro_name = tokenizer.nextNoWS();
if (macro_name.id == .keyword_defined) {
@@ -2524,7 +2567,7 @@ fn define(pp: *Preprocessor, tokenizer: *Tokenizer) Error!void {
// Check for function macros and empty defines.
var first = tokenizer.next();
switch (first.id) {
- .nl, .eof => return pp.defineMacro(macro_name, .{
+ .nl, .eof => return pp.defineMacro(define_tok, macro_name, .{
.params = &.{},
.tokens = &.{},
.var_args = false,
@@ -2532,7 +2575,7 @@ fn define(pp: *Preprocessor, tokenizer: *Tokenizer) Error!void {
.is_func = false,
}),
.whitespace => first = tokenizer.next(),
- .l_paren => return pp.defineFn(tokenizer, macro_name, first),
+ .l_paren => return pp.defineFn(tokenizer, define_tok, macro_name, first),
else => try pp.err(first, .whitespace_after_macro_name),
}
if (first.id == .hash_hash) {
@@ -2591,7 +2634,7 @@ fn define(pp: *Preprocessor, tokenizer: *Tokenizer) Error!void {
}
const list = try pp.arena.allocator().dupe(RawToken, pp.token_buf.items);
- try pp.defineMacro(macro_name, .{
+ try pp.defineMacro(define_tok, macro_name, .{
.loc = tokFromRaw(macro_name).loc,
.tokens = list,
.params = undefined,
@@ -2601,7 +2644,7 @@ fn define(pp: *Preprocessor, tokenizer: *Tokenizer) Error!void {
}
/// Handle a function like #define directive.
-fn defineFn(pp: *Preprocessor, tokenizer: *Tokenizer, macro_name: RawToken, l_paren: RawToken) Error!void {
+fn defineFn(pp: *Preprocessor, tokenizer: *Tokenizer, define_tok: RawToken, macro_name: RawToken, l_paren: RawToken) Error!void {
assert(macro_name.id.isMacroIdentifier());
var params = std.ArrayList([]const u8).init(pp.gpa);
defer params.deinit();
@@ -2778,7 +2821,7 @@ fn defineFn(pp: *Preprocessor, tokenizer: *Tokenizer, macro_name: RawToken, l_pa
const param_list = try pp.arena.allocator().dupe([]const u8, params.items);
const token_list = try pp.arena.allocator().dupe(RawToken, pp.token_buf.items);
- try pp.defineMacro(macro_name, .{
+ try pp.defineMacro(define_tok, macro_name, .{
.is_func = true,
.params = param_list,
.var_args = var_args or gnu_var_args.len != 0,
@@ -3241,8 +3284,78 @@ fn printLinemarker(
// After how many empty lines are needed to replace them with linemarkers.
const collapse_newlines = 8;
+pub const DumpMode = enum {
+ /// Standard preprocessor output; no macros
+ result_only,
+ /// Output only #define directives for all the macros defined during the execution of the preprocessor
+ /// Only macros which are still defined at the end of preprocessing are printed.
+ /// Only the most recent definition is printed
+ /// Defines are printed in arbitrary order
+ macros_only,
+ /// Standard preprocessor output; but additionally output #define's and #undef's for macros as they are encountered
+ macros_and_result,
+ /// Same as macros_and_result, except only the macro name is printed for #define's
+ macro_names_and_result,
+};
+
+/// Pretty-print the macro define or undef at location `loc`.
+/// We re-tokenize the directive because we are printing a macro that may have the same name as one in
+/// `pp.defines` but a different definition (due to being #undef'ed and then redefined)
+fn prettyPrintMacro(pp: *Preprocessor, w: anytype, loc: Source.Location, parts: enum { name_only, name_and_body }) !void {
+ const source = pp.comp.getSource(loc.id);
+ var tokenizer: Tokenizer = .{
+ .buf = source.buf,
+ .langopts = pp.comp.langopts,
+ .source = source.id,
+ .index = loc.byte_offset,
+ };
+ var prev_ws = false; // avoid printing multiple whitespace if /* */ comments are within the macro def
+ var saw_name = false; // do not print comments before the name token is seen.
+ while (true) {
+ const tok = tokenizer.next();
+ switch (tok.id) {
+ .comment => {
+ if (saw_name) {
+ prev_ws = false;
+ try w.print("{s}", .{pp.tokSlice(tok)});
+ }
+ },
+ .nl, .eof => break,
+ .whitespace => {
+ if (!prev_ws) {
+ try w.writeByte(' ');
+ prev_ws = true;
+ }
+ },
+ else => {
+ prev_ws = false;
+ try w.print("{s}", .{pp.tokSlice(tok)});
+ },
+ }
+ if (tok.id == .identifier or tok.id == .extended_identifier) {
+ if (parts == .name_only) break;
+ saw_name = true;
+ }
+ }
+}
+
+fn prettyPrintMacrosOnly(pp: *Preprocessor, w: anytype) !void {
+ var it = pp.defines.valueIterator();
+ while (it.next()) |macro| {
+ if (macro.is_builtin) continue;
+
+ try w.writeAll("#define ");
+ try pp.prettyPrintMacro(w, macro.loc, .name_and_body);
+ try w.writeByte('\n');
+ }
+}
+
/// Pretty print tokens and try to preserve whitespace.
-pub fn prettyPrintTokens(pp: *Preprocessor, w: anytype) !void {
+pub fn prettyPrintTokens(pp: *Preprocessor, w: anytype, macro_dump_mode: DumpMode) !void {
+ if (macro_dump_mode == .macros_only) {
+ return pp.prettyPrintMacrosOnly(w);
+ }
+
const tok_ids = pp.tokens.items(.id);
var i: u32 = 0;
@@ -3334,6 +3447,17 @@ pub fn prettyPrintTokens(pp: *Preprocessor, w: anytype) !void {
try pp.printLinemarker(w, line_col.line_no, source, .@"resume");
last_nl = true;
},
+ .keyword_define, .keyword_undef => {
+ switch (macro_dump_mode) {
+ .macros_and_result, .macro_names_and_result => {
+ try w.writeByte('#');
+ try pp.prettyPrintMacro(w, cur.loc, if (macro_dump_mode == .macros_and_result) .name_and_body else .name_only);
+ last_nl = false;
+ },
+ .result_only => unreachable, // `pp.store_macro_tokens` should be false for standard preprocessor output
+ .macros_only => unreachable, // handled by prettyPrintMacrosOnly
+ }
+ },
else => {
const slice = pp.expandedSlice(cur);
try w.writeAll(slice);
@@ -3350,7 +3474,7 @@ test "Preserve pragma tokens sometimes" {
var buf = std.ArrayList(u8).init(allocator);
defer buf.deinit();
- var comp = Compilation.init(allocator);
+ var comp = Compilation.init(allocator, std.fs.cwd());
defer comp.deinit();
try comp.addDefaultPragmaHandlers();
@@ -3364,7 +3488,7 @@ test "Preserve pragma tokens sometimes" {
const test_runner_macros = try comp.addSourceFromBuffer("<test_runner>", source_text);
const eof = try pp.preprocess(test_runner_macros);
try pp.addToken(eof);
- try pp.prettyPrintTokens(buf.writer());
+ try pp.prettyPrintTokens(buf.writer(), .result_only);
return allocator.dupe(u8, buf.items);
}
@@ -3410,7 +3534,7 @@ test "destringify" {
try std.testing.expectEqualStrings(destringified, pp.char_buf.items);
}
};
- var comp = Compilation.init(allocator);
+ var comp = Compilation.init(allocator, std.fs.cwd());
defer comp.deinit();
var pp = Preprocessor.init(&comp);
defer pp.deinit();
@@ -3468,7 +3592,7 @@ test "Include guards" {
}
fn testIncludeGuard(allocator: std.mem.Allocator, comptime template: []const u8, tok_id: RawToken.Id, expected_guards: u32) !void {
- var comp = Compilation.init(allocator);
+ var comp = Compilation.init(allocator, std.fs.cwd());
defer comp.deinit();
var pp = Preprocessor.init(&comp);
defer pp.deinit();
lib/compiler/aro/aro/record_layout.zig
@@ -19,6 +19,13 @@ const OngoingBitfield = struct {
unused_size_bits: u64,
};
+pub const Error = error{Overflow};
+
+fn alignForward(addr: u64, alignment: u64) !u64 {
+ const forward_addr = try std.math.add(u64, addr, alignment - 1);
+ return std.mem.alignBackward(u64, forward_addr, alignment);
+}
+
const SysVContext = struct {
/// Does the record have an __attribute__((packed)) annotation.
attr_packed: bool,
@@ -36,14 +43,8 @@ const SysVContext = struct {
comp: *const Compilation,
fn init(ty: Type, comp: *const Compilation, pragma_pack: ?u8) SysVContext {
- var pack_value: ?u64 = null;
- if (pragma_pack) |pak| {
- pack_value = pak * BITS_PER_BYTE;
- }
- var req_align: u29 = BITS_PER_BYTE;
- if (ty.requestedAlignment(comp)) |aln| {
- req_align = aln * BITS_PER_BYTE;
- }
+ const pack_value: ?u64 = if (pragma_pack) |pak| @as(u64, pak) * BITS_PER_BYTE else null;
+ const req_align = @as(u32, (ty.requestedAlignment(comp) orelse 1)) * BITS_PER_BYTE;
return SysVContext{
.attr_packed = ty.hasAttribute(.@"packed"),
.max_field_align_bits = pack_value,
@@ -55,7 +56,7 @@ const SysVContext = struct {
};
}
- fn layoutFields(self: *SysVContext, rec: *const Record) void {
+ fn layoutFields(self: *SysVContext, rec: *const Record) !void {
for (rec.fields, 0..) |*fld, fld_indx| {
if (fld.ty.specifier == .invalid) continue;
const type_layout = computeLayout(fld.ty, self.comp);
@@ -65,12 +66,12 @@ const SysVContext = struct {
field_attrs = attrs[fld_indx];
}
if (self.comp.target.isMinGW()) {
- fld.layout = self.layoutMinGWField(fld, field_attrs, type_layout);
+ fld.layout = try self.layoutMinGWField(fld, field_attrs, type_layout);
} else {
if (fld.isRegularField()) {
- fld.layout = self.layoutRegularField(field_attrs, type_layout);
+ fld.layout = try self.layoutRegularField(field_attrs, type_layout);
} else {
- fld.layout = self.layoutBitField(field_attrs, type_layout, fld.isNamed(), fld.specifiedBitWidth());
+ fld.layout = try self.layoutBitField(field_attrs, type_layout, fld.isNamed(), fld.specifiedBitWidth());
}
}
}
@@ -99,8 +100,8 @@ const SysVContext = struct {
field: *const Field,
field_attrs: ?[]const Attribute,
field_layout: TypeLayout,
- ) FieldLayout {
- const annotation_alignment_bits = BITS_PER_BYTE * (Type.annotationAlignment(self.comp, field_attrs) orelse 1);
+ ) !FieldLayout {
+ const annotation_alignment_bits = BITS_PER_BYTE * @as(u32, (Type.annotationAlignment(self.comp, Attribute.Iterator.initSlice(field_attrs)) orelse 1));
const is_attr_packed = self.attr_packed or isPacked(field_attrs);
const ignore_type_alignment = ignoreTypeAlignment(is_attr_packed, field.bit_width, self.ongoing_bitfield, field_layout);
@@ -157,7 +158,7 @@ const SysVContext = struct {
field_alignment_bits: u64,
is_named: bool,
width: u64,
- ) FieldLayout {
+ ) !FieldLayout {
std.debug.assert(width <= ty_size_bits); // validated in parser
// In a union, the size of the underlying type does not affect the size of the union.
@@ -194,8 +195,8 @@ const SysVContext = struct {
.unused_size_bits = ty_size_bits - width,
};
}
- const offset_bits = std.mem.alignForward(u64, self.size_bits, field_alignment_bits);
- self.size_bits = if (width == 0) offset_bits else offset_bits + ty_size_bits;
+ const offset_bits = try alignForward(self.size_bits, field_alignment_bits);
+ self.size_bits = if (width == 0) offset_bits else try std.math.add(u64, offset_bits, ty_size_bits);
if (!is_named) return .{};
return .{
.offset_bits = offset_bits,
@@ -207,16 +208,16 @@ const SysVContext = struct {
self: *SysVContext,
ty_size_bits: u64,
field_alignment_bits: u64,
- ) FieldLayout {
+ ) !FieldLayout {
self.ongoing_bitfield = null;
// A struct field starts at the next offset in the struct that is properly
// aligned with respect to the start of the struct. See test case 0033.
// A union field always starts at offset 0.
- const offset_bits = if (self.is_union) 0 else std.mem.alignForward(u64, self.size_bits, field_alignment_bits);
+ const offset_bits = if (self.is_union) 0 else try alignForward(self.size_bits, field_alignment_bits);
// Set the size of the record to the maximum of the current size and the end of
// the field. See test case 0034.
- self.size_bits = @max(self.size_bits, offset_bits + ty_size_bits);
+ self.size_bits = @max(self.size_bits, try std.math.add(u64, offset_bits, ty_size_bits));
return .{
.offset_bits = offset_bits,
@@ -228,7 +229,7 @@ const SysVContext = struct {
self: *SysVContext,
fld_attrs: ?[]const Attribute,
fld_layout: TypeLayout,
- ) FieldLayout {
+ ) !FieldLayout {
var fld_align_bits = fld_layout.field_alignment_bits;
// If the struct or the field is packed, then the alignment of the underlying type is
@@ -239,8 +240,8 @@ const SysVContext = struct {
// The field alignment can be increased by __attribute__((aligned)) annotations on the
// field. See test case 0085.
- if (Type.annotationAlignment(self.comp, fld_attrs)) |anno| {
- fld_align_bits = @max(fld_align_bits, anno * BITS_PER_BYTE);
+ if (Type.annotationAlignment(self.comp, Attribute.Iterator.initSlice(fld_attrs))) |anno| {
+ fld_align_bits = @max(fld_align_bits, @as(u32, anno) * BITS_PER_BYTE);
}
// #pragma pack takes precedence over all other attributes. See test cases 0084 and
@@ -251,12 +252,12 @@ const SysVContext = struct {
// A struct field starts at the next offset in the struct that is properly
// aligned with respect to the start of the struct.
- const offset_bits = if (self.is_union) 0 else std.mem.alignForward(u64, self.size_bits, fld_align_bits);
+ const offset_bits = if (self.is_union) 0 else try alignForward(self.size_bits, fld_align_bits);
const size_bits = fld_layout.size_bits;
// The alignment of a record is the maximum of its field alignments. See test cases
// 0084, 0085, 0086.
- self.size_bits = @max(self.size_bits, offset_bits + size_bits);
+ self.size_bits = @max(self.size_bits, try std.math.add(u64, offset_bits, size_bits));
self.aligned_bits = @max(self.aligned_bits, fld_align_bits);
return .{
@@ -271,7 +272,7 @@ const SysVContext = struct {
fld_layout: TypeLayout,
is_named: bool,
bit_width: u64,
- ) FieldLayout {
+ ) !FieldLayout {
const ty_size_bits = fld_layout.size_bits;
var ty_fld_algn_bits: u32 = fld_layout.field_alignment_bits;
@@ -301,7 +302,7 @@ const SysVContext = struct {
const attr_packed = self.attr_packed or isPacked(fld_attrs);
const has_packing_annotation = attr_packed or self.max_field_align_bits != null;
- const annotation_alignment: u32 = if (Type.annotationAlignment(self.comp, fld_attrs)) |anno| anno * BITS_PER_BYTE else 1;
+ const annotation_alignment = if (Type.annotationAlignment(self.comp, Attribute.Iterator.initSlice(fld_attrs))) |anno| @as(u32, anno) * BITS_PER_BYTE else 1;
const first_unused_bit: u64 = if (self.is_union) 0 else self.size_bits;
var field_align_bits: u64 = 1;
@@ -322,7 +323,7 @@ const SysVContext = struct {
// - the alignment of the type is larger than its size,
// then it is aligned to the type's field alignment. See test case 0083.
if (!has_packing_annotation) {
- const start_bit = std.mem.alignForward(u64, first_unused_bit, field_align_bits);
+ const start_bit = try alignForward(first_unused_bit, field_align_bits);
const does_field_cross_boundary = start_bit % ty_fld_algn_bits + bit_width > ty_size_bits;
@@ -349,8 +350,8 @@ const SysVContext = struct {
}
}
- const offset_bits = std.mem.alignForward(u64, first_unused_bit, field_align_bits);
- self.size_bits = @max(self.size_bits, offset_bits + bit_width);
+ const offset_bits = try alignForward(first_unused_bit, field_align_bits);
+ self.size_bits = @max(self.size_bits, try std.math.add(u64, offset_bits, bit_width));
// Unnamed fields do not contribute to the record alignment except on a few targets.
// See test case 0079.
@@ -419,10 +420,7 @@ const MsvcContext = struct {
// The required alignment can be increased by adding a __declspec(align)
// annotation. See test case 0023.
- var must_align: u29 = BITS_PER_BYTE;
- if (ty.requestedAlignment(comp)) |req_align| {
- must_align = req_align * BITS_PER_BYTE;
- }
+ const must_align = @as(u32, (ty.requestedAlignment(comp) orelse 1)) * BITS_PER_BYTE;
return MsvcContext{
.req_align_bits = must_align,
.pointer_align_bits = must_align,
@@ -436,15 +434,15 @@ const MsvcContext = struct {
};
}
- fn layoutField(self: *MsvcContext, fld: *const Field, fld_attrs: ?[]const Attribute) FieldLayout {
+ fn layoutField(self: *MsvcContext, fld: *const Field, fld_attrs: ?[]const Attribute) !FieldLayout {
const type_layout = computeLayout(fld.ty, self.comp);
// The required alignment of the field is the maximum of the required alignment of the
// underlying type and the __declspec(align) annotation on the field itself.
// See test case 0028.
var req_align = type_layout.required_alignment_bits;
- if (Type.annotationAlignment(self.comp, fld_attrs)) |anno| {
- req_align = @max(anno * BITS_PER_BYTE, req_align);
+ if (Type.annotationAlignment(self.comp, Attribute.Iterator.initSlice(fld_attrs))) |anno| {
+ req_align = @max(@as(u32, anno) * BITS_PER_BYTE, req_align);
}
// The required alignment of a record is the maximum of the required alignments of its
@@ -480,7 +478,7 @@ const MsvcContext = struct {
}
}
- fn layoutBitField(self: *MsvcContext, ty_size_bits: u64, field_align: u32, bit_width: u32) FieldLayout {
+ fn layoutBitField(self: *MsvcContext, ty_size_bits: u64, field_align: u32, bit_width: u32) !FieldLayout {
if (bit_width == 0) {
// A zero-sized bit-field that does not follow a non-zero-sized bit-field does not affect
// the overall layout of the record. Even in a union where the order would otherwise
@@ -522,7 +520,7 @@ const MsvcContext = struct {
self.pointer_align_bits = @max(self.pointer_align_bits, p_align);
self.field_align_bits = @max(self.field_align_bits, field_align);
- const offset_bits = std.mem.alignForward(u64, self.size_bits, field_align);
+ const offset_bits = try alignForward(self.size_bits, field_align);
self.size_bits = if (bit_width == 0) offset_bits else offset_bits + ty_size_bits;
break :bits offset_bits;
@@ -534,7 +532,7 @@ const MsvcContext = struct {
return .{ .offset_bits = offset_bits, .size_bits = bit_width };
}
- fn layoutRegularField(self: *MsvcContext, size_bits: u64, field_align: u32) FieldLayout {
+ fn layoutRegularField(self: *MsvcContext, size_bits: u64, field_align: u32) !FieldLayout {
self.contains_non_bitfield = true;
self.ongoing_bitfield = null;
// The alignment of the field affects both the pointer alignment and the field
@@ -543,7 +541,7 @@ const MsvcContext = struct {
self.field_align_bits = @max(self.field_align_bits, field_align);
const offset_bits = switch (self.is_union) {
true => 0,
- false => std.mem.alignForward(u64, self.size_bits, field_align),
+ false => try alignForward(self.size_bits, field_align),
};
self.size_bits = @max(self.size_bits, offset_bits + size_bits);
return .{ .offset_bits = offset_bits, .size_bits = size_bits };
@@ -569,14 +567,14 @@ const MsvcContext = struct {
}
};
-pub fn compute(rec: *Type.Record, ty: Type, comp: *const Compilation, pragma_pack: ?u8) void {
+pub fn compute(rec: *Type.Record, ty: Type, comp: *const Compilation, pragma_pack: ?u8) Error!void {
switch (comp.langopts.emulate) {
.gcc, .clang => {
var context = SysVContext.init(ty, comp, pragma_pack);
- context.layoutFields(rec);
+ try context.layoutFields(rec);
- context.size_bits = std.mem.alignForward(u64, context.size_bits, context.aligned_bits);
+ context.size_bits = try alignForward(context.size_bits, context.aligned_bits);
rec.type_layout = .{
.size_bits = context.size_bits,
@@ -594,7 +592,7 @@ pub fn compute(rec: *Type.Record, ty: Type, comp: *const Compilation, pragma_pac
field_attrs = attrs[fld_indx];
}
- fld.layout = context.layoutField(fld, field_attrs);
+ fld.layout = try context.layoutField(fld, field_attrs);
}
if (context.size_bits == 0) {
// As an extension, MSVC allows records that only contain zero-sized bitfields and empty
@@ -602,7 +600,7 @@ pub fn compute(rec: *Type.Record, ty: Type, comp: *const Compilation, pragma_pac
// ensure that there are no zero-sized records.
context.handleZeroSizedRecord();
}
- context.size_bits = std.mem.alignForward(u64, context.size_bits, context.pointer_align_bits);
+ context.size_bits = try alignForward(context.size_bits, context.pointer_align_bits);
rec.type_layout = .{
.size_bits = context.size_bits,
.field_alignment_bits = context.field_align_bits,
lib/compiler/aro/aro/Source.zig
@@ -75,7 +75,17 @@ pub fn lineCol(source: Source, loc: Location) LineCol {
i += 1;
continue;
};
- const cp = std.unicode.utf8Decode(source.buf[i..][0..len]) catch {
+ const slice = source.buf[i..];
+ if (len > slice.len) {
+ break;
+ }
+ const cp = switch (len) {
+ 1 => slice[0],
+ 2 => std.unicode.utf8Decode2(slice[0..2].*),
+ 3 => std.unicode.utf8Decode3(slice[0..3].*),
+ 4 => std.unicode.utf8Decode4(slice[0..4].*),
+ else => unreachable,
+ } catch {
i += 1;
continue;
};
lib/compiler/aro/aro/SymbolStack.zig
@@ -178,9 +178,11 @@ pub fn defineTypedef(
if (s.get(name, .vars)) |prev| {
switch (prev.kind) {
.typedef => {
- if (!ty.eql(prev.ty, p.comp, true)) {
- try p.errStr(.redefinition_of_typedef, tok, try p.typePairStrExtra(ty, " vs ", prev.ty));
- if (prev.tok != 0) try p.errTok(.previous_definition, prev.tok);
+ if (!prev.ty.is(.invalid)) {
+ if (!ty.eql(prev.ty, p.comp, true)) {
+ try p.errStr(.redefinition_of_typedef, tok, try p.typePairStrExtra(ty, " vs ", prev.ty));
+ if (prev.tok != 0) try p.errTok(.previous_definition, prev.tok);
+ }
}
},
.enumeration, .decl, .def, .constexpr => {
@@ -194,7 +196,12 @@ pub fn defineTypedef(
.kind = .typedef,
.name = name,
.tok = tok,
- .ty = ty,
+ .ty = .{
+ .name = name,
+ .specifier = ty.specifier,
+ .qual = ty.qual,
+ .data = ty.data,
+ },
.node = node,
.val = .{},
});
lib/compiler/aro/aro/target.zig
@@ -35,10 +35,7 @@ pub fn intMaxType(target: std.Target) Type {
/// intptr_t for this target
pub fn intPtrType(target: std.Target) Type {
- switch (target.os.tag) {
- .haiku => return .{ .specifier = .long },
- else => {},
- }
+ if (target.os.tag == .haiku) return .{ .specifier = .long };
switch (target.cpu.arch) {
.aarch64, .aarch64_be => switch (target.os.tag) {
@@ -127,6 +124,14 @@ pub fn int64Type(target: std.Target) Type {
return .{ .specifier = .long_long };
}
+pub fn float80Type(target: std.Target) ?Type {
+ switch (target.cpu.arch) {
+ .x86, .x86_64 => return .{ .specifier = .long_double },
+ else => {},
+ }
+ return null;
+}
+
/// This function returns 1 if function alignment is not observable or settable.
pub fn defaultFunctionAlignment(target: std.Target) u8 {
return switch (target.cpu.arch) {
@@ -474,6 +479,7 @@ pub fn get32BitArchVariant(target: std.Target) ?std.Target {
.kalimba,
.lanai,
.wasm32,
+ .spirv,
.spirv32,
.loongarch32,
.dxil,
@@ -544,6 +550,7 @@ pub fn get64BitArchVariant(target: std.Target) ?std.Target {
.powerpcle => copy.cpu.arch = .powerpc64le,
.riscv32 => copy.cpu.arch = .riscv64,
.sparc => copy.cpu.arch = .sparc64,
+ .spirv => copy.cpu.arch = .spirv64,
.spirv32 => copy.cpu.arch = .spirv64,
.thumb => copy.cpu.arch = .aarch64,
.thumbeb => copy.cpu.arch = .aarch64_be,
@@ -599,6 +606,7 @@ pub fn toLLVMTriple(target: std.Target, buf: []u8) []const u8 {
.xtensa => "xtensa",
.nvptx => "nvptx",
.nvptx64 => "nvptx64",
+ .spirv => "spirv",
.spirv32 => "spirv32",
.spirv64 => "spirv64",
.kalimba => "kalimba",
@@ -646,9 +654,10 @@ pub fn toLLVMTriple(target: std.Target, buf: []u8) []const u8 {
.ios => "ios",
.tvos => "tvos",
.watchos => "watchos",
- .visionos => "xros",
.driverkit => "driverkit",
.shadermodel => "shadermodel",
+ .visionos => "xros",
+ .serenity => "serenity",
.opencl,
.opengl,
.vulkan,
@@ -707,6 +716,7 @@ pub fn toLLVMTriple(target: std.Target, buf: []u8) []const u8 {
.callable => "callable",
.mesh => "mesh",
.amplification => "amplification",
+ .ohos => "openhos",
};
writer.writeAll(llvm_abi) catch unreachable;
return stream.getWritten();
lib/compiler/aro/aro/text_literal.zig
@@ -71,7 +71,7 @@ pub const Kind = enum {
pub fn maxCodepoint(kind: Kind, comp: *const Compilation) u21 {
return @intCast(switch (kind) {
.char => std.math.maxInt(u7),
- .wide => @min(0x10FFFF, comp.types.wchar.maxInt(comp)),
+ .wide => @min(0x10FFFF, comp.wcharMax()),
.utf_8 => std.math.maxInt(u7),
.utf_16 => std.math.maxInt(u16),
.utf_32 => 0x10FFFF,
@@ -83,7 +83,7 @@ pub const Kind = enum {
pub fn maxInt(kind: Kind, comp: *const Compilation) u32 {
return @intCast(switch (kind) {
.char, .utf_8 => std.math.maxInt(u8),
- .wide => comp.types.wchar.maxInt(comp),
+ .wide => comp.wcharMax(),
.utf_16 => std.math.maxInt(u16),
.utf_32 => std.math.maxInt(u32),
.unterminated => unreachable,
lib/compiler/aro/aro/Tokenizer.zig
@@ -178,6 +178,8 @@ pub const Token = struct {
keyword_return,
keyword_short,
keyword_signed,
+ keyword_signed1,
+ keyword_signed2,
keyword_sizeof,
keyword_static,
keyword_struct,
@@ -258,7 +260,6 @@ pub const Token = struct {
keyword_asm,
keyword_asm1,
keyword_asm2,
- keyword_float80,
/// _Float128
keyword_float128_1,
/// __float128
@@ -369,6 +370,8 @@ pub const Token = struct {
.keyword_return,
.keyword_short,
.keyword_signed,
+ .keyword_signed1,
+ .keyword_signed2,
.keyword_sizeof,
.keyword_static,
.keyword_struct,
@@ -417,7 +420,6 @@ pub const Token = struct {
.keyword_asm,
.keyword_asm1,
.keyword_asm2,
- .keyword_float80,
.keyword_float128_1,
.keyword_float128_2,
.keyword_int128,
@@ -627,6 +629,8 @@ pub const Token = struct {
.keyword_return => "return",
.keyword_short => "short",
.keyword_signed => "signed",
+ .keyword_signed1 => "__signed",
+ .keyword_signed2 => "__signed__",
.keyword_sizeof => "sizeof",
.keyword_static => "static",
.keyword_struct => "struct",
@@ -702,7 +706,6 @@ pub const Token = struct {
.keyword_asm => "asm",
.keyword_asm1 => "__asm",
.keyword_asm2 => "__asm__",
- .keyword_float80 => "__float80",
.keyword_float128_1 => "_Float128",
.keyword_float128_2 => "__float128",
.keyword_int128 => "__int128",
@@ -732,7 +735,8 @@ pub const Token = struct {
pub fn symbol(id: Id) []const u8 {
return switch (id) {
- .macro_string, .invalid => unreachable,
+ .macro_string => unreachable,
+ .invalid => "invalid bytes",
.identifier,
.extended_identifier,
.macro_func,
@@ -873,10 +877,7 @@ pub const Token = struct {
}
const all_kws = std.StaticStringMap(Id).initComptime(.{
- .{ "auto", auto: {
- @setEvalBranchQuota(3000);
- break :auto .keyword_auto;
- } },
+ .{ "auto", .keyword_auto },
.{ "break", .keyword_break },
.{ "case", .keyword_case },
.{ "char", .keyword_char },
@@ -898,6 +899,8 @@ pub const Token = struct {
.{ "return", .keyword_return },
.{ "short", .keyword_short },
.{ "signed", .keyword_signed },
+ .{ "__signed", .keyword_signed1 },
+ .{ "__signed__", .keyword_signed2 },
.{ "sizeof", .keyword_sizeof },
.{ "static", .keyword_static },
.{ "struct", .keyword_struct },
@@ -982,7 +985,6 @@ pub const Token = struct {
.{ "asm", .keyword_asm },
.{ "__asm", .keyword_asm1 },
.{ "__asm__", .keyword_asm2 },
- .{ "__float80", .keyword_float80 },
.{ "_Float128", .keyword_float128_1 },
.{ "__float128", .keyword_float128_2 },
.{ "__int128", .keyword_int128 },
@@ -1300,11 +1302,17 @@ pub fn next(self: *Tokenizer) Token {
else => {},
},
.char_escape_sequence => switch (c) {
- '\r', '\n' => unreachable, // removed by line splicing
+ '\r', '\n' => {
+ id = .unterminated_char_literal;
+ break;
+ },
else => state = .char_literal,
},
.string_escape_sequence => switch (c) {
- '\r', '\n' => unreachable, // removed by line splicing
+ '\r', '\n' => {
+ id = .unterminated_string_literal;
+ break;
+ },
else => state = .string_literal,
},
.identifier, .extended_identifier => switch (c) {
@@ -1792,7 +1800,7 @@ pub fn nextNoWSComments(self: *Tokenizer) Token {
/// Try to tokenize a '::' even if not supported by the current language standard.
pub fn colonColon(self: *Tokenizer) Token {
var tok = self.nextNoWS();
- if (tok.id == .colon and self.buf[self.index] == ':') {
+ if (tok.id == .colon and self.index < self.buf.len and self.buf[self.index] == ':') {
self.index += 1;
tok.id = .colon_colon;
}
@@ -2142,8 +2150,30 @@ test "C23 keywords" {
}, .c23);
}
+test "Tokenizer fuzz test" {
+ var comp = Compilation.init(std.testing.allocator, std.fs.cwd());
+ defer comp.deinit();
+
+ const input_bytes = std.testing.fuzzInput(.{});
+ if (input_bytes.len == 0) return;
+
+ const source = try comp.addSourceFromBuffer("fuzz.c", input_bytes);
+
+ var tokenizer: Tokenizer = .{
+ .buf = source.buf,
+ .source = source.id,
+ .langopts = comp.langopts,
+ };
+ while (true) {
+ const prev_index = tokenizer.index;
+ const tok = tokenizer.next();
+ if (tok.id == .eof) break;
+ try std.testing.expect(prev_index < tokenizer.index); // ensure that the tokenizer always makes progress
+ }
+}
+
fn expectTokensExtra(contents: []const u8, expected_tokens: []const Token.Id, standard: ?LangOpts.Standard) !void {
- var comp = Compilation.init(std.testing.allocator);
+ var comp = Compilation.init(std.testing.allocator, std.fs.cwd());
defer comp.deinit();
if (standard) |provided| {
comp.langopts.standard = provided;
lib/compiler/aro/aro/Tree.zig
@@ -137,15 +137,22 @@ pub const Node = struct {
tag: Tag,
ty: Type = .{ .specifier = .void },
data: Data,
+ loc: Loc = .none,
pub const Range = struct { start: u32, end: u32 };
+ pub const Loc = enum(u32) {
+ none = std.math.maxInt(u32),
+ _,
+ };
+
pub const Data = union {
decl: struct {
name: TokenIndex,
node: NodeIndex = .none,
},
decl_ref: TokenIndex,
+ two: [2]NodeIndex,
range: Range,
if3: struct {
cond: NodeIndex,
@@ -277,7 +284,8 @@ pub const Tag = enum(u8) {
// ====== Decl ======
- // _Static_assert
+ /// _Static_assert
+ /// loc is token index of _Static_assert
static_assert,
// function prototype
@@ -303,17 +311,18 @@ pub const Tag = enum(u8) {
threadlocal_static_var,
/// __asm__("...") at file scope
+ /// loc is token index of __asm__ keyword
file_scope_asm,
// typedef declaration
typedef,
// container declarations
- /// { lhs; rhs; }
+ /// { two[0]; two[1]; }
struct_decl_two,
- /// { lhs; rhs; }
+ /// { two[0]; two[1]; }
union_decl_two,
- /// { lhs, rhs, }
+ /// { two[0], two[1], }
enum_decl_two,
/// { range }
struct_decl,
@@ -339,7 +348,7 @@ pub const Tag = enum(u8) {
// ====== Stmt ======
labeled_stmt,
- /// { first; second; } first and second may be null
+ /// { two[0]; two[1]; } first and second may be null
compound_stmt_two,
/// { data }
compound_stmt,
@@ -476,7 +485,7 @@ pub const Tag = enum(u8) {
real_expr,
/// lhs[rhs] lhs is pointer/array type, rhs is integer type
array_access_expr,
- /// first(second) second may be 0
+ /// two[0](two[1]) two[1] may be 0
call_expr_one,
/// data[0](data[1..])
call_expr,
@@ -515,7 +524,7 @@ pub const Tag = enum(u8) {
sizeof_expr,
/// _Alignof(un?)
alignof_expr,
- /// _Generic(controlling lhs, chosen rhs)
+ /// _Generic(controlling two[0], chosen two[1])
generic_expr_one,
/// _Generic(controlling range[0], chosen range[1], rest range[2..])
generic_expr,
@@ -534,28 +543,34 @@ pub const Tag = enum(u8) {
// ====== Initializer expressions ======
- /// { lhs, rhs }
+ /// { two[0], two[1] }
array_init_expr_two,
/// { range }
array_init_expr,
- /// { lhs, rhs }
+ /// { two[0], two[1] }
struct_init_expr_two,
/// { range }
struct_init_expr,
/// { union_init }
union_init_expr,
+
/// (ty){ un }
+ /// loc is token index of l_paren
compound_literal_expr,
/// (static ty){ un }
+ /// loc is token index of l_paren
static_compound_literal_expr,
/// (thread_local ty){ un }
+ /// loc is token index of l_paren
thread_local_compound_literal_expr,
/// (static thread_local ty){ un }
+ /// loc is token index of l_paren
static_thread_local_compound_literal_expr,
/// Inserted at the end of a function body if no return stmt is found.
/// ty is the functions return type
/// data is return_zero which is true if the function is called "main" and ty is compatible with int
+ /// loc is token index of closing r_brace of function
implicit_return,
/// Inserted in array_init_expr to represent unspecified elements.
@@ -608,6 +623,57 @@ pub fn bitfieldWidth(tree: *const Tree, node: NodeIndex, inspect_lval: bool) ?u3
}
}
+const CallableResultUsage = struct {
+ /// name token of the thing being called, for diagnostics
+ tok: TokenIndex,
+ /// true if `nodiscard` attribute present
+ nodiscard: bool,
+ /// true if `warn_unused_result` attribute present
+ warn_unused_result: bool,
+};
+
+pub fn callableResultUsage(tree: *const Tree, node: NodeIndex) ?CallableResultUsage {
+ const data = tree.nodes.items(.data);
+
+ var cur_node = node;
+ while (true) switch (tree.nodes.items(.tag)[@intFromEnum(cur_node)]) {
+ .decl_ref_expr => {
+ const tok = data[@intFromEnum(cur_node)].decl_ref;
+ const fn_ty = tree.nodes.items(.ty)[@intFromEnum(node)].elemType();
+ return .{
+ .tok = tok,
+ .nodiscard = fn_ty.hasAttribute(.nodiscard),
+ .warn_unused_result = fn_ty.hasAttribute(.warn_unused_result),
+ };
+ },
+ .paren_expr => cur_node = data[@intFromEnum(cur_node)].un,
+ .comma_expr => cur_node = data[@intFromEnum(cur_node)].bin.rhs,
+
+ .explicit_cast, .implicit_cast => cur_node = data[@intFromEnum(cur_node)].cast.operand,
+ .addr_of_expr, .deref_expr => cur_node = data[@intFromEnum(cur_node)].un,
+ .call_expr_one => cur_node = data[@intFromEnum(cur_node)].two[0],
+ .call_expr => cur_node = tree.data[data[@intFromEnum(cur_node)].range.start],
+ .member_access_expr, .member_access_ptr_expr => {
+ const member = data[@intFromEnum(cur_node)].member;
+ var ty = tree.nodes.items(.ty)[@intFromEnum(member.lhs)];
+ if (ty.isPtr()) ty = ty.elemType();
+ const record = ty.getRecord().?;
+ const field = record.fields[member.index];
+ const attributes = if (record.field_attributes) |attrs| attrs[member.index] else &.{};
+ return .{
+ .tok = field.name_tok,
+ .nodiscard = for (attributes) |attr| {
+ if (attr.tag == .nodiscard) break true;
+ } else false,
+ .warn_unused_result = for (attributes) |attr| {
+ if (attr.tag == .warn_unused_result) break true;
+ } else false,
+ };
+ },
+ else => return null,
+ };
+}
+
pub fn isLval(tree: *const Tree, node: NodeIndex) bool {
var is_const: bool = undefined;
return tree.isLvalExtra(node, &is_const);
@@ -672,17 +738,66 @@ pub fn isLvalExtra(tree: *const Tree, node: NodeIndex, is_const: *bool) bool {
}
}
+/// This should only be used for node tags that represent AST nodes which have an arbitrary number of children
+/// It particular it should *not* be used for nodes with .un or .bin data types
+///
+/// For call expressions, child_nodes[0] is the function pointer being called and child_nodes[1..]
+/// are the arguments
+///
+/// For generic selection expressions, child_nodes[0] is the controlling expression,
+/// child_nodes[1] is the chosen expression (it is a syntax error for there to be no chosen expression),
+/// and child_nodes[2..] are the remaining expressions.
+pub fn childNodes(tree: *const Tree, node: NodeIndex) []const NodeIndex {
+ const tags = tree.nodes.items(.tag);
+ const data = tree.nodes.items(.data);
+ switch (tags[@intFromEnum(node)]) {
+ .compound_stmt_two,
+ .array_init_expr_two,
+ .struct_init_expr_two,
+ .enum_decl_two,
+ .struct_decl_two,
+ .union_decl_two,
+ .call_expr_one,
+ .generic_expr_one,
+ => {
+ const index: u32 = @intFromEnum(node);
+ const end = std.mem.indexOfScalar(NodeIndex, &data[index].two, .none) orelse 2;
+ return data[index].two[0..end];
+ },
+ .compound_stmt,
+ .array_init_expr,
+ .struct_init_expr,
+ .enum_decl,
+ .struct_decl,
+ .union_decl,
+ .call_expr,
+ .generic_expr,
+ => {
+ const range = data[@intFromEnum(node)].range;
+ return tree.data[range.start..range.end];
+ },
+ else => unreachable,
+ }
+}
+
pub fn tokSlice(tree: *const Tree, tok_i: TokenIndex) []const u8 {
if (tree.tokens.items(.id)[tok_i].lexeme()) |some| return some;
const loc = tree.tokens.items(.loc)[tok_i];
- var tmp_tokenizer = Tokenizer{
- .buf = tree.comp.getSource(loc.id).buf,
- .langopts = tree.comp.langopts,
- .index = loc.byte_offset,
- .source = .generated,
+ return tree.comp.locSlice(loc);
+}
+
+pub fn nodeTok(tree: *const Tree, node: NodeIndex) ?TokenIndex {
+ std.debug.assert(node != .none);
+ const loc = tree.nodes.items(.loc)[@intFromEnum(node)];
+ return switch (loc) {
+ .none => null,
+ else => |tok_i| @intFromEnum(tok_i),
};
- const tok = tmp_tokenizer.next();
- return tmp_tokenizer.buf[tok.start..tok.end];
+}
+
+pub fn nodeLoc(tree: *const Tree, node: NodeIndex) ?Source.Location {
+ const tok_i = tree.nodeTok(node) orelse return null;
+ return tree.tokens.items(.loc)[@intFromEnum(tok_i)];
}
pub fn dump(tree: *const Tree, config: std.io.tty.Config, writer: anytype) !void {
@@ -766,6 +881,10 @@ fn dumpNode(
}
try config.setColor(w, TYPE);
try w.writeByte('\'');
+ const name = ty.getName();
+ if (name != .empty) {
+ try w.print("{s}': '", .{mapper.lookup(name)});
+ }
try ty.dump(mapper, tree.comp.langopts, w);
try w.writeByte('\'');
@@ -794,7 +913,9 @@ fn dumpNode(
if (ty.specifier == .attributed) {
try config.setColor(w, ATTRIBUTE);
- for (ty.data.attributed.attributes) |attr| {
+ var it = Attribute.Iterator.initType(ty);
+ while (it.next()) |item| {
+ const attr, _ = item;
try w.writeByteNTimes(' ', level + half);
try w.print("attr: {s}", .{@tagName(attr.tag)});
try tree.dumpAttribute(attr, w);
@@ -900,9 +1021,16 @@ fn dumpNode(
.enum_decl,
.struct_decl,
.union_decl,
+ .compound_stmt_two,
+ .array_init_expr_two,
+ .struct_init_expr_two,
+ .enum_decl_two,
+ .struct_decl_two,
+ .union_decl_two,
=> {
+ const child_nodes = tree.childNodes(node);
const maybe_field_attributes = if (ty.getRecord()) |record| record.field_attributes else null;
- for (tree.data[data.range.start..data.range.end], 0..) |stmt, i| {
+ for (child_nodes, 0..) |stmt, i| {
if (i != 0) try w.writeByte('\n');
try tree.dumpNode(stmt, level + delta, mapper, config, w);
if (maybe_field_attributes) |field_attributes| {
@@ -914,33 +1042,6 @@ fn dumpNode(
}
}
},
- .compound_stmt_two,
- .array_init_expr_two,
- .struct_init_expr_two,
- .enum_decl_two,
- .struct_decl_two,
- .union_decl_two,
- => {
- var attr_array = [2][]const Attribute{ &.{}, &.{} };
- const empty: [][]const Attribute = &attr_array;
- const field_attributes = if (ty.getRecord()) |record| (record.field_attributes orelse empty.ptr) else empty.ptr;
- if (data.bin.lhs != .none) {
- try tree.dumpNode(data.bin.lhs, level + delta, mapper, config, w);
- if (field_attributes[0].len > 0) {
- try config.setColor(w, ATTRIBUTE);
- try tree.dumpFieldAttributes(field_attributes[0], level + delta + half, w);
- try config.setColor(w, .reset);
- }
- }
- if (data.bin.rhs != .none) {
- try tree.dumpNode(data.bin.rhs, level + delta, mapper, config, w);
- if (field_attributes[1].len > 0) {
- try config.setColor(w, ATTRIBUTE);
- try tree.dumpFieldAttributes(field_attributes[1], level + delta + half, w);
- try config.setColor(w, .reset);
- }
- }
- },
.union_init_expr => {
try w.writeByteNTimes(' ', level + half);
try w.writeAll("field index: ");
@@ -1130,23 +1231,21 @@ fn dumpNode(
try tree.dumpNode(data.un, level + delta, mapper, config, w);
}
},
- .call_expr => {
- try w.writeByteNTimes(' ', level + half);
- try w.writeAll("lhs:\n");
- try tree.dumpNode(tree.data[data.range.start], level + delta, mapper, config, w);
+ .call_expr, .call_expr_one => {
+ const child_nodes = tree.childNodes(node);
+ const fn_ptr = child_nodes[0];
+ const args = child_nodes[1..];
- try w.writeByteNTimes(' ', level + half);
- try w.writeAll("args:\n");
- for (tree.data[data.range.start + 1 .. data.range.end]) |arg| try tree.dumpNode(arg, level + delta, mapper, config, w);
- },
- .call_expr_one => {
try w.writeByteNTimes(' ', level + half);
try w.writeAll("lhs:\n");
- try tree.dumpNode(data.bin.lhs, level + delta, mapper, config, w);
- if (data.bin.rhs != .none) {
+ try tree.dumpNode(fn_ptr, level + delta, mapper, config, w);
+
+ if (args.len > 0) {
try w.writeByteNTimes(' ', level + half);
- try w.writeAll("arg:\n");
- try tree.dumpNode(data.bin.rhs, level + delta, mapper, config, w);
+ try w.writeAll("args:\n");
+ for (args) |arg| {
+ try tree.dumpNode(arg, level + delta, mapper, config, w);
+ }
}
},
.builtin_call_expr => {
@@ -1295,28 +1394,25 @@ fn dumpNode(
try tree.dumpNode(data.un, level + delta, mapper, config, w);
}
},
- .generic_expr_one => {
- try w.writeByteNTimes(' ', level + 1);
- try w.writeAll("controlling:\n");
- try tree.dumpNode(data.bin.lhs, level + delta, mapper, config, w);
- try w.writeByteNTimes(' ', level + 1);
- if (data.bin.rhs != .none) {
- try w.writeAll("chosen:\n");
- try tree.dumpNode(data.bin.rhs, level + delta, mapper, config, w);
- }
- },
- .generic_expr => {
- const nodes = tree.data[data.range.start..data.range.end];
+ .generic_expr, .generic_expr_one => {
+ const child_nodes = tree.childNodes(node);
+ const controlling = child_nodes[0];
+ const chosen = child_nodes[1];
+ const rest = child_nodes[2..];
+
try w.writeByteNTimes(' ', level + 1);
try w.writeAll("controlling:\n");
- try tree.dumpNode(nodes[0], level + delta, mapper, config, w);
+ try tree.dumpNode(controlling, level + delta, mapper, config, w);
try w.writeByteNTimes(' ', level + 1);
try w.writeAll("chosen:\n");
- try tree.dumpNode(nodes[1], level + delta, mapper, config, w);
- try w.writeByteNTimes(' ', level + 1);
- try w.writeAll("rest:\n");
- for (nodes[2..]) |expr| {
- try tree.dumpNode(expr, level + delta, mapper, config, w);
+ try tree.dumpNode(chosen, level + delta, mapper, config, w);
+
+ if (rest.len > 0) {
+ try w.writeByteNTimes(' ', level + 1);
+ try w.writeAll("rest:\n");
+ for (rest) |expr| {
+ try tree.dumpNode(expr, level + delta, mapper, config, w);
+ }
}
},
.generic_association_expr, .generic_default_expr, .stmt_expr, .imaginary_literal => {
lib/compiler/aro/aro/Type.zig
@@ -146,17 +146,14 @@ pub const Attributed = struct {
attributes: []Attribute,
base: Type,
- pub fn create(allocator: std.mem.Allocator, base: Type, existing_attributes: []const Attribute, attributes: []const Attribute) !*Attributed {
+ pub fn create(allocator: std.mem.Allocator, base_ty: Type, attributes: []const Attribute) !*Attributed {
const attributed_type = try allocator.create(Attributed);
errdefer allocator.destroy(attributed_type);
-
- const all_attrs = try allocator.alloc(Attribute, existing_attributes.len + attributes.len);
- @memcpy(all_attrs[0..existing_attributes.len], existing_attributes);
- @memcpy(all_attrs[existing_attributes.len..], attributes);
+ const duped = try allocator.dupe(Attribute, attributes);
attributed_type.* = .{
- .attributes = all_attrs,
- .base = base,
+ .attributes = duped,
+ .base = base_ty,
};
return attributed_type;
}
@@ -190,13 +187,10 @@ pub const Enum = struct {
}
};
-// might not need all 4 of these when finished,
-// but currently it helps having all 4 when diff-ing
-// the rust code.
pub const TypeLayout = struct {
/// The size of the type in bits.
///
- /// This is the value returned by `sizeof` and C and `std::mem::size_of` in Rust
+ /// This is the value returned by `sizeof` in C
/// (but in bits instead of bytes). This is a multiple of `pointer_alignment_bits`.
size_bits: u64,
/// The alignment of the type, in bits, when used as a field in a record.
@@ -205,9 +199,7 @@ pub const TypeLayout = struct {
/// cases in GCC where `_Alignof` returns a smaller value.
field_alignment_bits: u32,
/// The alignment, in bits, of valid pointers to this type.
- ///
- /// This is the value returned by `std::mem::align_of` in Rust
- /// (but in bits instead of bytes). `size_bits` is a multiple of this value.
+ /// `size_bits` is a multiple of this value.
pointer_alignment_bits: u32,
/// The required alignment of the type in bits.
///
@@ -301,6 +293,15 @@ pub const Record = struct {
}
return false;
}
+
+ pub fn hasField(self: *const Record, name: StringId) bool {
+ std.debug.assert(!self.isIncomplete());
+ for (self.fields) |f| {
+ if (f.isAnonymousRecord() and f.ty.getRecord().?.hasField(name)) return true;
+ if (name == f.name) return true;
+ }
+ return false;
+ }
};
pub const Specifier = enum {
@@ -354,12 +355,11 @@ pub const Specifier = enum {
float,
double,
long_double,
- float80,
float128,
+ complex_float16,
complex_float,
complex_double,
complex_long_double,
- complex_float80,
complex_float128,
// data.sub_type
@@ -422,6 +422,8 @@ data: union {
specifier: Specifier,
qual: Qualifiers = .{},
decayed: bool = false,
+/// typedef name, if any
+name: StringId = .empty,
pub const int = Type{ .specifier = .int };
pub const invalid = Type{ .specifier = .invalid };
@@ -435,8 +437,8 @@ pub fn is(ty: Type, specifier: Specifier) bool {
pub fn withAttributes(self: Type, allocator: std.mem.Allocator, attributes: []const Attribute) !Type {
if (attributes.len == 0) return self;
- const attributed_type = try Type.Attributed.create(allocator, self, self.getAttributes(), attributes);
- return Type{ .specifier = .attributed, .data = .{ .attributed = attributed_type }, .decayed = self.decayed };
+ const attributed_type = try Type.Attributed.create(allocator, self, attributes);
+ return .{ .specifier = .attributed, .data = .{ .attributed = attributed_type }, .decayed = self.decayed };
}
pub fn isCallable(ty: Type) ?Type {
@@ -470,6 +472,23 @@ pub fn isArray(ty: Type) bool {
};
}
+/// Must only be used to set the length of an incomplete array as determined by its initializer
+pub fn setIncompleteArrayLen(ty: *Type, len: u64) void {
+ switch (ty.specifier) {
+ .incomplete_array => {
+ // Modifying .data is exceptionally allowed for .incomplete_array.
+ ty.data.array.len = len;
+ ty.specifier = .array;
+ },
+
+ .typeof_type => ty.data.sub_type.setIncompleteArrayLen(len),
+ .typeof_expr => ty.data.expr.ty.setIncompleteArrayLen(len),
+ .attributed => ty.data.attributed.base.setIncompleteArrayLen(len),
+
+ else => unreachable,
+ }
+}
+
/// Whether the type is promoted if used as a variadic argument or as an argument to a function with no prototype
fn undergoesDefaultArgPromotion(ty: Type, comp: *const Compilation) bool {
return switch (ty.specifier) {
@@ -536,7 +555,7 @@ pub fn isFloat(ty: Type) bool {
return switch (ty.specifier) {
// zig fmt: off
.float, .double, .long_double, .complex_float, .complex_double, .complex_long_double,
- .fp16, .float16, .float80, .float128, .complex_float80, .complex_float128 => true,
+ .fp16, .float16, .float128, .complex_float128, .complex_float16 => true,
// zig fmt: on
.typeof_type => ty.data.sub_type.isFloat(),
.typeof_expr => ty.data.expr.ty.isFloat(),
@@ -548,11 +567,11 @@ pub fn isFloat(ty: Type) bool {
pub fn isReal(ty: Type) bool {
return switch (ty.specifier) {
// zig fmt: off
- .complex_float, .complex_double, .complex_long_double, .complex_float80,
+ .complex_float, .complex_double, .complex_long_double,
.complex_float128, .complex_char, .complex_schar, .complex_uchar, .complex_short,
.complex_ushort, .complex_int, .complex_uint, .complex_long, .complex_ulong,
.complex_long_long, .complex_ulong_long, .complex_int128, .complex_uint128,
- .complex_bit_int => false,
+ .complex_bit_int, .complex_float16 => false,
// zig fmt: on
.typeof_type => ty.data.sub_type.isReal(),
.typeof_expr => ty.data.expr.ty.isReal(),
@@ -564,11 +583,11 @@ pub fn isReal(ty: Type) bool {
pub fn isComplex(ty: Type) bool {
return switch (ty.specifier) {
// zig fmt: off
- .complex_float, .complex_double, .complex_long_double, .complex_float80,
+ .complex_float, .complex_double, .complex_long_double,
.complex_float128, .complex_char, .complex_schar, .complex_uchar, .complex_short,
.complex_ushort, .complex_int, .complex_uint, .complex_long, .complex_ulong,
.complex_long_long, .complex_ulong_long, .complex_int128, .complex_uint128,
- .complex_bit_int => true,
+ .complex_bit_int, .complex_float16 => true,
// zig fmt: on
.typeof_type => ty.data.sub_type.isComplex(),
.typeof_expr => ty.data.expr.ty.isComplex(),
@@ -671,11 +690,11 @@ pub fn elemType(ty: Type) Type {
.attributed => ty.data.attributed.base.elemType(),
.invalid => Type.invalid,
// zig fmt: off
- .complex_float, .complex_double, .complex_long_double, .complex_float80,
+ .complex_float, .complex_double, .complex_long_double,
.complex_float128, .complex_char, .complex_schar, .complex_uchar, .complex_short,
.complex_ushort, .complex_int, .complex_uint, .complex_long, .complex_ulong,
.complex_long_long, .complex_ulong_long, .complex_int128, .complex_uint128,
- .complex_bit_int => ty.makeReal(),
+ .complex_bit_int, .complex_float16 => ty.makeReal(),
// zig fmt: on
else => unreachable,
};
@@ -703,6 +722,16 @@ pub fn params(ty: Type) []Func.Param {
};
}
+/// Returns true if the return value or any param of `ty` is `.invalid`
+/// Asserts that ty is a function type
+pub fn isInvalidFunc(ty: Type) bool {
+ if (ty.returnType().is(.invalid)) return true;
+ for (ty.params()) |param| {
+ if (param.ty.is(.invalid)) return true;
+ }
+ return false;
+}
+
pub fn arrayLen(ty: Type) ?u64 {
return switch (ty.specifier) {
.array, .static_array => ty.data.array.len,
@@ -726,15 +755,6 @@ pub fn anyQual(ty: Type) bool {
};
}
-pub fn getAttributes(ty: Type) []const Attribute {
- return switch (ty.specifier) {
- .attributed => ty.data.attributed.attributes,
- .typeof_type => ty.data.sub_type.getAttributes(),
- .typeof_expr => ty.data.expr.ty.getAttributes(),
- else => &.{},
- };
-}
-
pub fn getRecord(ty: Type) ?*const Type.Record {
return switch (ty.specifier) {
.attributed => ty.data.attributed.base.getRecord(),
@@ -795,8 +815,8 @@ fn realIntegerConversion(a: Type, b: Type, comp: *const Compilation) Type {
pub fn makeIntegerUnsigned(ty: Type) Type {
// TODO discards attributed/typeof
- var base = ty.canonicalize(.standard);
- switch (base.specifier) {
+ var base_ty = ty.canonicalize(.standard);
+ switch (base_ty.specifier) {
// zig fmt: off
.uchar, .ushort, .uint, .ulong, .ulong_long, .uint128,
.complex_uchar, .complex_ushort, .complex_uint, .complex_ulong, .complex_ulong_long, .complex_uint128,
@@ -804,21 +824,21 @@ pub fn makeIntegerUnsigned(ty: Type) Type {
// zig fmt: on
.char, .complex_char => {
- base.specifier = @enumFromInt(@intFromEnum(base.specifier) + 2);
- return base;
+ base_ty.specifier = @enumFromInt(@intFromEnum(base_ty.specifier) + 2);
+ return base_ty;
},
// zig fmt: off
.schar, .short, .int, .long, .long_long, .int128,
.complex_schar, .complex_short, .complex_int, .complex_long, .complex_long_long, .complex_int128 => {
- base.specifier = @enumFromInt(@intFromEnum(base.specifier) + 1);
- return base;
+ base_ty.specifier = @enumFromInt(@intFromEnum(base_ty.specifier) + 1);
+ return base_ty;
},
// zig fmt: on
.bit_int, .complex_bit_int => {
- base.data.int.signedness = .unsigned;
- return base;
+ base_ty.data.int.signedness = .unsigned;
+ return base_ty;
},
else => unreachable,
}
@@ -837,6 +857,8 @@ pub fn integerPromotion(ty: Type, comp: *Compilation) Type {
switch (specifier) {
.@"enum" => {
if (ty.hasIncompleteSize()) return .{ .specifier = .int };
+ if (ty.data.@"enum".fixed) return ty.data.@"enum".tag_ty.integerPromotion(comp);
+
specifier = ty.data.@"enum".tag_ty.specifier;
},
.bit_int, .complex_bit_int => return .{ .specifier = specifier, .data = ty.data },
@@ -915,53 +937,7 @@ pub fn hasUnboundVLA(ty: Type) bool {
}
pub fn hasField(ty: Type, name: StringId) bool {
- switch (ty.specifier) {
- .@"struct" => {
- std.debug.assert(!ty.data.record.isIncomplete());
- for (ty.data.record.fields) |f| {
- if (f.isAnonymousRecord() and f.ty.hasField(name)) return true;
- if (name == f.name) return true;
- }
- },
- .@"union" => {
- std.debug.assert(!ty.data.record.isIncomplete());
- for (ty.data.record.fields) |f| {
- if (f.isAnonymousRecord() and f.ty.hasField(name)) return true;
- if (name == f.name) return true;
- }
- },
- .typeof_type => return ty.data.sub_type.hasField(name),
- .typeof_expr => return ty.data.expr.ty.hasField(name),
- .attributed => return ty.data.attributed.base.hasField(name),
- .invalid => return false,
- else => unreachable,
- }
- return false;
-}
-
-// TODO handle bitints
-pub fn minInt(ty: Type, comp: *const Compilation) i64 {
- std.debug.assert(ty.isInt());
- if (ty.isUnsignedInt(comp)) return 0;
- return switch (ty.sizeof(comp).?) {
- 1 => std.math.minInt(i8),
- 2 => std.math.minInt(i16),
- 4 => std.math.minInt(i32),
- 8 => std.math.minInt(i64),
- else => unreachable,
- };
-}
-
-// TODO handle bitints
-pub fn maxInt(ty: Type, comp: *const Compilation) u64 {
- std.debug.assert(ty.isInt());
- return switch (ty.sizeof(comp).?) {
- 1 => if (ty.isUnsignedInt(comp)) @as(u64, std.math.maxInt(u8)) else std.math.maxInt(i8),
- 2 => if (ty.isUnsignedInt(comp)) @as(u64, std.math.maxInt(u16)) else std.math.maxInt(i16),
- 4 => if (ty.isUnsignedInt(comp)) @as(u64, std.math.maxInt(u32)) else std.math.maxInt(i32),
- 8 => if (ty.isUnsignedInt(comp)) @as(u64, std.math.maxInt(u64)) else std.math.maxInt(i64),
- else => unreachable,
- };
+ return ty.getRecord().?.hasField(name);
}
const TypeSizeOrder = enum {
@@ -1004,16 +980,15 @@ pub fn sizeof(ty: Type, comp: *const Compilation) ?u64 {
.fp16, .float16 => 2,
.float => comp.target.cTypeByteSize(.float),
.double => comp.target.cTypeByteSize(.double),
- .float80 => 16,
.float128 => 16,
.bit_int => {
- return std.mem.alignForward(u64, (ty.data.int.bits + 7) / 8, ty.alignof(comp));
+ return std.mem.alignForward(u64, (@as(u32, ty.data.int.bits) + 7) / 8, ty.alignof(comp));
},
// zig fmt: off
.complex_char, .complex_schar, .complex_uchar, .complex_short, .complex_ushort, .complex_int,
.complex_uint, .complex_long, .complex_ulong, .complex_long_long, .complex_ulong_long,
.complex_int128, .complex_uint128, .complex_float, .complex_double,
- .complex_long_double, .complex_float80, .complex_float128, .complex_bit_int,
+ .complex_long_double, .complex_float128, .complex_bit_int, .complex_float16,
=> return 2 * ty.makeReal().sizeof(comp).?,
// zig fmt: on
.pointer => unreachable,
@@ -1050,7 +1025,6 @@ pub fn bitSizeof(ty: Type, comp: *const Compilation) ?u64 {
.attributed => ty.data.attributed.base.bitSizeof(comp),
.bit_int => return ty.data.int.bits,
.long_double => comp.target.cTypeBitSize(.longdouble),
- .float80 => return 80,
else => 8 * (ty.sizeof(comp) orelse return null),
};
}
@@ -1100,7 +1074,7 @@ pub fn alignof(ty: Type, comp: *const Compilation) u29 {
.complex_char, .complex_schar, .complex_uchar, .complex_short, .complex_ushort, .complex_int,
.complex_uint, .complex_long, .complex_ulong, .complex_long_long, .complex_ulong_long,
.complex_int128, .complex_uint128, .complex_float, .complex_double,
- .complex_long_double, .complex_float80, .complex_float128, .complex_bit_int,
+ .complex_long_double, .complex_float128, .complex_bit_int, .complex_float16,
=> return ty.makeReal().alignof(comp),
// zig fmt: on
@@ -1114,10 +1088,15 @@ pub fn alignof(ty: Type, comp: *const Compilation) u29 {
.long_long => comp.target.cTypeAlignment(.longlong),
.ulong_long => comp.target.cTypeAlignment(.ulonglong),
- .bit_int => @min(
- std.math.ceilPowerOfTwoPromote(u16, (ty.data.int.bits + 7) / 8),
- 16, // comp.target.maxIntAlignment(), please use your own logic for this value as it is implementation-defined
- ),
+ .bit_int => {
+ // https://www.open-std.org/jtc1/sc22/wg14/www/docs/n2709.pdf
+ // _BitInt(N) types align with existing calling conventions. They have the same size and alignment as the
+ // smallest basic type that can contain them. Types that are larger than __int64_t are conceptually treated
+ // as struct of register size chunks. The number of chunks is the smallest number that can contain the type.
+ if (ty.data.int.bits > 64) return 8;
+ const basic_type = comp.intLeastN(ty.data.int.bits, ty.data.int.signedness);
+ return basic_type.alignof(comp);
+ },
.float => comp.target.cTypeAlignment(.float),
.double => comp.target.cTypeAlignment(.double),
@@ -1126,7 +1105,7 @@ pub fn alignof(ty: Type, comp: *const Compilation) u29 {
.int128, .uint128 => if (comp.target.cpu.arch == .s390x and comp.target.os.tag == .linux and comp.target.isGnu()) 8 else 16,
.fp16, .float16 => 2,
- .float80, .float128 => 16,
+ .float128 => 16,
.pointer,
.static_array,
.nullptr_t,
@@ -1142,7 +1121,11 @@ pub fn alignof(ty: Type, comp: *const Compilation) u29 {
};
}
-pub const QualHandling = enum { standard, preserve_quals };
+// This enum should be kept public because it is used by the downstream zig translate-c
+pub const QualHandling = enum {
+ standard,
+ preserve_quals,
+};
/// Canonicalize a possibly-typeof() type. If the type is not a typeof() type, simply
/// return it. Otherwise, determine the actual qualified type.
@@ -1151,17 +1134,12 @@ pub const QualHandling = enum { standard, preserve_quals };
/// arrays and pointers.
pub fn canonicalize(ty: Type, qual_handling: QualHandling) Type {
var cur = ty;
- if (cur.specifier == .attributed) {
- cur = cur.data.attributed.base;
- cur.decayed = ty.decayed;
- }
- if (!cur.isTypeof()) return cur;
-
var qual = cur.qual;
while (true) {
switch (cur.specifier) {
.typeof_type => cur = cur.data.sub_type.*,
.typeof_expr => cur = cur.data.expr.ty,
+ .attributed => cur = cur.data.attributed.base,
else => break,
}
qual = qual.mergeAll(cur.qual);
@@ -1189,7 +1167,7 @@ pub fn requestedAlignment(ty: Type, comp: *const Compilation) ?u29 {
return switch (ty.specifier) {
.typeof_type => ty.data.sub_type.requestedAlignment(comp),
.typeof_expr => ty.data.expr.ty.requestedAlignment(comp),
- .attributed => annotationAlignment(comp, ty.data.attributed.attributes),
+ .attributed => annotationAlignment(comp, Attribute.Iterator.initType(ty)),
else => null,
};
}
@@ -1199,12 +1177,27 @@ pub fn enumIsPacked(ty: Type, comp: *const Compilation) bool {
return comp.langopts.short_enums or target_util.packAllEnums(comp.target) or ty.hasAttribute(.@"packed");
}
-pub fn annotationAlignment(comp: *const Compilation, attrs: ?[]const Attribute) ?u29 {
- const a = attrs orelse return null;
+pub fn getName(ty: Type) StringId {
+ return switch (ty.specifier) {
+ .typeof_type => if (ty.name == .empty) ty.data.sub_type.getName() else ty.name,
+ .typeof_expr => if (ty.name == .empty) ty.data.expr.ty.getName() else ty.name,
+ .attributed => if (ty.name == .empty) ty.data.attributed.base.getName() else ty.name,
+ else => ty.name,
+ };
+}
+pub fn annotationAlignment(comp: *const Compilation, attrs: Attribute.Iterator) ?u29 {
+ var it = attrs;
var max_requested: ?u29 = null;
- for (a) |attribute| {
+ var last_aligned_index: ?usize = null;
+ while (it.next()) |item| {
+ const attribute, const index = item;
if (attribute.tag != .aligned) continue;
+ if (last_aligned_index) |aligned_index| {
+ // once we recurse into a new type, after an `aligned` attribute was found, we're done
+ if (index <= aligned_index) break;
+ }
+ last_aligned_index = index;
const requested = if (attribute.args.aligned.alignment) |alignment| alignment.requested else target_util.defaultAlignment(comp.target);
if (max_requested == null or max_requested.? < requested) {
max_requested = requested;
@@ -1225,6 +1218,10 @@ pub fn eql(a_param: Type, b_param: Type, comp: *const Compilation, check_qualifi
if (!b.isFunc()) return false;
} else if (a.isArray()) {
if (!b.isArray()) return false;
+ } else if (a.specifier == .@"enum" and b.specifier != .@"enum") {
+ return a.data.@"enum".tag_ty.eql(b, comp, check_qualifiers);
+ } else if (b.specifier == .@"enum" and a.specifier != .@"enum") {
+ return a.eql(b.data.@"enum".tag_ty, comp, check_qualifiers);
} else if (a.specifier != b.specifier) return false;
if (a.qual.atomic != b.qual.atomic) return false;
@@ -1315,6 +1312,12 @@ pub fn integerRank(ty: Type, comp: *const Compilation) usize {
.long_long, .ulong_long => 6 + (ty.bitSizeof(comp).? << 3),
.int128, .uint128 => 7 + (ty.bitSizeof(comp).? << 3),
+ .typeof_type => ty.data.sub_type.integerRank(comp),
+ .typeof_expr => ty.data.expr.ty.integerRank(comp),
+ .attributed => ty.data.attributed.base.integerRank(comp),
+
+ .@"enum" => real.data.@"enum".tag_ty.integerRank(comp),
+
else => unreachable,
});
}
@@ -1322,25 +1325,26 @@ pub fn integerRank(ty: Type, comp: *const Compilation) usize {
/// Returns true if `a` and `b` are integer types that differ only in sign
pub fn sameRankDifferentSign(a: Type, b: Type, comp: *const Compilation) bool {
if (!a.isInt() or !b.isInt()) return false;
+ if (a.hasIncompleteSize() or b.hasIncompleteSize()) return false;
if (a.integerRank(comp) != b.integerRank(comp)) return false;
return a.isUnsignedInt(comp) != b.isUnsignedInt(comp);
}
pub fn makeReal(ty: Type) Type {
// TODO discards attributed/typeof
- var base = ty.canonicalize(.standard);
- switch (base.specifier) {
- .complex_float, .complex_double, .complex_long_double, .complex_float80, .complex_float128 => {
- base.specifier = @enumFromInt(@intFromEnum(base.specifier) - 5);
- return base;
+ var base_ty = ty.canonicalize(.standard);
+ switch (base_ty.specifier) {
+ .complex_float16, .complex_float, .complex_double, .complex_long_double, .complex_float128 => {
+ base_ty.specifier = @enumFromInt(@intFromEnum(base_ty.specifier) - 5);
+ return base_ty;
},
.complex_char, .complex_schar, .complex_uchar, .complex_short, .complex_ushort, .complex_int, .complex_uint, .complex_long, .complex_ulong, .complex_long_long, .complex_ulong_long, .complex_int128, .complex_uint128 => {
- base.specifier = @enumFromInt(@intFromEnum(base.specifier) - 13);
- return base;
+ base_ty.specifier = @enumFromInt(@intFromEnum(base_ty.specifier) - 13);
+ return base_ty;
},
.complex_bit_int => {
- base.specifier = .bit_int;
- return base;
+ base_ty.specifier = .bit_int;
+ return base_ty;
},
else => return ty,
}
@@ -1348,19 +1352,19 @@ pub fn makeReal(ty: Type) Type {
pub fn makeComplex(ty: Type) Type {
// TODO discards attributed/typeof
- var base = ty.canonicalize(.standard);
- switch (base.specifier) {
- .float, .double, .long_double, .float80, .float128 => {
- base.specifier = @enumFromInt(@intFromEnum(base.specifier) + 5);
- return base;
+ var base_ty = ty.canonicalize(.standard);
+ switch (base_ty.specifier) {
+ .float, .double, .long_double, .float128 => {
+ base_ty.specifier = @enumFromInt(@intFromEnum(base_ty.specifier) + 5);
+ return base_ty;
},
.char, .schar, .uchar, .short, .ushort, .int, .uint, .long, .ulong, .long_long, .ulong_long, .int128, .uint128 => {
- base.specifier = @enumFromInt(@intFromEnum(base.specifier) + 13);
- return base;
+ base_ty.specifier = @enumFromInt(@intFromEnum(base_ty.specifier) + 13);
+ return base_ty;
},
.bit_int => {
- base.specifier = .complex_bit_int;
- return base;
+ base_ty.specifier = .complex_bit_int;
+ return base_ty;
},
else => return ty,
}
@@ -1541,13 +1545,12 @@ pub const Builder = struct {
float,
double,
long_double,
- float80,
float128,
complex,
+ complex_float16,
complex_float,
complex_double,
complex_long_double,
- complex_float80,
complex_float128,
pointer: *Type,
@@ -1613,9 +1616,6 @@ pub const Builder = struct {
.int128 => "__int128",
.sint128 => "signed __int128",
.uint128 => "unsigned __int128",
- .bit_int => "_BitInt",
- .sbit_int => "signed _BitInt",
- .ubit_int => "unsigned _BitInt",
.complex_char => "_Complex char",
.complex_schar => "_Complex signed char",
.complex_uchar => "_Complex unsigned char",
@@ -1645,22 +1645,18 @@ pub const Builder = struct {
.complex_int128 => "_Complex __int128",
.complex_sint128 => "_Complex signed __int128",
.complex_uint128 => "_Complex unsigned __int128",
- .complex_bit_int => "_Complex _BitInt",
- .complex_sbit_int => "_Complex signed _BitInt",
- .complex_ubit_int => "_Complex unsigned _BitInt",
.fp16 => "__fp16",
.float16 => "_Float16",
.float => "float",
.double => "double",
.long_double => "long double",
- .float80 => "__float80",
.float128 => "__float128",
.complex => "_Complex",
+ .complex_float16 => "_Complex _Float16",
.complex_float => "_Complex float",
.complex_double => "_Complex double",
.complex_long_double => "_Complex long double",
- .complex_float80 => "_Complex __float80",
.complex_float128 => "_Complex __float128",
.attributed => |attributed| Builder.fromType(attributed.base).str(langopts),
@@ -1757,19 +1753,20 @@ pub const Builder = struct {
.complex_uint128 => ty.specifier = .complex_uint128,
.bit_int, .sbit_int, .ubit_int, .complex_bit_int, .complex_ubit_int, .complex_sbit_int => |bits| {
const unsigned = b.specifier == .ubit_int or b.specifier == .complex_ubit_int;
+ const complex_str = if (b.complex_tok != null) "_Complex " else "";
if (unsigned) {
if (bits < 1) {
- try p.errStr(.unsigned_bit_int_too_small, b.bit_int_tok.?, b.specifier.str(p.comp.langopts).?);
+ try p.errStr(.unsigned_bit_int_too_small, b.bit_int_tok.?, complex_str);
return Type.invalid;
}
} else {
if (bits < 2) {
- try p.errStr(.signed_bit_int_too_small, b.bit_int_tok.?, b.specifier.str(p.comp.langopts).?);
+ try p.errStr(.signed_bit_int_too_small, b.bit_int_tok.?, complex_str);
return Type.invalid;
}
}
if (bits > Compilation.bit_int_max_bits) {
- try p.errStr(.bit_int_too_big, b.bit_int_tok.?, b.specifier.str(p.comp.langopts).?);
+ try p.errStr(if (unsigned) .unsigned_bit_int_too_big else .signed_bit_int_too_big, b.bit_int_tok.?, complex_str);
return Type.invalid;
}
ty.specifier = if (b.complex_tok != null) .complex_bit_int else .bit_int;
@@ -1784,12 +1781,11 @@ pub const Builder = struct {
.float => ty.specifier = .float,
.double => ty.specifier = .double,
.long_double => ty.specifier = .long_double,
- .float80 => ty.specifier = .float80,
.float128 => ty.specifier = .float128,
+ .complex_float16 => ty.specifier = .complex_float16,
.complex_float => ty.specifier = .complex_float,
.complex_double => ty.specifier = .complex_double,
.complex_long_double => ty.specifier = .complex_long_double,
- .complex_float80 => ty.specifier = .complex_float80,
.complex_float128 => ty.specifier = .complex_float128,
.complex => {
try p.errTok(.plain_complex, p.tok_i - 1);
@@ -1907,6 +1903,7 @@ pub const Builder = struct {
/// Try to combine type from typedef, returns true if successful.
pub fn combineTypedef(b: *Builder, p: *Parser, typedef_ty: Type, name_tok: TokenIndex) bool {
+ if (typedef_ty.is(.invalid)) return false;
b.error_on_invalid = true;
defer b.error_on_invalid = false;
@@ -2094,6 +2091,7 @@ pub const Builder = struct {
},
.long => b.specifier = switch (b.specifier) {
.none => .long,
+ .double => .long_double,
.long => .long_long,
.unsigned => .ulong,
.signed => .long,
@@ -2106,6 +2104,7 @@ pub const Builder = struct {
.complex_long => .complex_long_long,
.complex_slong => .complex_slong_long,
.complex_ulong => .complex_ulong_long,
+ .complex_double => .complex_long_double,
else => return b.cannotCombine(p, source_tok),
},
.int128 => b.specifier = switch (b.specifier) {
@@ -2140,6 +2139,7 @@ pub const Builder = struct {
},
.float16 => b.specifier = switch (b.specifier) {
.none => .float16,
+ .complex => .complex_float16,
else => return b.cannotCombine(p, source_tok),
},
.float => b.specifier = switch (b.specifier) {
@@ -2154,11 +2154,6 @@ pub const Builder = struct {
.complex => .complex_double,
else => return b.cannotCombine(p, source_tok),
},
- .float80 => b.specifier = switch (b.specifier) {
- .none => .float80,
- .complex => .complex_float80,
- else => return b.cannotCombine(p, source_tok),
- },
.float128 => b.specifier = switch (b.specifier) {
.none => .float128,
.complex => .complex_float128,
@@ -2166,10 +2161,10 @@ pub const Builder = struct {
},
.complex => b.specifier = switch (b.specifier) {
.none => .complex,
+ .float16 => .complex_float16,
.float => .complex_float,
.double => .complex_double,
.long_double => .complex_long_double,
- .float80 => .complex_float80,
.float128 => .complex_float128,
.char => .complex_char,
.schar => .complex_schar,
@@ -2207,7 +2202,6 @@ pub const Builder = struct {
.complex_float,
.complex_double,
.complex_long_double,
- .complex_float80,
.complex_float128,
.complex_char,
.complex_schar,
@@ -2294,13 +2288,12 @@ pub const Builder = struct {
.float16 => .float16,
.float => .float,
.double => .double,
- .float80 => .float80,
.float128 => .float128,
.long_double => .long_double,
+ .complex_float16 => .complex_float16,
.complex_float => .complex_float,
.complex_double => .complex_double,
.complex_long_double => .complex_long_double,
- .complex_float80 => .complex_float80,
.complex_float128 => .complex_float128,
.pointer => .{ .pointer = ty.data.sub_type },
@@ -2350,22 +2343,30 @@ pub const Builder = struct {
}
};
+/// Use with caution
+pub fn base(ty: *Type) *Type {
+ return switch (ty.specifier) {
+ .typeof_type => ty.data.sub_type.base(),
+ .typeof_expr => ty.data.expr.ty.base(),
+ .attributed => ty.data.attributed.base.base(),
+ else => ty,
+ };
+}
+
pub fn getAttribute(ty: Type, comptime tag: Attribute.Tag) ?Attribute.ArgumentsForTag(tag) {
- switch (ty.specifier) {
- .typeof_type => return ty.data.sub_type.getAttribute(tag),
- .typeof_expr => return ty.data.expr.ty.getAttribute(tag),
- .attributed => {
- for (ty.data.attributed.attributes) |attribute| {
- if (attribute.tag == tag) return @field(attribute.args, @tagName(tag));
- }
- return null;
- },
- else => return null,
+ if (tag == .aligned) @compileError("use requestedAlignment");
+ var it = Attribute.Iterator.initType(ty);
+ while (it.next()) |item| {
+ const attribute, _ = item;
+ if (attribute.tag == tag) return @field(attribute.args, @tagName(tag));
}
+ return null;
}
pub fn hasAttribute(ty: Type, tag: Attribute.Tag) bool {
- for (ty.getAttributes()) |attr| {
+ var it = Attribute.Iterator.initType(ty);
+ while (it.next()) |item| {
+ const attr, _ = item;
if (attr.tag == tag) return true;
}
return false;
@@ -2489,6 +2490,8 @@ fn printPrologue(ty: Type, mapper: StringInterner.TypeMapper, langopts: LangOpts
_ = try elem_ty.printPrologue(mapper, langopts, w);
try w.writeAll("' values)");
},
+ .bit_int => try w.print("{s} _BitInt({d})", .{ @tagName(ty.data.int.signedness), ty.data.int.bits }),
+ .complex_bit_int => try w.print("_Complex {s} _BitInt({d})", .{ @tagName(ty.data.int.signedness), ty.data.int.bits }),
else => try w.writeAll(Builder.fromType(ty).str(langopts).?),
}
return true;
@@ -2644,15 +2647,12 @@ pub fn dump(ty: Type, mapper: StringInterner.TypeMapper, langopts: LangOpts, w:
.attributed => {
if (ty.isDecayed()) try w.writeAll("*d:");
try w.writeAll("attributed(");
- try ty.data.attributed.base.dump(mapper, langopts, w);
+ try ty.data.attributed.base.canonicalize(.standard).dump(mapper, langopts, w);
try w.writeAll(")");
},
- else => {
- try w.writeAll(Builder.fromType(ty).str(langopts).?);
- if (ty.specifier == .bit_int or ty.specifier == .complex_bit_int) {
- try w.print("({d})", .{ty.data.int.bits});
- }
- },
+ .bit_int => try w.print("{s} _BitInt({d})", .{ @tagName(ty.data.int.signedness), ty.data.int.bits }),
+ .complex_bit_int => try w.print("_Complex {s} _BitInt({d})", .{ @tagName(ty.data.int.signedness), ty.data.int.bits }),
+ else => try w.writeAll(Builder.fromType(ty).str(langopts).?),
}
}
lib/compiler/aro/aro/Value.zig
@@ -8,6 +8,7 @@ const BigIntSpace = Interner.Tag.Int.BigIntSpace;
const Compilation = @import("Compilation.zig");
const Type = @import("Type.zig");
const target_util = @import("target.zig");
+const annex_g = @import("annex_g.zig");
const Value = @This();
@@ -41,6 +42,14 @@ pub fn is(v: Value, tag: std.meta.Tag(Interner.Key), comp: *const Compilation) b
return comp.interner.get(v.ref()) == tag;
}
+pub fn isArithmetic(v: Value, comp: *const Compilation) bool {
+ if (v.opt_ref == .none) return false;
+ return switch (comp.interner.get(v.ref())) {
+ .int, .float, .complex => true,
+ else => false,
+ };
+}
+
/// Number of bits needed to hold `v`.
/// Asserts that `v` is not negative
pub fn minUnsignedBits(v: Value, comp: *const Compilation) usize {
@@ -58,7 +67,7 @@ test "minUnsignedBits" {
}
};
- var comp = Compilation.init(std.testing.allocator);
+ var comp = Compilation.init(std.testing.allocator, std.fs.cwd());
defer comp.deinit();
const target_query = try std.Target.Query.parse(.{ .arch_os_abi = "x86_64-linux-gnu" });
comp.target = try std.zig.system.resolveTargetQuery(target_query);
@@ -93,7 +102,7 @@ test "minSignedBits" {
}
};
- var comp = Compilation.init(std.testing.allocator);
+ var comp = Compilation.init(std.testing.allocator, std.fs.cwd());
defer comp.deinit();
const target_query = try std.Target.Query.parse(.{ .arch_os_abi = "x86_64-linux-gnu" });
comp.target = try std.zig.system.resolveTargetQuery(target_query);
@@ -134,7 +143,7 @@ pub fn floatToInt(v: *Value, dest_ty: Type, comp: *Compilation) !FloatToIntChang
v.* = fromBool(!was_zero);
if (was_zero or was_one) return .none;
return .value_changed;
- } else if (dest_ty.isUnsignedInt(comp) and v.compare(.lt, zero, comp)) {
+ } else if (dest_ty.isUnsignedInt(comp) and float_val < 0) {
v.* = zero;
return .out_of_range;
}
@@ -154,7 +163,7 @@ pub fn floatToInt(v: *Value, dest_ty: Type, comp: *Compilation) !FloatToIntChang
};
// The float is reduced in rational.setFloat, so we assert that denominator is equal to one
- const big_one = std.math.big.int.Const{ .limbs = &.{1}, .positive = true };
+ const big_one = BigIntConst{ .limbs = &.{1}, .positive = true };
assert(rational.q.toConst().eqlAbs(big_one));
if (is_negative) {
@@ -179,6 +188,20 @@ pub fn floatToInt(v: *Value, dest_ty: Type, comp: *Compilation) !FloatToIntChang
/// `.none` value remains unchanged.
pub fn intToFloat(v: *Value, dest_ty: Type, comp: *Compilation) !void {
if (v.opt_ref == .none) return;
+
+ if (dest_ty.isComplex()) {
+ const bits = dest_ty.bitSizeof(comp).?;
+ const cf: Interner.Key.Complex = switch (bits) {
+ 32 => .{ .cf16 = .{ v.toFloat(f16, comp), 0 } },
+ 64 => .{ .cf32 = .{ v.toFloat(f32, comp), 0 } },
+ 128 => .{ .cf64 = .{ v.toFloat(f64, comp), 0 } },
+ 160 => .{ .cf80 = .{ v.toFloat(f80, comp), 0 } },
+ 256 => .{ .cf128 = .{ v.toFloat(f128, comp), 0 } },
+ else => unreachable,
+ };
+ v.* = try intern(comp, .{ .complex = cf });
+ return;
+ }
const bits = dest_ty.bitSizeof(comp).?;
return switch (comp.interner.get(v.ref()).int) {
inline .u64, .i64 => |data| {
@@ -207,40 +230,89 @@ pub fn intToFloat(v: *Value, dest_ty: Type, comp: *Compilation) !void {
};
}
+pub const IntCastChangeKind = enum {
+ /// value did not change
+ none,
+ /// Truncation occurred (e.g., i32 to i16)
+ truncated,
+ /// Sign conversion occurred (e.g., i32 to u32)
+ sign_changed,
+};
+
/// Truncates or extends bits based on type.
/// `.none` value remains unchanged.
-pub fn intCast(v: *Value, dest_ty: Type, comp: *Compilation) !void {
- if (v.opt_ref == .none) return;
- const bits: usize = @intCast(dest_ty.bitSizeof(comp).?);
+pub fn intCast(v: *Value, dest_ty: Type, comp: *Compilation) !IntCastChangeKind {
+ if (v.opt_ref == .none) return .none;
+
+ const dest_bits: usize = @intCast(dest_ty.bitSizeof(comp).?);
+ const dest_signed = dest_ty.signedness(comp) == .signed;
+
var space: BigIntSpace = undefined;
const big = v.toBigInt(&space, comp);
+ const value_bits = big.bitCountTwosComp();
+
+ // if big is negative, then is signed.
+ const src_signed = !big.positive;
+ const sign_change = src_signed != dest_signed;
const limbs = try comp.gpa.alloc(
std.math.big.Limb,
- std.math.big.int.calcTwosCompLimbCount(@max(big.bitCountTwosComp(), bits)),
+ std.math.big.int.calcTwosCompLimbCount(@max(value_bits, dest_bits)),
);
defer comp.gpa.free(limbs);
- var result_bigint = std.math.big.int.Mutable{ .limbs = limbs, .positive = undefined, .len = undefined };
- result_bigint.truncate(big, dest_ty.signedness(comp), bits);
+
+ var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined };
+ result_bigint.truncate(big, dest_ty.signedness(comp), dest_bits);
v.* = try intern(comp, .{ .int = .{ .big_int = result_bigint.toConst() } });
+
+ const truncation_occurred = value_bits > dest_bits;
+ if (truncation_occurred) {
+ return .truncated;
+ } else if (sign_change) {
+ return .sign_changed;
+ } else {
+ return .none;
+ }
}
/// Converts the stored value to a float of the specified type
/// `.none` value remains unchanged.
pub fn floatCast(v: *Value, dest_ty: Type, comp: *Compilation) !void {
if (v.opt_ref == .none) return;
- // TODO complex values
- const bits = dest_ty.makeReal().bitSizeof(comp).?;
- const f: Interner.Key.Float = switch (bits) {
- 16 => .{ .f16 = v.toFloat(f16, comp) },
- 32 => .{ .f32 = v.toFloat(f32, comp) },
- 64 => .{ .f64 = v.toFloat(f64, comp) },
- 80 => .{ .f80 = v.toFloat(f80, comp) },
- 128 => .{ .f128 = v.toFloat(f128, comp) },
+ const bits = dest_ty.bitSizeof(comp).?;
+ if (dest_ty.isComplex()) {
+ const cf: Interner.Key.Complex = switch (bits) {
+ 32 => .{ .cf16 = .{ v.toFloat(f16, comp), v.imag(f16, comp) } },
+ 64 => .{ .cf32 = .{ v.toFloat(f32, comp), v.imag(f32, comp) } },
+ 128 => .{ .cf64 = .{ v.toFloat(f64, comp), v.imag(f64, comp) } },
+ 160 => .{ .cf80 = .{ v.toFloat(f80, comp), v.imag(f80, comp) } },
+ 256 => .{ .cf128 = .{ v.toFloat(f128, comp), v.imag(f128, comp) } },
+ else => unreachable,
+ };
+ v.* = try intern(comp, .{ .complex = cf });
+ } else {
+ const f: Interner.Key.Float = switch (bits) {
+ 16 => .{ .f16 = v.toFloat(f16, comp) },
+ 32 => .{ .f32 = v.toFloat(f32, comp) },
+ 64 => .{ .f64 = v.toFloat(f64, comp) },
+ 80 => .{ .f80 = v.toFloat(f80, comp) },
+ 128 => .{ .f128 = v.toFloat(f128, comp) },
+ else => unreachable,
+ };
+ v.* = try intern(comp, .{ .float = f });
+ }
+}
+
+pub fn imag(v: Value, comptime T: type, comp: *const Compilation) T {
+ return switch (comp.interner.get(v.ref())) {
+ .int => 0.0,
+ .float => 0.0,
+ .complex => |repr| switch (repr) {
+ inline else => |components| return @floatCast(components[1]),
+ },
else => unreachable,
};
- v.* = try intern(comp, .{ .float = f });
}
pub fn toFloat(v: Value, comptime T: type, comp: *const Compilation) T {
@@ -252,6 +324,39 @@ pub fn toFloat(v: Value, comptime T: type, comp: *const Compilation) T {
.float => |repr| switch (repr) {
inline else => |data| @floatCast(data),
},
+ .complex => |repr| switch (repr) {
+ inline else => |components| @floatCast(components[0]),
+ },
+ else => unreachable,
+ };
+}
+
+pub fn realPart(v: Value, comp: *Compilation) !Value {
+ if (v.opt_ref == .none) return v;
+ return switch (comp.interner.get(v.ref())) {
+ .int, .float => v,
+ .complex => |repr| Value.intern(comp, switch (repr) {
+ .cf16 => |components| .{ .float = .{ .f16 = components[0] } },
+ .cf32 => |components| .{ .float = .{ .f32 = components[0] } },
+ .cf64 => |components| .{ .float = .{ .f64 = components[0] } },
+ .cf80 => |components| .{ .float = .{ .f80 = components[0] } },
+ .cf128 => |components| .{ .float = .{ .f128 = components[0] } },
+ }),
+ else => unreachable,
+ };
+}
+
+pub fn imaginaryPart(v: Value, comp: *Compilation) !Value {
+ if (v.opt_ref == .none) return v;
+ return switch (comp.interner.get(v.ref())) {
+ .int, .float => Value.zero,
+ .complex => |repr| Value.intern(comp, switch (repr) {
+ .cf16 => |components| .{ .float = .{ .f16 = components[1] } },
+ .cf32 => |components| .{ .float = .{ .f32 = components[1] } },
+ .cf64 => |components| .{ .float = .{ .f64 = components[1] } },
+ .cf80 => |components| .{ .float = .{ .f80 = components[1] } },
+ .cf128 => |components| .{ .float = .{ .f128 = components[1] } },
+ }),
else => unreachable,
};
}
@@ -298,11 +403,56 @@ pub fn isZero(v: Value, comp: *const Compilation) bool {
inline .i64, .u64 => |data| return data == 0,
.big_int => |data| return data.eqlZero(),
},
+ .complex => |repr| switch (repr) {
+ inline else => |data| return data[0] == 0.0 and data[1] == 0.0,
+ },
.bytes => return false,
else => unreachable,
}
}
+const IsInfKind = enum(i32) {
+ negative = -1,
+ finite = 0,
+ positive = 1,
+ unknown = std.math.maxInt(i32),
+};
+
+pub fn isInfSign(v: Value, comp: *const Compilation) IsInfKind {
+ if (v.opt_ref == .none) return .unknown;
+ return switch (comp.interner.get(v.ref())) {
+ .float => |repr| switch (repr) {
+ inline else => |data| if (std.math.isPositiveInf(data)) .positive else if (std.math.isNegativeInf(data)) .negative else .finite,
+ },
+ else => .unknown,
+ };
+}
+pub fn isInf(v: Value, comp: *const Compilation) bool {
+ if (v.opt_ref == .none) return false;
+ return switch (comp.interner.get(v.ref())) {
+ .float => |repr| switch (repr) {
+ inline else => |data| std.math.isInf(data),
+ },
+ .complex => |repr| switch (repr) {
+ inline else => |components| std.math.isInf(components[0]) or std.math.isInf(components[1]),
+ },
+ else => false,
+ };
+}
+
+pub fn isNan(v: Value, comp: *const Compilation) bool {
+ if (v.opt_ref == .none) return false;
+ return switch (comp.interner.get(v.ref())) {
+ .float => |repr| switch (repr) {
+ inline else => |data| std.math.isNan(data),
+ },
+ .complex => |repr| switch (repr) {
+ inline else => |components| std.math.isNan(components[0]) or std.math.isNan(components[1]),
+ },
+ else => false,
+ };
+}
+
/// Converts value to zero or one;
/// `.none` value remains unchanged.
pub fn boolCast(v: *Value, comp: *const Compilation) void {
@@ -326,9 +476,45 @@ pub fn toInt(v: Value, comptime T: type, comp: *const Compilation) ?T {
return big_int.to(T) catch null;
}
+const ComplexOp = enum {
+ add,
+ sub,
+};
+
+fn complexAddSub(lhs: Value, rhs: Value, comptime T: type, op: ComplexOp, comp: *Compilation) !Value {
+ const res_re = switch (op) {
+ .add => lhs.toFloat(T, comp) + rhs.toFloat(T, comp),
+ .sub => lhs.toFloat(T, comp) - rhs.toFloat(T, comp),
+ };
+ const res_im = switch (op) {
+ .add => lhs.imag(T, comp) + rhs.imag(T, comp),
+ .sub => lhs.imag(T, comp) - rhs.imag(T, comp),
+ };
+
+ return switch (T) {
+ f16 => intern(comp, .{ .complex = .{ .cf16 = .{ res_re, res_im } } }),
+ f32 => intern(comp, .{ .complex = .{ .cf32 = .{ res_re, res_im } } }),
+ f64 => intern(comp, .{ .complex = .{ .cf64 = .{ res_re, res_im } } }),
+ f80 => intern(comp, .{ .complex = .{ .cf80 = .{ res_re, res_im } } }),
+ f128 => intern(comp, .{ .complex = .{ .cf128 = .{ res_re, res_im } } }),
+ else => unreachable,
+ };
+}
+
pub fn add(res: *Value, lhs: Value, rhs: Value, ty: Type, comp: *Compilation) !bool {
const bits: usize = @intCast(ty.bitSizeof(comp).?);
if (ty.isFloat()) {
+ if (ty.isComplex()) {
+ res.* = switch (bits) {
+ 32 => try complexAddSub(lhs, rhs, f16, .add, comp),
+ 64 => try complexAddSub(lhs, rhs, f32, .add, comp),
+ 128 => try complexAddSub(lhs, rhs, f64, .add, comp),
+ 160 => try complexAddSub(lhs, rhs, f80, .add, comp),
+ 256 => try complexAddSub(lhs, rhs, f128, .add, comp),
+ else => unreachable,
+ };
+ return false;
+ }
const f: Interner.Key.Float = switch (bits) {
16 => .{ .f16 = lhs.toFloat(f16, comp) + rhs.toFloat(f16, comp) },
32 => .{ .f32 = lhs.toFloat(f32, comp) + rhs.toFloat(f32, comp) },
@@ -350,7 +536,7 @@ pub fn add(res: *Value, lhs: Value, rhs: Value, ty: Type, comp: *Compilation) !b
std.math.big.int.calcTwosCompLimbCount(bits),
);
defer comp.gpa.free(limbs);
- var result_bigint = std.math.big.int.Mutable{ .limbs = limbs, .positive = undefined, .len = undefined };
+ var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined };
const overflowed = result_bigint.addWrap(lhs_bigint, rhs_bigint, ty.signedness(comp), bits);
res.* = try intern(comp, .{ .int = .{ .big_int = result_bigint.toConst() } });
@@ -361,6 +547,17 @@ pub fn add(res: *Value, lhs: Value, rhs: Value, ty: Type, comp: *Compilation) !b
pub fn sub(res: *Value, lhs: Value, rhs: Value, ty: Type, comp: *Compilation) !bool {
const bits: usize = @intCast(ty.bitSizeof(comp).?);
if (ty.isFloat()) {
+ if (ty.isComplex()) {
+ res.* = switch (bits) {
+ 32 => try complexAddSub(lhs, rhs, f16, .sub, comp),
+ 64 => try complexAddSub(lhs, rhs, f32, .sub, comp),
+ 128 => try complexAddSub(lhs, rhs, f64, .sub, comp),
+ 160 => try complexAddSub(lhs, rhs, f80, .sub, comp),
+ 256 => try complexAddSub(lhs, rhs, f128, .sub, comp),
+ else => unreachable,
+ };
+ return false;
+ }
const f: Interner.Key.Float = switch (bits) {
16 => .{ .f16 = lhs.toFloat(f16, comp) - rhs.toFloat(f16, comp) },
32 => .{ .f32 = lhs.toFloat(f32, comp) - rhs.toFloat(f32, comp) },
@@ -382,7 +579,7 @@ pub fn sub(res: *Value, lhs: Value, rhs: Value, ty: Type, comp: *Compilation) !b
std.math.big.int.calcTwosCompLimbCount(bits),
);
defer comp.gpa.free(limbs);
- var result_bigint = std.math.big.int.Mutable{ .limbs = limbs, .positive = undefined, .len = undefined };
+ var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined };
const overflowed = result_bigint.subWrap(lhs_bigint, rhs_bigint, ty.signedness(comp), bits);
res.* = try intern(comp, .{ .int = .{ .big_int = result_bigint.toConst() } });
@@ -393,6 +590,18 @@ pub fn sub(res: *Value, lhs: Value, rhs: Value, ty: Type, comp: *Compilation) !b
pub fn mul(res: *Value, lhs: Value, rhs: Value, ty: Type, comp: *Compilation) !bool {
const bits: usize = @intCast(ty.bitSizeof(comp).?);
if (ty.isFloat()) {
+ if (ty.isComplex()) {
+ const cf: Interner.Key.Complex = switch (bits) {
+ 32 => .{ .cf16 = annex_g.complexFloatMul(f16, lhs.toFloat(f16, comp), lhs.imag(f16, comp), rhs.toFloat(f16, comp), rhs.imag(f16, comp)) },
+ 64 => .{ .cf32 = annex_g.complexFloatMul(f32, lhs.toFloat(f32, comp), lhs.imag(f32, comp), rhs.toFloat(f32, comp), rhs.imag(f32, comp)) },
+ 128 => .{ .cf64 = annex_g.complexFloatMul(f64, lhs.toFloat(f64, comp), lhs.imag(f64, comp), rhs.toFloat(f64, comp), rhs.imag(f64, comp)) },
+ 160 => .{ .cf80 = annex_g.complexFloatMul(f80, lhs.toFloat(f80, comp), lhs.imag(f80, comp), rhs.toFloat(f80, comp), rhs.imag(f80, comp)) },
+ 256 => .{ .cf128 = annex_g.complexFloatMul(f128, lhs.toFloat(f128, comp), lhs.imag(f128, comp), rhs.toFloat(f128, comp), rhs.imag(f128, comp)) },
+ else => unreachable,
+ };
+ res.* = try intern(comp, .{ .complex = cf });
+ return false;
+ }
const f: Interner.Key.Float = switch (bits) {
16 => .{ .f16 = lhs.toFloat(f16, comp) * rhs.toFloat(f16, comp) },
32 => .{ .f32 = lhs.toFloat(f32, comp) * rhs.toFloat(f32, comp) },
@@ -438,6 +647,18 @@ pub fn mul(res: *Value, lhs: Value, rhs: Value, ty: Type, comp: *Compilation) !b
pub fn div(res: *Value, lhs: Value, rhs: Value, ty: Type, comp: *Compilation) !bool {
const bits: usize = @intCast(ty.bitSizeof(comp).?);
if (ty.isFloat()) {
+ if (ty.isComplex()) {
+ const cf: Interner.Key.Complex = switch (bits) {
+ 32 => .{ .cf16 = annex_g.complexFloatDiv(f16, lhs.toFloat(f16, comp), lhs.imag(f16, comp), rhs.toFloat(f16, comp), rhs.imag(f16, comp)) },
+ 64 => .{ .cf32 = annex_g.complexFloatDiv(f32, lhs.toFloat(f32, comp), lhs.imag(f32, comp), rhs.toFloat(f32, comp), rhs.imag(f32, comp)) },
+ 128 => .{ .cf64 = annex_g.complexFloatDiv(f64, lhs.toFloat(f64, comp), lhs.imag(f64, comp), rhs.toFloat(f64, comp), rhs.imag(f64, comp)) },
+ 160 => .{ .cf80 = annex_g.complexFloatDiv(f80, lhs.toFloat(f80, comp), lhs.imag(f80, comp), rhs.toFloat(f80, comp), rhs.imag(f80, comp)) },
+ 256 => .{ .cf128 = annex_g.complexFloatDiv(f128, lhs.toFloat(f128, comp), lhs.imag(f128, comp), rhs.toFloat(f128, comp), rhs.imag(f128, comp)) },
+ else => unreachable,
+ };
+ res.* = try intern(comp, .{ .complex = cf });
+ return false;
+ }
const f: Interner.Key.Float = switch (bits) {
16 => .{ .f16 = lhs.toFloat(f16, comp) / rhs.toFloat(f16, comp) },
32 => .{ .f32 = lhs.toFloat(f32, comp) / rhs.toFloat(f32, comp) },
@@ -491,11 +712,11 @@ pub fn rem(lhs: Value, rhs: Value, ty: Type, comp: *Compilation) !Value {
const signedness = ty.signedness(comp);
if (signedness == .signed) {
- var spaces: [3]BigIntSpace = undefined;
- const min_val = BigIntMutable.init(&spaces[0].limbs, ty.minInt(comp)).toConst();
- const negative = BigIntMutable.init(&spaces[1].limbs, -1).toConst();
- const big_one = BigIntMutable.init(&spaces[2].limbs, 1).toConst();
- if (lhs_bigint.eql(min_val) and rhs_bigint.eql(negative)) {
+ var spaces: [2]BigIntSpace = undefined;
+ const min_val = try Value.minInt(ty, comp);
+ const negative = BigIntMutable.init(&spaces[0].limbs, -1).toConst();
+ const big_one = BigIntMutable.init(&spaces[1].limbs, 1).toConst();
+ if (lhs.compare(.eq, min_val, comp) and rhs_bigint.eql(negative)) {
return .{};
} else if (rhs_bigint.order(big_one).compare(.lt)) {
// lhs - @divTrunc(lhs, rhs) * rhs
@@ -542,7 +763,7 @@ pub fn bitOr(lhs: Value, rhs: Value, comp: *Compilation) !Value {
@max(lhs_bigint.limbs.len, rhs_bigint.limbs.len),
);
defer comp.gpa.free(limbs);
- var result_bigint = std.math.big.int.Mutable{ .limbs = limbs, .positive = undefined, .len = undefined };
+ var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined };
result_bigint.bitOr(lhs_bigint, rhs_bigint);
return intern(comp, .{ .int = .{ .big_int = result_bigint.toConst() } });
@@ -554,12 +775,13 @@ pub fn bitXor(lhs: Value, rhs: Value, comp: *Compilation) !Value {
const lhs_bigint = lhs.toBigInt(&lhs_space, comp);
const rhs_bigint = rhs.toBigInt(&rhs_space, comp);
+ const extra = @intFromBool(lhs_bigint.positive != rhs_bigint.positive);
const limbs = try comp.gpa.alloc(
std.math.big.Limb,
- @max(lhs_bigint.limbs.len, rhs_bigint.limbs.len),
+ @max(lhs_bigint.limbs.len, rhs_bigint.limbs.len) + extra,
);
defer comp.gpa.free(limbs);
- var result_bigint = std.math.big.int.Mutable{ .limbs = limbs, .positive = undefined, .len = undefined };
+ var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined };
result_bigint.bitXor(lhs_bigint, rhs_bigint);
return intern(comp, .{ .int = .{ .big_int = result_bigint.toConst() } });
@@ -571,12 +793,18 @@ pub fn bitAnd(lhs: Value, rhs: Value, comp: *Compilation) !Value {
const lhs_bigint = lhs.toBigInt(&lhs_space, comp);
const rhs_bigint = rhs.toBigInt(&rhs_space, comp);
- const limbs = try comp.gpa.alloc(
- std.math.big.Limb,
- @max(lhs_bigint.limbs.len, rhs_bigint.limbs.len),
- );
+ const limb_count = if (lhs_bigint.positive and rhs_bigint.positive)
+ @min(lhs_bigint.limbs.len, rhs_bigint.limbs.len)
+ else if (lhs_bigint.positive)
+ lhs_bigint.limbs.len
+ else if (rhs_bigint.positive)
+ rhs_bigint.limbs.len
+ else
+ @max(lhs_bigint.limbs.len, rhs_bigint.limbs.len) + 1;
+
+ const limbs = try comp.gpa.alloc(std.math.big.Limb, limb_count);
defer comp.gpa.free(limbs);
- var result_bigint = std.math.big.int.Mutable{ .limbs = limbs, .positive = undefined, .len = undefined };
+ var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined };
result_bigint.bitAnd(lhs_bigint, rhs_bigint);
return intern(comp, .{ .int = .{ .big_int = result_bigint.toConst() } });
@@ -592,7 +820,7 @@ pub fn bitNot(val: Value, ty: Type, comp: *Compilation) !Value {
std.math.big.int.calcTwosCompLimbCount(bits),
);
defer comp.gpa.free(limbs);
- var result_bigint = std.math.big.int.Mutable{ .limbs = limbs, .positive = undefined, .len = undefined };
+ var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined };
result_bigint.bitNotWrap(val_bigint, ty.signedness(comp), bits);
return intern(comp, .{ .int = .{ .big_int = result_bigint.toConst() } });
@@ -606,9 +834,9 @@ pub fn shl(res: *Value, lhs: Value, rhs: Value, ty: Type, comp: *Compilation) !b
const bits: usize = @intCast(ty.bitSizeof(comp).?);
if (shift > bits) {
if (lhs_bigint.positive) {
- res.* = try intern(comp, .{ .int = .{ .u64 = ty.maxInt(comp) } });
+ res.* = try Value.maxInt(ty, comp);
} else {
- res.* = try intern(comp, .{ .int = .{ .i64 = ty.minInt(comp) } });
+ res.* = try Value.minInt(ty, comp);
}
return true;
}
@@ -618,7 +846,7 @@ pub fn shl(res: *Value, lhs: Value, rhs: Value, ty: Type, comp: *Compilation) !b
lhs_bigint.limbs.len + (shift / (@sizeOf(std.math.big.Limb) * 8)) + 1,
);
defer comp.gpa.free(limbs);
- var result_bigint = std.math.big.int.Mutable{ .limbs = limbs, .positive = undefined, .len = undefined };
+ var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined };
result_bigint.shiftLeft(lhs_bigint, shift);
const signedness = ty.signedness(comp);
@@ -652,12 +880,25 @@ pub fn shr(lhs: Value, rhs: Value, ty: Type, comp: *Compilation) !Value {
std.math.big.int.calcTwosCompLimbCount(bits),
);
defer comp.gpa.free(limbs);
- var result_bigint = std.math.big.int.Mutable{ .limbs = limbs, .positive = undefined, .len = undefined };
+ var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined };
result_bigint.shiftRight(lhs_bigint, shift);
return intern(comp, .{ .int = .{ .big_int = result_bigint.toConst() } });
}
+pub fn complexConj(val: Value, ty: Type, comp: *Compilation) !Value {
+ const bits = ty.bitSizeof(comp).?;
+ const cf: Interner.Key.Complex = switch (bits) {
+ 32 => .{ .cf16 = .{ val.toFloat(f16, comp), -val.imag(f16, comp) } },
+ 64 => .{ .cf32 = .{ val.toFloat(f32, comp), -val.imag(f32, comp) } },
+ 128 => .{ .cf64 = .{ val.toFloat(f64, comp), -val.imag(f64, comp) } },
+ 160 => .{ .cf80 = .{ val.toFloat(f80, comp), -val.imag(f80, comp) } },
+ 256 => .{ .cf128 = .{ val.toFloat(f128, comp), -val.imag(f128, comp) } },
+ else => unreachable,
+ };
+ return intern(comp, .{ .complex = cf });
+}
+
pub fn compare(lhs: Value, op: std.math.CompareOperator, rhs: Value, comp: *const Compilation) bool {
if (op == .eq) {
return lhs.opt_ref == rhs.opt_ref;
@@ -672,6 +913,12 @@ pub fn compare(lhs: Value, op: std.math.CompareOperator, rhs: Value, comp: *cons
const rhs_f128 = rhs.toFloat(f128, comp);
return std.math.compare(lhs_f128, op, rhs_f128);
}
+ if (lhs_key == .complex or rhs_key == .complex) {
+ assert(op == .neq);
+ const real_equal = std.math.compare(lhs.toFloat(f128, comp), .eq, rhs.toFloat(f128, comp));
+ const imag_equal = std.math.compare(lhs.imag(f128, comp), .eq, rhs.imag(f128, comp));
+ return !real_equal or !imag_equal;
+ }
var lhs_bigint_space: BigIntSpace = undefined;
var rhs_bigint_space: BigIntSpace = undefined;
@@ -680,6 +927,42 @@ pub fn compare(lhs: Value, op: std.math.CompareOperator, rhs: Value, comp: *cons
return lhs_bigint.order(rhs_bigint).compare(op);
}
+fn twosCompIntLimit(limit: std.math.big.int.TwosCompIntLimit, ty: Type, comp: *Compilation) !Value {
+ const signedness = ty.signedness(comp);
+ if (limit == .min and signedness == .unsigned) return Value.zero;
+ const mag_bits: usize = @intCast(ty.bitSizeof(comp).?);
+ switch (mag_bits) {
+ inline 8, 16, 32, 64 => |bits| {
+ if (limit == .min) return Value.int(@as(i64, std.math.minInt(std.meta.Int(.signed, bits))), comp);
+ return switch (signedness) {
+ inline else => |sign| Value.int(std.math.maxInt(std.meta.Int(sign, bits)), comp),
+ };
+ },
+ else => {},
+ }
+
+ const sign_bits = @intFromBool(signedness == .signed);
+ const total_bits = mag_bits + sign_bits;
+
+ const limbs = try comp.gpa.alloc(
+ std.math.big.Limb,
+ std.math.big.int.calcTwosCompLimbCount(total_bits),
+ );
+ defer comp.gpa.free(limbs);
+
+ var result_bigint: BigIntMutable = .{ .limbs = limbs, .positive = undefined, .len = undefined };
+ result_bigint.setTwosCompIntLimit(limit, signedness, mag_bits);
+ return Value.intern(comp, .{ .int = .{ .big_int = result_bigint.toConst() } });
+}
+
+pub fn minInt(ty: Type, comp: *Compilation) !Value {
+ return twosCompIntLimit(.min, ty, comp);
+}
+
+pub fn maxInt(ty: Type, comp: *Compilation) !Value {
+ return twosCompIntLimit(.max, ty, comp);
+}
+
pub fn print(v: Value, ty: Type, comp: *const Compilation, w: anytype) @TypeOf(w).Error!void {
if (ty.is(.bool)) {
return w.writeAll(if (v.isZero(comp)) "false" else "true");
@@ -696,6 +979,10 @@ pub fn print(v: Value, ty: Type, comp: *const Compilation, w: anytype) @TypeOf(w
inline else => |x| return w.print("{d}", .{@as(f64, @floatCast(x))}),
},
.bytes => |b| return printString(b, ty, comp, w),
+ .complex => |repr| switch (repr) {
+ .cf32 => |components| return w.print("{d} + {d}i", .{ @round(@as(f64, @floatCast(components[0])) * 1000000) / 1000000, @round(@as(f64, @floatCast(components[1])) * 1000000) / 1000000 }),
+ inline else => |components| return w.print("{d} + {d}i", .{ @as(f64, @floatCast(components[0])), @as(f64, @floatCast(components[1])) }),
+ },
else => unreachable, // not a value
}
}
@@ -703,26 +990,44 @@ pub fn print(v: Value, ty: Type, comp: *const Compilation, w: anytype) @TypeOf(w
pub fn printString(bytes: []const u8, ty: Type, comp: *const Compilation, w: anytype) @TypeOf(w).Error!void {
const size: Compilation.CharUnitSize = @enumFromInt(ty.elemType().sizeof(comp).?);
const without_null = bytes[0 .. bytes.len - @intFromEnum(size)];
+ try w.writeByte('"');
switch (size) {
- inline .@"1", .@"2" => |sz| {
- const data_slice: []const sz.Type() = @alignCast(std.mem.bytesAsSlice(sz.Type(), without_null));
- const formatter = if (sz == .@"1") std.zig.fmtEscapes(data_slice) else std.unicode.fmtUtf16Le(data_slice);
- try w.print("\"{}\"", .{formatter});
+ .@"1" => try w.print("{}", .{std.zig.fmtEscapes(without_null)}),
+ .@"2" => {
+ var items: [2]u16 = undefined;
+ var i: usize = 0;
+ while (i < without_null.len) {
+ @memcpy(std.mem.sliceAsBytes(items[0..1]), without_null[i..][0..2]);
+ i += 2;
+ const is_surrogate = std.unicode.utf16IsHighSurrogate(items[0]);
+ if (is_surrogate and i < without_null.len) {
+ @memcpy(std.mem.sliceAsBytes(items[1..2]), without_null[i..][0..2]);
+ if (std.unicode.utf16DecodeSurrogatePair(&items)) |decoded| {
+ i += 2;
+ try w.print("{u}", .{decoded});
+ } else |_| {
+ try w.print("\\x{x}", .{items[0]});
+ }
+ } else if (is_surrogate) {
+ try w.print("\\x{x}", .{items[0]});
+ } else {
+ try w.print("{u}", .{items[0]});
+ }
+ }
},
.@"4" => {
- try w.writeByte('"');
- const data_slice = std.mem.bytesAsSlice(u32, without_null);
- var buf: [4]u8 = undefined;
- for (data_slice) |item| {
- if (item <= std.math.maxInt(u21) and std.unicode.utf8ValidCodepoint(@intCast(item))) {
- const codepoint: u21 = @intCast(item);
- const written = std.unicode.utf8Encode(codepoint, &buf) catch unreachable;
- try w.print("{s}", .{buf[0..written]});
+ var item: [1]u32 = undefined;
+ const data_slice = std.mem.sliceAsBytes(item[0..1]);
+ for (0..@divExact(without_null.len, 4)) |n| {
+ @memcpy(data_slice, without_null[n * 4 ..][0..4]);
+ if (item[0] <= std.math.maxInt(u21) and std.unicode.utf8ValidCodepoint(@intCast(item[0]))) {
+ const codepoint: u21 = @intCast(item[0]);
+ try w.print("{u}", .{codepoint});
} else {
- try w.print("\\x{x}", .{item});
+ try w.print("\\x{x}", .{item[0]});
}
}
- try w.writeByte('"');
},
}
+ try w.writeByte('"');
}
lib/compiler/aro/backend/Interner.zig
@@ -34,6 +34,7 @@ const KeyAdapter = struct {
pub const Key = union(enum) {
int_ty: u16,
float_ty: u16,
+ complex_ty: u16,
ptr_ty,
noreturn_ty,
void_ty,
@@ -62,6 +63,7 @@ pub const Key = union(enum) {
}
},
float: Float,
+ complex: Complex,
bytes: []const u8,
pub const Float = union(enum) {
@@ -71,6 +73,13 @@ pub const Key = union(enum) {
f80: f80,
f128: f128,
};
+ pub const Complex = union(enum) {
+ cf16: [2]f16,
+ cf32: [2]f32,
+ cf64: [2]f64,
+ cf80: [2]f80,
+ cf128: [2]f128,
+ };
pub fn hash(key: Key) u32 {
var hasher = Hash.init(0);
@@ -89,6 +98,12 @@ pub const Key = union(enum) {
@as(std.meta.Int(.unsigned, @bitSizeOf(@TypeOf(data))), @bitCast(data)),
),
},
+ .complex => |repr| switch (repr) {
+ inline else => |data| std.hash.autoHash(
+ &hasher,
+ @as(std.meta.Int(.unsigned, @bitSizeOf(@TypeOf(data))), @bitCast(data)),
+ ),
+ },
.int => |repr| {
var space: Tag.Int.BigIntSpace = undefined;
const big = repr.toBigInt(&space);
@@ -154,6 +169,14 @@ pub const Key = union(enum) {
128 => return .f128,
else => unreachable,
},
+ .complex_ty => |bits| switch (bits) {
+ 16 => return .cf16,
+ 32 => return .cf32,
+ 64 => return .cf64,
+ 80 => return .cf80,
+ 128 => return .cf128,
+ else => unreachable,
+ },
.ptr_ty => return .ptr,
.func_ty => return .func,
.noreturn_ty => return .noreturn,
@@ -199,6 +222,11 @@ pub const Ref = enum(u32) {
zero = max - 16,
one = max - 17,
null = max - 18,
+ cf16 = max - 19,
+ cf32 = max - 20,
+ cf64 = max - 21,
+ cf80 = max - 22,
+ cf128 = max - 23,
_,
};
@@ -224,6 +252,11 @@ pub const OptRef = enum(u32) {
zero = max - 16,
one = max - 17,
null = max - 18,
+ cf16 = max - 19,
+ cf32 = max - 20,
+ cf64 = max - 21,
+ cf80 = max - 22,
+ cf128 = max - 23,
_,
};
@@ -232,6 +265,8 @@ pub const Tag = enum(u8) {
int_ty,
/// `data` is `u16`
float_ty,
+ /// `data` is `u16`
+ complex_ty,
/// `data` is index to `Array`
array_ty,
/// `data` is index to `Vector`
@@ -254,6 +289,16 @@ pub const Tag = enum(u8) {
f80,
/// `data` is `F128`
f128,
+ /// `data` is `CF16`
+ cf16,
+ /// `data` is `CF32`
+ cf32,
+ /// `data` is `CF64`
+ cf64,
+ /// `data` is `CF80`
+ cf80,
+ /// `data` is `CF128`
+ cf128,
/// `data` is `Bytes`
bytes,
/// `data` is `Record`
@@ -354,6 +399,134 @@ pub const Tag = enum(u8) {
}
};
+ pub const CF16 = struct {
+ piece0: u32,
+
+ pub fn get(self: CF16) [2]f16 {
+ const real: f16 = @bitCast(@as(u16, @truncate(self.piece0 >> 16)));
+ const imag: f16 = @bitCast(@as(u16, @truncate(self.piece0)));
+ return .{
+ real,
+ imag,
+ };
+ }
+
+ fn pack(val: [2]f16) CF16 {
+ const real: u16 = @bitCast(val[0]);
+ const imag: u16 = @bitCast(val[1]);
+ return .{
+ .piece0 = (@as(u32, real) << 16) | @as(u32, imag),
+ };
+ }
+ };
+
+ pub const CF32 = struct {
+ piece0: u32,
+ piece1: u32,
+
+ pub fn get(self: CF32) [2]f32 {
+ return .{
+ @bitCast(self.piece0),
+ @bitCast(self.piece1),
+ };
+ }
+
+ fn pack(val: [2]f32) CF32 {
+ return .{
+ .piece0 = @bitCast(val[0]),
+ .piece1 = @bitCast(val[1]),
+ };
+ }
+ };
+
+ pub const CF64 = struct {
+ piece0: u32,
+ piece1: u32,
+ piece2: u32,
+ piece3: u32,
+
+ pub fn get(self: CF64) [2]f64 {
+ return .{
+ (F64{ .piece0 = self.piece0, .piece1 = self.piece1 }).get(),
+ (F64{ .piece0 = self.piece2, .piece1 = self.piece3 }).get(),
+ };
+ }
+
+ fn pack(val: [2]f64) CF64 {
+ const real = F64.pack(val[0]);
+ const imag = F64.pack(val[1]);
+ return .{
+ .piece0 = real.piece0,
+ .piece1 = real.piece1,
+ .piece2 = imag.piece0,
+ .piece3 = imag.piece1,
+ };
+ }
+ };
+
+ /// TODO pack into 5 pieces
+ pub const CF80 = struct {
+ piece0: u32,
+ piece1: u32,
+ piece2: u32, // u16 part, top bits
+ piece3: u32,
+ piece4: u32,
+ piece5: u32, // u16 part, top bits
+
+ pub fn get(self: CF80) [2]f80 {
+ return .{
+ (F80{ .piece0 = self.piece0, .piece1 = self.piece1, .piece2 = self.piece2 }).get(),
+ (F80{ .piece0 = self.piece3, .piece1 = self.piece4, .piece2 = self.piece5 }).get(),
+ };
+ }
+
+ fn pack(val: [2]f80) CF80 {
+ const real = F80.pack(val[0]);
+ const imag = F80.pack(val[1]);
+ return .{
+ .piece0 = real.piece0,
+ .piece1 = real.piece1,
+ .piece2 = real.piece2,
+ .piece3 = imag.piece0,
+ .piece4 = imag.piece1,
+ .piece5 = imag.piece2,
+ };
+ }
+ };
+
+ pub const CF128 = struct {
+ piece0: u32,
+ piece1: u32,
+ piece2: u32,
+ piece3: u32,
+ piece4: u32,
+ piece5: u32,
+ piece6: u32,
+ piece7: u32,
+
+ pub fn get(self: CF128) [2]f128 {
+ return .{
+ (F128{ .piece0 = self.piece0, .piece1 = self.piece1, .piece2 = self.piece2, .piece3 = self.piece3 }).get(),
+ (F128{ .piece0 = self.piece4, .piece1 = self.piece5, .piece2 = self.piece6, .piece3 = self.piece7 }).get(),
+ };
+ }
+
+ fn pack(val: [2]f128) CF128 {
+ const real = F128.pack(val[0]);
+ const imag = F128.pack(val[1]);
+ return .{
+ .piece0 = real.piece0,
+ .piece1 = real.piece1,
+ .piece2 = real.piece2,
+ .piece3 = real.piece3,
+ .piece4 = imag.piece0,
+ .piece5 = imag.piece1,
+ .piece6 = imag.piece2,
+ .piece7 = imag.piece3,
+ };
+ }
+ };
+
pub const Bytes = struct {
strings_index: u32,
len: u32,
@@ -407,6 +580,12 @@ pub fn put(i: *Interner, gpa: Allocator, key: Key) !Ref {
.data = bits,
});
},
+ .complex_ty => |bits| {
+ i.items.appendAssumeCapacity(.{
+ .tag = .complex_ty,
+ .data = bits,
+ });
+ },
.array_ty => |info| {
const split_len = PackedU64.init(info.len);
i.items.appendAssumeCapacity(.{
@@ -493,6 +672,28 @@ pub fn put(i: *Interner, gpa: Allocator, key: Key) !Ref {
.data = try i.addExtra(gpa, Tag.F128.pack(data)),
}),
},
+ .complex => |repr| switch (repr) {
+ .cf16 => |data| i.items.appendAssumeCapacity(.{
+ .tag = .cf16,
+ .data = try i.addExtra(gpa, Tag.CF16.pack(data)),
+ }),
+ .cf32 => |data| i.items.appendAssumeCapacity(.{
+ .tag = .cf32,
+ .data = try i.addExtra(gpa, Tag.CF32.pack(data)),
+ }),
+ .cf64 => |data| i.items.appendAssumeCapacity(.{
+ .tag = .cf64,
+ .data = try i.addExtra(gpa, Tag.CF64.pack(data)),
+ }),
+ .cf80 => |data| i.items.appendAssumeCapacity(.{
+ .tag = .cf80,
+ .data = try i.addExtra(gpa, Tag.CF80.pack(data)),
+ }),
+ .cf128 => |data| i.items.appendAssumeCapacity(.{
+ .tag = .cf128,
+ .data = try i.addExtra(gpa, Tag.CF128.pack(data)),
+ }),
+ },
.bytes => |bytes| {
const strings_index: u32 = @intCast(i.strings.items.len);
try i.strings.appendSlice(gpa, bytes);
@@ -564,6 +765,10 @@ pub fn get(i: *const Interner, ref: Ref) Key {
.zero => return .{ .int = .{ .u64 = 0 } },
.one => return .{ .int = .{ .u64 = 1 } },
.null => return .null,
+ .cf16 => return .{ .complex_ty = 16 },
+ .cf32 => return .{ .complex_ty = 32 },
+ .cf64 => return .{ .complex_ty = 64 },
+ .cf80 => return .{ .complex_ty = 80 },
else => {},
}
@@ -572,6 +777,7 @@ pub fn get(i: *const Interner, ref: Ref) Key {
return switch (item.tag) {
.int_ty => .{ .int_ty = @intCast(data) },
.float_ty => .{ .float_ty = @intCast(data) },
+ .complex_ty => .{ .complex_ty = @intCast(data) },
.array_ty => {
const array_ty = i.extraData(Tag.Array, data);
return .{ .array_ty = .{
@@ -612,6 +818,26 @@ pub fn get(i: *const Interner, ref: Ref) Key {
const float = i.extraData(Tag.F128, data);
return .{ .float = .{ .f128 = float.get() } };
},
+ .cf16 => {
+ const components = i.extraData(Tag.CF16, data);
+ return .{ .complex = .{ .cf16 = components.get() } };
+ },
+ .cf32 => {
+ const components = i.extraData(Tag.CF32, data);
+ return .{ .complex = .{ .cf32 = components.get() } };
+ },
+ .cf64 => {
+ const components = i.extraData(Tag.CF64, data);
+ return .{ .complex = .{ .cf64 = components.get() } };
+ },
+ .cf80 => {
+ const components = i.extraData(Tag.CF80, data);
+ return .{ .complex = .{ .cf80 = components.get() } };
+ },
+ .cf128 => {
+ const components = i.extraData(Tag.CF128, data);
+ return .{ .complex = .{ .cf128 = components.get() } };
+ },
.bytes => {
const bytes = i.extraData(Tag.Bytes, data);
return .{ .bytes = i.strings.items[bytes.strings_index..][0..bytes.len] };
lib/compiler/aro/backend/Ir.zig
@@ -37,6 +37,7 @@ pub const Builder = struct {
for (b.decls.values()) |*decl| {
decl.deinit(b.gpa);
}
+ b.decls.deinit(b.gpa);
b.arena.deinit();
b.instructions.deinit(b.gpa);
b.body.deinit(b.gpa);
lib/compiler/aro/backend/Object.zig
@@ -16,7 +16,7 @@ pub fn create(gpa: Allocator, target: std.Target) !*Object {
pub fn deinit(obj: *Object) void {
switch (obj.format) {
- .elf => @as(*Elf, @fieldParentPtr("obj", obj)).deinit(),
+ .elf => @as(*Elf, @alignCast(@fieldParentPtr("obj", obj))).deinit(),
else => unreachable,
}
}
@@ -32,7 +32,7 @@ pub const Section = union(enum) {
pub fn getSection(obj: *Object, section: Section) !*std.ArrayList(u8) {
switch (obj.format) {
- .elf => return @as(*Elf, @fieldParentPtr("obj", obj)).getSection(section),
+ .elf => return @as(*Elf, @alignCast(@fieldParentPtr("obj", obj))).getSection(section),
else => unreachable,
}
}
@@ -53,21 +53,21 @@ pub fn declareSymbol(
size: u64,
) ![]const u8 {
switch (obj.format) {
- .elf => return @as(*Elf, @fieldParentPtr("obj", obj)).declareSymbol(section, name, linkage, @"type", offset, size),
+ .elf => return @as(*Elf, @alignCast(@fieldParentPtr("obj", obj))).declareSymbol(section, name, linkage, @"type", offset, size),
else => unreachable,
}
}
pub fn addRelocation(obj: *Object, name: []const u8, section: Section, address: u64, addend: i64) !void {
switch (obj.format) {
- .elf => return @as(*Elf, @fieldParentPtr("obj", obj)).addRelocation(name, section, address, addend),
+ .elf => return @as(*Elf, @alignCast(@fieldParentPtr("obj", obj))).addRelocation(name, section, address, addend),
else => unreachable,
}
}
pub fn finish(obj: *Object, file: std.fs.File) !void {
switch (obj.format) {
- .elf => return @as(*Elf, @fieldParentPtr("obj", obj)).finish(file),
+ .elf => return @as(*Elf, @alignCast(@fieldParentPtr("obj", obj))).finish(file),
else => unreachable,
}
}
lib/compiler/aro/aro.zig
@@ -23,6 +23,7 @@ pub const version_str = backend.version_str;
pub const version = backend.version;
test {
+ _ = @import("aro/annex_g.zig");
_ = @import("aro/Builtins.zig");
_ = @import("aro/char_info.zig");
_ = @import("aro/Compilation.zig");
lib/compiler/resinator/main.zig
@@ -126,7 +126,7 @@ pub fn main() !void {
defer aro_arena_state.deinit();
const aro_arena = aro_arena_state.allocator();
- var comp = aro.Compilation.init(aro_arena);
+ var comp = aro.Compilation.init(aro_arena, std.fs.cwd());
defer comp.deinit();
var argv = std.ArrayList([]const u8).init(comp.gpa);
lib/compiler/resinator/preprocess.zig
@@ -59,7 +59,7 @@ pub fn preprocess(
if (hasAnyErrors(comp)) return error.PreprocessError;
- try pp.prettyPrintTokens(writer);
+ try pp.prettyPrintTokens(writer, .result_only);
if (maybe_dependencies_list) |dependencies_list| {
for (comp.sources.values()) |comp_source| {
lib/compiler/aro_translate_c.zig
@@ -731,7 +731,6 @@ fn transType(c: *Context, scope: *Scope, raw_ty: Type, qual_handling: Type.QualH
.float => return ZigTag.type.create(c.arena, "f32"),
.double => return ZigTag.type.create(c.arena, "f64"),
.long_double => return ZigTag.type.create(c.arena, "c_longdouble"),
- .float80 => return ZigTag.type.create(c.arena, "f80"),
.float128 => return ZigTag.type.create(c.arena, "f128"),
.@"enum" => {
const enum_decl = ty.data.@"enum";
@@ -1799,7 +1798,7 @@ pub fn main() !void {
const args = try std.process.argsAlloc(arena);
- var aro_comp = aro.Compilation.init(gpa);
+ var aro_comp = aro.Compilation.init(gpa, std.fs.cwd());
defer aro_comp.deinit();
var tree = translate(gpa, &aro_comp, args) catch |err| switch (err) {
src/mingw.zig
@@ -230,7 +230,7 @@ pub fn buildImportLib(comp: *Compilation, lib_name: []const u8) !void {
};
const aro = @import("aro");
- var aro_comp = aro.Compilation.init(comp.gpa);
+ var aro_comp = aro.Compilation.init(comp.gpa, std.fs.cwd());
defer aro_comp.deinit();
const include_dir = try comp.zig_lib_directory.join(arena, &[_][]const u8{ "libc", "mingw", "def-include" });
@@ -268,7 +268,7 @@ pub fn buildImportLib(comp: *Compilation, lib_name: []const u8) !void {
// new scope to ensure definition file is written before passing the path to WriteImportLibrary
const def_final_file = try o_dir.createFile(final_def_basename, .{ .truncate = true });
defer def_final_file.close();
- try pp.prettyPrintTokens(def_final_file.writer());
+ try pp.prettyPrintTokens(def_final_file.writer(), .result_only);
}
const lib_final_path = try comp.global_cache_directory.join(comp.gpa, &[_][]const u8{
src/translate_c.zig
@@ -5258,7 +5258,7 @@ fn getMacroText(unit: *const clang.ASTUnit, c: *const Context, macro: *const cla
const end_c = c.source_manager.getCharacterData(end_loc);
const slice_len = @intFromPtr(end_c) - @intFromPtr(begin_c);
- var comp = aro.Compilation.init(c.gpa);
+ var comp = aro.Compilation.init(c.gpa, std.fs.cwd());
defer comp.deinit();
const result = comp.addSourceFromBuffer("", begin_c[0..slice_len]) catch return error.OutOfMemory;