| author | |
| committer | |
| log | de8cece6e7be570a7e622c54c0cbbc7c99308a08 |
| tree | abf91cfa0c3d74725a6a6c17496a9999dcd99d81 |
| parent | 9e6d167bb7878682e99482bfaef12e45376665f9 |
ref: adfd13c6ffb563b1379052b92f6ae4148b91cc1235 files changed, 2281 insertions(+), 794 deletions(-)
lib/compiler/aro/aro.zig+1| ... | ... | @@ -23,6 +23,7 @@ pub const version_str = backend.version_str; |
| 23 | 23 | pub const version = backend.version; |
| 24 | 24 | |
| 25 | 25 | test { |
| 26 | _ = @import("aro/annex_g.zig"); | |
| 26 | 27 | _ = @import("aro/Builtins.zig"); |
| 27 | 28 | _ = @import("aro/char_info.zig"); |
| 28 | 29 | _ = @import("aro/Compilation.zig"); |
lib/compiler/aro/aro/Attribute.zig+97-47| ... | ... | @@ -38,12 +38,64 @@ pub const Kind = enum { |
| 38 | 38 | } |
| 39 | 39 | }; |
| 40 | 40 | |
| 41 | pub const Iterator = struct { | |
| 42 | source: union(enum) { | |
| 43 | ty: Type, | |
| 44 | slice: []const Attribute, | |
| 45 | }, | |
| 46 | index: usize, | |
| 47 | ||
| 48 | pub fn initSlice(slice: ?[]const Attribute) Iterator { | |
| 49 | return .{ .source = .{ .slice = slice orelse &.{} }, .index = 0 }; | |
| 50 | } | |
| 51 | ||
| 52 | pub fn initType(ty: Type) Iterator { | |
| 53 | return .{ .source = .{ .ty = ty }, .index = 0 }; | |
| 54 | } | |
| 55 | ||
| 56 | /// returns the next attribute as well as its index within the slice or current type | |
| 57 | /// The index can be used to determine when a nested type has been recursed into | |
| 58 | pub fn next(self: *Iterator) ?struct { Attribute, usize } { | |
| 59 | switch (self.source) { | |
| 60 | .slice => |slice| { | |
| 61 | if (self.index < slice.len) { | |
| 62 | defer self.index += 1; | |
| 63 | return .{ slice[self.index], self.index }; | |
| 64 | } | |
| 65 | }, | |
| 66 | .ty => |ty| { | |
| 67 | switch (ty.specifier) { | |
| 68 | .typeof_type => { | |
| 69 | self.* = .{ .source = .{ .ty = ty.data.sub_type.* }, .index = 0 }; | |
| 70 | return self.next(); | |
| 71 | }, | |
| 72 | .typeof_expr => { | |
| 73 | self.* = .{ .source = .{ .ty = ty.data.expr.ty }, .index = 0 }; | |
| 74 | return self.next(); | |
| 75 | }, | |
| 76 | .attributed => { | |
| 77 | if (self.index < ty.data.attributed.attributes.len) { | |
| 78 | defer self.index += 1; | |
| 79 | return .{ ty.data.attributed.attributes[self.index], self.index }; | |
| 80 | } | |
| 81 | self.* = .{ .source = .{ .ty = ty.data.attributed.base }, .index = 0 }; | |
| 82 | return self.next(); | |
| 83 | }, | |
| 84 | else => {}, | |
| 85 | } | |
| 86 | }, | |
| 87 | } | |
| 88 | return null; | |
| 89 | } | |
| 90 | }; | |
| 91 | ||
| 41 | 92 | pub const ArgumentType = enum { |
| 42 | 93 | string, |
| 43 | 94 | identifier, |
| 44 | 95 | int, |
| 45 | 96 | alignment, |
| 46 | 97 | float, |
| 98 | complex_float, | |
| 47 | 99 | expression, |
| 48 | 100 | nullptr_t, |
| 49 | 101 | |
| ... | ... | @@ -54,6 +106,7 @@ pub const ArgumentType = enum { |
| 54 | 106 | .int, .alignment => "an integer constant", |
| 55 | 107 | .nullptr_t => "nullptr", |
| 56 | 108 | .float => "a floating point number", |
| 109 | .complex_float => "a complex floating point number", | |
| 57 | 110 | .expression => "an expression", |
| 58 | 111 | }; |
| 59 | 112 | } |
| ... | ... | @@ -65,7 +118,7 @@ pub fn requiredArgCount(attr: Tag) u32 { |
| 65 | 118 | inline else => |tag| { |
| 66 | 119 | comptime var needed = 0; |
| 67 | 120 | comptime { |
| 68 | const fields = std.meta.fields(@field(attributes, @tagName(tag))); | |
| 121 | const fields = @typeInfo(@field(attributes, @tagName(tag))).@"struct".fields; | |
| 69 | 122 | for (fields) |arg_field| { |
| 70 | 123 | if (!mem.eql(u8, arg_field.name, "__name_tok") and @typeInfo(arg_field.type) != .optional) needed += 1; |
| 71 | 124 | } |
| ... | ... | @@ -81,7 +134,7 @@ pub fn maxArgCount(attr: Tag) u32 { |
| 81 | 134 | inline else => |tag| { |
| 82 | 135 | comptime var max = 0; |
| 83 | 136 | comptime { |
| 84 | const fields = std.meta.fields(@field(attributes, @tagName(tag))); | |
| 137 | const fields = @typeInfo(@field(attributes, @tagName(tag))).@"struct".fields; | |
| 85 | 138 | for (fields) |arg_field| { |
| 86 | 139 | if (!mem.eql(u8, arg_field.name, "__name_tok")) max += 1; |
| 87 | 140 | } |
| ... | ... | @@ -106,7 +159,7 @@ pub const Formatting = struct { |
| 106 | 159 | switch (attr) { |
| 107 | 160 | .calling_convention => unreachable, |
| 108 | 161 | inline else => |tag| { |
| 109 | const fields = std.meta.fields(@field(attributes, @tagName(tag))); | |
| 162 | const fields = @typeInfo(@field(attributes, @tagName(tag))).@"struct".fields; | |
| 110 | 163 | |
| 111 | 164 | if (fields.len == 0) unreachable; |
| 112 | 165 | const Unwrapped = UnwrapOptional(fields[0].type); |
| ... | ... | @@ -123,14 +176,13 @@ pub const Formatting = struct { |
| 123 | 176 | switch (attr) { |
| 124 | 177 | .calling_convention => unreachable, |
| 125 | 178 | inline else => |tag| { |
| 126 | const fields = std.meta.fields(@field(attributes, @tagName(tag))); | |
| 179 | const fields = @typeInfo(@field(attributes, @tagName(tag))).@"struct".fields; | |
| 127 | 180 | |
| 128 | 181 | if (fields.len == 0) unreachable; |
| 129 | 182 | const Unwrapped = UnwrapOptional(fields[0].type); |
| 130 | 183 | if (@typeInfo(Unwrapped) != .@"enum") unreachable; |
| 131 | 184 | |
| 132 | 185 | const enum_fields = @typeInfo(Unwrapped).@"enum".fields; |
| 133 | @setEvalBranchQuota(3000); | |
| 134 | 186 | const quote = comptime quoteChar(@enumFromInt(@intFromEnum(tag))); |
| 135 | 187 | comptime var values: []const u8 = quote ++ enum_fields[0].name ++ quote; |
| 136 | 188 | inline for (enum_fields[1..]) |enum_field| { |
| ... | ... | @@ -148,7 +200,7 @@ pub fn wantsIdentEnum(attr: Tag) bool { |
| 148 | 200 | switch (attr) { |
| 149 | 201 | .calling_convention => return false, |
| 150 | 202 | inline else => |tag| { |
| 151 | const fields = std.meta.fields(@field(attributes, @tagName(tag))); | |
| 203 | const fields = @typeInfo(@field(attributes, @tagName(tag))).@"struct".fields; | |
| 152 | 204 | |
| 153 | 205 | if (fields.len == 0) return false; |
| 154 | 206 | const Unwrapped = UnwrapOptional(fields[0].type); |
| ... | ... | @@ -162,7 +214,7 @@ pub fn wantsIdentEnum(attr: Tag) bool { |
| 162 | 214 | pub fn diagnoseIdent(attr: Tag, arguments: *Arguments, ident: []const u8) ?Diagnostics.Message { |
| 163 | 215 | switch (attr) { |
| 164 | 216 | inline else => |tag| { |
| 165 | const fields = std.meta.fields(@field(attributes, @tagName(tag))); | |
| 217 | const fields = @typeInfo(@field(attributes, @tagName(tag))).@"struct".fields; | |
| 166 | 218 | if (fields.len == 0) unreachable; |
| 167 | 219 | const Unwrapped = UnwrapOptional(fields[0].type); |
| 168 | 220 | if (@typeInfo(Unwrapped) != .@"enum") unreachable; |
| ... | ... | @@ -181,7 +233,7 @@ pub fn diagnoseIdent(attr: Tag, arguments: *Arguments, ident: []const u8) ?Diagn |
| 181 | 233 | pub fn wantsAlignment(attr: Tag, idx: usize) bool { |
| 182 | 234 | switch (attr) { |
| 183 | 235 | inline else => |tag| { |
| 184 | const fields = std.meta.fields(@field(attributes, @tagName(tag))); | |
| 236 | const fields = @typeInfo(@field(attributes, @tagName(tag))).@"struct".fields; | |
| 185 | 237 | if (fields.len == 0) return false; |
| 186 | 238 | |
| 187 | 239 | return switch (idx) { |
| ... | ... | @@ -195,7 +247,7 @@ pub fn wantsAlignment(attr: Tag, idx: usize) bool { |
| 195 | 247 | pub fn diagnoseAlignment(attr: Tag, arguments: *Arguments, arg_idx: u32, res: Parser.Result, p: *Parser) !?Diagnostics.Message { |
| 196 | 248 | switch (attr) { |
| 197 | 249 | inline else => |tag| { |
| 198 | const arg_fields = std.meta.fields(@field(attributes, @tagName(tag))); | |
| 250 | const arg_fields = @typeInfo(@field(attributes, @tagName(tag))).@"struct".fields; | |
| 199 | 251 | if (arg_fields.len == 0) unreachable; |
| 200 | 252 | |
| 201 | 253 | switch (arg_idx) { |
| ... | ... | @@ -249,8 +301,7 @@ fn diagnoseField( |
| 249 | 301 | }, |
| 250 | 302 | .bytes => |bytes| { |
| 251 | 303 | if (Wanted == Value) { |
| 252 | std.debug.assert(node.tag == .string_literal_expr); | |
| 253 | if (!node.ty.elemType().is(.char) and !node.ty.elemType().is(.uchar)) { | |
| 304 | if (node.tag != .string_literal_expr or (!node.ty.elemType().is(.char) and !node.ty.elemType().is(.uchar))) { | |
| 254 | 305 | return .{ |
| 255 | 306 | .tag = .attribute_requires_string, |
| 256 | 307 | .extra = .{ .str = decl.name }, |
| ... | ... | @@ -264,7 +315,6 @@ fn diagnoseField( |
| 264 | 315 | @field(@field(arguments, decl.name), field.name) = enum_val; |
| 265 | 316 | return null; |
| 266 | 317 | } else { |
| 267 | @setEvalBranchQuota(3000); | |
| 268 | 318 | return .{ |
| 269 | 319 | .tag = .unknown_attr_enum, |
| 270 | 320 | .extra = .{ .attr_enum = .{ .tag = std.meta.stringToEnum(Tag, decl.name).? } }, |
| ... | ... | @@ -278,8 +328,19 @@ fn diagnoseField( |
| 278 | 328 | .int => .int, |
| 279 | 329 | .bytes => .string, |
| 280 | 330 | .float => .float, |
| 331 | .complex => .complex_float, | |
| 281 | 332 | .null => .nullptr_t, |
| 282 | else => unreachable, | |
| 333 | .int_ty, | |
| 334 | .float_ty, | |
| 335 | .complex_ty, | |
| 336 | .ptr_ty, | |
| 337 | .noreturn_ty, | |
| 338 | .void_ty, | |
| 339 | .func_ty, | |
| 340 | .array_ty, | |
| 341 | .vector_ty, | |
| 342 | .record_ty, | |
| 343 | => unreachable, | |
| 283 | 344 | }); |
| 284 | 345 | } |
| 285 | 346 | |
| ... | ... | @@ -309,7 +370,7 @@ pub fn diagnose(attr: Tag, arguments: *Arguments, arg_idx: u32, res: Parser.Resu |
| 309 | 370 | .tag = .attribute_too_many_args, |
| 310 | 371 | .extra = .{ .attr_arg_count = .{ .attribute = attr, .expected = max_arg_count } }, |
| 311 | 372 | }; |
| 312 | const arg_fields = std.meta.fields(@field(attributes, decl.name)); | |
| 373 | const arg_fields = @typeInfo(@field(attributes, decl.name)).@"struct".fields; | |
| 313 | 374 | switch (arg_idx) { |
| 314 | 375 | inline 0...arg_fields.len - 1 => |arg_i| { |
| 315 | 376 | 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: { |
| 645 | 706 | var union_fields: [decls.len]ZigType.UnionField = undefined; |
| 646 | 707 | for (decls, &union_fields) |decl, *field| { |
| 647 | 708 | field.* = .{ |
| 648 | .name = decl.name ++ "", | |
| 709 | .name = decl.name, | |
| 649 | 710 | .type = @field(attributes, decl.name), |
| 650 | 711 | .alignment = 0, |
| 651 | 712 | }; |
| ... | ... | @@ -730,7 +791,6 @@ pub fn applyVariableAttributes(p: *Parser, ty: Type, attr_buf_start: usize, tag: |
| 730 | 791 | const toks = p.attr_buf.items(.tok)[attr_buf_start..]; |
| 731 | 792 | p.attr_application_buf.items.len = 0; |
| 732 | 793 | var base_ty = ty; |
| 733 | if (base_ty.specifier == .attributed) base_ty = base_ty.data.attributed.base; | |
| 734 | 794 | var common = false; |
| 735 | 795 | var nocommon = false; |
| 736 | 796 | for (attrs, toks) |attr, tok| switch (attr.tag) { |
| ... | ... | @@ -772,15 +832,10 @@ pub fn applyVariableAttributes(p: *Parser, ty: Type, attr_buf_start: usize, tag: |
| 772 | 832 | .copy, |
| 773 | 833 | .tls_model, |
| 774 | 834 | .visibility, |
| 775 | => std.debug.panic("apply variable attribute {s}", .{@tagName(attr.tag)}), | |
| 835 | => |t| try p.errExtra(.attribute_todo, tok, .{ .attribute_todo = .{ .tag = t, .kind = .variables } }), | |
| 776 | 836 | else => try ignoredAttrErr(p, tok, attr.tag, "variables"), |
| 777 | 837 | }; |
| 778 | const existing = ty.getAttributes(); | |
| 779 | if (existing.len == 0 and p.attr_application_buf.items.len == 0) return base_ty; | |
| 780 | if (existing.len == 0) return base_ty.withAttributes(p.arena, p.attr_application_buf.items); | |
| 781 | ||
| 782 | const attributed_type = try Type.Attributed.create(p.arena, base_ty, existing, p.attr_application_buf.items); | |
| 783 | return Type{ .specifier = .attributed, .data = .{ .attributed = attributed_type } }; | |
| 838 | return base_ty.withAttributes(p.arena, p.attr_application_buf.items); | |
| 784 | 839 | } |
| 785 | 840 | |
| 786 | 841 | 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) |
| 789 | 844 | p.attr_application_buf.items.len = 0; |
| 790 | 845 | for (attrs, toks) |attr, tok| switch (attr.tag) { |
| 791 | 846 | // zig fmt: off |
| 792 | .@"packed", .may_alias, .deprecated, .unavailable, .unused, .warn_if_not_aligned, .mode, | |
| 847 | .@"packed", .may_alias, .deprecated, .unavailable, .unused, .warn_if_not_aligned, .mode, .warn_unused_result, .nodiscard, | |
| 793 | 848 | => try p.attr_application_buf.append(p.gpa, attr), |
| 794 | 849 | // zig fmt: on |
| 795 | 850 | .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 |
| 805 | 860 | const toks = p.attr_buf.items(.tok)[attr_buf_start..]; |
| 806 | 861 | p.attr_application_buf.items.len = 0; |
| 807 | 862 | var base_ty = ty; |
| 808 | if (base_ty.specifier == .attributed) base_ty = base_ty.data.attributed.base; | |
| 809 | 863 | for (attrs, toks) |attr, tok| switch (attr.tag) { |
| 810 | 864 | // zig fmt: off |
| 811 | 865 | .@"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 |
| 823 | 877 | .copy, |
| 824 | 878 | .scalar_storage_order, |
| 825 | 879 | .nonstring, |
| 826 | => std.debug.panic("apply type attribute {s}", .{@tagName(attr.tag)}), | |
| 880 | => |t| try p.errExtra(.attribute_todo, tok, .{ .attribute_todo = .{ .tag = t, .kind = .types } }), | |
| 827 | 881 | else => try ignoredAttrErr(p, tok, attr.tag, "types"), |
| 828 | 882 | }; |
| 829 | ||
| 830 | const existing = ty.getAttributes(); | |
| 831 | // TODO: the alignment annotation on a type should override | |
| 832 | // the decl it refers to. This might not be true for others. Maybe bug. | |
| 833 | ||
| 834 | // if there are annotations on this type def use those. | |
| 835 | if (p.attr_application_buf.items.len > 0) { | |
| 836 | return try base_ty.withAttributes(p.arena, p.attr_application_buf.items); | |
| 837 | } else if (existing.len > 0) { | |
| 838 | // else use the ones on the typedef decl we were refering to. | |
| 839 | return try base_ty.withAttributes(p.arena, existing); | |
| 840 | } | |
| 841 | return base_ty; | |
| 883 | return base_ty.withAttributes(p.arena, p.attr_application_buf.items); | |
| 842 | 884 | } |
| 843 | 885 | |
| 844 | 886 | 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 |
| 846 | 888 | const toks = p.attr_buf.items(.tok)[attr_buf_start..]; |
| 847 | 889 | p.attr_application_buf.items.len = 0; |
| 848 | 890 | var base_ty = ty; |
| 849 | if (base_ty.specifier == .attributed) base_ty = base_ty.data.attributed.base; | |
| 850 | 891 | var hot = false; |
| 851 | 892 | var cold = false; |
| 852 | 893 | var @"noinline" = false; |
| ... | ... | @@ -896,6 +937,13 @@ pub fn applyFunctionAttributes(p: *Parser, ty: Type, attr_buf_start: usize) !Typ |
| 896 | 937 | else => try p.errStr(.callconv_not_supported, tok, p.tok_ids[tok].lexeme().?), |
| 897 | 938 | }, |
| 898 | 939 | }, |
| 940 | .malloc => { | |
| 941 | if (base_ty.returnType().isPtr()) { | |
| 942 | try p.attr_application_buf.append(p.gpa, attr); | |
| 943 | } else { | |
| 944 | try ignoredAttrErr(p, tok, attr.tag, "functions that do not return pointers"); | |
| 945 | } | |
| 946 | }, | |
| 899 | 947 | .access, |
| 900 | 948 | .alloc_align, |
| 901 | 949 | .alloc_size, |
| ... | ... | @@ -908,7 +956,6 @@ pub fn applyFunctionAttributes(p: *Parser, ty: Type, attr_buf_start: usize) !Typ |
| 908 | 956 | .ifunc, |
| 909 | 957 | .interrupt, |
| 910 | 958 | .interrupt_handler, |
| 911 | .malloc, | |
| 912 | 959 | .no_address_safety_analysis, |
| 913 | 960 | .no_icf, |
| 914 | 961 | .no_instrument_function, |
| ... | ... | @@ -937,7 +984,7 @@ pub fn applyFunctionAttributes(p: *Parser, ty: Type, attr_buf_start: usize) !Typ |
| 937 | 984 | .visibility, |
| 938 | 985 | .weakref, |
| 939 | 986 | .zero_call_used_regs, |
| 940 | => std.debug.panic("apply type attribute {s}", .{@tagName(attr.tag)}), | |
| 987 | => |t| try p.errExtra(.attribute_todo, tok, .{ .attribute_todo = .{ .tag = t, .kind = .functions } }), | |
| 941 | 988 | else => try ignoredAttrErr(p, tok, attr.tag, "functions"), |
| 942 | 989 | }; |
| 943 | 990 | return ty.withAttributes(p.arena, p.attr_application_buf.items); |
| ... | ... | @@ -1043,11 +1090,14 @@ fn applyTransparentUnion(attr: Attribute, p: *Parser, tok: TokenIndex, ty: Type) |
| 1043 | 1090 | } |
| 1044 | 1091 | |
| 1045 | 1092 | fn applyVectorSize(attr: Attribute, p: *Parser, tok: TokenIndex, ty: *Type) !void { |
| 1046 | if (!(ty.isInt() or ty.isFloat()) or !ty.isReal()) { | |
| 1047 | const orig_ty = try p.typeStr(ty.*); | |
| 1048 | ty.* = Type.invalid; | |
| 1049 | return p.errStr(.invalid_vec_elem_ty, tok, orig_ty); | |
| 1093 | const base = ty.base(); | |
| 1094 | const is_enum = ty.is(.@"enum"); | |
| 1095 | if (!(ty.isInt() or ty.isFloat()) or !ty.isReal() or (is_enum and p.comp.langopts.emulate == .gcc)) { | |
| 1096 | try p.errStr(.invalid_vec_elem_ty, tok, try p.typeStr(ty.*)); | |
| 1097 | return error.ParsingFailed; | |
| 1050 | 1098 | } |
| 1099 | if (is_enum) return; | |
| 1100 | ||
| 1051 | 1101 | const vec_bytes = attr.args.vector_size.bytes; |
| 1052 | 1102 | const ty_size = ty.sizeof(p.comp).?; |
| 1053 | 1103 | if (vec_bytes % ty_size != 0) { |
| ... | ... | @@ -1057,7 +1107,7 @@ fn applyVectorSize(attr: Attribute, p: *Parser, tok: TokenIndex, ty: *Type) !voi |
| 1057 | 1107 | |
| 1058 | 1108 | const arr_ty = try p.arena.create(Type.Array); |
| 1059 | 1109 | arr_ty.* = .{ .elem = ty.*, .len = vec_size }; |
| 1060 | ty.* = Type{ | |
| 1110 | base.* = .{ | |
| 1061 | 1111 | .specifier = .vector, |
| 1062 | 1112 | .data = .{ .array = arr_ty }, |
| 1063 | 1113 | }; |
lib/compiler/aro/aro/Attribute/names.zig+2-1| ... | ... | @@ -69,6 +69,7 @@ pub const longest_name = 30; |
| 69 | 69 | /// If found, returns the index of the node within the `dafsa` array. |
| 70 | 70 | /// Otherwise, returns `null`. |
| 71 | 71 | pub fn findInList(first_child_index: u16, char: u8) ?u16 { |
| 72 | @setEvalBranchQuota(206); | |
| 72 | 73 | var index = first_child_index; |
| 73 | 74 | while (true) { |
| 74 | 75 | if (dafsa[index].char == char) return index; |
| ... | ... | @@ -787,7 +788,7 @@ const dafsa = [_]Node{ |
| 787 | 788 | .{ .char = 'i', .end_of_word = false, .end_of_list = true, .number = 1, .child_index = 215 }, |
| 788 | 789 | }; |
| 789 | 790 | pub const data = blk: { |
| 790 | @setEvalBranchQuota(103); | |
| 791 | @setEvalBranchQuota(721); | |
| 791 | 792 | break :blk [_]@This(){ |
| 792 | 793 | // access |
| 793 | 794 | .{ .tag = @enumFromInt(0), .properties = .{ .tag = .access, .gnu = true } }, |
lib/compiler/aro/aro/Builtins.zig+2-2| ... | ... | @@ -350,7 +350,7 @@ test Iterator { |
| 350 | 350 | } |
| 351 | 351 | |
| 352 | 352 | test "All builtins" { |
| 353 | var comp = Compilation.init(std.testing.allocator); | |
| 353 | var comp = Compilation.init(std.testing.allocator, std.fs.cwd()); | |
| 354 | 354 | defer comp.deinit(); |
| 355 | 355 | _ = try comp.generateBuiltinMacros(.include_system_defines); |
| 356 | 356 | var arena = std.heap.ArenaAllocator.init(std.testing.allocator); |
| ... | ... | @@ -373,7 +373,7 @@ test "All builtins" { |
| 373 | 373 | test "Allocation failures" { |
| 374 | 374 | const Test = struct { |
| 375 | 375 | fn testOne(allocator: std.mem.Allocator) !void { |
| 376 | var comp = Compilation.init(allocator); | |
| 376 | var comp = Compilation.init(allocator, std.fs.cwd()); | |
| 377 | 377 | defer comp.deinit(); |
| 378 | 378 | _ = try comp.generateBuiltinMacros(.include_system_defines); |
| 379 | 379 | var arena = std.heap.ArenaAllocator.init(comp.gpa); |
lib/compiler/aro/aro/Builtins/Builtin.zig+2-1| ... | ... | @@ -71,6 +71,7 @@ pub const longest_name = 43; |
| 71 | 71 | /// If found, returns the index of the node within the `dafsa` array. |
| 72 | 72 | /// Otherwise, returns `null`. |
| 73 | 73 | pub fn findInList(first_child_index: u16, char: u8) ?u16 { |
| 74 | @setEvalBranchQuota(7972); | |
| 74 | 75 | var index = first_child_index; |
| 75 | 76 | while (true) { |
| 76 | 77 | if (dafsa[index].char == char) return index; |
| ... | ... | @@ -5165,7 +5166,7 @@ const dafsa = [_]Node{ |
| 5165 | 5166 | .{ .char = 'e', .end_of_word = false, .end_of_list = true, .number = 1, .child_index = 4913 }, |
| 5166 | 5167 | }; |
| 5167 | 5168 | pub const data = blk: { |
| 5168 | @setEvalBranchQuota(30_000); | |
| 5169 | @setEvalBranchQuota(27902); | |
| 5169 | 5170 | break :blk [_]@This(){ |
| 5170 | 5171 | // _Block_object_assign |
| 5171 | 5172 | .{ .tag = @enumFromInt(0), .properties = .{ .param_str = "vv*vC*iC", .header = .blocks, .attributes = .{ .lib_function_without_prefix = true } } }, |
lib/compiler/aro/aro/Builtins/eval.zig created+86| ... | ... | @@ -0,0 +1,86 @@ |
| 1 | const std = @import("std"); | |
| 2 | const backend = @import("../../backend.zig"); | |
| 3 | const Interner = backend.Interner; | |
| 4 | const Builtins = @import("../Builtins.zig"); | |
| 5 | const Builtin = Builtins.Builtin; | |
| 6 | const Parser = @import("../Parser.zig"); | |
| 7 | const Tree = @import("../Tree.zig"); | |
| 8 | const NodeIndex = Tree.NodeIndex; | |
| 9 | const Type = @import("../Type.zig"); | |
| 10 | const Value = @import("../Value.zig"); | |
| 11 | ||
| 12 | fn makeNan(comptime T: type, str: []const u8) T { | |
| 13 | const UnsignedSameSize = std.meta.Int(.unsigned, @bitSizeOf(T)); | |
| 14 | const parsed = std.fmt.parseUnsigned(UnsignedSameSize, str[0 .. str.len - 1], 0) catch 0; | |
| 15 | const bits: switch (T) { | |
| 16 | f32 => u23, | |
| 17 | f64 => u52, | |
| 18 | f80 => u63, | |
| 19 | f128 => u112, | |
| 20 | else => @compileError("Invalid type for makeNan"), | |
| 21 | } = @truncate(parsed); | |
| 22 | return @bitCast(@as(UnsignedSameSize, bits) | @as(UnsignedSameSize, @bitCast(std.math.nan(T)))); | |
| 23 | } | |
| 24 | ||
| 25 | pub fn eval(tag: Builtin.Tag, p: *Parser, args: []const NodeIndex) !Value { | |
| 26 | const builtin = Builtin.fromTag(tag); | |
| 27 | if (!builtin.properties.attributes.const_evaluable) return .{}; | |
| 28 | ||
| 29 | switch (tag) { | |
| 30 | Builtin.tagFromName("__builtin_inff").?, | |
| 31 | Builtin.tagFromName("__builtin_inf").?, | |
| 32 | Builtin.tagFromName("__builtin_infl").?, | |
| 33 | => { | |
| 34 | const ty: Type = switch (tag) { | |
| 35 | Builtin.tagFromName("__builtin_inff").? => .{ .specifier = .float }, | |
| 36 | Builtin.tagFromName("__builtin_inf").? => .{ .specifier = .double }, | |
| 37 | Builtin.tagFromName("__builtin_infl").? => .{ .specifier = .long_double }, | |
| 38 | else => unreachable, | |
| 39 | }; | |
| 40 | const f: Interner.Key.Float = switch (ty.bitSizeof(p.comp).?) { | |
| 41 | 32 => .{ .f32 = std.math.inf(f32) }, | |
| 42 | 64 => .{ .f64 = std.math.inf(f64) }, | |
| 43 | 80 => .{ .f80 = std.math.inf(f80) }, | |
| 44 | 128 => .{ .f128 = std.math.inf(f128) }, | |
| 45 | else => unreachable, | |
| 46 | }; | |
| 47 | return Value.intern(p.comp, .{ .float = f }); | |
| 48 | }, | |
| 49 | Builtin.tagFromName("__builtin_isinf").? => blk: { | |
| 50 | if (args.len == 0) break :blk; | |
| 51 | const val = p.value_map.get(args[0]) orelse break :blk; | |
| 52 | return Value.fromBool(val.isInf(p.comp)); | |
| 53 | }, | |
| 54 | Builtin.tagFromName("__builtin_isinf_sign").? => blk: { | |
| 55 | if (args.len == 0) break :blk; | |
| 56 | const val = p.value_map.get(args[0]) orelse break :blk; | |
| 57 | switch (val.isInfSign(p.comp)) { | |
| 58 | .unknown => {}, | |
| 59 | .finite => return Value.zero, | |
| 60 | .positive => return Value.one, | |
| 61 | .negative => return Value.int(@as(i64, -1), p.comp), | |
| 62 | } | |
| 63 | }, | |
| 64 | Builtin.tagFromName("__builtin_isnan").? => blk: { | |
| 65 | if (args.len == 0) break :blk; | |
| 66 | const val = p.value_map.get(args[0]) orelse break :blk; | |
| 67 | return Value.fromBool(val.isNan(p.comp)); | |
| 68 | }, | |
| 69 | Builtin.tagFromName("__builtin_nan").? => blk: { | |
| 70 | if (args.len == 0) break :blk; | |
| 71 | const val = p.getDecayedStringLiteral(args[0]) orelse break :blk; | |
| 72 | const bytes = p.comp.interner.get(val.ref()).bytes; | |
| 73 | ||
| 74 | const f: Interner.Key.Float = switch ((Type{ .specifier = .double }).bitSizeof(p.comp).?) { | |
| 75 | 32 => .{ .f32 = makeNan(f32, bytes) }, | |
| 76 | 64 => .{ .f64 = makeNan(f64, bytes) }, | |
| 77 | 80 => .{ .f80 = makeNan(f80, bytes) }, | |
| 78 | 128 => .{ .f128 = makeNan(f128, bytes) }, | |
| 79 | else => unreachable, | |
| 80 | }; | |
| 81 | return Value.intern(p.comp, .{ .float = f }); | |
| 82 | }, | |
| 83 | else => {}, | |
| 84 | } | |
| 85 | return .{}; | |
| 86 | } |
lib/compiler/aro/aro/Compilation.zig+67-23| ... | ... | @@ -127,22 +127,27 @@ types: struct { |
| 127 | 127 | } = .{}, |
| 128 | 128 | string_interner: StrInt = .{}, |
| 129 | 129 | interner: Interner = .{}, |
| 130 | /// If this is not null, the directory containing the specified Source will be searched for includes | |
| 131 | /// Used by MS extensions which allow searching for includes relative to the directory of the main source file. | |
| 130 | 132 | ms_cwd_source_id: ?Source.Id = null, |
| 133 | cwd: std.fs.Dir, | |
| 131 | 134 | |
| 132 | pub fn init(gpa: Allocator) Compilation { | |
| 135 | pub fn init(gpa: Allocator, cwd: std.fs.Dir) Compilation { | |
| 133 | 136 | return .{ |
| 134 | 137 | .gpa = gpa, |
| 135 | 138 | .diagnostics = Diagnostics.init(gpa), |
| 139 | .cwd = cwd, | |
| 136 | 140 | }; |
| 137 | 141 | } |
| 138 | 142 | |
| 139 | 143 | /// Initialize Compilation with default environment, |
| 140 | 144 | /// pragma handlers and emulation mode set to target. |
| 141 | pub fn initDefault(gpa: Allocator) !Compilation { | |
| 145 | pub fn initDefault(gpa: Allocator, cwd: std.fs.Dir) !Compilation { | |
| 142 | 146 | var comp: Compilation = .{ |
| 143 | 147 | .gpa = gpa, |
| 144 | 148 | .environment = try Environment.loadAll(gpa), |
| 145 | 149 | .diagnostics = Diagnostics.init(gpa), |
| 150 | .cwd = cwd, | |
| 146 | 151 | }; |
| 147 | 152 | errdefer comp.deinit(); |
| 148 | 153 | try comp.addDefaultPragmaHandlers(); |
| ... | ... | @@ -534,7 +539,7 @@ pub fn generateBuiltinMacros(comp: *Compilation, system_defines_mode: SystemDefi |
| 534 | 539 | if (system_defines_mode == .include_system_defines) { |
| 535 | 540 | try buf.appendSlice( |
| 536 | 541 | \\#define __VERSION__ "Aro |
| 537 | ++ @import("../backend.zig").version_str ++ "\"\n" ++ | |
| 542 | ++ " " ++ @import("../backend.zig").version_str ++ "\"\n" ++ | |
| 538 | 543 | \\#define __Aro__ |
| 539 | 544 | \\ |
| 540 | 545 | ); |
| ... | ... | @@ -550,6 +555,9 @@ pub fn generateBuiltinMacros(comp: *Compilation, system_defines_mode: SystemDefi |
| 550 | 555 | \\#define __STDC_NO_VLA__ 1 |
| 551 | 556 | \\#define __STDC_UTF_16__ 1 |
| 552 | 557 | \\#define __STDC_UTF_32__ 1 |
| 558 | \\#define __STDC_EMBED_NOT_FOUND__ 0 | |
| 559 | \\#define __STDC_EMBED_FOUND__ 1 | |
| 560 | \\#define __STDC_EMBED_EMPTY__ 2 | |
| 553 | 561 | \\ |
| 554 | 562 | ); |
| 555 | 563 | if (comp.langopts.standard.StdCVersionMacro()) |stdc_version| { |
| ... | ... | @@ -719,8 +727,13 @@ fn generateBuiltinTypes(comp: *Compilation) !void { |
| 719 | 727 | try comp.generateNsConstantStringType(); |
| 720 | 728 | } |
| 721 | 729 | |
| 730 | pub fn float80Type(comp: *const Compilation) ?Type { | |
| 731 | if (comp.langopts.emulate != .gcc) return null; | |
| 732 | return target_util.float80Type(comp.target); | |
| 733 | } | |
| 734 | ||
| 722 | 735 | /// Smallest integer type with at least N bits |
| 723 | fn intLeastN(comp: *const Compilation, bits: usize, signedness: std.builtin.Signedness) Type { | |
| 736 | pub fn intLeastN(comp: *const Compilation, bits: usize, signedness: std.builtin.Signedness) Type { | |
| 724 | 737 | if (bits == 64 and (comp.target.isDarwin() or comp.target.isWasm())) { |
| 725 | 738 | // WebAssembly and Darwin use `long long` for `int_least64_t` and `int_fast64_t`. |
| 726 | 739 | return .{ .specifier = if (signedness == .signed) .long_long else .ulong_long }; |
| ... | ... | @@ -903,7 +916,7 @@ fn generateNsConstantStringType(comp: *Compilation) !void { |
| 903 | 916 | comp.types.ns_constant_string.fields[2] = .{ .name = try StrInt.intern(comp, "str"), .ty = const_char_ptr }; |
| 904 | 917 | comp.types.ns_constant_string.fields[3] = .{ .name = try StrInt.intern(comp, "length"), .ty = .{ .specifier = .long } }; |
| 905 | 918 | comp.types.ns_constant_string.ty = .{ .specifier = .@"struct", .data = .{ .record = &comp.types.ns_constant_string.record } }; |
| 906 | record_layout.compute(&comp.types.ns_constant_string.record, comp.types.ns_constant_string.ty, comp, null); | |
| 919 | record_layout.compute(&comp.types.ns_constant_string.record, comp.types.ns_constant_string.ty, comp, null) catch unreachable; | |
| 907 | 920 | } |
| 908 | 921 | |
| 909 | 922 | fn generateVaListType(comp: *Compilation) !Type { |
| ... | ... | @@ -911,12 +924,12 @@ fn generateVaListType(comp: *Compilation) !Type { |
| 911 | 924 | const kind: Kind = switch (comp.target.cpu.arch) { |
| 912 | 925 | .aarch64 => switch (comp.target.os.tag) { |
| 913 | 926 | .windows => @as(Kind, .char_ptr), |
| 914 | .ios, .macos, .tvos, .watchos, .visionos => .char_ptr, | |
| 927 | .ios, .macos, .tvos, .watchos => .char_ptr, | |
| 915 | 928 | else => .aarch64_va_list, |
| 916 | 929 | }, |
| 917 | 930 | .sparc, .wasm32, .wasm64, .bpfel, .bpfeb, .riscv32, .riscv64, .avr, .spirv32, .spirv64 => .void_ptr, |
| 918 | 931 | .powerpc => switch (comp.target.os.tag) { |
| 919 | .ios, .macos, .tvos, .watchos, .visionos, .aix => @as(Kind, .char_ptr), | |
| 932 | .ios, .macos, .tvos, .watchos, .aix => @as(Kind, .char_ptr), | |
| 920 | 933 | else => return Type{ .specifier = .void }, // unknown |
| 921 | 934 | }, |
| 922 | 935 | .x86, .msp430 => .char_ptr, |
| ... | ... | @@ -951,7 +964,7 @@ fn generateVaListType(comp: *Compilation) !Type { |
| 951 | 964 | record_ty.fields[3] = .{ .name = try StrInt.intern(comp, "__gr_offs"), .ty = .{ .specifier = .int } }; |
| 952 | 965 | record_ty.fields[4] = .{ .name = try StrInt.intern(comp, "__vr_offs"), .ty = .{ .specifier = .int } }; |
| 953 | 966 | ty = .{ .specifier = .@"struct", .data = .{ .record = record_ty } }; |
| 954 | record_layout.compute(record_ty, ty, comp, null); | |
| 967 | record_layout.compute(record_ty, ty, comp, null) catch unreachable; | |
| 955 | 968 | }, |
| 956 | 969 | .x86_64_va_list => { |
| 957 | 970 | const record_ty = try arena.create(Type.Record); |
| ... | ... | @@ -969,7 +982,7 @@ fn generateVaListType(comp: *Compilation) !Type { |
| 969 | 982 | record_ty.fields[2] = .{ .name = try StrInt.intern(comp, "overflow_arg_area"), .ty = void_ptr }; |
| 970 | 983 | record_ty.fields[3] = .{ .name = try StrInt.intern(comp, "reg_save_area"), .ty = void_ptr }; |
| 971 | 984 | ty = .{ .specifier = .@"struct", .data = .{ .record = record_ty } }; |
| 972 | record_layout.compute(record_ty, ty, comp, null); | |
| 985 | record_layout.compute(record_ty, ty, comp, null) catch unreachable; | |
| 973 | 986 | }, |
| 974 | 987 | } |
| 975 | 988 | if (kind == .char_ptr or kind == .void_ptr) { |
| ... | ... | @@ -988,13 +1001,28 @@ fn generateVaListType(comp: *Compilation) !Type { |
| 988 | 1001 | fn generateIntMax(comp: *const Compilation, w: anytype, name: []const u8, ty: Type) !void { |
| 989 | 1002 | const bit_count: u8 = @intCast(ty.sizeof(comp).? * 8); |
| 990 | 1003 | const unsigned = ty.isUnsignedInt(comp); |
| 991 | const max = if (bit_count == 128) | |
| 992 | @as(u128, if (unsigned) std.math.maxInt(u128) else std.math.maxInt(u128)) | |
| 993 | else | |
| 994 | ty.maxInt(comp); | |
| 1004 | const max: u128 = switch (bit_count) { | |
| 1005 | 8 => if (unsigned) std.math.maxInt(u8) else std.math.maxInt(i8), | |
| 1006 | 16 => if (unsigned) std.math.maxInt(u16) else std.math.maxInt(i16), | |
| 1007 | 32 => if (unsigned) std.math.maxInt(u32) else std.math.maxInt(i32), | |
| 1008 | 64 => if (unsigned) std.math.maxInt(u64) else std.math.maxInt(i64), | |
| 1009 | 128 => if (unsigned) std.math.maxInt(u128) else std.math.maxInt(i128), | |
| 1010 | else => unreachable, | |
| 1011 | }; | |
| 995 | 1012 | try w.print("#define __{s}_MAX__ {d}{s}\n", .{ name, max, ty.intValueSuffix(comp) }); |
| 996 | 1013 | } |
| 997 | 1014 | |
| 1015 | /// Largest value that can be stored in wchar_t | |
| 1016 | pub fn wcharMax(comp: *const Compilation) u32 { | |
| 1017 | const unsigned = comp.types.wchar.isUnsignedInt(comp); | |
| 1018 | return switch (comp.types.wchar.bitSizeof(comp).?) { | |
| 1019 | 8 => if (unsigned) std.math.maxInt(u8) else std.math.maxInt(i8), | |
| 1020 | 16 => if (unsigned) std.math.maxInt(u16) else std.math.maxInt(i16), | |
| 1021 | 32 => if (unsigned) std.math.maxInt(u32) else std.math.maxInt(i32), | |
| 1022 | else => unreachable, | |
| 1023 | }; | |
| 1024 | } | |
| 1025 | ||
| 998 | 1026 | fn generateExactWidthIntMax(comp: *const Compilation, w: anytype, specifier: Type.Specifier) !void { |
| 999 | 1027 | var ty = Type{ .specifier = specifier }; |
| 1000 | 1028 | const bit_count: u8 = @intCast(ty.sizeof(comp).? * 8); |
| ... | ... | @@ -1039,6 +1067,12 @@ pub fn nextLargestIntSameSign(comp: *const Compilation, ty: Type) ?Type { |
| 1039 | 1067 | return null; |
| 1040 | 1068 | } |
| 1041 | 1069 | |
| 1070 | /// Maximum size of an array, in bytes | |
| 1071 | pub fn maxArrayBytes(comp: *const Compilation) u64 { | |
| 1072 | const max_bits = @min(61, comp.target.ptrBitWidth()); | |
| 1073 | return (@as(u64, 1) << @truncate(max_bits)) - 1; | |
| 1074 | } | |
| 1075 | ||
| 1042 | 1076 | /// If `enum E { ... }` syntax has a fixed underlying integer type regardless of the presence of |
| 1043 | 1077 | /// __attribute__((packed)) or the range of values of the corresponding enumerator constants, |
| 1044 | 1078 | /// specify it here. |
| ... | ... | @@ -1060,7 +1094,7 @@ pub fn getCharSignedness(comp: *const Compilation) std.builtin.Signedness { |
| 1060 | 1094 | pub fn addBuiltinIncludeDir(comp: *Compilation, aro_dir: []const u8) !void { |
| 1061 | 1095 | var search_path = aro_dir; |
| 1062 | 1096 | while (std.fs.path.dirname(search_path)) |dirname| : (search_path = dirname) { |
| 1063 | var base_dir = std.fs.cwd().openDir(dirname, .{}) catch continue; | |
| 1097 | var base_dir = comp.cwd.openDir(dirname, .{}) catch continue; | |
| 1064 | 1098 | defer base_dir.close(); |
| 1065 | 1099 | |
| 1066 | 1100 | base_dir.access("include/stddef.h", .{}) catch continue; |
| ... | ... | @@ -1266,7 +1300,7 @@ fn addSourceFromPathExtra(comp: *Compilation, path: []const u8, kind: Source.Kin |
| 1266 | 1300 | return error.FileNotFound; |
| 1267 | 1301 | } |
| 1268 | 1302 | |
| 1269 | const file = try std.fs.cwd().openFile(path, .{}); | |
| 1303 | const file = try comp.cwd.openFile(path, .{}); | |
| 1270 | 1304 | defer file.close(); |
| 1271 | 1305 | |
| 1272 | 1306 | const contents = file.readToEndAlloc(comp.gpa, std.math.maxInt(u32)) catch |err| switch (err) { |
| ... | ... | @@ -1349,10 +1383,9 @@ pub fn hasInclude( |
| 1349 | 1383 | return false; |
| 1350 | 1384 | } |
| 1351 | 1385 | |
| 1352 | const cwd = std.fs.cwd(); | |
| 1353 | 1386 | if (std.fs.path.isAbsolute(filename)) { |
| 1354 | 1387 | if (which == .next) return false; |
| 1355 | return !std.meta.isError(cwd.access(filename, .{})); | |
| 1388 | return !std.meta.isError(comp.cwd.access(filename, .{})); | |
| 1356 | 1389 | } |
| 1357 | 1390 | |
| 1358 | 1391 | const cwd_source_id = switch (include_type) { |
| ... | ... | @@ -1372,7 +1405,7 @@ pub fn hasInclude( |
| 1372 | 1405 | |
| 1373 | 1406 | while (try it.nextWithFile(filename, sf_allocator)) |found| { |
| 1374 | 1407 | defer sf_allocator.free(found.path); |
| 1375 | if (!std.meta.isError(cwd.access(found.path, .{}))) return true; | |
| 1408 | if (!std.meta.isError(comp.cwd.access(found.path, .{}))) return true; | |
| 1376 | 1409 | } |
| 1377 | 1410 | return false; |
| 1378 | 1411 | } |
| ... | ... | @@ -1392,7 +1425,7 @@ fn getFileContents(comp: *Compilation, path: []const u8, limit: ?u32) ![]const u |
| 1392 | 1425 | return error.FileNotFound; |
| 1393 | 1426 | } |
| 1394 | 1427 | |
| 1395 | const file = try std.fs.cwd().openFile(path, .{}); | |
| 1428 | const file = try comp.cwd.openFile(path, .{}); | |
| 1396 | 1429 | defer file.close(); |
| 1397 | 1430 | |
| 1398 | 1431 | var buf = std.ArrayList(u8).init(comp.gpa); |
| ... | ... | @@ -1571,6 +1604,17 @@ pub fn hasBuiltinFunction(comp: *const Compilation, builtin: Builtin) bool { |
| 1571 | 1604 | } |
| 1572 | 1605 | } |
| 1573 | 1606 | |
| 1607 | pub fn locSlice(comp: *const Compilation, loc: Source.Location) []const u8 { | |
| 1608 | var tmp_tokenizer = Tokenizer{ | |
| 1609 | .buf = comp.getSource(loc.id).buf, | |
| 1610 | .langopts = comp.langopts, | |
| 1611 | .index = loc.byte_offset, | |
| 1612 | .source = .generated, | |
| 1613 | }; | |
| 1614 | const tok = tmp_tokenizer.next(); | |
| 1615 | return tmp_tokenizer.buf[tok.start..tok.end]; | |
| 1616 | } | |
| 1617 | ||
| 1574 | 1618 | pub const CharUnitSize = enum(u32) { |
| 1575 | 1619 | @"1" = 1, |
| 1576 | 1620 | @"2" = 2, |
| ... | ... | @@ -1590,7 +1634,7 @@ pub const addDiagnostic = Diagnostics.add; |
| 1590 | 1634 | test "addSourceFromReader" { |
| 1591 | 1635 | const Test = struct { |
| 1592 | 1636 | fn addSourceFromReader(str: []const u8, expected: []const u8, warning_count: u32, splices: []const u32) !void { |
| 1593 | var comp = Compilation.init(std.testing.allocator); | |
| 1637 | var comp = Compilation.init(std.testing.allocator, std.fs.cwd()); | |
| 1594 | 1638 | defer comp.deinit(); |
| 1595 | 1639 | |
| 1596 | 1640 | var buf_reader = std.io.fixedBufferStream(str); |
| ... | ... | @@ -1602,7 +1646,7 @@ test "addSourceFromReader" { |
| 1602 | 1646 | } |
| 1603 | 1647 | |
| 1604 | 1648 | fn withAllocationFailures(allocator: std.mem.Allocator) !void { |
| 1605 | var comp = Compilation.init(allocator); | |
| 1649 | var comp = Compilation.init(allocator, std.fs.cwd()); | |
| 1606 | 1650 | defer comp.deinit(); |
| 1607 | 1651 | |
| 1608 | 1652 | _ = try comp.addSourceFromBuffer("path", "spliced\\\nbuffer\n"); |
| ... | ... | @@ -1644,7 +1688,7 @@ test "addSourceFromReader - exhaustive check for carriage return elimination" { |
| 1644 | 1688 | const alen = alphabet.len; |
| 1645 | 1689 | var buf: [alphabet.len]u8 = [1]u8{alphabet[0]} ** alen; |
| 1646 | 1690 | |
| 1647 | var comp = Compilation.init(std.testing.allocator); | |
| 1691 | var comp = Compilation.init(std.testing.allocator, std.fs.cwd()); | |
| 1648 | 1692 | defer comp.deinit(); |
| 1649 | 1693 | |
| 1650 | 1694 | var source_count: u32 = 0; |
| ... | ... | @@ -1672,7 +1716,7 @@ test "ignore BOM at beginning of file" { |
| 1672 | 1716 | |
| 1673 | 1717 | const Test = struct { |
| 1674 | 1718 | fn run(buf: []const u8) !void { |
| 1675 | var comp = Compilation.init(std.testing.allocator); | |
| 1719 | var comp = Compilation.init(std.testing.allocator, std.fs.cwd()); | |
| 1676 | 1720 | defer comp.deinit(); |
| 1677 | 1721 | |
| 1678 | 1722 | var buf_reader = std.io.fixedBufferStream(buf); |
lib/compiler/aro/aro/Diagnostics.zig+13-2| ... | ... | @@ -47,6 +47,10 @@ pub const Message = struct { |
| 47 | 47 | tag: Attribute.Tag, |
| 48 | 48 | specifier: enum { @"struct", @"union", @"enum" }, |
| 49 | 49 | }, |
| 50 | attribute_todo: struct { | |
| 51 | tag: Attribute.Tag, | |
| 52 | kind: enum { variables, fields, types, functions }, | |
| 53 | }, | |
| 50 | 54 | builtin_with_header: struct { |
| 51 | 55 | builtin: Builtin.Tag, |
| 52 | 56 | header: Header, |
| ... | ... | @@ -210,6 +214,9 @@ pub const Options = struct { |
| 210 | 214 | normalized: Kind = .default, |
| 211 | 215 | @"shift-count-negative": Kind = .default, |
| 212 | 216 | @"shift-count-overflow": Kind = .default, |
| 217 | @"constant-conversion": Kind = .default, | |
| 218 | @"sign-conversion": Kind = .default, | |
| 219 | nonnull: Kind = .default, | |
| 213 | 220 | }; |
| 214 | 221 | |
| 215 | 222 | const Diagnostics = @This(); |
| ... | ... | @@ -222,14 +229,14 @@ errors: u32 = 0, |
| 222 | 229 | macro_backtrace_limit: u32 = 6, |
| 223 | 230 | |
| 224 | 231 | pub fn warningExists(name: []const u8) bool { |
| 225 | inline for (std.meta.fields(Options)) |f| { | |
| 232 | inline for (@typeInfo(Options).@"struct".fields) |f| { | |
| 226 | 233 | if (mem.eql(u8, f.name, name)) return true; |
| 227 | 234 | } |
| 228 | 235 | return false; |
| 229 | 236 | } |
| 230 | 237 | |
| 231 | 238 | pub fn set(d: *Diagnostics, name: []const u8, to: Kind) !void { |
| 232 | inline for (std.meta.fields(Options)) |f| { | |
| 239 | inline for (@typeInfo(Options).@"struct".fields) |f| { | |
| 233 | 240 | if (mem.eql(u8, f.name, name)) { |
| 234 | 241 | @field(d.options, f.name) = to; |
| 235 | 242 | return; |
| ... | ... | @@ -422,6 +429,10 @@ pub fn renderMessage(comp: *Compilation, m: anytype, msg: Message) void { |
| 422 | 429 | @tagName(msg.extra.ignored_record_attr.tag), |
| 423 | 430 | @tagName(msg.extra.ignored_record_attr.specifier), |
| 424 | 431 | }), |
| 432 | .attribute_todo => printRt(m, prop.msg, .{ "{s}", "{s}" }, .{ | |
| 433 | @tagName(msg.extra.attribute_todo.tag), | |
| 434 | @tagName(msg.extra.attribute_todo.kind), | |
| 435 | }), | |
| 425 | 436 | .builtin_with_header => printRt(m, prop.msg, .{ "{s}", "{s}" }, .{ |
| 426 | 437 | @tagName(msg.extra.builtin_with_header.header), |
| 427 | 438 | Builtin.nameFromTag(msg.extra.builtin_with_header.builtin).span(), |
lib/compiler/aro/aro/Diagnostics/messages.zig+24-4| ... | ... | @@ -107,6 +107,9 @@ pub const Tag = enum { |
| 107 | 107 | multiple_default, |
| 108 | 108 | previous_case, |
| 109 | 109 | expected_arguments, |
| 110 | callee_with_static_array, | |
| 111 | array_argument_too_small, | |
| 112 | non_null_argument, | |
| 110 | 113 | expected_arguments_old, |
| 111 | 114 | expected_at_least_arguments, |
| 112 | 115 | invalid_static_star, |
| ... | ... | @@ -214,6 +217,7 @@ pub const Tag = enum { |
| 214 | 217 | pre_c23_compat, |
| 215 | 218 | unbound_vla, |
| 216 | 219 | array_too_large, |
| 220 | record_too_large, | |
| 217 | 221 | incompatible_ptr_init, |
| 218 | 222 | incompatible_ptr_init_sign, |
| 219 | 223 | incompatible_ptr_assign, |
| ... | ... | @@ -349,6 +353,8 @@ pub const Tag = enum { |
| 349 | 353 | non_standard_escape_char, |
| 350 | 354 | invalid_pp_stringify_escape, |
| 351 | 355 | vla, |
| 356 | int_value_changed, | |
| 357 | sign_conversion, | |
| 352 | 358 | float_overflow_conversion, |
| 353 | 359 | float_out_of_range, |
| 354 | 360 | float_zero_conversion, |
| ... | ... | @@ -425,7 +431,8 @@ pub const Tag = enum { |
| 425 | 431 | bit_int, |
| 426 | 432 | unsigned_bit_int_too_small, |
| 427 | 433 | signed_bit_int_too_small, |
| 428 | bit_int_too_big, | |
| 434 | unsigned_bit_int_too_big, | |
| 435 | signed_bit_int_too_big, | |
| 429 | 436 | keyword_macro, |
| 430 | 437 | ptr_arithmetic_incomplete, |
| 431 | 438 | callconv_not_supported, |
| ... | ... | @@ -509,6 +516,9 @@ pub const Tag = enum { |
| 509 | 516 | complex_conj, |
| 510 | 517 | overflow_builtin_requires_int, |
| 511 | 518 | overflow_result_requires_ptr, |
| 519 | attribute_todo, | |
| 520 | invalid_type_underlying_enum, | |
| 521 | auto_type_self_initialized, | |
| 512 | 522 | |
| 513 | 523 | pub fn property(tag: Tag) Properties { |
| 514 | 524 | return named_data[@intFromEnum(tag)]; |
| ... | ... | @@ -613,6 +623,9 @@ pub const Tag = enum { |
| 613 | 623 | .{ .msg = "multiple default cases in the same switch", .kind = .@"error" }, |
| 614 | 624 | .{ .msg = "previous case defined here", .kind = .note }, |
| 615 | 625 | .{ .msg = expected_arguments, .extra = .arguments, .kind = .@"error" }, |
| 626 | .{ .msg = "callee declares array parameter as static here", .kind = .note }, | |
| 627 | .{ .msg = "array argument is too small; contains {d} elements, callee requires at least {d}", .extra = .arguments, .kind = .warning, .opt = W("array-bounds") }, | |
| 628 | .{ .msg = "null passed to a callee that requires a non-null argument", .kind = .warning, .opt = W("nonnull") }, | |
| 616 | 629 | .{ .msg = expected_arguments, .extra = .arguments, .kind = .warning }, |
| 617 | 630 | .{ .msg = "expected at least {d} argument(s) got {d}", .extra = .arguments, .kind = .warning }, |
| 618 | 631 | .{ .msg = "'static' may not be used with an unspecified variable length array size", .kind = .@"error" }, |
| ... | ... | @@ -720,6 +733,7 @@ pub const Tag = enum { |
| 720 | 733 | .{ .msg = "{s} is incompatible with C standards before C23", .extra = .str, .kind = .off, .suppress_unless_version = .c23, .opt = W("pre-c23-compat") }, |
| 721 | 734 | .{ .msg = "variable length array must be bound in function definition", .kind = .@"error" }, |
| 722 | 735 | .{ .msg = "array is too large", .kind = .@"error" }, |
| 736 | .{ .msg = "type '{s}' is too large", .kind = .@"error", .extra = .str }, | |
| 723 | 737 | .{ .msg = "incompatible pointer types initializing {s}", .extra = .str, .opt = W("incompatible-pointer-types"), .kind = .warning }, |
| 724 | 738 | .{ .msg = "incompatible pointer types initializing {s}" ++ pointer_sign_message, .extra = .str, .opt = W("pointer-sign"), .kind = .warning }, |
| 725 | 739 | .{ .msg = "incompatible pointer types assigning to {s}", .extra = .str, .opt = W("incompatible-pointer-types"), .kind = .warning }, |
| ... | ... | @@ -855,6 +869,8 @@ pub const Tag = enum { |
| 855 | 869 | .{ .msg = "use of non-standard escape character '\\{s}'", .kind = .off, .opt = W("pedantic"), .extra = .invalid_escape }, |
| 856 | 870 | .{ .msg = "invalid string literal, ignoring final '\\'", .kind = .warning }, |
| 857 | 871 | .{ .msg = "variable length array used", .kind = .off, .opt = W("vla") }, |
| 872 | .{ .msg = "implicit conversion from {s}", .extra = .str, .kind = .warning, .opt = W("constant-conversion") }, | |
| 873 | .{ .msg = "implicit conversion changes signedness: {s}", .extra = .str, .kind = .off, .opt = W("sign-conversion") }, | |
| 858 | 874 | .{ .msg = "implicit conversion of non-finite value from {s} is undefined", .extra = .str, .kind = .off, .opt = W("float-overflow-conversion") }, |
| 859 | 875 | .{ .msg = "implicit conversion of out of range value from {s} is undefined", .extra = .str, .kind = .warning, .opt = W("literal-conversion") }, |
| 860 | 876 | .{ .msg = "implicit conversion from {s}", .extra = .str, .kind = .off, .opt = W("float-zero-conversion") }, |
| ... | ... | @@ -929,9 +945,10 @@ pub const Tag = enum { |
| 929 | 945 | .{ .msg = "this declarator", .kind = .note }, |
| 930 | 946 | .{ .msg = "{s} is not supported on this target", .extra = .str, .kind = .@"error" }, |
| 931 | 947 | .{ .msg = "'_BitInt' in C17 and earlier is a Clang extension'", .kind = .off, .pedantic = true, .opt = W("bit-int-extension"), .suppress_version = .c23 }, |
| 932 | .{ .msg = "{s} must have a bit size of at least 1", .extra = .str, .kind = .@"error" }, | |
| 933 | .{ .msg = "{s} must have a bit size of at least 2", .extra = .str, .kind = .@"error" }, | |
| 934 | .{ .msg = "{s} of bit sizes greater than " ++ std.fmt.comptimePrint("{d}", .{Properties.max_bits}) ++ " not supported", .extra = .str, .kind = .@"error" }, | |
| 948 | .{ .msg = "{s}unsigned _BitInt must have a bit size of at least 1", .extra = .str, .kind = .@"error" }, | |
| 949 | .{ .msg = "{s}signed _BitInt must have a bit size of at least 2", .extra = .str, .kind = .@"error" }, | |
| 950 | .{ .msg = "{s}unsigned _BitInt of bit sizes greater than " ++ std.fmt.comptimePrint("{d}", .{Properties.max_bits}) ++ " not supported", .extra = .str, .kind = .@"error" }, | |
| 951 | .{ .msg = "{s}signed _BitInt of bit sizes greater than " ++ std.fmt.comptimePrint("{d}", .{Properties.max_bits}) ++ " not supported", .extra = .str, .kind = .@"error" }, | |
| 935 | 952 | .{ .msg = "keyword is hidden by macro definition", .kind = .off, .pedantic = true, .opt = W("keyword-macro") }, |
| 936 | 953 | .{ .msg = "arithmetic on a pointer to an incomplete type '{s}'", .extra = .str, .kind = .@"error" }, |
| 937 | 954 | .{ .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 { |
| 1015 | 1032 | .{ .msg = "ISO C does not support '~' for complex conjugation of '{s}'", .opt = W("pedantic"), .extra = .str, .kind = .off }, |
| 1016 | 1033 | .{ .msg = "operand argument to overflow builtin must be an integer ('{s}' invalid)", .extra = .str, .kind = .@"error" }, |
| 1017 | 1034 | .{ .msg = "result argument to overflow builtin must be a pointer to a non-const integer ('{s}' invalid)", .extra = .str, .kind = .@"error" }, |
| 1035 | .{ .msg = "TODO: implement '{s}' attribute for {s}", .extra = .attribute_todo, .kind = .@"error" }, | |
| 1036 | .{ .msg = "non-integral type '{s}' is an invalid underlying type", .extra = .str, .kind = .@"error" }, | |
| 1037 | .{ .msg = "variable '{s}' declared with deduced type '__auto_type' cannot appear in its own initializer", .extra = .str, .kind = .@"error" }, | |
| 1018 | 1038 | }; |
| 1019 | 1039 | }; |
| 1020 | 1040 | }; |
lib/compiler/aro/aro/Driver.zig+30-2| ... | ... | @@ -47,6 +47,20 @@ color: ?bool = null, |
| 47 | 47 | nobuiltininc: bool = false, |
| 48 | 48 | nostdinc: bool = false, |
| 49 | 49 | nostdlibinc: bool = false, |
| 50 | debug_dump_letters: packed struct(u3) { | |
| 51 | d: bool = false, | |
| 52 | m: bool = false, | |
| 53 | n: bool = false, | |
| 54 | ||
| 55 | /// According to GCC, specifying letters whose behavior conflicts is undefined. | |
| 56 | /// We follow clang in that `-dM` always takes precedence over `-dD` | |
| 57 | pub fn getPreprocessorDumpMode(self: @This()) Preprocessor.DumpMode { | |
| 58 | if (self.m) return .macros_only; | |
| 59 | if (self.d) return .macros_and_result; | |
| 60 | if (self.n) return .macro_names_and_result; | |
| 61 | return .result_only; | |
| 62 | } | |
| 63 | } = .{}, | |
| 50 | 64 | |
| 51 | 65 | /// Full path to the aro executable |
| 52 | 66 | aro_name: []const u8 = "", |
| ... | ... | @@ -92,6 +106,9 @@ pub const usage = |
| 92 | 106 | \\ |
| 93 | 107 | \\Compile options: |
| 94 | 108 | \\ -c, --compile Only run preprocess, compile, and assemble steps |
| 109 | \\ -dM Output #define directives for all the macros defined during the execution of the preprocessor | |
| 110 | \\ -dD Like -dM except that it outputs both the #define directives and the result of preprocessing | |
| 111 | \\ -dN Like -dD, but emit only the macro names, not their expansions. | |
| 95 | 112 | \\ -D <macro>=<value> Define <macro> to <value> (defaults to 1) |
| 96 | 113 | \\ -E Only run the preprocessor |
| 97 | 114 | \\ -fchar8_t Enable char8_t (enabled by default in C23 and later) |
| ... | ... | @@ -234,6 +251,12 @@ pub fn parseArgs( |
| 234 | 251 | d.system_defines = .no_system_defines; |
| 235 | 252 | } else if (mem.eql(u8, arg, "-c") or mem.eql(u8, arg, "--compile")) { |
| 236 | 253 | d.only_compile = true; |
| 254 | } else if (mem.eql(u8, arg, "-dD")) { | |
| 255 | d.debug_dump_letters.d = true; | |
| 256 | } else if (mem.eql(u8, arg, "-dM")) { | |
| 257 | d.debug_dump_letters.m = true; | |
| 258 | } else if (mem.eql(u8, arg, "-dN")) { | |
| 259 | d.debug_dump_letters.n = true; | |
| 237 | 260 | } else if (mem.eql(u8, arg, "-E")) { |
| 238 | 261 | d.only_preprocess = true; |
| 239 | 262 | } else if (mem.eql(u8, arg, "-P") or mem.eql(u8, arg, "--no-line-commands")) { |
| ... | ... | @@ -636,13 +659,17 @@ fn processSource( |
| 636 | 659 | if (d.comp.langopts.ms_extensions) { |
| 637 | 660 | d.comp.ms_cwd_source_id = source.id; |
| 638 | 661 | } |
| 639 | ||
| 662 | const dump_mode = d.debug_dump_letters.getPreprocessorDumpMode(); | |
| 640 | 663 | if (d.verbose_pp) pp.verbose = true; |
| 641 | 664 | if (d.only_preprocess) { |
| 642 | 665 | pp.preserve_whitespace = true; |
| 643 | 666 | if (d.line_commands) { |
| 644 | 667 | pp.linemarkers = if (d.use_line_directives) .line_directives else .numeric_directives; |
| 645 | 668 | } |
| 669 | switch (dump_mode) { | |
| 670 | .macros_and_result, .macro_names_and_result => pp.store_macro_tokens = true, | |
| 671 | .result_only, .macros_only => {}, | |
| 672 | } | |
| 646 | 673 | } |
| 647 | 674 | |
| 648 | 675 | try pp.preprocessSources(&.{ source, builtin, user_macros }); |
| ... | ... | @@ -663,7 +690,8 @@ fn processSource( |
| 663 | 690 | defer if (d.output_name != null) file.close(); |
| 664 | 691 | |
| 665 | 692 | var buf_w = std.io.bufferedWriter(file.writer()); |
| 666 | pp.prettyPrintTokens(buf_w.writer()) catch |er| | |
| 693 | ||
| 694 | pp.prettyPrintTokens(buf_w.writer(), dump_mode) catch |er| | |
| 667 | 695 | return d.fatal("unable to write result: {s}", .{errorDescription(er)}); |
| 668 | 696 | |
| 669 | 697 | buf_w.flush() catch |er| |
lib/compiler/aro/aro/Driver/Filesystem.zig+2-2| ... | ... | @@ -56,7 +56,7 @@ fn existsFake(entries: []const Filesystem.Entry, path: []const u8) bool { |
| 56 | 56 | } |
| 57 | 57 | |
| 58 | 58 | fn canExecutePosix(path: []const u8) bool { |
| 59 | std.os.access(path, std.os.X_OK) catch return false; | |
| 59 | std.posix.access(path, std.posix.X_OK) catch return false; | |
| 60 | 60 | // Todo: ensure path is not a directory |
| 61 | 61 | return true; |
| 62 | 62 | } |
| ... | ... | @@ -173,7 +173,7 @@ pub const Filesystem = union(enum) { |
| 173 | 173 | pub fn exists(fs: Filesystem, path: []const u8) bool { |
| 174 | 174 | switch (fs) { |
| 175 | 175 | .real => { |
| 176 | std.os.access(path, std.os.F_OK) catch return false; | |
| 176 | std.fs.cwd().access(path, .{}) catch return false; | |
| 177 | 177 | return true; |
| 178 | 178 | }, |
| 179 | 179 | .fake => |paths| return existsFake(paths, path), |
lib/compiler/aro/aro/Hideset.zig+27-22| ... | ... | @@ -46,15 +46,15 @@ const Item = struct { |
| 46 | 46 | const List = std.MultiArrayList(Item); |
| 47 | 47 | }; |
| 48 | 48 | |
| 49 | const Index = enum(u32) { | |
| 49 | pub const Index = enum(u32) { | |
| 50 | 50 | none = std.math.maxInt(u32), |
| 51 | 51 | _, |
| 52 | 52 | }; |
| 53 | 53 | |
| 54 | 54 | map: std.AutoHashMapUnmanaged(Identifier, Index) = .{}, |
| 55 | /// Used for computing intersection of two lists; stored here so that allocations can be retained | |
| 55 | /// Used for computing union/intersection of two lists; stored here so that allocations can be retained | |
| 56 | 56 | /// until hideset is deinit'ed |
| 57 | intersection_map: std.AutoHashMapUnmanaged(Identifier, void) = .{}, | |
| 57 | tmp_map: std.AutoHashMapUnmanaged(Identifier, void) = .{}, | |
| 58 | 58 | linked_list: Item.List = .{}, |
| 59 | 59 | comp: *const Compilation, |
| 60 | 60 | |
| ... | ... | @@ -72,7 +72,7 @@ const Iterator = struct { |
| 72 | 72 | |
| 73 | 73 | pub fn deinit(self: *Hideset) void { |
| 74 | 74 | self.map.deinit(self.comp.gpa); |
| 75 | self.intersection_map.deinit(self.comp.gpa); | |
| 75 | self.tmp_map.deinit(self.comp.gpa); | |
| 76 | 76 | self.linked_list.deinit(self.comp.gpa); |
| 77 | 77 | } |
| 78 | 78 | |
| ... | ... | @@ -83,7 +83,7 @@ pub fn clearRetainingCapacity(self: *Hideset) void { |
| 83 | 83 | |
| 84 | 84 | pub fn clearAndFree(self: *Hideset) void { |
| 85 | 85 | self.map.clearAndFree(self.comp.gpa); |
| 86 | self.intersection_map.clearAndFree(self.comp.gpa); | |
| 86 | self.tmp_map.clearAndFree(self.comp.gpa); | |
| 87 | 87 | self.linked_list.shrinkAndFree(self.comp.gpa, 0); |
| 88 | 88 | } |
| 89 | 89 | |
| ... | ... | @@ -109,8 +109,13 @@ fn ensureUnusedCapacity(self: *Hideset, new_size: usize) !void { |
| 109 | 109 | |
| 110 | 110 | /// Creates a one-item list with contents `identifier` |
| 111 | 111 | fn createNodeAssumeCapacity(self: *Hideset, identifier: Identifier) Index { |
| 112 | return self.createNodeAssumeCapacityExtra(identifier, .none); | |
| 113 | } | |
| 114 | ||
| 115 | /// Creates a one-item list with contents `identifier` | |
| 116 | fn createNodeAssumeCapacityExtra(self: *Hideset, identifier: Identifier, next: Index) Index { | |
| 112 | 117 | const next_idx = self.linked_list.len; |
| 113 | self.linked_list.appendAssumeCapacity(.{ .identifier = identifier }); | |
| 118 | self.linked_list.appendAssumeCapacity(.{ .identifier = identifier, .next = next }); | |
| 114 | 119 | return @enumFromInt(next_idx); |
| 115 | 120 | } |
| 116 | 121 | |
| ... | ... | @@ -121,24 +126,24 @@ pub fn prepend(self: *Hideset, loc: Source.Location, tail: Index) !Index { |
| 121 | 126 | return @enumFromInt(new_idx); |
| 122 | 127 | } |
| 123 | 128 | |
| 124 | /// Copy a, then attach b at the end | |
| 129 | /// Attach elements of `b` to the front of `a` (if they're not in `a`) | |
| 125 | 130 | pub fn @"union"(self: *Hideset, a: Index, b: Index) !Index { |
| 126 | var cur: Index = .none; | |
| 131 | if (a == .none) return b; | |
| 132 | if (b == .none) return a; | |
| 133 | self.tmp_map.clearRetainingCapacity(); | |
| 134 | ||
| 135 | var it = self.iterator(b); | |
| 136 | while (it.next()) |identifier| { | |
| 137 | try self.tmp_map.put(self.comp.gpa, identifier, {}); | |
| 138 | } | |
| 139 | ||
| 127 | 140 | var head: Index = b; |
| 128 | 141 | try self.ensureUnusedCapacity(self.len(a)); |
| 129 | var it = self.iterator(a); | |
| 142 | it = self.iterator(a); | |
| 130 | 143 | while (it.next()) |identifier| { |
| 131 | const new_idx = self.createNodeAssumeCapacity(identifier); | |
| 132 | if (head == b) { | |
| 133 | head = new_idx; | |
| 144 | if (!self.tmp_map.contains(identifier)) { | |
| 145 | head = self.createNodeAssumeCapacityExtra(identifier, head); | |
| 134 | 146 | } |
| 135 | if (cur != .none) { | |
| 136 | self.linked_list.items(.next)[@intFromEnum(cur)] = new_idx; | |
| 137 | } | |
| 138 | cur = new_idx; | |
| 139 | } | |
| 140 | if (cur != .none) { | |
| 141 | self.linked_list.items(.next)[@intFromEnum(cur)] = b; | |
| 142 | 147 | } |
| 143 | 148 | return head; |
| 144 | 149 | } |
| ... | ... | @@ -163,20 +168,20 @@ fn len(self: *const Hideset, list: Index) usize { |
| 163 | 168 | |
| 164 | 169 | pub fn intersection(self: *Hideset, a: Index, b: Index) !Index { |
| 165 | 170 | if (a == .none or b == .none) return .none; |
| 166 | self.intersection_map.clearRetainingCapacity(); | |
| 171 | self.tmp_map.clearRetainingCapacity(); | |
| 167 | 172 | |
| 168 | 173 | var cur: Index = .none; |
| 169 | 174 | var head: Index = .none; |
| 170 | 175 | var it = self.iterator(a); |
| 171 | 176 | var a_len: usize = 0; |
| 172 | 177 | while (it.next()) |identifier| : (a_len += 1) { |
| 173 | try self.intersection_map.put(self.comp.gpa, identifier, {}); | |
| 178 | try self.tmp_map.put(self.comp.gpa, identifier, {}); | |
| 174 | 179 | } |
| 175 | 180 | try self.ensureUnusedCapacity(@min(a_len, self.len(b))); |
| 176 | 181 | |
| 177 | 182 | it = self.iterator(b); |
| 178 | 183 | while (it.next()) |identifier| { |
| 179 | if (self.intersection_map.contains(identifier)) { | |
| 184 | if (self.tmp_map.contains(identifier)) { | |
| 180 | 185 | const new_idx = self.createNodeAssumeCapacity(identifier); |
| 181 | 186 | if (head == .none) { |
| 182 | 187 | head = new_idx; |
lib/compiler/aro/aro/Parser.zig+572-261| ... | ... | @@ -28,6 +28,7 @@ const StrInt = @import("StringInterner.zig"); |
| 28 | 28 | const StringId = StrInt.StringId; |
| 29 | 29 | const Builtins = @import("Builtins.zig"); |
| 30 | 30 | const Builtin = Builtins.Builtin; |
| 31 | const evalBuiltin = @import("Builtins/eval.zig").eval; | |
| 31 | 32 | const target_util = @import("target.zig"); |
| 32 | 33 | |
| 33 | 34 | const Switch = struct { |
| ... | ... | @@ -100,7 +101,7 @@ value_map: Tree.ValueMap, |
| 100 | 101 | |
| 101 | 102 | // buffers used during compilation |
| 102 | 103 | syms: SymbolStack = .{}, |
| 103 | strings: std.ArrayList(u8), | |
| 104 | strings: std.ArrayListAligned(u8, 4), | |
| 104 | 105 | labels: std.ArrayList(Label), |
| 105 | 106 | list_buf: NodeList, |
| 106 | 107 | decl_buf: NodeList, |
| ... | ... | @@ -130,6 +131,10 @@ const_decl_folding: ConstDeclFoldingMode = .fold_const_decls, |
| 130 | 131 | /// address-of-label expression (tracked with contains_address_of_label) |
| 131 | 132 | computed_goto_tok: ?TokenIndex = null, |
| 132 | 133 | |
| 134 | /// __auto_type may only be used with a single declarator. Keep track of the name | |
| 135 | /// so that it is not used in its own initializer. | |
| 136 | auto_type_decl_name: StringId = .empty, | |
| 137 | ||
| 133 | 138 | /// Various variables that are different for each function. |
| 134 | 139 | func: struct { |
| 135 | 140 | /// null if not in function, will always be plain func, var_args_func or old_style_func |
| ... | ... | @@ -160,7 +165,7 @@ record: struct { |
| 160 | 165 | } |
| 161 | 166 | |
| 162 | 167 | fn addFieldsFromAnonymous(r: @This(), p: *Parser, ty: Type) Error!void { |
| 163 | for (ty.data.record.fields) |f| { | |
| 168 | for (ty.getRecord().?.fields) |f| { | |
| 164 | 169 | if (f.isAnonymousRecord()) { |
| 165 | 170 | try r.addFieldsFromAnonymous(p, f.ty.canonicalize(.standard)); |
| 166 | 171 | } else if (f.name_tok != 0) { |
| ... | ... | @@ -470,7 +475,7 @@ pub fn typePairStrExtra(p: *Parser, a: Type, msg: []const u8, b: Type) ![]const |
| 470 | 475 | return try p.comp.diagnostics.arena.allocator().dupe(u8, p.strings.items[strings_top..]); |
| 471 | 476 | } |
| 472 | 477 | |
| 473 | pub fn floatValueChangedStr(p: *Parser, res: *Result, old_value: Value, int_ty: Type) ![]const u8 { | |
| 478 | pub fn valueChangedStr(p: *Parser, res: *Result, old_value: Value, int_ty: Type) ![]const u8 { | |
| 474 | 479 | const strings_top = p.strings.items.len; |
| 475 | 480 | defer p.strings.items.len = strings_top; |
| 476 | 481 | |
| ... | ... | @@ -572,6 +577,14 @@ fn nodeIs(p: *Parser, node: NodeIndex, tag: Tree.Tag) bool { |
| 572 | 577 | return p.getNode(node, tag) != null; |
| 573 | 578 | } |
| 574 | 579 | |
| 580 | pub fn getDecayedStringLiteral(p: *Parser, node: NodeIndex) ?Value { | |
| 581 | const cast_node = p.getNode(node, .implicit_cast) orelse return null; | |
| 582 | const data = p.nodes.items(.data)[@intFromEnum(cast_node)]; | |
| 583 | if (data.cast.kind != .array_to_pointer) return null; | |
| 584 | const literal_node = p.getNode(data.cast.operand, .string_literal_expr) orelse return null; | |
| 585 | return p.value_map.get(literal_node); | |
| 586 | } | |
| 587 | ||
| 575 | 588 | fn getNode(p: *Parser, node: NodeIndex, tag: Tree.Tag) ?NodeIndex { |
| 576 | 589 | var cur = node; |
| 577 | 590 | const tags = p.nodes.items(.tag); |
| ... | ... | @@ -680,7 +693,7 @@ pub fn parse(pp: *Preprocessor) Compilation.Error!Tree { |
| 680 | 693 | .gpa = pp.comp.gpa, |
| 681 | 694 | .arena = arena.allocator(), |
| 682 | 695 | .tok_ids = pp.tokens.items(.id), |
| 683 | .strings = std.ArrayList(u8).init(pp.comp.gpa), | |
| 696 | .strings = std.ArrayListAligned(u8, 4).init(pp.comp.gpa), | |
| 684 | 697 | .value_map = Tree.ValueMap.init(pp.comp.gpa), |
| 685 | 698 | .data = NodeList.init(pp.comp.gpa), |
| 686 | 699 | .labels = std.ArrayList(Label).init(pp.comp.gpa), |
| ... | ... | @@ -725,7 +738,7 @@ pub fn parse(pp: *Preprocessor) Compilation.Error!Tree { |
| 725 | 738 | defer p.syms.popScope(); |
| 726 | 739 | |
| 727 | 740 | // NodeIndex 0 must be invalid |
| 728 | _ = try p.addNode(.{ .tag = .invalid, .ty = undefined, .data = undefined }); | |
| 741 | _ = try p.addNode(.{ .tag = .invalid, .ty = undefined, .data = undefined, .loc = undefined }); | |
| 729 | 742 | |
| 730 | 743 | { |
| 731 | 744 | if (p.comp.langopts.hasChar8_T()) { |
| ... | ... | @@ -747,6 +760,10 @@ pub fn parse(pp: *Preprocessor) Compilation.Error!Tree { |
| 747 | 760 | if (ty.isArray()) ty.decayArray(); |
| 748 | 761 | |
| 749 | 762 | try p.syms.defineTypedef(&p, try StrInt.intern(p.comp, "__NSConstantString"), pp.comp.types.ns_constant_string.ty, 0, .none); |
| 763 | ||
| 764 | if (p.comp.float80Type()) |float80_ty| { | |
| 765 | try p.syms.defineTypedef(&p, try StrInt.intern(p.comp, "__float80"), float80_ty, 0, .none); | |
| 766 | } | |
| 750 | 767 | } |
| 751 | 768 | |
| 752 | 769 | while (p.eatToken(.eof) == null) { |
| ... | ... | @@ -862,6 +879,8 @@ fn nextExternDecl(p: *Parser) void { |
| 862 | 879 | .keyword_int, |
| 863 | 880 | .keyword_long, |
| 864 | 881 | .keyword_signed, |
| 882 | .keyword_signed1, | |
| 883 | .keyword_signed2, | |
| 865 | 884 | .keyword_unsigned, |
| 866 | 885 | .keyword_float, |
| 867 | 886 | .keyword_double, |
| ... | ... | @@ -1018,10 +1037,8 @@ fn decl(p: *Parser) Error!bool { |
| 1018 | 1037 | |
| 1019 | 1038 | // Collect old style parameter declarations. |
| 1020 | 1039 | if (init_d.d.old_style_func != null) { |
| 1021 | const attrs = init_d.d.ty.getAttributes(); | |
| 1022 | var base_ty = if (init_d.d.ty.specifier == .attributed) init_d.d.ty.data.attributed.base else init_d.d.ty; | |
| 1040 | var base_ty = init_d.d.ty.base(); | |
| 1023 | 1041 | base_ty.specifier = .func; |
| 1024 | init_d.d.ty = try base_ty.withAttributes(p.arena, attrs); | |
| 1025 | 1042 | |
| 1026 | 1043 | const param_buf_top = p.param_buf.items.len; |
| 1027 | 1044 | defer p.param_buf.items.len = param_buf_top; |
| ... | ... | @@ -1116,6 +1133,7 @@ fn decl(p: *Parser) Error!bool { |
| 1116 | 1133 | .ty = init_d.d.ty, |
| 1117 | 1134 | .tag = try decl_spec.validateFnDef(p), |
| 1118 | 1135 | .data = .{ .decl = .{ .name = init_d.d.name, .node = body } }, |
| 1136 | .loc = @enumFromInt(init_d.d.name), | |
| 1119 | 1137 | }); |
| 1120 | 1138 | try p.decl_buf.append(node); |
| 1121 | 1139 | |
| ... | ... | @@ -1142,9 +1160,18 @@ fn decl(p: *Parser) Error!bool { |
| 1142 | 1160 | if (init_d.d.old_style_func) |tok_i| try p.errTok(.invalid_old_style_params, tok_i); |
| 1143 | 1161 | const tag = try decl_spec.validate(p, &init_d.d.ty, init_d.initializer.node != .none); |
| 1144 | 1162 | |
| 1145 | const node = try p.addNode(.{ .ty = init_d.d.ty, .tag = tag, .data = .{ | |
| 1146 | .decl = .{ .name = init_d.d.name, .node = init_d.initializer.node }, | |
| 1147 | } }); | |
| 1163 | const tok = switch (decl_spec.storage_class) { | |
| 1164 | .auto, .@"extern", .register, .static, .typedef => |tok| tok, | |
| 1165 | .none => init_d.d.name, | |
| 1166 | }; | |
| 1167 | const node = try p.addNode(.{ | |
| 1168 | .ty = init_d.d.ty, | |
| 1169 | .tag = tag, | |
| 1170 | .data = .{ | |
| 1171 | .decl = .{ .name = init_d.d.name, .node = init_d.initializer.node }, | |
| 1172 | }, | |
| 1173 | .loc = @enumFromInt(tok), | |
| 1174 | }); | |
| 1148 | 1175 | try p.decl_buf.append(node); |
| 1149 | 1176 | |
| 1150 | 1177 | const interned_name = try StrInt.intern(p.comp, p.tokSlice(init_d.d.name)); |
| ... | ... | @@ -1287,6 +1314,7 @@ fn staticAssert(p: *Parser) Error!bool { |
| 1287 | 1314 | .lhs = res.node, |
| 1288 | 1315 | .rhs = str.node, |
| 1289 | 1316 | } }, |
| 1317 | .loc = @enumFromInt(static_assert), | |
| 1290 | 1318 | }); |
| 1291 | 1319 | try p.decl_buf.append(node); |
| 1292 | 1320 | return true; |
| ... | ... | @@ -1407,6 +1435,8 @@ fn typeof(p: *Parser) Error!?Type { |
| 1407 | 1435 | const l_paren = try p.expectToken(.l_paren); |
| 1408 | 1436 | if (try p.typeName()) |ty| { |
| 1409 | 1437 | try p.expectClosing(l_paren, .r_paren); |
| 1438 | if (ty.is(.invalid)) return null; | |
| 1439 | ||
| 1410 | 1440 | const typeof_ty = try p.arena.create(Type); |
| 1411 | 1441 | typeof_ty.* = .{ |
| 1412 | 1442 | .data = ty.data, |
| ... | ... | @@ -1428,6 +1458,8 @@ fn typeof(p: *Parser) Error!?Type { |
| 1428 | 1458 | .specifier = .nullptr_t, |
| 1429 | 1459 | .qual = if (unqual) .{} else typeof_expr.ty.qual.inheritFromTypeof(), |
| 1430 | 1460 | }; |
| 1461 | } else if (typeof_expr.ty.is(.invalid)) { | |
| 1462 | return null; | |
| 1431 | 1463 | } |
| 1432 | 1464 | |
| 1433 | 1465 | const inner = try p.arena.create(Type.Expr); |
| ... | ... | @@ -1774,6 +1806,8 @@ fn initDeclarator(p: *Parser, decl_spec: *DeclSpec, attr_buf_top: usize) Error!? |
| 1774 | 1806 | } else { |
| 1775 | 1807 | apply_var_attributes = true; |
| 1776 | 1808 | } |
| 1809 | const c23_auto = init_d.d.ty.is(.c23_auto); | |
| 1810 | const auto_type = init_d.d.ty.is(.auto_type); | |
| 1777 | 1811 | |
| 1778 | 1812 | if (p.eatToken(.equal)) |eq| init: { |
| 1779 | 1813 | if (decl_spec.storage_class == .typedef or |
| ... | ... | @@ -1801,19 +1835,21 @@ fn initDeclarator(p: *Parser, decl_spec: *DeclSpec, attr_buf_top: usize) Error!? |
| 1801 | 1835 | |
| 1802 | 1836 | const interned_name = try StrInt.intern(p.comp, p.tokSlice(init_d.d.name)); |
| 1803 | 1837 | try p.syms.declareSymbol(p, interned_name, init_d.d.ty, init_d.d.name, .none); |
| 1838 | if (c23_auto or auto_type) { | |
| 1839 | p.auto_type_decl_name = interned_name; | |
| 1840 | } | |
| 1841 | defer p.auto_type_decl_name = .empty; | |
| 1842 | ||
| 1804 | 1843 | var init_list_expr = try p.initializer(init_d.d.ty); |
| 1805 | 1844 | init_d.initializer = init_list_expr; |
| 1806 | 1845 | if (!init_list_expr.ty.isArray()) break :init; |
| 1807 | if (init_d.d.ty.specifier == .incomplete_array) { | |
| 1808 | // Modifying .data is exceptionally allowed for .incomplete_array. | |
| 1809 | init_d.d.ty.data.array.len = init_list_expr.ty.arrayLen() orelse break :init; | |
| 1810 | init_d.d.ty.specifier = .array; | |
| 1846 | if (init_d.d.ty.is(.incomplete_array)) { | |
| 1847 | init_d.d.ty.setIncompleteArrayLen(init_list_expr.ty.arrayLen() orelse break :init); | |
| 1811 | 1848 | } |
| 1812 | 1849 | } |
| 1813 | 1850 | |
| 1814 | 1851 | const name = init_d.d.name; |
| 1815 | const c23_auto = init_d.d.ty.is(.c23_auto); | |
| 1816 | if (init_d.d.ty.is(.auto_type) or c23_auto) { | |
| 1852 | if (auto_type or c23_auto) { | |
| 1817 | 1853 | if (init_d.initializer.node == .none) { |
| 1818 | 1854 | init_d.d.ty = Type.invalid; |
| 1819 | 1855 | if (c23_auto) { |
| ... | ... | @@ -1872,6 +1908,8 @@ fn initDeclarator(p: *Parser, decl_spec: *DeclSpec, attr_buf_top: usize) Error!? |
| 1872 | 1908 | /// | keyword_float |
| 1873 | 1909 | /// | keyword_double |
| 1874 | 1910 | /// | keyword_signed |
| 1911 | /// | keyword_signed1 | |
| 1912 | /// | keyword_signed2 | |
| 1875 | 1913 | /// | keyword_unsigned |
| 1876 | 1914 | /// | keyword_bool |
| 1877 | 1915 | /// | keyword_c23_bool |
| ... | ... | @@ -1911,14 +1949,13 @@ fn typeSpec(p: *Parser, ty: *Type.Builder) Error!bool { |
| 1911 | 1949 | .keyword_long => try ty.combine(p, .long, p.tok_i), |
| 1912 | 1950 | .keyword_int64, .keyword_int64_2 => try ty.combine(p, .long_long, p.tok_i), |
| 1913 | 1951 | .keyword_int128 => try ty.combine(p, .int128, p.tok_i), |
| 1914 | .keyword_signed => try ty.combine(p, .signed, p.tok_i), | |
| 1952 | .keyword_signed, .keyword_signed1, .keyword_signed2 => try ty.combine(p, .signed, p.tok_i), | |
| 1915 | 1953 | .keyword_unsigned => try ty.combine(p, .unsigned, p.tok_i), |
| 1916 | 1954 | .keyword_fp16 => try ty.combine(p, .fp16, p.tok_i), |
| 1917 | 1955 | .keyword_float16 => try ty.combine(p, .float16, p.tok_i), |
| 1918 | 1956 | .keyword_float => try ty.combine(p, .float, p.tok_i), |
| 1919 | 1957 | .keyword_double => try ty.combine(p, .double, p.tok_i), |
| 1920 | 1958 | .keyword_complex => try ty.combine(p, .complex, p.tok_i), |
| 1921 | .keyword_float80 => try ty.combine(p, .float80, p.tok_i), | |
| 1922 | 1959 | .keyword_float128_1, .keyword_float128_2 => { |
| 1923 | 1960 | if (!p.comp.hasFloat128()) { |
| 1924 | 1961 | 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 { |
| 2128 | 2165 | .tag = if (is_struct) .struct_forward_decl else .union_forward_decl, |
| 2129 | 2166 | .ty = ty, |
| 2130 | 2167 | .data = .{ .decl_ref = ident }, |
| 2168 | .loc = @enumFromInt(ident), | |
| 2131 | 2169 | })); |
| 2132 | 2170 | return ty; |
| 2133 | 2171 | } |
| ... | ... | @@ -2248,19 +2286,22 @@ fn recordSpec(p: *Parser) Error!Type { |
| 2248 | 2286 | // TODO: msvc considers `#pragma pack` on a per-field basis |
| 2249 | 2287 | .msvc => p.pragma_pack, |
| 2250 | 2288 | }; |
| 2251 | record_layout.compute(record_ty, ty, p.comp, pragma_pack_value); | |
| 2289 | record_layout.compute(record_ty, ty, p.comp, pragma_pack_value) catch |er| switch (er) { | |
| 2290 | error.Overflow => try p.errStr(.record_too_large, maybe_ident orelse kind_tok, try p.typeStr(ty)), | |
| 2291 | }; | |
| 2252 | 2292 | } |
| 2253 | 2293 | |
| 2254 | 2294 | // finish by creating a node |
| 2255 | 2295 | var node: Tree.Node = .{ |
| 2256 | 2296 | .tag = if (is_struct) .struct_decl_two else .union_decl_two, |
| 2257 | 2297 | .ty = ty, |
| 2258 | .data = .{ .bin = .{ .lhs = .none, .rhs = .none } }, | |
| 2298 | .data = .{ .two = .{ .none, .none } }, | |
| 2299 | .loc = @enumFromInt(maybe_ident orelse kind_tok), | |
| 2259 | 2300 | }; |
| 2260 | 2301 | switch (record_decls.len) { |
| 2261 | 2302 | 0 => {}, |
| 2262 | 1 => node.data = .{ .bin = .{ .lhs = record_decls[0], .rhs = .none } }, | |
| 2263 | 2 => node.data = .{ .bin = .{ .lhs = record_decls[0], .rhs = record_decls[1] } }, | |
| 2303 | 1 => node.data = .{ .two = .{ record_decls[0], .none } }, | |
| 2304 | 2 => node.data = .{ .two = .{ record_decls[0], record_decls[1] } }, | |
| 2264 | 2305 | else => { |
| 2265 | 2306 | node.tag = if (is_struct) .struct_decl else .union_decl; |
| 2266 | 2307 | node.data = .{ .range = try p.addList(record_decls) }; |
| ... | ... | @@ -2383,6 +2424,7 @@ fn recordDeclarator(p: *Parser) Error!bool { |
| 2383 | 2424 | .tag = .indirect_record_field_decl, |
| 2384 | 2425 | .ty = ty, |
| 2385 | 2426 | .data = undefined, |
| 2427 | .loc = @enumFromInt(first_tok), | |
| 2386 | 2428 | }); |
| 2387 | 2429 | try p.decl_buf.append(node); |
| 2388 | 2430 | try p.record.addFieldsFromAnonymous(p, ty); |
| ... | ... | @@ -2402,6 +2444,7 @@ fn recordDeclarator(p: *Parser) Error!bool { |
| 2402 | 2444 | .tag = .record_field_decl, |
| 2403 | 2445 | .ty = ty, |
| 2404 | 2446 | .data = .{ .decl = .{ .name = name_tok, .node = bits_node } }, |
| 2447 | .loc = @enumFromInt(if (name_tok != 0) name_tok else first_tok), | |
| 2405 | 2448 | }); |
| 2406 | 2449 | try p.decl_buf.append(node); |
| 2407 | 2450 | } |
| ... | ... | @@ -2461,7 +2504,8 @@ fn enumSpec(p: *Parser) Error!Type { |
| 2461 | 2504 | |
| 2462 | 2505 | const maybe_ident = try p.eatIdentifier(); |
| 2463 | 2506 | const fixed_ty = if (p.eatToken(.colon)) |colon| fixed: { |
| 2464 | const fixed = (try p.typeName()) orelse { | |
| 2507 | const ty_start = p.tok_i; | |
| 2508 | const fixed = (try p.specQual()) orelse { | |
| 2465 | 2509 | if (p.record.kind != .invalid) { |
| 2466 | 2510 | // This is a bit field. |
| 2467 | 2511 | p.tok_i -= 1; |
| ... | ... | @@ -2471,6 +2515,12 @@ fn enumSpec(p: *Parser) Error!Type { |
| 2471 | 2515 | try p.errTok(.enum_fixed, colon); |
| 2472 | 2516 | break :fixed null; |
| 2473 | 2517 | }; |
| 2518 | ||
| 2519 | if (!fixed.isInt() or fixed.is(.@"enum")) { | |
| 2520 | try p.errStr(.invalid_type_underlying_enum, ty_start, try p.typeStr(fixed)); | |
| 2521 | break :fixed Type.int; | |
| 2522 | } | |
| 2523 | ||
| 2474 | 2524 | try p.errTok(.enum_fixed, colon); |
| 2475 | 2525 | break :fixed fixed; |
| 2476 | 2526 | } else null; |
| ... | ... | @@ -2505,6 +2555,7 @@ fn enumSpec(p: *Parser) Error!Type { |
| 2505 | 2555 | .tag = .enum_forward_decl, |
| 2506 | 2556 | .ty = ty, |
| 2507 | 2557 | .data = .{ .decl_ref = ident }, |
| 2558 | .loc = @enumFromInt(ident), | |
| 2508 | 2559 | })); |
| 2509 | 2560 | return ty; |
| 2510 | 2561 | } |
| ... | ... | @@ -2587,7 +2638,7 @@ fn enumSpec(p: *Parser) Error!Type { |
| 2587 | 2638 | continue; |
| 2588 | 2639 | |
| 2589 | 2640 | const symbol = p.syms.getPtr(field.name, .vars); |
| 2590 | try symbol.val.intCast(dest_ty, p.comp); | |
| 2641 | _ = try symbol.val.intCast(dest_ty, p.comp); | |
| 2591 | 2642 | symbol.ty = dest_ty; |
| 2592 | 2643 | p.nodes.items(.ty)[@intFromEnum(field_nodes[i])] = dest_ty; |
| 2593 | 2644 | field.ty = dest_ty; |
| ... | ... | @@ -2615,13 +2666,18 @@ fn enumSpec(p: *Parser) Error!Type { |
| 2615 | 2666 | } |
| 2616 | 2667 | |
| 2617 | 2668 | // finish by creating a node |
| 2618 | var node: Tree.Node = .{ .tag = .enum_decl_two, .ty = ty, .data = .{ | |
| 2619 | .bin = .{ .lhs = .none, .rhs = .none }, | |
| 2620 | } }; | |
| 2669 | var node: Tree.Node = .{ | |
| 2670 | .tag = .enum_decl_two, | |
| 2671 | .ty = ty, | |
| 2672 | .data = .{ | |
| 2673 | .two = .{ .none, .none }, | |
| 2674 | }, | |
| 2675 | .loc = @enumFromInt(maybe_ident orelse enum_tok), | |
| 2676 | }; | |
| 2621 | 2677 | switch (field_nodes.len) { |
| 2622 | 2678 | 0 => {}, |
| 2623 | 1 => node.data = .{ .bin = .{ .lhs = field_nodes[0], .rhs = .none } }, | |
| 2624 | 2 => node.data = .{ .bin = .{ .lhs = field_nodes[0], .rhs = field_nodes[1] } }, | |
| 2679 | 1 => node.data = .{ .two = .{ field_nodes[0], .none } }, | |
| 2680 | 2 => node.data = .{ .two = .{ field_nodes[0], field_nodes[1] } }, | |
| 2625 | 2681 | else => { |
| 2626 | 2682 | node.tag = .enum_decl; |
| 2627 | 2683 | node.data = .{ .range = try p.addList(field_nodes) }; |
| ... | ... | @@ -2679,8 +2735,6 @@ const Enumerator = struct { |
| 2679 | 2735 | return; |
| 2680 | 2736 | } |
| 2681 | 2737 | if (try e.res.val.add(e.res.val, Value.one, e.res.ty, p.comp)) { |
| 2682 | const byte_size = e.res.ty.sizeof(p.comp).?; | |
| 2683 | const bit_size: u8 = @intCast(if (e.res.ty.isUnsignedInt(p.comp)) byte_size * 8 else byte_size * 8 - 1); | |
| 2684 | 2738 | if (e.fixed) { |
| 2685 | 2739 | try p.errStr(.enum_not_representable_fixed, tok, try p.typeStr(e.res.ty)); |
| 2686 | 2740 | return; |
| ... | ... | @@ -2689,6 +2743,8 @@ const Enumerator = struct { |
| 2689 | 2743 | try p.errTok(.enumerator_overflow, tok); |
| 2690 | 2744 | break :blk larger; |
| 2691 | 2745 | } else blk: { |
| 2746 | const signed = !e.res.ty.isUnsignedInt(p.comp); | |
| 2747 | const bit_size: u8 = @intCast(e.res.ty.bitSizeof(p.comp).? - @intFromBool(signed)); | |
| 2692 | 2748 | try p.errExtra(.enum_not_representable, tok, .{ .pow_2_as_string = bit_size }); |
| 2693 | 2749 | break :blk Type{ .specifier = .ulong_long }; |
| 2694 | 2750 | }; |
| ... | ... | @@ -2792,14 +2848,12 @@ fn enumerator(p: *Parser, e: *Enumerator) Error!?EnumFieldAndNode { |
| 2792 | 2848 | if (err_start == p.comp.diagnostics.list.items.len) { |
| 2793 | 2849 | // only do these warnings if we didn't already warn about overflow or non-representable values |
| 2794 | 2850 | if (e.res.val.compare(.lt, Value.zero, p.comp)) { |
| 2795 | const min_int = (Type{ .specifier = .int }).minInt(p.comp); | |
| 2796 | const min_val = try Value.int(min_int, p.comp); | |
| 2851 | const min_val = try Value.minInt(Type.int, p.comp); | |
| 2797 | 2852 | if (e.res.val.compare(.lt, min_val, p.comp)) { |
| 2798 | 2853 | try p.errStr(.enumerator_too_small, name_tok, try e.res.str(p)); |
| 2799 | 2854 | } |
| 2800 | 2855 | } else { |
| 2801 | const max_int = (Type{ .specifier = .int }).maxInt(p.comp); | |
| 2802 | const max_val = try Value.int(max_int, p.comp); | |
| 2856 | const max_val = try Value.maxInt(Type.int, p.comp); | |
| 2803 | 2857 | if (e.res.val.compare(.gt, max_val, p.comp)) { |
| 2804 | 2858 | try p.errStr(.enumerator_too_large, name_tok, try e.res.str(p)); |
| 2805 | 2859 | } |
| ... | ... | @@ -2815,6 +2869,7 @@ fn enumerator(p: *Parser, e: *Enumerator) Error!?EnumFieldAndNode { |
| 2815 | 2869 | .name = name_tok, |
| 2816 | 2870 | .node = res.node, |
| 2817 | 2871 | } }, |
| 2872 | .loc = @enumFromInt(name_tok), | |
| 2818 | 2873 | }); |
| 2819 | 2874 | try p.value_map.put(node, e.res.val); |
| 2820 | 2875 | return EnumFieldAndNode{ .field = .{ |
| ... | ... | @@ -2991,15 +3046,12 @@ fn directDeclarator(p: *Parser, base_type: Type, d: *Declarator, kind: Declarato |
| 2991 | 3046 | } |
| 2992 | 3047 | |
| 2993 | 3048 | const outer = try p.directDeclarator(base_type, d, kind); |
| 2994 | var max_bits = p.comp.target.ptrBitWidth(); | |
| 2995 | if (max_bits > 61) max_bits = 61; | |
| 2996 | const max_bytes = (@as(u64, 1) << @truncate(max_bits)) - 1; | |
| 2997 | 3049 | |
| 2998 | 3050 | if (!size.ty.isInt()) { |
| 2999 | 3051 | try p.errStr(.array_size_non_int, size_tok, try p.typeStr(size.ty)); |
| 3000 | 3052 | return error.ParsingFailed; |
| 3001 | 3053 | } |
| 3002 | if (base_type.is(.c23_auto)) { | |
| 3054 | if (base_type.is(.c23_auto) or outer.is(.invalid)) { | |
| 3003 | 3055 | // issue error later |
| 3004 | 3056 | return Type.invalid; |
| 3005 | 3057 | } else if (size.val.opt_ref == .none) { |
| ... | ... | @@ -3030,7 +3082,7 @@ fn directDeclarator(p: *Parser, base_type: Type, d: *Declarator, kind: Declarato |
| 3030 | 3082 | } else { |
| 3031 | 3083 | // `outer` is validated later so it may be invalid here |
| 3032 | 3084 | const outer_size = outer.sizeof(p.comp); |
| 3033 | const max_elems = max_bytes / @max(1, outer_size orelse 1); | |
| 3085 | const max_elems = p.comp.maxArrayBytes() / @max(1, outer_size orelse 1); | |
| 3034 | 3086 | |
| 3035 | 3087 | var size_val = size.val; |
| 3036 | 3088 | if (size_val.isZero(p.comp)) { |
| ... | ... | @@ -3047,7 +3099,7 @@ fn directDeclarator(p: *Parser, base_type: Type, d: *Declarator, kind: Declarato |
| 3047 | 3099 | arr_ty.len = max_elems; |
| 3048 | 3100 | } |
| 3049 | 3101 | res_ty.data = .{ .array = arr_ty }; |
| 3050 | res_ty.specifier = .array; | |
| 3102 | res_ty.specifier = if (static != null) .static_array else .array; | |
| 3051 | 3103 | } |
| 3052 | 3104 | |
| 3053 | 3105 | try res_ty.combine(outer); |
| ... | ... | @@ -3120,12 +3172,14 @@ fn directDeclarator(p: *Parser, base_type: Type, d: *Declarator, kind: Declarato |
| 3120 | 3172 | fn pointer(p: *Parser, base_ty: Type) Error!Type { |
| 3121 | 3173 | var ty = base_ty; |
| 3122 | 3174 | while (p.eatToken(.asterisk)) |_| { |
| 3123 | const elem_ty = try p.arena.create(Type); | |
| 3124 | elem_ty.* = ty; | |
| 3125 | ty = Type{ | |
| 3126 | .specifier = .pointer, | |
| 3127 | .data = .{ .sub_type = elem_ty }, | |
| 3128 | }; | |
| 3175 | if (!ty.is(.invalid)) { | |
| 3176 | const elem_ty = try p.arena.create(Type); | |
| 3177 | elem_ty.* = ty; | |
| 3178 | ty = Type{ | |
| 3179 | .specifier = .pointer, | |
| 3180 | .data = .{ .sub_type = elem_ty }, | |
| 3181 | }; | |
| 3182 | } | |
| 3129 | 3183 | var quals = Type.Qualifiers.Builder{}; |
| 3130 | 3184 | _ = try p.typeQual(&quals); |
| 3131 | 3185 | try quals.finish(p, &ty); |
| ... | ... | @@ -3237,6 +3291,75 @@ fn typeName(p: *Parser) Error!?Type { |
| 3237 | 3291 | return try Attribute.applyTypeAttributes(p, ty, attr_buf_top, .align_ignored); |
| 3238 | 3292 | } |
| 3239 | 3293 | |
| 3294 | fn complexInitializer(p: *Parser, init_ty: Type) Error!Result { | |
| 3295 | assert(p.tok_ids[p.tok_i] == .l_brace); | |
| 3296 | assert(init_ty.isComplex()); | |
| 3297 | ||
| 3298 | const real_ty = init_ty.makeReal(); | |
| 3299 | if (real_ty.isInt()) { | |
| 3300 | return p.todo("Complex integer initializers"); | |
| 3301 | } | |
| 3302 | const l_brace = p.tok_i; | |
| 3303 | p.tok_i += 1; | |
| 3304 | try p.errTok(.complex_component_init, l_brace); | |
| 3305 | ||
| 3306 | const first_tok = p.tok_i; | |
| 3307 | var first = try p.assignExpr(); | |
| 3308 | try first.expect(p); | |
| 3309 | try p.coerceInit(&first, first_tok, real_ty); | |
| 3310 | ||
| 3311 | var second: Result = .{ | |
| 3312 | .ty = real_ty, | |
| 3313 | .val = Value.zero, | |
| 3314 | }; | |
| 3315 | if (p.eatToken(.comma)) |_| { | |
| 3316 | const second_tok = p.tok_i; | |
| 3317 | const maybe_second = try p.assignExpr(); | |
| 3318 | if (!maybe_second.empty(p)) { | |
| 3319 | second = maybe_second; | |
| 3320 | try p.coerceInit(&second, second_tok, real_ty); | |
| 3321 | } | |
| 3322 | } | |
| 3323 | ||
| 3324 | // Eat excess initializers | |
| 3325 | var extra_tok: ?TokenIndex = null; | |
| 3326 | while (p.eatToken(.comma)) |_| { | |
| 3327 | if (p.tok_ids[p.tok_i] == .r_brace) break; | |
| 3328 | extra_tok = p.tok_i; | |
| 3329 | const extra = try p.assignExpr(); | |
| 3330 | if (extra.empty(p)) { | |
| 3331 | try p.errTok(.expected_expr, p.tok_i); | |
| 3332 | p.skipTo(.r_brace); | |
| 3333 | return error.ParsingFailed; | |
| 3334 | } | |
| 3335 | } | |
| 3336 | try p.expectClosing(l_brace, .r_brace); | |
| 3337 | if (extra_tok) |tok| { | |
| 3338 | try p.errTok(.excess_scalar_init, tok); | |
| 3339 | } | |
| 3340 | ||
| 3341 | const arr_init_node: Tree.Node = .{ | |
| 3342 | .tag = .array_init_expr_two, | |
| 3343 | .ty = init_ty, | |
| 3344 | .data = .{ .two = .{ first.node, second.node } }, | |
| 3345 | .loc = @enumFromInt(l_brace), | |
| 3346 | }; | |
| 3347 | var res: Result = .{ | |
| 3348 | .node = try p.addNode(arr_init_node), | |
| 3349 | .ty = init_ty, | |
| 3350 | }; | |
| 3351 | if (first.val.opt_ref != .none and second.val.opt_ref != .none) { | |
| 3352 | res.val = try Value.intern(p.comp, switch (real_ty.bitSizeof(p.comp).?) { | |
| 3353 | 32 => .{ .complex = .{ .cf32 = .{ first.val.toFloat(f32, p.comp), second.val.toFloat(f32, p.comp) } } }, | |
| 3354 | 64 => .{ .complex = .{ .cf64 = .{ first.val.toFloat(f64, p.comp), second.val.toFloat(f64, p.comp) } } }, | |
| 3355 | 80 => .{ .complex = .{ .cf80 = .{ first.val.toFloat(f80, p.comp), second.val.toFloat(f80, p.comp) } } }, | |
| 3356 | 128 => .{ .complex = .{ .cf128 = .{ first.val.toFloat(f128, p.comp), second.val.toFloat(f128, p.comp) } } }, | |
| 3357 | else => unreachable, | |
| 3358 | }); | |
| 3359 | } | |
| 3360 | return res; | |
| 3361 | } | |
| 3362 | ||
| 3240 | 3363 | /// initializer |
| 3241 | 3364 | /// : assignExpr |
| 3242 | 3365 | /// | '{' initializerItems '}' |
| ... | ... | @@ -3255,6 +3378,9 @@ fn initializer(p: *Parser, init_ty: Type) Error!Result { |
| 3255 | 3378 | return error.ParsingFailed; |
| 3256 | 3379 | } |
| 3257 | 3380 | |
| 3381 | if (init_ty.isComplex()) { | |
| 3382 | return p.complexInitializer(init_ty); | |
| 3383 | } | |
| 3258 | 3384 | var il: InitList = .{}; |
| 3259 | 3385 | defer il.deinit(p.gpa); |
| 3260 | 3386 | |
| ... | ... | @@ -3754,9 +3880,15 @@ fn convertInitList(p: *Parser, il: InitList, init_ty: Type) Error!NodeIndex { |
| 3754 | 3880 | var arr_init_node: Tree.Node = .{ |
| 3755 | 3881 | .tag = .array_init_expr_two, |
| 3756 | 3882 | .ty = init_ty, |
| 3757 | .data = .{ .bin = .{ .lhs = .none, .rhs = .none } }, | |
| 3883 | .data = .{ .two = .{ .none, .none } }, | |
| 3758 | 3884 | }; |
| 3759 | 3885 | |
| 3886 | const max_elems = p.comp.maxArrayBytes() / (@max(1, elem_ty.sizeof(p.comp) orelse 1)); | |
| 3887 | if (start > max_elems) { | |
| 3888 | try p.errTok(.array_too_large, il.tok); | |
| 3889 | start = max_elems; | |
| 3890 | } | |
| 3891 | ||
| 3760 | 3892 | if (init_ty.specifier == .incomplete_array) { |
| 3761 | 3893 | arr_init_node.ty.specifier = .array; |
| 3762 | 3894 | arr_init_node.ty.data.array.len = start; |
| ... | ... | @@ -3767,8 +3899,6 @@ fn convertInitList(p: *Parser, il: InitList, init_ty: Type) Error!NodeIndex { |
| 3767 | 3899 | .specifier = .array, |
| 3768 | 3900 | .data = .{ .array = arr_ty }, |
| 3769 | 3901 | }; |
| 3770 | const attrs = init_ty.getAttributes(); | |
| 3771 | arr_init_node.ty = try arr_init_node.ty.withAttributes(p.arena, attrs); | |
| 3772 | 3902 | } else if (start < max_items) { |
| 3773 | 3903 | const elem = try p.addNode(.{ |
| 3774 | 3904 | .tag = .array_filler_expr, |
| ... | ... | @@ -3781,8 +3911,8 @@ fn convertInitList(p: *Parser, il: InitList, init_ty: Type) Error!NodeIndex { |
| 3781 | 3911 | const items = p.list_buf.items[list_buf_top..]; |
| 3782 | 3912 | switch (items.len) { |
| 3783 | 3913 | 0 => {}, |
| 3784 | 1 => arr_init_node.data.bin.lhs = items[0], | |
| 3785 | 2 => arr_init_node.data.bin = .{ .lhs = items[0], .rhs = items[1] }, | |
| 3914 | 1 => arr_init_node.data.two[0] = items[0], | |
| 3915 | 2 => arr_init_node.data.two = .{ items[0], items[1] }, | |
| 3786 | 3916 | else => { |
| 3787 | 3917 | arr_init_node.tag = .array_init_expr; |
| 3788 | 3918 | arr_init_node.data = .{ .range = try p.addList(items) }; |
| ... | ... | @@ -3813,13 +3943,13 @@ fn convertInitList(p: *Parser, il: InitList, init_ty: Type) Error!NodeIndex { |
| 3813 | 3943 | var struct_init_node: Tree.Node = .{ |
| 3814 | 3944 | .tag = .struct_init_expr_two, |
| 3815 | 3945 | .ty = init_ty, |
| 3816 | .data = .{ .bin = .{ .lhs = .none, .rhs = .none } }, | |
| 3946 | .data = .{ .two = .{ .none, .none } }, | |
| 3817 | 3947 | }; |
| 3818 | 3948 | const items = p.list_buf.items[list_buf_top..]; |
| 3819 | 3949 | switch (items.len) { |
| 3820 | 3950 | 0 => {}, |
| 3821 | 1 => struct_init_node.data.bin.lhs = items[0], | |
| 3822 | 2 => struct_init_node.data.bin = .{ .lhs = items[0], .rhs = items[1] }, | |
| 3951 | 1 => struct_init_node.data.two[0] = items[0], | |
| 3952 | 2 => struct_init_node.data.two = .{ items[0], items[1] }, | |
| 3823 | 3953 | else => { |
| 3824 | 3954 | struct_init_node.tag = .struct_init_expr; |
| 3825 | 3955 | struct_init_node.data = .{ .range = try p.addList(items) }; |
| ... | ... | @@ -3894,7 +4024,7 @@ fn asmOperand(p: *Parser, names: *std.ArrayList(?TokenIndex), constraints: *Node |
| 3894 | 4024 | /// | asmStr ':' asmOperand* ':' asmOperand* |
| 3895 | 4025 | /// | asmStr ':' asmOperand* ':' asmOperand* : asmStr? (',' asmStr)* |
| 3896 | 4026 | /// | asmStr ':' asmOperand* ':' asmOperand* : asmStr? (',' asmStr)* : IDENTIFIER (',' IDENTIFIER)* |
| 3897 | fn gnuAsmStmt(p: *Parser, quals: Tree.GNUAssemblyQualifiers, l_paren: TokenIndex) Error!NodeIndex { | |
| 4027 | fn gnuAsmStmt(p: *Parser, quals: Tree.GNUAssemblyQualifiers, asm_tok: TokenIndex, l_paren: TokenIndex) Error!NodeIndex { | |
| 3898 | 4028 | const asm_str = try p.asmStr(); |
| 3899 | 4029 | try p.checkAsmStr(asm_str.val, l_paren); |
| 3900 | 4030 | |
| ... | ... | @@ -3903,6 +4033,7 @@ fn gnuAsmStmt(p: *Parser, quals: Tree.GNUAssemblyQualifiers, l_paren: TokenIndex |
| 3903 | 4033 | .tag = .gnu_asm_simple, |
| 3904 | 4034 | .ty = .{ .specifier = .void }, |
| 3905 | 4035 | .data = .{ .un = asm_str.node }, |
| 4036 | .loc = @enumFromInt(asm_tok), | |
| 3906 | 4037 | }); |
| 3907 | 4038 | } |
| 3908 | 4039 | |
| ... | ... | @@ -4007,6 +4138,7 @@ fn gnuAsmStmt(p: *Parser, quals: Tree.GNUAssemblyQualifiers, l_paren: TokenIndex |
| 4007 | 4138 | .tag = .addr_of_label, |
| 4008 | 4139 | .data = .{ .decl_ref = label }, |
| 4009 | 4140 | .ty = result_ty, |
| 4141 | .loc = @enumFromInt(ident), | |
| 4010 | 4142 | }); |
| 4011 | 4143 | try exprs.append(label_addr_node); |
| 4012 | 4144 | |
| ... | ... | @@ -4088,9 +4220,10 @@ fn assembly(p: *Parser, kind: enum { global, decl_label, stmt }) Error!?NodeInde |
| 4088 | 4220 | .tag = .file_scope_asm, |
| 4089 | 4221 | .ty = .{ .specifier = .void }, |
| 4090 | 4222 | .data = .{ .decl = .{ .name = asm_tok, .node = asm_str.node } }, |
| 4223 | .loc = @enumFromInt(asm_tok), | |
| 4091 | 4224 | }); |
| 4092 | 4225 | }, |
| 4093 | .stmt => result_node = try p.gnuAsmStmt(quals, l_paren), | |
| 4226 | .stmt => result_node = try p.gnuAsmStmt(quals, asm_tok, l_paren), | |
| 4094 | 4227 | } |
| 4095 | 4228 | try p.expectClosing(l_paren, .r_paren); |
| 4096 | 4229 | |
| ... | ... | @@ -4141,7 +4274,7 @@ fn asmStr(p: *Parser) Error!Result { |
| 4141 | 4274 | fn stmt(p: *Parser) Error!NodeIndex { |
| 4142 | 4275 | if (try p.labeledStmt()) |some| return some; |
| 4143 | 4276 | if (try p.compoundStmt(false, null)) |some| return some; |
| 4144 | if (p.eatToken(.keyword_if)) |_| { | |
| 4277 | if (p.eatToken(.keyword_if)) |kw_if| { | |
| 4145 | 4278 | const l_paren = try p.expectToken(.l_paren); |
| 4146 | 4279 | const cond_tok = p.tok_i; |
| 4147 | 4280 | var cond = try p.expr(); |
| ... | ... | @@ -4160,14 +4293,16 @@ fn stmt(p: *Parser) Error!NodeIndex { |
| 4160 | 4293 | return try p.addNode(.{ |
| 4161 | 4294 | .tag = .if_then_else_stmt, |
| 4162 | 4295 | .data = .{ .if3 = .{ .cond = cond.node, .body = (try p.addList(&.{ then, @"else" })).start } }, |
| 4296 | .loc = @enumFromInt(kw_if), | |
| 4163 | 4297 | }) |
| 4164 | 4298 | else |
| 4165 | 4299 | return try p.addNode(.{ |
| 4166 | 4300 | .tag = .if_then_stmt, |
| 4167 | 4301 | .data = .{ .bin = .{ .lhs = cond.node, .rhs = then } }, |
| 4302 | .loc = @enumFromInt(kw_if), | |
| 4168 | 4303 | }); |
| 4169 | 4304 | } |
| 4170 | if (p.eatToken(.keyword_switch)) |_| { | |
| 4305 | if (p.eatToken(.keyword_switch)) |kw_switch| { | |
| 4171 | 4306 | const l_paren = try p.expectToken(.l_paren); |
| 4172 | 4307 | const cond_tok = p.tok_i; |
| 4173 | 4308 | var cond = try p.expr(); |
| ... | ... | @@ -4197,9 +4332,10 @@ fn stmt(p: *Parser) Error!NodeIndex { |
| 4197 | 4332 | return try p.addNode(.{ |
| 4198 | 4333 | .tag = .switch_stmt, |
| 4199 | 4334 | .data = .{ .bin = .{ .lhs = cond.node, .rhs = body } }, |
| 4335 | .loc = @enumFromInt(kw_switch), | |
| 4200 | 4336 | }); |
| 4201 | 4337 | } |
| 4202 | if (p.eatToken(.keyword_while)) |_| { | |
| 4338 | if (p.eatToken(.keyword_while)) |kw_while| { | |
| 4203 | 4339 | const l_paren = try p.expectToken(.l_paren); |
| 4204 | 4340 | const cond_tok = p.tok_i; |
| 4205 | 4341 | var cond = try p.expr(); |
| ... | ... | @@ -4221,9 +4357,10 @@ fn stmt(p: *Parser) Error!NodeIndex { |
| 4221 | 4357 | return try p.addNode(.{ |
| 4222 | 4358 | .tag = .while_stmt, |
| 4223 | 4359 | .data = .{ .bin = .{ .lhs = cond.node, .rhs = body } }, |
| 4360 | .loc = @enumFromInt(kw_while), | |
| 4224 | 4361 | }); |
| 4225 | 4362 | } |
| 4226 | if (p.eatToken(.keyword_do)) |_| { | |
| 4363 | if (p.eatToken(.keyword_do)) |kw_do| { | |
| 4227 | 4364 | const body = body: { |
| 4228 | 4365 | const old_loop = p.in_loop; |
| 4229 | 4366 | p.in_loop = true; |
| ... | ... | @@ -4248,9 +4385,10 @@ fn stmt(p: *Parser) Error!NodeIndex { |
| 4248 | 4385 | return try p.addNode(.{ |
| 4249 | 4386 | .tag = .do_while_stmt, |
| 4250 | 4387 | .data = .{ .bin = .{ .lhs = cond.node, .rhs = body } }, |
| 4388 | .loc = @enumFromInt(kw_do), | |
| 4251 | 4389 | }); |
| 4252 | 4390 | } |
| 4253 | if (p.eatToken(.keyword_for)) |_| { | |
| 4391 | if (p.eatToken(.keyword_for)) |kw_for| { | |
| 4254 | 4392 | try p.syms.pushScope(p); |
| 4255 | 4393 | defer p.syms.popScope(); |
| 4256 | 4394 | const decl_buf_top = p.decl_buf.items.len; |
| ... | ... | @@ -4301,16 +4439,22 @@ fn stmt(p: *Parser) Error!NodeIndex { |
| 4301 | 4439 | return try p.addNode(.{ |
| 4302 | 4440 | .tag = .for_decl_stmt, |
| 4303 | 4441 | .data = .{ .range = .{ .start = start, .end = end } }, |
| 4442 | .loc = @enumFromInt(kw_for), | |
| 4304 | 4443 | }); |
| 4305 | 4444 | } else if (init.node == .none and cond.node == .none and incr.node == .none) { |
| 4306 | 4445 | return try p.addNode(.{ |
| 4307 | 4446 | .tag = .forever_stmt, |
| 4308 | 4447 | .data = .{ .un = body }, |
| 4448 | .loc = @enumFromInt(kw_for), | |
| 4309 | 4449 | }); |
| 4310 | } else return try p.addNode(.{ .tag = .for_stmt, .data = .{ .if3 = .{ | |
| 4311 | .cond = body, | |
| 4312 | .body = (try p.addList(&.{ init.node, cond.node, incr.node })).start, | |
| 4313 | } } }); | |
| 4450 | } else return try p.addNode(.{ | |
| 4451 | .tag = .for_stmt, | |
| 4452 | .data = .{ .if3 = .{ | |
| 4453 | .cond = body, | |
| 4454 | .body = (try p.addList(&.{ init.node, cond.node, incr.node })).start, | |
| 4455 | } }, | |
| 4456 | .loc = @enumFromInt(kw_for), | |
| 4457 | }); | |
| 4314 | 4458 | } |
| 4315 | 4459 | if (p.eatToken(.keyword_goto)) |goto_tok| { |
| 4316 | 4460 | if (p.eatToken(.asterisk)) |_| { |
| ... | ... | @@ -4338,7 +4482,7 @@ fn stmt(p: *Parser) Error!NodeIndex { |
| 4338 | 4482 | } |
| 4339 | 4483 | } |
| 4340 | 4484 | |
| 4341 | try e.un(p, .computed_goto_stmt); | |
| 4485 | try e.un(p, .computed_goto_stmt, goto_tok); | |
| 4342 | 4486 | _ = try p.expectToken(.semicolon); |
| 4343 | 4487 | return e.node; |
| 4344 | 4488 | } |
| ... | ... | @@ -4351,17 +4495,18 @@ fn stmt(p: *Parser) Error!NodeIndex { |
| 4351 | 4495 | return try p.addNode(.{ |
| 4352 | 4496 | .tag = .goto_stmt, |
| 4353 | 4497 | .data = .{ .decl_ref = name_tok }, |
| 4498 | .loc = @enumFromInt(goto_tok), | |
| 4354 | 4499 | }); |
| 4355 | 4500 | } |
| 4356 | 4501 | if (p.eatToken(.keyword_continue)) |cont| { |
| 4357 | 4502 | if (!p.in_loop) try p.errTok(.continue_not_in_loop, cont); |
| 4358 | 4503 | _ = try p.expectToken(.semicolon); |
| 4359 | return try p.addNode(.{ .tag = .continue_stmt, .data = undefined }); | |
| 4504 | return try p.addNode(.{ .tag = .continue_stmt, .data = undefined, .loc = @enumFromInt(cont) }); | |
| 4360 | 4505 | } |
| 4361 | 4506 | if (p.eatToken(.keyword_break)) |br| { |
| 4362 | 4507 | if (!p.in_loop and p.@"switch" == null) try p.errTok(.break_not_in_loop_or_switch, br); |
| 4363 | 4508 | _ = try p.expectToken(.semicolon); |
| 4364 | return try p.addNode(.{ .tag = .break_stmt, .data = undefined }); | |
| 4509 | return try p.addNode(.{ .tag = .break_stmt, .data = undefined, .loc = @enumFromInt(br) }); | |
| 4365 | 4510 | } |
| 4366 | 4511 | if (try p.returnStmt()) |some| return some; |
| 4367 | 4512 | if (try p.assembly(.stmt)) |some| return some; |
| ... | ... | @@ -4380,8 +4525,8 @@ fn stmt(p: *Parser) Error!NodeIndex { |
| 4380 | 4525 | defer p.attr_buf.len = attr_buf_top; |
| 4381 | 4526 | try p.attributeSpecifier(); |
| 4382 | 4527 | |
| 4383 | if (p.eatToken(.semicolon)) |_| { | |
| 4384 | var null_node: Tree.Node = .{ .tag = .null_stmt, .data = undefined }; | |
| 4528 | if (p.eatToken(.semicolon)) |semicolon| { | |
| 4529 | var null_node: Tree.Node = .{ .tag = .null_stmt, .data = undefined, .loc = @enumFromInt(semicolon) }; | |
| 4385 | 4530 | null_node.ty = try Attribute.applyStatementAttributes(p, null_node.ty, expr_start, attr_buf_top); |
| 4386 | 4531 | return p.addNode(null_node); |
| 4387 | 4532 | } |
| ... | ... | @@ -4422,6 +4567,7 @@ fn labeledStmt(p: *Parser) Error!?NodeIndex { |
| 4422 | 4567 | var labeled_stmt = Tree.Node{ |
| 4423 | 4568 | .tag = .labeled_stmt, |
| 4424 | 4569 | .data = .{ .decl = .{ .name = name_tok, .node = try p.labelableStmt() } }, |
| 4570 | .loc = @enumFromInt(name_tok), | |
| 4425 | 4571 | }; |
| 4426 | 4572 | labeled_stmt.ty = try Attribute.applyLabelAttributes(p, labeled_stmt.ty, attr_buf_top); |
| 4427 | 4573 | return try p.addNode(labeled_stmt); |
| ... | ... | @@ -4464,9 +4610,11 @@ fn labeledStmt(p: *Parser) Error!?NodeIndex { |
| 4464 | 4610 | if (second_item) |some| return try p.addNode(.{ |
| 4465 | 4611 | .tag = .case_range_stmt, |
| 4466 | 4612 | .data = .{ .if3 = .{ .cond = s, .body = (try p.addList(&.{ first_item.node, some.node })).start } }, |
| 4613 | .loc = @enumFromInt(case), | |
| 4467 | 4614 | }) else return try p.addNode(.{ |
| 4468 | 4615 | .tag = .case_stmt, |
| 4469 | 4616 | .data = .{ .bin = .{ .lhs = first_item.node, .rhs = s } }, |
| 4617 | .loc = @enumFromInt(case), | |
| 4470 | 4618 | }); |
| 4471 | 4619 | } else if (p.eatToken(.keyword_default)) |default| { |
| 4472 | 4620 | _ = try p.expectToken(.colon); |
| ... | ... | @@ -4474,6 +4622,7 @@ fn labeledStmt(p: *Parser) Error!?NodeIndex { |
| 4474 | 4622 | const node = try p.addNode(.{ |
| 4475 | 4623 | .tag = .default_stmt, |
| 4476 | 4624 | .data = .{ .un = s }, |
| 4625 | .loc = @enumFromInt(default), | |
| 4477 | 4626 | }); |
| 4478 | 4627 | const @"switch" = p.@"switch" orelse { |
| 4479 | 4628 | try p.errStr(.case_not_in_switch, default, "default"); |
| ... | ... | @@ -4492,7 +4641,7 @@ fn labeledStmt(p: *Parser) Error!?NodeIndex { |
| 4492 | 4641 | fn labelableStmt(p: *Parser) Error!NodeIndex { |
| 4493 | 4642 | if (p.tok_ids[p.tok_i] == .r_brace) { |
| 4494 | 4643 | try p.err(.label_compound_end); |
| 4495 | return p.addNode(.{ .tag = .null_stmt, .data = undefined }); | |
| 4644 | return p.addNode(.{ .tag = .null_stmt, .data = undefined, .loc = @enumFromInt(p.tok_i) }); | |
| 4496 | 4645 | } |
| 4497 | 4646 | return p.stmt(); |
| 4498 | 4647 | } |
| ... | ... | @@ -4557,6 +4706,7 @@ fn compoundStmt(p: *Parser, is_fn_body: bool, stmt_expr_state: ?*StmtExprState) |
| 4557 | 4706 | else => {}, |
| 4558 | 4707 | } |
| 4559 | 4708 | } |
| 4709 | const r_brace = p.tok_i - 1; | |
| 4560 | 4710 | |
| 4561 | 4711 | if (noreturn_index) |some| { |
| 4562 | 4712 | // 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) |
| 4580 | 4730 | try p.errStr(.func_does_not_return, p.tok_i - 1, func_name); |
| 4581 | 4731 | } |
| 4582 | 4732 | } |
| 4583 | try p.decl_buf.append(try p.addNode(.{ .tag = .implicit_return, .ty = p.func.ty.?.returnType(), .data = .{ .return_zero = return_zero } })); | |
| 4733 | 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) })); | |
| 4584 | 4734 | } |
| 4585 | 4735 | if (p.func.ident) |some| try p.decl_buf.insert(decl_buf_top, some.node); |
| 4586 | 4736 | 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) |
| 4588 | 4738 | |
| 4589 | 4739 | var node: Tree.Node = .{ |
| 4590 | 4740 | .tag = .compound_stmt_two, |
| 4591 | .data = .{ .bin = .{ .lhs = .none, .rhs = .none } }, | |
| 4741 | .data = .{ .two = .{ .none, .none } }, | |
| 4742 | .loc = @enumFromInt(l_brace), | |
| 4592 | 4743 | }; |
| 4593 | 4744 | const statements = p.decl_buf.items[decl_buf_top..]; |
| 4594 | 4745 | switch (statements.len) { |
| 4595 | 4746 | 0 => {}, |
| 4596 | 1 => node.data = .{ .bin = .{ .lhs = statements[0], .rhs = .none } }, | |
| 4597 | 2 => node.data = .{ .bin = .{ .lhs = statements[0], .rhs = statements[1] } }, | |
| 4747 | 1 => node.data = .{ .two = .{ statements[0], .none } }, | |
| 4748 | 2 => node.data = .{ .two = .{ statements[0], statements[1] } }, | |
| 4598 | 4749 | else => { |
| 4599 | 4750 | node.tag = .compound_stmt; |
| 4600 | 4751 | node.data = .{ .range = try p.addList(statements) }; |
| ... | ... | @@ -4618,8 +4769,8 @@ fn nodeIsNoreturn(p: *Parser, node: NodeIndex) NoreturnKind { |
| 4618 | 4769 | }, |
| 4619 | 4770 | .compound_stmt_two => { |
| 4620 | 4771 | const data = p.nodes.items(.data)[@intFromEnum(node)]; |
| 4621 | const lhs_type = if (data.bin.lhs != .none) p.nodeIsNoreturn(data.bin.lhs) else .no; | |
| 4622 | const rhs_type = if (data.bin.rhs != .none) p.nodeIsNoreturn(data.bin.rhs) else .no; | |
| 4772 | const lhs_type = if (data.two[0] != .none) p.nodeIsNoreturn(data.two[0]) else .no; | |
| 4773 | const rhs_type = if (data.two[1] != .none) p.nodeIsNoreturn(data.two[1]) else .no; | |
| 4623 | 4774 | if (lhs_type == .complex or rhs_type == .complex) return .complex; |
| 4624 | 4775 | if (lhs_type == .yes or rhs_type == .yes) return .yes; |
| 4625 | 4776 | return .no; |
| ... | ... | @@ -4704,6 +4855,8 @@ fn nextStmt(p: *Parser, l_brace: TokenIndex) !void { |
| 4704 | 4855 | .keyword_int, |
| 4705 | 4856 | .keyword_long, |
| 4706 | 4857 | .keyword_signed, |
| 4858 | .keyword_signed1, | |
| 4859 | .keyword_signed2, | |
| 4707 | 4860 | .keyword_unsigned, |
| 4708 | 4861 | .keyword_float, |
| 4709 | 4862 | .keyword_double, |
| ... | ... | @@ -4743,17 +4896,17 @@ fn returnStmt(p: *Parser) Error!?NodeIndex { |
| 4743 | 4896 | |
| 4744 | 4897 | if (e.node == .none) { |
| 4745 | 4898 | if (!ret_ty.is(.void)) try p.errStr(.func_should_return, ret_tok, p.tokSlice(p.func.name)); |
| 4746 | return try p.addNode(.{ .tag = .return_stmt, .data = .{ .un = e.node } }); | |
| 4899 | return try p.addNode(.{ .tag = .return_stmt, .data = .{ .un = e.node }, .loc = @enumFromInt(ret_tok) }); | |
| 4747 | 4900 | } else if (ret_ty.is(.void)) { |
| 4748 | 4901 | try p.errStr(.void_func_returns_value, e_tok, p.tokSlice(p.func.name)); |
| 4749 | return try p.addNode(.{ .tag = .return_stmt, .data = .{ .un = e.node } }); | |
| 4902 | return try p.addNode(.{ .tag = .return_stmt, .data = .{ .un = e.node }, .loc = @enumFromInt(ret_tok) }); | |
| 4750 | 4903 | } |
| 4751 | 4904 | |
| 4752 | 4905 | try e.lvalConversion(p); |
| 4753 | 4906 | try e.coerce(p, ret_ty, e_tok, .ret); |
| 4754 | 4907 | |
| 4755 | 4908 | try e.saveValue(p); |
| 4756 | return try p.addNode(.{ .tag = .return_stmt, .data = .{ .un = e.node } }); | |
| 4909 | return try p.addNode(.{ .tag = .return_stmt, .data = .{ .un = e.node }, .loc = @enumFromInt(ret_tok) }); | |
| 4757 | 4910 | } |
| 4758 | 4911 | |
| 4759 | 4912 | // ====== expressions ====== |
| ... | ... | @@ -4802,7 +4955,6 @@ const CallExpr = union(enum) { |
| 4802 | 4955 | } |
| 4803 | 4956 | |
| 4804 | 4957 | fn shouldPromoteVarArg(self: CallExpr, arg_idx: u32) bool { |
| 4805 | @setEvalBranchQuota(2000); | |
| 4806 | 4958 | return switch (self) { |
| 4807 | 4959 | .standard => true, |
| 4808 | 4960 | .builtin => |builtin| switch (builtin.tag) { |
| ... | ... | @@ -4810,10 +4962,13 @@ const CallExpr = union(enum) { |
| 4810 | 4962 | Builtin.tagFromName("__va_start").?, |
| 4811 | 4963 | Builtin.tagFromName("va_start").?, |
| 4812 | 4964 | => arg_idx != 1, |
| 4813 | Builtin.tagFromName("__builtin_complex").?, | |
| 4814 | 4965 | Builtin.tagFromName("__builtin_add_overflow").?, |
| 4815 | Builtin.tagFromName("__builtin_sub_overflow").?, | |
| 4966 | Builtin.tagFromName("__builtin_complex").?, | |
| 4967 | Builtin.tagFromName("__builtin_isinf").?, | |
| 4968 | Builtin.tagFromName("__builtin_isinf_sign").?, | |
| 4816 | 4969 | Builtin.tagFromName("__builtin_mul_overflow").?, |
| 4970 | Builtin.tagFromName("__builtin_isnan").?, | |
| 4971 | Builtin.tagFromName("__builtin_sub_overflow").?, | |
| 4817 | 4972 | => false, |
| 4818 | 4973 | else => true, |
| 4819 | 4974 | }, |
| ... | ... | @@ -4827,7 +4982,6 @@ const CallExpr = union(enum) { |
| 4827 | 4982 | } |
| 4828 | 4983 | |
| 4829 | 4984 | fn checkVarArg(self: CallExpr, p: *Parser, first_after: TokenIndex, param_tok: TokenIndex, arg: *Result, arg_idx: u32) !void { |
| 4830 | @setEvalBranchQuota(10_000); | |
| 4831 | 4985 | if (self == .standard) return; |
| 4832 | 4986 | |
| 4833 | 4987 | const builtin_tok = p.nodes.items(.data)[@intFromEnum(self.builtin.node)].decl.name; |
| ... | ... | @@ -4852,13 +5006,15 @@ const CallExpr = union(enum) { |
| 4852 | 5006 | /// of arguments, `paramCountOverride` is used to tell us how many arguments we should actually expect to see for |
| 4853 | 5007 | /// these custom-typechecked functions. |
| 4854 | 5008 | fn paramCountOverride(self: CallExpr) ?u32 { |
| 4855 | @setEvalBranchQuota(10_000); | |
| 4856 | 5009 | return switch (self) { |
| 4857 | 5010 | .standard => null, |
| 4858 | 5011 | .builtin => |builtin| switch (builtin.tag) { |
| 4859 | 5012 | Builtin.tagFromName("__c11_atomic_thread_fence").?, |
| 4860 | 5013 | Builtin.tagFromName("__c11_atomic_signal_fence").?, |
| 4861 | 5014 | Builtin.tagFromName("__c11_atomic_is_lock_free").?, |
| 5015 | Builtin.tagFromName("__builtin_isinf").?, | |
| 5016 | Builtin.tagFromName("__builtin_isinf_sign").?, | |
| 5017 | Builtin.tagFromName("__builtin_isnan").?, | |
| 4862 | 5018 | => 1, |
| 4863 | 5019 | |
| 4864 | 5020 | Builtin.tagFromName("__builtin_complex").?, |
| ... | ... | @@ -4903,7 +5059,6 @@ const CallExpr = union(enum) { |
| 4903 | 5059 | } |
| 4904 | 5060 | |
| 4905 | 5061 | fn returnType(self: CallExpr, p: *Parser, callable_ty: Type) Type { |
| 4906 | @setEvalBranchQuota(6000); | |
| 4907 | 5062 | return switch (self) { |
| 4908 | 5063 | .standard => callable_ty.returnType(), |
| 4909 | 5064 | .builtin => |builtin| switch (builtin.tag) { |
| ... | ... | @@ -4977,12 +5132,12 @@ const CallExpr = union(enum) { |
| 4977 | 5132 | var call_node: Tree.Node = .{ |
| 4978 | 5133 | .tag = .call_expr_one, |
| 4979 | 5134 | .ty = ret_ty, |
| 4980 | .data = .{ .bin = .{ .lhs = func_node, .rhs = .none } }, | |
| 5135 | .data = .{ .two = .{ func_node, .none } }, | |
| 4981 | 5136 | }; |
| 4982 | 5137 | const args = p.list_buf.items[list_buf_top..]; |
| 4983 | 5138 | switch (arg_count) { |
| 4984 | 5139 | 0 => {}, |
| 4985 | 1 => call_node.data.bin.rhs = args[1], // args[0] == func.node | |
| 5140 | 1 => call_node.data.two[1] = args[1], // args[0] == func.node | |
| 4986 | 5141 | else => { |
| 4987 | 5142 | call_node.tag = .call_expr; |
| 4988 | 5143 | call_node.data = .{ .range = try p.addList(args) }; |
| ... | ... | @@ -5005,7 +5160,8 @@ const CallExpr = union(enum) { |
| 5005 | 5160 | call_node.data = .{ .range = try p.addList(args) }; |
| 5006 | 5161 | }, |
| 5007 | 5162 | } |
| 5008 | return Result{ .node = builtin.node, .ty = ret_ty }; | |
| 5163 | const val = try evalBuiltin(builtin.tag, p, args[1..]); | |
| 5164 | return Result{ .node = builtin.node, .ty = ret_ty, .val = val }; | |
| 5009 | 5165 | }, |
| 5010 | 5166 | } |
| 5011 | 5167 | } |
| ... | ... | @@ -5016,6 +5172,8 @@ pub const Result = struct { |
| 5016 | 5172 | ty: Type = .{ .specifier = .int }, |
| 5017 | 5173 | val: Value = .{}, |
| 5018 | 5174 | |
| 5175 | const invalid: Result = .{ .ty = Type.invalid }; | |
| 5176 | ||
| 5019 | 5177 | pub fn str(res: Result, p: *Parser) ![]const u8 { |
| 5020 | 5178 | switch (res.val.opt_ref) { |
| 5021 | 5179 | .none => return "(none)", |
| ... | ... | @@ -5073,30 +5231,21 @@ pub const Result = struct { |
| 5073 | 5231 | .post_inc_expr, |
| 5074 | 5232 | .post_dec_expr, |
| 5075 | 5233 | => return, |
| 5076 | .call_expr_one => { | |
| 5077 | const fn_ptr = p.nodes.items(.data)[@intFromEnum(cur_node)].bin.lhs; | |
| 5078 | const fn_ty = p.nodes.items(.ty)[@intFromEnum(fn_ptr)].elemType(); | |
| 5079 | const cast_info = p.nodes.items(.data)[@intFromEnum(fn_ptr)].cast.operand; | |
| 5080 | const decl_ref = p.nodes.items(.data)[@intFromEnum(cast_info)].decl_ref; | |
| 5081 | if (fn_ty.hasAttribute(.nodiscard)) try p.errStr(.nodiscard_unused, expr_start, p.tokSlice(decl_ref)); | |
| 5082 | if (fn_ty.hasAttribute(.warn_unused_result)) try p.errStr(.warn_unused_result, expr_start, p.tokSlice(decl_ref)); | |
| 5083 | return; | |
| 5084 | }, | |
| 5085 | .call_expr => { | |
| 5086 | const fn_ptr = p.data.items[p.nodes.items(.data)[@intFromEnum(cur_node)].range.start]; | |
| 5087 | const fn_ty = p.nodes.items(.ty)[@intFromEnum(fn_ptr)].elemType(); | |
| 5088 | const cast_info = p.nodes.items(.data)[@intFromEnum(fn_ptr)].cast.operand; | |
| 5089 | const decl_ref = p.nodes.items(.data)[@intFromEnum(cast_info)].decl_ref; | |
| 5090 | if (fn_ty.hasAttribute(.nodiscard)) try p.errStr(.nodiscard_unused, expr_start, p.tokSlice(decl_ref)); | |
| 5091 | if (fn_ty.hasAttribute(.warn_unused_result)) try p.errStr(.warn_unused_result, expr_start, p.tokSlice(decl_ref)); | |
| 5234 | .call_expr, .call_expr_one => { | |
| 5235 | const tmp_tree = p.tmpTree(); | |
| 5236 | const child_nodes = tmp_tree.childNodes(cur_node); | |
| 5237 | const fn_ptr = child_nodes[0]; | |
| 5238 | const call_info = tmp_tree.callableResultUsage(fn_ptr) orelse return; | |
| 5239 | if (call_info.nodiscard) try p.errStr(.nodiscard_unused, expr_start, p.tokSlice(call_info.tok)); | |
| 5240 | if (call_info.warn_unused_result) try p.errStr(.warn_unused_result, expr_start, p.tokSlice(call_info.tok)); | |
| 5092 | 5241 | return; |
| 5093 | 5242 | }, |
| 5094 | 5243 | .stmt_expr => { |
| 5095 | 5244 | const body = p.nodes.items(.data)[@intFromEnum(cur_node)].un; |
| 5096 | 5245 | switch (p.nodes.items(.tag)[@intFromEnum(body)]) { |
| 5097 | 5246 | .compound_stmt_two => { |
| 5098 | const body_stmt = p.nodes.items(.data)[@intFromEnum(body)].bin; | |
| 5099 | cur_node = if (body_stmt.rhs != .none) body_stmt.rhs else body_stmt.lhs; | |
| 5247 | const body_stmt = p.nodes.items(.data)[@intFromEnum(body)].two; | |
| 5248 | cur_node = if (body_stmt[1] != .none) body_stmt[1] else body_stmt[0]; | |
| 5100 | 5249 | }, |
| 5101 | 5250 | .compound_stmt => { |
| 5102 | 5251 | const data = p.nodes.items(.data)[@intFromEnum(body)]; |
| ... | ... | @@ -5112,29 +5261,31 @@ pub const Result = struct { |
| 5112 | 5261 | try p.errTok(.unused_value, expr_start); |
| 5113 | 5262 | } |
| 5114 | 5263 | |
| 5115 | fn boolRes(lhs: *Result, p: *Parser, tag: Tree.Tag, rhs: Result) !void { | |
| 5264 | fn boolRes(lhs: *Result, p: *Parser, tag: Tree.Tag, rhs: Result, tok_i: TokenIndex) !void { | |
| 5116 | 5265 | if (lhs.val.opt_ref == .null) { |
| 5117 | 5266 | lhs.val = Value.zero; |
| 5118 | 5267 | } |
| 5119 | 5268 | if (lhs.ty.specifier != .invalid) { |
| 5120 | 5269 | lhs.ty = Type.int; |
| 5121 | 5270 | } |
| 5122 | return lhs.bin(p, tag, rhs); | |
| 5271 | return lhs.bin(p, tag, rhs, tok_i); | |
| 5123 | 5272 | } |
| 5124 | 5273 | |
| 5125 | fn bin(lhs: *Result, p: *Parser, tag: Tree.Tag, rhs: Result) !void { | |
| 5274 | fn bin(lhs: *Result, p: *Parser, tag: Tree.Tag, rhs: Result, tok_i: TokenIndex) !void { | |
| 5126 | 5275 | lhs.node = try p.addNode(.{ |
| 5127 | 5276 | .tag = tag, |
| 5128 | 5277 | .ty = lhs.ty, |
| 5129 | 5278 | .data = .{ .bin = .{ .lhs = lhs.node, .rhs = rhs.node } }, |
| 5279 | .loc = @enumFromInt(tok_i), | |
| 5130 | 5280 | }); |
| 5131 | 5281 | } |
| 5132 | 5282 | |
| 5133 | fn un(operand: *Result, p: *Parser, tag: Tree.Tag) Error!void { | |
| 5283 | fn un(operand: *Result, p: *Parser, tag: Tree.Tag, tok_i: TokenIndex) Error!void { | |
| 5134 | 5284 | operand.node = try p.addNode(.{ |
| 5135 | 5285 | .tag = tag, |
| 5136 | 5286 | .ty = operand.ty, |
| 5137 | 5287 | .data = .{ .un = operand.node }, |
| 5288 | .loc = @enumFromInt(tok_i), | |
| 5138 | 5289 | }); |
| 5139 | 5290 | } |
| 5140 | 5291 | |
| ... | ... | @@ -5368,10 +5519,14 @@ pub const Result = struct { |
| 5368 | 5519 | |
| 5369 | 5520 | fn lvalConversion(res: *Result, p: *Parser) Error!void { |
| 5370 | 5521 | if (res.ty.isFunc()) { |
| 5371 | const elem_ty = try p.arena.create(Type); | |
| 5372 | elem_ty.* = res.ty; | |
| 5373 | res.ty.specifier = .pointer; | |
| 5374 | res.ty.data = .{ .sub_type = elem_ty }; | |
| 5522 | if (res.ty.isInvalidFunc()) { | |
| 5523 | res.ty = .{ .specifier = .invalid }; | |
| 5524 | } else { | |
| 5525 | const elem_ty = try p.arena.create(Type); | |
| 5526 | elem_ty.* = res.ty; | |
| 5527 | res.ty.specifier = .pointer; | |
| 5528 | res.ty.data = .{ .sub_type = elem_ty }; | |
| 5529 | } | |
| 5375 | 5530 | try res.implicitCast(p, .function_to_pointer); |
| 5376 | 5531 | } else if (res.ty.isArray()) { |
| 5377 | 5532 | res.val = .{}; |
| ... | ... | @@ -5455,7 +5610,14 @@ pub const Result = struct { |
| 5455 | 5610 | try res.implicitCast(p, .complex_float_to_complex_int); |
| 5456 | 5611 | } |
| 5457 | 5612 | } else if (!res.ty.eql(int_ty, p.comp, true)) { |
| 5458 | try res.val.intCast(int_ty, p.comp); | |
| 5613 | const old_val = res.val; | |
| 5614 | const value_change_kind = try res.val.intCast(int_ty, p.comp); | |
| 5615 | switch (value_change_kind) { | |
| 5616 | .none => {}, | |
| 5617 | .truncated => try p.errStr(.int_value_changed, tok, try p.valueChangedStr(res, old_val, int_ty)), | |
| 5618 | .sign_changed => try p.errStr(.sign_conversion, tok, try p.typePairStrExtra(res.ty, " to ", int_ty)), | |
| 5619 | } | |
| 5620 | ||
| 5459 | 5621 | const old_real = res.ty.isReal(); |
| 5460 | 5622 | const new_real = int_ty.isReal(); |
| 5461 | 5623 | if (old_real and new_real) { |
| ... | ... | @@ -5486,8 +5648,8 @@ pub const Result = struct { |
| 5486 | 5648 | .none => return p.errStr(.float_to_int, tok, try p.typePairStrExtra(res.ty, " to ", int_ty)), |
| 5487 | 5649 | .out_of_range => return p.errStr(.float_out_of_range, tok, try p.typePairStrExtra(res.ty, " to ", int_ty)), |
| 5488 | 5650 | .overflow => return p.errStr(.float_overflow_conversion, tok, try p.typePairStrExtra(res.ty, " to ", int_ty)), |
| 5489 | .nonzero_to_zero => return p.errStr(.float_zero_conversion, tok, try p.floatValueChangedStr(res, old_value, int_ty)), | |
| 5490 | .value_changed => return p.errStr(.float_value_changed, tok, try p.floatValueChangedStr(res, old_value, int_ty)), | |
| 5651 | .nonzero_to_zero => return p.errStr(.float_zero_conversion, tok, try p.valueChangedStr(res, old_value, int_ty)), | |
| 5652 | .value_changed => return p.errStr(.float_value_changed, tok, try p.valueChangedStr(res, old_value, int_ty)), | |
| 5491 | 5653 | } |
| 5492 | 5654 | } |
| 5493 | 5655 | |
| ... | ... | @@ -5555,7 +5717,7 @@ pub const Result = struct { |
| 5555 | 5717 | res.ty = ptr_ty; |
| 5556 | 5718 | try res.implicitCast(p, .bool_to_pointer); |
| 5557 | 5719 | } else if (res.ty.isInt()) { |
| 5558 | try res.val.intCast(ptr_ty, p.comp); | |
| 5720 | _ = try res.val.intCast(ptr_ty, p.comp); | |
| 5559 | 5721 | res.ty = ptr_ty; |
| 5560 | 5722 | try res.implicitCast(p, .int_to_pointer); |
| 5561 | 5723 | } |
| ... | ... | @@ -5620,16 +5782,14 @@ pub const Result = struct { |
| 5620 | 5782 | |
| 5621 | 5783 | // if either is a float cast to that type |
| 5622 | 5784 | if (a.ty.isFloat() or b.ty.isFloat()) { |
| 5623 | const float_types = [7][2]Type.Specifier{ | |
| 5785 | const float_types = [6][2]Type.Specifier{ | |
| 5624 | 5786 | .{ .complex_long_double, .long_double }, |
| 5625 | 5787 | .{ .complex_float128, .float128 }, |
| 5626 | .{ .complex_float80, .float80 }, | |
| 5627 | 5788 | .{ .complex_double, .double }, |
| 5628 | 5789 | .{ .complex_float, .float }, |
| 5629 | 5790 | // No `_Complex __fp16` type |
| 5630 | 5791 | .{ .invalid, .fp16 }, |
| 5631 | // No `_Complex _Float16` | |
| 5632 | .{ .invalid, .float16 }, | |
| 5792 | .{ .complex_float16, .float16 }, | |
| 5633 | 5793 | }; |
| 5634 | 5794 | const a_spec = a.ty.canonicalize(.standard).specifier; |
| 5635 | 5795 | const b_spec = b.ty.canonicalize(.standard).specifier; |
| ... | ... | @@ -5647,7 +5807,7 @@ pub const Result = struct { |
| 5647 | 5807 | if (try a.floatConversion(b, a_spec, b_spec, p, float_types[3])) return; |
| 5648 | 5808 | if (try a.floatConversion(b, a_spec, b_spec, p, float_types[4])) return; |
| 5649 | 5809 | if (try a.floatConversion(b, a_spec, b_spec, p, float_types[5])) return; |
| 5650 | if (try a.floatConversion(b, a_spec, b_spec, p, float_types[6])) return; | |
| 5810 | unreachable; | |
| 5651 | 5811 | } |
| 5652 | 5812 | |
| 5653 | 5813 | if (a.ty.eql(b.ty, p.comp, true)) { |
| ... | ... | @@ -5875,6 +6035,10 @@ pub const Result = struct { |
| 5875 | 6035 | if (to.is(.bool)) { |
| 5876 | 6036 | res.val.boolCast(p.comp); |
| 5877 | 6037 | } else if (old_float and new_int) { |
| 6038 | if (to.hasIncompleteSize()) { | |
| 6039 | try p.errStr(.cast_to_incomplete_type, l_paren, try p.typeStr(to)); | |
| 6040 | return error.ParsingFailed; | |
| 6041 | } | |
| 5878 | 6042 | // Explicit cast, no conversion warning |
| 5879 | 6043 | _ = try res.val.floatToInt(to, p.comp); |
| 5880 | 6044 | } else if (new_float and old_int) { |
| ... | ... | @@ -5886,7 +6050,7 @@ pub const Result = struct { |
| 5886 | 6050 | try p.errStr(.cast_to_incomplete_type, l_paren, try p.typeStr(to)); |
| 5887 | 6051 | return error.ParsingFailed; |
| 5888 | 6052 | } |
| 5889 | try res.val.intCast(to, p.comp); | |
| 6053 | _ = try res.val.intCast(to, p.comp); | |
| 5890 | 6054 | } |
| 5891 | 6055 | } else if (to.get(.@"union")) |union_ty| { |
| 5892 | 6056 | if (union_ty.data.record.hasFieldOfType(res.ty, p.comp)) { |
| ... | ... | @@ -5918,12 +6082,13 @@ pub const Result = struct { |
| 5918 | 6082 | .tag = .explicit_cast, |
| 5919 | 6083 | .ty = res.ty, |
| 5920 | 6084 | .data = .{ .cast = .{ .operand = res.node, .kind = cast_kind } }, |
| 6085 | .loc = @enumFromInt(l_paren), | |
| 5921 | 6086 | }); |
| 5922 | 6087 | } |
| 5923 | 6088 | |
| 5924 | 6089 | fn intFitsInType(res: Result, p: *Parser, ty: Type) !bool { |
| 5925 | const max_int = try Value.int(ty.maxInt(p.comp), p.comp); | |
| 5926 | const min_int = try Value.int(ty.minInt(p.comp), p.comp); | |
| 6090 | const max_int = try Value.maxInt(ty, p.comp); | |
| 6091 | const min_int = try Value.minInt(ty, p.comp); | |
| 5927 | 6092 | return res.val.compare(.lte, max_int, p.comp) and |
| 5928 | 6093 | (res.ty.isUnsignedInt(p.comp) or res.val.compare(.gte, min_int, p.comp)); |
| 5929 | 6094 | } |
| ... | ... | @@ -6091,7 +6256,7 @@ fn expr(p: *Parser) Error!Result { |
| 6091 | 6256 | var err_start = p.comp.diagnostics.list.items.len; |
| 6092 | 6257 | var lhs = try p.assignExpr(); |
| 6093 | 6258 | if (p.tok_ids[p.tok_i] == .comma) try lhs.expect(p); |
| 6094 | while (p.eatToken(.comma)) |_| { | |
| 6259 | while (p.eatToken(.comma)) |comma| { | |
| 6095 | 6260 | try lhs.maybeWarnUnused(p, expr_start, err_start); |
| 6096 | 6261 | expr_start = p.tok_i; |
| 6097 | 6262 | err_start = p.comp.diagnostics.list.items.len; |
| ... | ... | @@ -6101,7 +6266,7 @@ fn expr(p: *Parser) Error!Result { |
| 6101 | 6266 | try rhs.lvalConversion(p); |
| 6102 | 6267 | lhs.val = rhs.val; |
| 6103 | 6268 | lhs.ty = rhs.ty; |
| 6104 | try lhs.bin(p, .comma_expr, rhs); | |
| 6269 | try lhs.bin(p, .comma_expr, rhs, comma); | |
| 6105 | 6270 | } |
| 6106 | 6271 | return lhs; |
| 6107 | 6272 | } |
| ... | ... | @@ -6183,7 +6348,7 @@ fn assignExpr(p: *Parser) Error!Result { |
| 6183 | 6348 | } |
| 6184 | 6349 | } |
| 6185 | 6350 | _ = try lhs_copy.adjustTypes(tok, &rhs, p, if (tag == .mod_assign_expr) .integer else .arithmetic); |
| 6186 | try lhs.bin(p, tag, rhs); | |
| 6351 | try lhs.bin(p, tag, rhs, bit_or.?); | |
| 6187 | 6352 | return lhs; |
| 6188 | 6353 | }, |
| 6189 | 6354 | .sub_assign_expr, |
| ... | ... | @@ -6194,7 +6359,7 @@ fn assignExpr(p: *Parser) Error!Result { |
| 6194 | 6359 | } else { |
| 6195 | 6360 | _ = try lhs_copy.adjustTypes(tok, &rhs, p, .arithmetic); |
| 6196 | 6361 | } |
| 6197 | try lhs.bin(p, tag, rhs); | |
| 6362 | try lhs.bin(p, tag, rhs, bit_or.?); | |
| 6198 | 6363 | return lhs; |
| 6199 | 6364 | }, |
| 6200 | 6365 | .shl_assign_expr, |
| ... | ... | @@ -6204,7 +6369,7 @@ fn assignExpr(p: *Parser) Error!Result { |
| 6204 | 6369 | .bit_or_assign_expr, |
| 6205 | 6370 | => { |
| 6206 | 6371 | _ = try lhs_copy.adjustTypes(tok, &rhs, p, .integer); |
| 6207 | try lhs.bin(p, tag, rhs); | |
| 6372 | try lhs.bin(p, tag, rhs, bit_or.?); | |
| 6208 | 6373 | return lhs; |
| 6209 | 6374 | }, |
| 6210 | 6375 | else => unreachable, |
| ... | ... | @@ -6212,7 +6377,7 @@ fn assignExpr(p: *Parser) Error!Result { |
| 6212 | 6377 | |
| 6213 | 6378 | try rhs.coerce(p, lhs.ty, tok, .assign); |
| 6214 | 6379 | |
| 6215 | try lhs.bin(p, tag, rhs); | |
| 6380 | try lhs.bin(p, tag, rhs, bit_or.?); | |
| 6216 | 6381 | return lhs; |
| 6217 | 6382 | } |
| 6218 | 6383 | |
| ... | ... | @@ -6280,6 +6445,7 @@ fn condExpr(p: *Parser) Error!Result { |
| 6280 | 6445 | .tag = .binary_cond_expr, |
| 6281 | 6446 | .ty = cond.ty, |
| 6282 | 6447 | .data = .{ .if3 = .{ .cond = cond.node, .body = (try p.addList(&.{ cond_then.node, then_expr.node })).start } }, |
| 6448 | .loc = @enumFromInt(cond_tok), | |
| 6283 | 6449 | }); |
| 6284 | 6450 | return cond; |
| 6285 | 6451 | } |
| ... | ... | @@ -6305,6 +6471,7 @@ fn condExpr(p: *Parser) Error!Result { |
| 6305 | 6471 | .tag = .cond_expr, |
| 6306 | 6472 | .ty = cond.ty, |
| 6307 | 6473 | .data = .{ .if3 = .{ .cond = cond.node, .body = (try p.addList(&.{ then_expr.node, else_expr.node })).start } }, |
| 6474 | .loc = @enumFromInt(cond_tok), | |
| 6308 | 6475 | }); |
| 6309 | 6476 | return cond; |
| 6310 | 6477 | } |
| ... | ... | @@ -6324,8 +6491,10 @@ fn lorExpr(p: *Parser) Error!Result { |
| 6324 | 6491 | if (try lhs.adjustTypes(tok, &rhs, p, .boolean_logic)) { |
| 6325 | 6492 | const res = lhs.val.toBool(p.comp) or rhs.val.toBool(p.comp); |
| 6326 | 6493 | lhs.val = Value.fromBool(res); |
| 6494 | } else { | |
| 6495 | lhs.val.boolCast(p.comp); | |
| 6327 | 6496 | } |
| 6328 | try lhs.boolRes(p, .bool_or_expr, rhs); | |
| 6497 | try lhs.boolRes(p, .bool_or_expr, rhs, tok); | |
| 6329 | 6498 | } |
| 6330 | 6499 | return lhs; |
| 6331 | 6500 | } |
| ... | ... | @@ -6345,8 +6514,10 @@ fn landExpr(p: *Parser) Error!Result { |
| 6345 | 6514 | if (try lhs.adjustTypes(tok, &rhs, p, .boolean_logic)) { |
| 6346 | 6515 | const res = lhs.val.toBool(p.comp) and rhs.val.toBool(p.comp); |
| 6347 | 6516 | lhs.val = Value.fromBool(res); |
| 6517 | } else { | |
| 6518 | lhs.val.boolCast(p.comp); | |
| 6348 | 6519 | } |
| 6349 | try lhs.boolRes(p, .bool_and_expr, rhs); | |
| 6520 | try lhs.boolRes(p, .bool_and_expr, rhs, tok); | |
| 6350 | 6521 | } |
| 6351 | 6522 | return lhs; |
| 6352 | 6523 | } |
| ... | ... | @@ -6362,7 +6533,7 @@ fn orExpr(p: *Parser) Error!Result { |
| 6362 | 6533 | if (try lhs.adjustTypes(tok, &rhs, p, .integer)) { |
| 6363 | 6534 | lhs.val = try lhs.val.bitOr(rhs.val, p.comp); |
| 6364 | 6535 | } |
| 6365 | try lhs.bin(p, .bit_or_expr, rhs); | |
| 6536 | try lhs.bin(p, .bit_or_expr, rhs, tok); | |
| 6366 | 6537 | } |
| 6367 | 6538 | return lhs; |
| 6368 | 6539 | } |
| ... | ... | @@ -6378,7 +6549,7 @@ fn xorExpr(p: *Parser) Error!Result { |
| 6378 | 6549 | if (try lhs.adjustTypes(tok, &rhs, p, .integer)) { |
| 6379 | 6550 | lhs.val = try lhs.val.bitXor(rhs.val, p.comp); |
| 6380 | 6551 | } |
| 6381 | try lhs.bin(p, .bit_xor_expr, rhs); | |
| 6552 | try lhs.bin(p, .bit_xor_expr, rhs, tok); | |
| 6382 | 6553 | } |
| 6383 | 6554 | return lhs; |
| 6384 | 6555 | } |
| ... | ... | @@ -6394,7 +6565,7 @@ fn andExpr(p: *Parser) Error!Result { |
| 6394 | 6565 | if (try lhs.adjustTypes(tok, &rhs, p, .integer)) { |
| 6395 | 6566 | lhs.val = try lhs.val.bitAnd(rhs.val, p.comp); |
| 6396 | 6567 | } |
| 6397 | try lhs.bin(p, .bit_and_expr, rhs); | |
| 6568 | try lhs.bin(p, .bit_and_expr, rhs, tok); | |
| 6398 | 6569 | } |
| 6399 | 6570 | return lhs; |
| 6400 | 6571 | } |
| ... | ... | @@ -6414,8 +6585,10 @@ fn eqExpr(p: *Parser) Error!Result { |
| 6414 | 6585 | const op: std.math.CompareOperator = if (tag == .equal_expr) .eq else .neq; |
| 6415 | 6586 | const res = lhs.val.compare(op, rhs.val, p.comp); |
| 6416 | 6587 | lhs.val = Value.fromBool(res); |
| 6588 | } else { | |
| 6589 | lhs.val.boolCast(p.comp); | |
| 6417 | 6590 | } |
| 6418 | try lhs.boolRes(p, tag, rhs); | |
| 6591 | try lhs.boolRes(p, tag, rhs, ne.?); | |
| 6419 | 6592 | } |
| 6420 | 6593 | return lhs; |
| 6421 | 6594 | } |
| ... | ... | @@ -6443,8 +6616,10 @@ fn compExpr(p: *Parser) Error!Result { |
| 6443 | 6616 | }; |
| 6444 | 6617 | const res = lhs.val.compare(op, rhs.val, p.comp); |
| 6445 | 6618 | lhs.val = Value.fromBool(res); |
| 6619 | } else { | |
| 6620 | lhs.val.boolCast(p.comp); | |
| 6446 | 6621 | } |
| 6447 | try lhs.boolRes(p, tag, rhs); | |
| 6622 | try lhs.boolRes(p, tag, rhs, ge.?); | |
| 6448 | 6623 | } |
| 6449 | 6624 | return lhs; |
| 6450 | 6625 | } |
| ... | ... | @@ -6474,7 +6649,7 @@ fn shiftExpr(p: *Parser) Error!Result { |
| 6474 | 6649 | lhs.val = try lhs.val.shr(rhs.val, lhs.ty, p.comp); |
| 6475 | 6650 | } |
| 6476 | 6651 | } |
| 6477 | try lhs.bin(p, tag, rhs); | |
| 6652 | try lhs.bin(p, tag, rhs, shr.?); | |
| 6478 | 6653 | } |
| 6479 | 6654 | return lhs; |
| 6480 | 6655 | } |
| ... | ... | @@ -6504,7 +6679,7 @@ fn addExpr(p: *Parser) Error!Result { |
| 6504 | 6679 | try p.errStr(.ptr_arithmetic_incomplete, minus.?, try p.typeStr(lhs_ty.elemType())); |
| 6505 | 6680 | lhs.ty = Type.invalid; |
| 6506 | 6681 | } |
| 6507 | try lhs.bin(p, tag, rhs); | |
| 6682 | try lhs.bin(p, tag, rhs, minus.?); | |
| 6508 | 6683 | } |
| 6509 | 6684 | return lhs; |
| 6510 | 6685 | } |
| ... | ... | @@ -6538,7 +6713,7 @@ fn mulExpr(p: *Parser) Error!Result { |
| 6538 | 6713 | lhs.ty.signedness(p.comp) != .unsigned) try p.errOverflow(mul.?, lhs); |
| 6539 | 6714 | } else if (div != null) { |
| 6540 | 6715 | if (try lhs.val.div(lhs.val, rhs.val, lhs.ty, p.comp) and |
| 6541 | lhs.ty.signedness(p.comp) != .unsigned) try p.errOverflow(mul.?, lhs); | |
| 6716 | lhs.ty.signedness(p.comp) != .unsigned) try p.errOverflow(div.?, lhs); | |
| 6542 | 6717 | } else { |
| 6543 | 6718 | var res = try Value.rem(lhs.val, rhs.val, lhs.ty, p.comp); |
| 6544 | 6719 | if (res.opt_ref == .none) { |
| ... | ... | @@ -6554,7 +6729,7 @@ fn mulExpr(p: *Parser) Error!Result { |
| 6554 | 6729 | } |
| 6555 | 6730 | } |
| 6556 | 6731 | |
| 6557 | try lhs.bin(p, tag, rhs); | |
| 6732 | try lhs.bin(p, tag, rhs, percent.?); | |
| 6558 | 6733 | } |
| 6559 | 6734 | return lhs; |
| 6560 | 6735 | } |
| ... | ... | @@ -6573,7 +6748,7 @@ fn removeUnusedWarningForTok(p: *Parser, last_expr_tok: TokenIndex) void { |
| 6573 | 6748 | } |
| 6574 | 6749 | |
| 6575 | 6750 | /// castExpr |
| 6576 | /// : '(' compoundStmt ')' | |
| 6751 | /// : '(' compoundStmt ')' suffixExpr* | |
| 6577 | 6752 | /// | '(' typeName ')' castExpr |
| 6578 | 6753 | /// | '(' typeName ')' '{' initializerItems '}' |
| 6579 | 6754 | /// | __builtin_choose_expr '(' integerConstExpr ',' assignExpr ',' assignExpr ')' |
| ... | ... | @@ -6584,6 +6759,7 @@ fn removeUnusedWarningForTok(p: *Parser, last_expr_tok: TokenIndex) void { |
| 6584 | 6759 | fn castExpr(p: *Parser) Error!Result { |
| 6585 | 6760 | if (p.eatToken(.l_paren)) |l_paren| cast_expr: { |
| 6586 | 6761 | if (p.tok_ids[p.tok_i] == .l_brace) { |
| 6762 | const tok = p.tok_i; | |
| 6587 | 6763 | try p.err(.gnu_statement_expression); |
| 6588 | 6764 | if (p.func.ty == null) { |
| 6589 | 6765 | try p.err(.stmt_expr_not_allowed_file_scope); |
| ... | ... | @@ -6599,7 +6775,12 @@ fn castExpr(p: *Parser) Error!Result { |
| 6599 | 6775 | .val = stmt_expr_state.last_expr_res.val, |
| 6600 | 6776 | }; |
| 6601 | 6777 | try p.expectClosing(l_paren, .r_paren); |
| 6602 | try res.un(p, .stmt_expr); | |
| 6778 | try res.un(p, .stmt_expr, tok); | |
| 6779 | while (true) { | |
| 6780 | const suffix = try p.suffixExpr(res); | |
| 6781 | if (suffix.empty(p)) break; | |
| 6782 | res = suffix; | |
| 6783 | } | |
| 6603 | 6784 | return res; |
| 6604 | 6785 | } |
| 6605 | 6786 | const ty = (try p.typeName()) orelse { |
| ... | ... | @@ -6634,23 +6815,26 @@ fn castExpr(p: *Parser) Error!Result { |
| 6634 | 6815 | } |
| 6635 | 6816 | |
| 6636 | 6817 | fn typesCompatible(p: *Parser) Error!Result { |
| 6818 | const builtin_tok = p.tok_i; | |
| 6637 | 6819 | p.tok_i += 1; |
| 6638 | 6820 | const l_paren = try p.expectToken(.l_paren); |
| 6639 | 6821 | |
| 6822 | const first_tok = p.tok_i; | |
| 6640 | 6823 | const first = (try p.typeName()) orelse { |
| 6641 | 6824 | try p.err(.expected_type); |
| 6642 | 6825 | p.skipTo(.r_paren); |
| 6643 | 6826 | return error.ParsingFailed; |
| 6644 | 6827 | }; |
| 6645 | const lhs = try p.addNode(.{ .tag = .invalid, .ty = first, .data = undefined }); | |
| 6828 | const lhs = try p.addNode(.{ .tag = .invalid, .ty = first, .data = undefined, .loc = @enumFromInt(first_tok) }); | |
| 6646 | 6829 | _ = try p.expectToken(.comma); |
| 6647 | 6830 | |
| 6831 | const second_tok = p.tok_i; | |
| 6648 | 6832 | const second = (try p.typeName()) orelse { |
| 6649 | 6833 | try p.err(.expected_type); |
| 6650 | 6834 | p.skipTo(.r_paren); |
| 6651 | 6835 | return error.ParsingFailed; |
| 6652 | 6836 | }; |
| 6653 | const rhs = try p.addNode(.{ .tag = .invalid, .ty = second, .data = undefined }); | |
| 6837 | const rhs = try p.addNode(.{ .tag = .invalid, .ty = second, .data = undefined, .loc = @enumFromInt(second_tok) }); | |
| 6654 | 6838 | |
| 6655 | 6839 | try p.expectClosing(l_paren, .r_paren); |
| 6656 | 6840 | |
| ... | ... | @@ -6665,10 +6849,15 @@ fn typesCompatible(p: *Parser) Error!Result { |
| 6665 | 6849 | |
| 6666 | 6850 | const res = Result{ |
| 6667 | 6851 | .val = Value.fromBool(compatible), |
| 6668 | .node = try p.addNode(.{ .tag = .builtin_types_compatible_p, .ty = Type.int, .data = .{ .bin = .{ | |
| 6669 | .lhs = lhs, | |
| 6670 | .rhs = rhs, | |
| 6671 | } } }), | |
| 6852 | .node = try p.addNode(.{ | |
| 6853 | .tag = .builtin_types_compatible_p, | |
| 6854 | .ty = Type.int, | |
| 6855 | .data = .{ .bin = .{ | |
| 6856 | .lhs = lhs, | |
| 6857 | .rhs = rhs, | |
| 6858 | } }, | |
| 6859 | .loc = @enumFromInt(builtin_tok), | |
| 6860 | }), | |
| 6672 | 6861 | }; |
| 6673 | 6862 | try p.value_map.put(res.node, res.val); |
| 6674 | 6863 | return res; |
| ... | ... | @@ -6786,11 +6975,11 @@ fn offsetofMemberDesignator(p: *Parser, base_ty: Type, want_bits: bool) Error!Re |
| 6786 | 6975 | errdefer p.skipTo(.r_paren); |
| 6787 | 6976 | const base_field_name_tok = try p.expectIdentifier(); |
| 6788 | 6977 | const base_field_name = try StrInt.intern(p.comp, p.tokSlice(base_field_name_tok)); |
| 6789 | try p.validateFieldAccess(base_ty, base_ty, base_field_name_tok, base_field_name); | |
| 6978 | const base_record_ty = base_ty.getRecord().?; | |
| 6979 | try p.validateFieldAccess(base_record_ty, base_ty, base_field_name_tok, base_field_name); | |
| 6790 | 6980 | const base_node = try p.addNode(.{ .tag = .default_init_expr, .ty = base_ty, .data = undefined }); |
| 6791 | 6981 | |
| 6792 | 6982 | var cur_offset: u64 = 0; |
| 6793 | const base_record_ty = base_ty.canonicalize(.standard); | |
| 6794 | 6983 | var lhs = try p.fieldAccessExtra(base_node, base_record_ty, base_field_name, false, &cur_offset); |
| 6795 | 6984 | |
| 6796 | 6985 | var total_offset = cur_offset; |
| ... | ... | @@ -6800,13 +6989,12 @@ fn offsetofMemberDesignator(p: *Parser, base_ty: Type, want_bits: bool) Error!Re |
| 6800 | 6989 | const field_name_tok = try p.expectIdentifier(); |
| 6801 | 6990 | const field_name = try StrInt.intern(p.comp, p.tokSlice(field_name_tok)); |
| 6802 | 6991 | |
| 6803 | if (!lhs.ty.isRecord()) { | |
| 6992 | const lhs_record_ty = lhs.ty.getRecord() orelse { | |
| 6804 | 6993 | try p.errStr(.offsetof_ty, field_name_tok, try p.typeStr(lhs.ty)); |
| 6805 | 6994 | return error.ParsingFailed; |
| 6806 | } | |
| 6807 | try p.validateFieldAccess(lhs.ty, lhs.ty, field_name_tok, field_name); | |
| 6808 | const record_ty = lhs.ty.canonicalize(.standard); | |
| 6809 | lhs = try p.fieldAccessExtra(lhs.node, record_ty, field_name, false, &cur_offset); | |
| 6995 | }; | |
| 6996 | try p.validateFieldAccess(lhs_record_ty, lhs.ty, field_name_tok, field_name); | |
| 6997 | lhs = try p.fieldAccessExtra(lhs.node, lhs_record_ty, field_name, false, &cur_offset); | |
| 6810 | 6998 | total_offset += cur_offset; |
| 6811 | 6999 | }, |
| 6812 | 7000 | .l_bracket => { |
| ... | ... | @@ -6824,11 +7012,14 @@ fn offsetofMemberDesignator(p: *Parser, base_ty: Type, want_bits: bool) Error!Re |
| 6824 | 7012 | try ptr.lvalConversion(p); |
| 6825 | 7013 | try index.lvalConversion(p); |
| 6826 | 7014 | |
| 6827 | if (!index.ty.isInt()) try p.errTok(.invalid_index, l_bracket_tok); | |
| 6828 | try p.checkArrayBounds(index, lhs, l_bracket_tok); | |
| 7015 | if (index.ty.isInt()) { | |
| 7016 | try p.checkArrayBounds(index, lhs, l_bracket_tok); | |
| 7017 | } else { | |
| 7018 | try p.errTok(.invalid_index, l_bracket_tok); | |
| 7019 | } | |
| 6829 | 7020 | |
| 6830 | 7021 | try index.saveValue(p); |
| 6831 | try ptr.bin(p, .array_access_expr, index); | |
| 7022 | try ptr.bin(p, .array_access_expr, index, l_bracket_tok); | |
| 6832 | 7023 | lhs = ptr; |
| 6833 | 7024 | }, |
| 6834 | 7025 | else => break, |
| ... | ... | @@ -6867,6 +7058,7 @@ fn unExpr(p: *Parser) Error!Result { |
| 6867 | 7058 | .tag = .addr_of_label, |
| 6868 | 7059 | .data = .{ .decl_ref = name_tok }, |
| 6869 | 7060 | .ty = result_ty, |
| 7061 | .loc = @enumFromInt(address_tok), | |
| 6870 | 7062 | }), |
| 6871 | 7063 | .ty = result_ty, |
| 6872 | 7064 | }; |
| ... | ... | @@ -6886,19 +7078,21 @@ fn unExpr(p: *Parser) Error!Result { |
| 6886 | 7078 | { |
| 6887 | 7079 | if (tree.isBitfield(member_node)) try p.errTok(.addr_of_bitfield, tok); |
| 6888 | 7080 | } |
| 6889 | if (!tree.isLval(operand.node)) { | |
| 7081 | if (!tree.isLval(operand.node) and !operand.ty.is(.invalid)) { | |
| 6890 | 7082 | try p.errTok(.addr_of_rvalue, tok); |
| 6891 | 7083 | } |
| 6892 | 7084 | if (operand.ty.qual.register) try p.errTok(.addr_of_register, tok); |
| 6893 | 7085 | |
| 6894 | const elem_ty = try p.arena.create(Type); | |
| 6895 | elem_ty.* = operand.ty; | |
| 6896 | operand.ty = Type{ | |
| 6897 | .specifier = .pointer, | |
| 6898 | .data = .{ .sub_type = elem_ty }, | |
| 6899 | }; | |
| 7086 | if (!operand.ty.is(.invalid)) { | |
| 7087 | const elem_ty = try p.arena.create(Type); | |
| 7088 | elem_ty.* = operand.ty; | |
| 7089 | operand.ty = Type{ | |
| 7090 | .specifier = .pointer, | |
| 7091 | .data = .{ .sub_type = elem_ty }, | |
| 7092 | }; | |
| 7093 | } | |
| 6900 | 7094 | try operand.saveValue(p); |
| 6901 | try operand.un(p, .addr_of_expr); | |
| 7095 | try operand.un(p, .addr_of_expr, tok); | |
| 6902 | 7096 | return operand; |
| 6903 | 7097 | }, |
| 6904 | 7098 | .asterisk => { |
| ... | ... | @@ -6917,7 +7111,7 @@ fn unExpr(p: *Parser) Error!Result { |
| 6917 | 7111 | try p.errStr(.deref_incomplete_ty_ptr, asterisk_loc, try p.typeStr(operand.ty)); |
| 6918 | 7112 | } |
| 6919 | 7113 | operand.ty.qual = .{}; |
| 6920 | try operand.un(p, .deref_expr); | |
| 7114 | try operand.un(p, .deref_expr, tok); | |
| 6921 | 7115 | return operand; |
| 6922 | 7116 | }, |
| 6923 | 7117 | .plus => { |
| ... | ... | @@ -6943,12 +7137,12 @@ fn unExpr(p: *Parser) Error!Result { |
| 6943 | 7137 | try p.errStr(.invalid_argument_un, tok, try p.typeStr(operand.ty)); |
| 6944 | 7138 | |
| 6945 | 7139 | try operand.usualUnaryConversion(p, tok); |
| 6946 | if (operand.val.is(.int, p.comp) or operand.val.is(.float, p.comp)) { | |
| 7140 | if (operand.val.isArithmetic(p.comp)) { | |
| 6947 | 7141 | _ = try operand.val.sub(Value.zero, operand.val, operand.ty, p.comp); |
| 6948 | 7142 | } else { |
| 6949 | 7143 | operand.val = .{}; |
| 6950 | 7144 | } |
| 6951 | try operand.un(p, .negate_expr); | |
| 7145 | try operand.un(p, .negate_expr, tok); | |
| 6952 | 7146 | return operand; |
| 6953 | 7147 | }, |
| 6954 | 7148 | .plus_plus => { |
| ... | ... | @@ -6974,7 +7168,7 @@ fn unExpr(p: *Parser) Error!Result { |
| 6974 | 7168 | operand.val = .{}; |
| 6975 | 7169 | } |
| 6976 | 7170 | |
| 6977 | try operand.un(p, .pre_inc_expr); | |
| 7171 | try operand.un(p, .pre_inc_expr, tok); | |
| 6978 | 7172 | return operand; |
| 6979 | 7173 | }, |
| 6980 | 7174 | .minus_minus => { |
| ... | ... | @@ -7000,7 +7194,7 @@ fn unExpr(p: *Parser) Error!Result { |
| 7000 | 7194 | operand.val = .{}; |
| 7001 | 7195 | } |
| 7002 | 7196 | |
| 7003 | try operand.un(p, .pre_dec_expr); | |
| 7197 | try operand.un(p, .pre_dec_expr, tok); | |
| 7004 | 7198 | return operand; |
| 7005 | 7199 | }, |
| 7006 | 7200 | .tilde => { |
| ... | ... | @@ -7016,11 +7210,14 @@ fn unExpr(p: *Parser) Error!Result { |
| 7016 | 7210 | } |
| 7017 | 7211 | } else if (operand.ty.isComplex()) { |
| 7018 | 7212 | try p.errStr(.complex_conj, tok, try p.typeStr(operand.ty)); |
| 7213 | if (operand.val.is(.complex, p.comp)) { | |
| 7214 | operand.val = try operand.val.complexConj(operand.ty, p.comp); | |
| 7215 | } | |
| 7019 | 7216 | } else { |
| 7020 | 7217 | try p.errStr(.invalid_argument_un, tok, try p.typeStr(operand.ty)); |
| 7021 | 7218 | operand.val = .{}; |
| 7022 | 7219 | } |
| 7023 | try operand.un(p, .bit_not_expr); | |
| 7220 | try operand.un(p, .bit_not_expr, tok); | |
| 7024 | 7221 | return operand; |
| 7025 | 7222 | }, |
| 7026 | 7223 | .bang => { |
| ... | ... | @@ -7045,7 +7242,7 @@ fn unExpr(p: *Parser) Error!Result { |
| 7045 | 7242 | } |
| 7046 | 7243 | } |
| 7047 | 7244 | operand.ty = .{ .specifier = .int }; |
| 7048 | try operand.un(p, .bool_not_expr); | |
| 7245 | try operand.un(p, .bool_not_expr, tok); | |
| 7049 | 7246 | return operand; |
| 7050 | 7247 | }, |
| 7051 | 7248 | .keyword_sizeof => { |
| ... | ... | @@ -7089,7 +7286,7 @@ fn unExpr(p: *Parser) Error!Result { |
| 7089 | 7286 | res.ty = p.comp.types.size; |
| 7090 | 7287 | } |
| 7091 | 7288 | } |
| 7092 | try res.un(p, .sizeof_expr); | |
| 7289 | try res.un(p, .sizeof_expr, tok); | |
| 7093 | 7290 | return res; |
| 7094 | 7291 | }, |
| 7095 | 7292 | .keyword_alignof, |
| ... | ... | @@ -7127,7 +7324,7 @@ fn unExpr(p: *Parser) Error!Result { |
| 7127 | 7324 | try p.errStr(.invalid_alignof, expected_paren, try p.typeStr(res.ty)); |
| 7128 | 7325 | res.ty = Type.invalid; |
| 7129 | 7326 | } |
| 7130 | try res.un(p, .alignof_expr); | |
| 7327 | try res.un(p, .alignof_expr, tok); | |
| 7131 | 7328 | return res; |
| 7132 | 7329 | }, |
| 7133 | 7330 | .keyword_extension => { |
| ... | ... | @@ -7147,15 +7344,18 @@ fn unExpr(p: *Parser) Error!Result { |
| 7147 | 7344 | var operand = try p.castExpr(); |
| 7148 | 7345 | try operand.expect(p); |
| 7149 | 7346 | try operand.lvalConversion(p); |
| 7347 | if (operand.ty.is(.invalid)) return Result.invalid; | |
| 7150 | 7348 | if (!operand.ty.isInt() and !operand.ty.isFloat()) { |
| 7151 | 7349 | try p.errStr(.invalid_imag, imag_tok, try p.typeStr(operand.ty)); |
| 7152 | 7350 | } |
| 7153 | if (operand.ty.isReal()) { | |
| 7351 | if (operand.ty.isComplex()) { | |
| 7352 | operand.val = try operand.val.imaginaryPart(p.comp); | |
| 7353 | } else if (operand.ty.isReal()) { | |
| 7154 | 7354 | switch (p.comp.langopts.emulate) { |
| 7155 | 7355 | .msvc => {}, // Doesn't support `_Complex` or `__imag` in the first place |
| 7156 | 7356 | .gcc => operand.val = Value.zero, |
| 7157 | 7357 | .clang => { |
| 7158 | if (operand.val.is(.int, p.comp)) { | |
| 7358 | if (operand.val.is(.int, p.comp) or operand.val.is(.float, p.comp)) { | |
| 7159 | 7359 | operand.val = Value.zero; |
| 7160 | 7360 | } else { |
| 7161 | 7361 | operand.val = .{}; |
| ... | ... | @@ -7165,7 +7365,7 @@ fn unExpr(p: *Parser) Error!Result { |
| 7165 | 7365 | } |
| 7166 | 7366 | // convert _Complex T to T |
| 7167 | 7367 | operand.ty = operand.ty.makeReal(); |
| 7168 | try operand.un(p, .imag_expr); | |
| 7368 | try operand.un(p, .imag_expr, tok); | |
| 7169 | 7369 | return operand; |
| 7170 | 7370 | }, |
| 7171 | 7371 | .keyword_real1, .keyword_real2 => { |
| ... | ... | @@ -7175,12 +7375,14 @@ fn unExpr(p: *Parser) Error!Result { |
| 7175 | 7375 | var operand = try p.castExpr(); |
| 7176 | 7376 | try operand.expect(p); |
| 7177 | 7377 | try operand.lvalConversion(p); |
| 7378 | if (operand.ty.is(.invalid)) return Result.invalid; | |
| 7178 | 7379 | if (!operand.ty.isInt() and !operand.ty.isFloat()) { |
| 7179 | 7380 | try p.errStr(.invalid_real, real_tok, try p.typeStr(operand.ty)); |
| 7180 | 7381 | } |
| 7181 | 7382 | // convert _Complex T to T |
| 7182 | 7383 | operand.ty = operand.ty.makeReal(); |
| 7183 | try operand.un(p, .real_expr); | |
| 7384 | operand.val = try operand.val.realPart(p.comp); | |
| 7385 | try operand.un(p, .real_expr, tok); | |
| 7184 | 7386 | return operand; |
| 7185 | 7387 | }, |
| 7186 | 7388 | else => { |
| ... | ... | @@ -7253,7 +7455,7 @@ fn compoundLiteral(p: *Parser) Error!Result { |
| 7253 | 7455 | if (d.constexpr) |_| { |
| 7254 | 7456 | // TODO error if not constexpr |
| 7255 | 7457 | } |
| 7256 | try init_list_expr.un(p, tag); | |
| 7458 | try init_list_expr.un(p, tag, l_paren); | |
| 7257 | 7459 | return init_list_expr; |
| 7258 | 7460 | } |
| 7259 | 7461 | |
| ... | ... | @@ -7284,7 +7486,7 @@ fn suffixExpr(p: *Parser, lhs: Result) Error!Result { |
| 7284 | 7486 | } |
| 7285 | 7487 | try operand.usualUnaryConversion(p, p.tok_i); |
| 7286 | 7488 | |
| 7287 | try operand.un(p, .post_inc_expr); | |
| 7489 | try operand.un(p, .post_inc_expr, p.tok_i); | |
| 7288 | 7490 | return operand; |
| 7289 | 7491 | }, |
| 7290 | 7492 | .minus_minus => { |
| ... | ... | @@ -7302,7 +7504,7 @@ fn suffixExpr(p: *Parser, lhs: Result) Error!Result { |
| 7302 | 7504 | } |
| 7303 | 7505 | try operand.usualUnaryConversion(p, p.tok_i); |
| 7304 | 7506 | |
| 7305 | try operand.un(p, .post_dec_expr); | |
| 7507 | try operand.un(p, .post_dec_expr, p.tok_i); | |
| 7306 | 7508 | return operand; |
| 7307 | 7509 | }, |
| 7308 | 7510 | .l_bracket => { |
| ... | ... | @@ -7319,12 +7521,18 @@ fn suffixExpr(p: *Parser, lhs: Result) Error!Result { |
| 7319 | 7521 | try index.lvalConversion(p); |
| 7320 | 7522 | if (ptr.ty.isPtr()) { |
| 7321 | 7523 | ptr.ty = ptr.ty.elemType(); |
| 7322 | if (!index.ty.isInt()) try p.errTok(.invalid_index, l_bracket); | |
| 7323 | try p.checkArrayBounds(index_before_conversion, array_before_conversion, l_bracket); | |
| 7524 | if (index.ty.isInt()) { | |
| 7525 | try p.checkArrayBounds(index_before_conversion, array_before_conversion, l_bracket); | |
| 7526 | } else { | |
| 7527 | try p.errTok(.invalid_index, l_bracket); | |
| 7528 | } | |
| 7324 | 7529 | } else if (index.ty.isPtr()) { |
| 7325 | 7530 | index.ty = index.ty.elemType(); |
| 7326 | if (!ptr.ty.isInt()) try p.errTok(.invalid_index, l_bracket); | |
| 7327 | try p.checkArrayBounds(array_before_conversion, index_before_conversion, l_bracket); | |
| 7531 | if (ptr.ty.isInt()) { | |
| 7532 | try p.checkArrayBounds(array_before_conversion, index_before_conversion, l_bracket); | |
| 7533 | } else { | |
| 7534 | try p.errTok(.invalid_index, l_bracket); | |
| 7535 | } | |
| 7328 | 7536 | std.mem.swap(Result, &ptr, &index); |
| 7329 | 7537 | } else { |
| 7330 | 7538 | try p.errTok(.invalid_subscript, l_bracket); |
| ... | ... | @@ -7332,7 +7540,7 @@ fn suffixExpr(p: *Parser, lhs: Result) Error!Result { |
| 7332 | 7540 | |
| 7333 | 7541 | try ptr.saveValue(p); |
| 7334 | 7542 | try index.saveValue(p); |
| 7335 | try ptr.bin(p, .array_access_expr, index); | |
| 7543 | try ptr.bin(p, .array_access_expr, index, l_bracket); | |
| 7336 | 7544 | return ptr; |
| 7337 | 7545 | }, |
| 7338 | 7546 | .period => { |
| ... | ... | @@ -7364,16 +7572,12 @@ fn fieldAccess( |
| 7364 | 7572 | const expr_ty = lhs.ty; |
| 7365 | 7573 | const is_ptr = expr_ty.isPtr(); |
| 7366 | 7574 | const expr_base_ty = if (is_ptr) expr_ty.elemType() else expr_ty; |
| 7367 | const record_ty = expr_base_ty.canonicalize(.standard); | |
| 7575 | const record_ty = expr_base_ty.getRecord() orelse { | |
| 7576 | try p.errStr(.expected_record_ty, field_name_tok, try p.typeStr(expr_ty)); | |
| 7577 | return error.ParsingFailed; | |
| 7578 | }; | |
| 7368 | 7579 | |
| 7369 | switch (record_ty.specifier) { | |
| 7370 | .@"struct", .@"union" => {}, | |
| 7371 | else => { | |
| 7372 | try p.errStr(.expected_record_ty, field_name_tok, try p.typeStr(expr_ty)); | |
| 7373 | return error.ParsingFailed; | |
| 7374 | }, | |
| 7375 | } | |
| 7376 | if (record_ty.hasIncompleteSize()) { | |
| 7580 | if (record_ty.isIncomplete()) { | |
| 7377 | 7581 | try p.errStr(.deref_incomplete_ty_ptr, field_name_tok - 2, try p.typeStr(expr_base_ty)); |
| 7378 | 7582 | return error.ParsingFailed; |
| 7379 | 7583 | } |
| ... | ... | @@ -7386,7 +7590,7 @@ fn fieldAccess( |
| 7386 | 7590 | return p.fieldAccessExtra(lhs.node, record_ty, field_name, is_arrow, &discard); |
| 7387 | 7591 | } |
| 7388 | 7592 | |
| 7389 | fn validateFieldAccess(p: *Parser, record_ty: Type, expr_ty: Type, field_name_tok: TokenIndex, field_name: StringId) Error!void { | |
| 7593 | fn validateFieldAccess(p: *Parser, record_ty: *const Type.Record, expr_ty: Type, field_name_tok: TokenIndex, field_name: StringId) Error!void { | |
| 7390 | 7594 | if (record_ty.hasField(field_name)) return; |
| 7391 | 7595 | |
| 7392 | 7596 | p.strings.items.len = 0; |
| ... | ... | @@ -7401,8 +7605,8 @@ fn validateFieldAccess(p: *Parser, record_ty: Type, expr_ty: Type, field_name_to |
| 7401 | 7605 | return error.ParsingFailed; |
| 7402 | 7606 | } |
| 7403 | 7607 | |
| 7404 | fn fieldAccessExtra(p: *Parser, lhs: NodeIndex, record_ty: Type, field_name: StringId, is_arrow: bool, offset_bits: *u64) Error!Result { | |
| 7405 | for (record_ty.data.record.fields, 0..) |f, i| { | |
| 7608 | fn fieldAccessExtra(p: *Parser, lhs: NodeIndex, record_ty: *const Type.Record, field_name: StringId, is_arrow: bool, offset_bits: *u64) Error!Result { | |
| 7609 | for (record_ty.fields, 0..) |f, i| { | |
| 7406 | 7610 | if (f.isAnonymousRecord()) { |
| 7407 | 7611 | if (!f.ty.hasField(field_name)) continue; |
| 7408 | 7612 | const inner = try p.addNode(.{ |
| ... | ... | @@ -7410,7 +7614,7 @@ fn fieldAccessExtra(p: *Parser, lhs: NodeIndex, record_ty: Type, field_name: Str |
| 7410 | 7614 | .ty = f.ty, |
| 7411 | 7615 | .data = .{ .member = .{ .lhs = lhs, .index = @intCast(i) } }, |
| 7412 | 7616 | }); |
| 7413 | const ret = p.fieldAccessExtra(inner, f.ty, field_name, false, offset_bits); | |
| 7617 | const ret = p.fieldAccessExtra(inner, f.ty.getRecord().?, field_name, false, offset_bits); | |
| 7414 | 7618 | offset_bits.* = f.layout.offset_bits; |
| 7415 | 7619 | return ret; |
| 7416 | 7620 | } |
| ... | ... | @@ -7527,6 +7731,23 @@ fn callExpr(p: *Parser, lhs: Result) Error!Result { |
| 7527 | 7731 | continue; |
| 7528 | 7732 | } |
| 7529 | 7733 | const p_ty = params[arg_count].ty; |
| 7734 | if (p_ty.specifier == .static_array) { | |
| 7735 | const arg_array_len: u64 = arg.ty.arrayLen() orelse std.math.maxInt(u64); | |
| 7736 | const param_array_len: u64 = p_ty.arrayLen().?; | |
| 7737 | if (arg_array_len < param_array_len) { | |
| 7738 | const extra = Diagnostics.Message.Extra{ .arguments = .{ | |
| 7739 | .expected = @intCast(arg_array_len), | |
| 7740 | .actual = @intCast(param_array_len), | |
| 7741 | } }; | |
| 7742 | try p.errExtra(.array_argument_too_small, param_tok, extra); | |
| 7743 | try p.errTok(.callee_with_static_array, params[arg_count].name_tok); | |
| 7744 | } | |
| 7745 | if (arg.val.isZero(p.comp)) { | |
| 7746 | try p.errTok(.non_null_argument, param_tok); | |
| 7747 | try p.errTok(.callee_with_static_array, params[arg_count].name_tok); | |
| 7748 | } | |
| 7749 | } | |
| 7750 | ||
| 7530 | 7751 | if (call_expr.shouldCoerceArg(arg_count)) { |
| 7531 | 7752 | try arg.coerce(p, p_ty, param_tok, .{ .arg = params[arg_count].name_tok }); |
| 7532 | 7753 | } |
| ... | ... | @@ -7618,7 +7839,7 @@ fn primaryExpr(p: *Parser) Error!Result { |
| 7618 | 7839 | var e = try p.expr(); |
| 7619 | 7840 | try e.expect(p); |
| 7620 | 7841 | try p.expectClosing(l_paren, .r_paren); |
| 7621 | try e.un(p, .paren_expr); | |
| 7842 | try e.un(p, .paren_expr, l_paren); | |
| 7622 | 7843 | return e; |
| 7623 | 7844 | } |
| 7624 | 7845 | switch (p.tok_ids[p.tok_i]) { |
| ... | ... | @@ -7626,6 +7847,10 @@ fn primaryExpr(p: *Parser) Error!Result { |
| 7626 | 7847 | const name_tok = try p.expectIdentifier(); |
| 7627 | 7848 | const name = p.tokSlice(name_tok); |
| 7628 | 7849 | const interned_name = try StrInt.intern(p.comp, name); |
| 7850 | if (interned_name == p.auto_type_decl_name) { | |
| 7851 | try p.errStr(.auto_type_self_initialized, name_tok, name); | |
| 7852 | return error.ParsingFailed; | |
| 7853 | } | |
| 7629 | 7854 | if (p.syms.findSymbol(interned_name)) |sym| { |
| 7630 | 7855 | try p.checkDeprecatedUnavailable(sym.ty, name_tok, sym.tok); |
| 7631 | 7856 | if (sym.kind == .constexpr) { |
| ... | ... | @@ -7636,6 +7861,7 @@ fn primaryExpr(p: *Parser) Error!Result { |
| 7636 | 7861 | .tag = .decl_ref_expr, |
| 7637 | 7862 | .ty = sym.ty, |
| 7638 | 7863 | .data = .{ .decl_ref = name_tok }, |
| 7864 | .loc = @enumFromInt(name_tok), | |
| 7639 | 7865 | }), |
| 7640 | 7866 | }; |
| 7641 | 7867 | } |
| ... | ... | @@ -7653,6 +7879,7 @@ fn primaryExpr(p: *Parser) Error!Result { |
| 7653 | 7879 | .tag = if (sym.kind == .enumeration) .enumeration_ref else .decl_ref_expr, |
| 7654 | 7880 | .ty = sym.ty, |
| 7655 | 7881 | .data = .{ .decl_ref = name_tok }, |
| 7882 | .loc = @enumFromInt(name_tok), | |
| 7656 | 7883 | }), |
| 7657 | 7884 | }; |
| 7658 | 7885 | } |
| ... | ... | @@ -7679,6 +7906,7 @@ fn primaryExpr(p: *Parser) Error!Result { |
| 7679 | 7906 | .tag = .builtin_call_expr_one, |
| 7680 | 7907 | .ty = some.ty, |
| 7681 | 7908 | .data = .{ .decl = .{ .name = name_tok, .node = .none } }, |
| 7909 | .loc = @enumFromInt(name_tok), | |
| 7682 | 7910 | }), |
| 7683 | 7911 | }; |
| 7684 | 7912 | } |
| ... | ... | @@ -7696,6 +7924,7 @@ fn primaryExpr(p: *Parser) Error!Result { |
| 7696 | 7924 | .ty = ty, |
| 7697 | 7925 | .tag = .fn_proto, |
| 7698 | 7926 | .data = .{ .decl = .{ .name = name_tok } }, |
| 7927 | .loc = @enumFromInt(name_tok), | |
| 7699 | 7928 | }); |
| 7700 | 7929 | |
| 7701 | 7930 | try p.decl_buf.append(node); |
| ... | ... | @@ -7707,6 +7936,7 @@ fn primaryExpr(p: *Parser) Error!Result { |
| 7707 | 7936 | .tag = .decl_ref_expr, |
| 7708 | 7937 | .ty = ty, |
| 7709 | 7938 | .data = .{ .decl_ref = name_tok }, |
| 7939 | .loc = @enumFromInt(name_tok), | |
| 7710 | 7940 | }), |
| 7711 | 7941 | }; |
| 7712 | 7942 | } |
| ... | ... | @@ -7714,11 +7944,12 @@ fn primaryExpr(p: *Parser) Error!Result { |
| 7714 | 7944 | return error.ParsingFailed; |
| 7715 | 7945 | }, |
| 7716 | 7946 | .keyword_true, .keyword_false => |id| { |
| 7947 | const tok_i = p.tok_i; | |
| 7717 | 7948 | p.tok_i += 1; |
| 7718 | 7949 | const res = Result{ |
| 7719 | 7950 | .val = Value.fromBool(id == .keyword_true), |
| 7720 | 7951 | .ty = .{ .specifier = .bool }, |
| 7721 | .node = try p.addNode(.{ .tag = .bool_literal, .ty = .{ .specifier = .bool }, .data = undefined }), | |
| 7952 | .node = try p.addNode(.{ .tag = .bool_literal, .ty = .{ .specifier = .bool }, .data = undefined, .loc = @enumFromInt(tok_i) }), | |
| 7722 | 7953 | }; |
| 7723 | 7954 | std.debug.assert(!p.in_macro); // Should have been replaced with .one / .zero |
| 7724 | 7955 | try p.value_map.put(res.node, res.val); |
| ... | ... | @@ -7734,6 +7965,7 @@ fn primaryExpr(p: *Parser) Error!Result { |
| 7734 | 7965 | .tag = .nullptr_literal, |
| 7735 | 7966 | .ty = .{ .specifier = .nullptr_t }, |
| 7736 | 7967 | .data = undefined, |
| 7968 | .loc = @enumFromInt(p.tok_i), | |
| 7737 | 7969 | }), |
| 7738 | 7970 | }; |
| 7739 | 7971 | }, |
| ... | ... | @@ -7770,6 +8002,7 @@ fn primaryExpr(p: *Parser) Error!Result { |
| 7770 | 8002 | .tag = .decl_ref_expr, |
| 7771 | 8003 | .ty = ty, |
| 7772 | 8004 | .data = .{ .decl_ref = tok }, |
| 8005 | .loc = @enumFromInt(tok), | |
| 7773 | 8006 | }), |
| 7774 | 8007 | }; |
| 7775 | 8008 | }, |
| ... | ... | @@ -7805,6 +8038,7 @@ fn primaryExpr(p: *Parser) Error!Result { |
| 7805 | 8038 | .tag = .decl_ref_expr, |
| 7806 | 8039 | .ty = ty, |
| 7807 | 8040 | .data = .{ .decl_ref = p.tok_i }, |
| 8041 | .loc = @enumFromInt(p.tok_i), | |
| 7808 | 8042 | }), |
| 7809 | 8043 | }; |
| 7810 | 8044 | }, |
| ... | ... | @@ -7824,16 +8058,16 @@ fn primaryExpr(p: *Parser) Error!Result { |
| 7824 | 8058 | .unterminated_char_literal, |
| 7825 | 8059 | => return p.charLiteral(), |
| 7826 | 8060 | .zero => { |
| 7827 | p.tok_i += 1; | |
| 8061 | defer p.tok_i += 1; | |
| 7828 | 8062 | var res: Result = .{ .val = Value.zero, .ty = if (p.in_macro) p.comp.types.intmax else Type.int }; |
| 7829 | res.node = try p.addNode(.{ .tag = .int_literal, .ty = res.ty, .data = undefined }); | |
| 8063 | res.node = try p.addNode(.{ .tag = .int_literal, .ty = res.ty, .data = undefined, .loc = @enumFromInt(p.tok_i) }); | |
| 7830 | 8064 | if (!p.in_macro) try p.value_map.put(res.node, res.val); |
| 7831 | 8065 | return res; |
| 7832 | 8066 | }, |
| 7833 | 8067 | .one => { |
| 7834 | p.tok_i += 1; | |
| 8068 | defer p.tok_i += 1; | |
| 7835 | 8069 | var res: Result = .{ .val = Value.one, .ty = if (p.in_macro) p.comp.types.intmax else Type.int }; |
| 7836 | res.node = try p.addNode(.{ .tag = .int_literal, .ty = res.ty, .data = undefined }); | |
| 8070 | res.node = try p.addNode(.{ .tag = .int_literal, .ty = res.ty, .data = undefined, .loc = @enumFromInt(p.tok_i) }); | |
| 7837 | 8071 | if (!p.in_macro) try p.value_map.put(res.node, res.val); |
| 7838 | 8072 | return res; |
| 7839 | 8073 | }, |
| ... | ... | @@ -7841,7 +8075,7 @@ fn primaryExpr(p: *Parser) Error!Result { |
| 7841 | 8075 | .embed_byte => { |
| 7842 | 8076 | assert(!p.in_macro); |
| 7843 | 8077 | const loc = p.pp.tokens.items(.loc)[p.tok_i]; |
| 7844 | p.tok_i += 1; | |
| 8078 | defer p.tok_i += 1; | |
| 7845 | 8079 | const buf = p.comp.getSource(.generated).buf[loc.byte_offset..]; |
| 7846 | 8080 | var byte: u8 = buf[0] - '0'; |
| 7847 | 8081 | for (buf[1..]) |c| { |
| ... | ... | @@ -7850,7 +8084,7 @@ fn primaryExpr(p: *Parser) Error!Result { |
| 7850 | 8084 | byte += c - '0'; |
| 7851 | 8085 | } |
| 7852 | 8086 | var res: Result = .{ .val = try Value.int(byte, p.comp) }; |
| 7853 | res.node = try p.addNode(.{ .tag = .int_literal, .ty = res.ty, .data = undefined }); | |
| 8087 | res.node = try p.addNode(.{ .tag = .int_literal, .ty = res.ty, .data = undefined, .loc = @enumFromInt(p.tok_i) }); | |
| 7854 | 8088 | try p.value_map.put(res.node, res.val); |
| 7855 | 8089 | return res; |
| 7856 | 8090 | }, |
| ... | ... | @@ -7869,17 +8103,19 @@ fn makePredefinedIdentifier(p: *Parser, strings_top: usize) !Result { |
| 7869 | 8103 | const slice = p.strings.items[strings_top..]; |
| 7870 | 8104 | const val = try Value.intern(p.comp, .{ .bytes = slice }); |
| 7871 | 8105 | |
| 7872 | const str_lit = try p.addNode(.{ .tag = .string_literal_expr, .ty = ty, .data = undefined }); | |
| 8106 | const str_lit = try p.addNode(.{ .tag = .string_literal_expr, .ty = ty, .data = undefined, .loc = @enumFromInt(p.tok_i) }); | |
| 7873 | 8107 | if (!p.in_macro) try p.value_map.put(str_lit, val); |
| 7874 | 8108 | |
| 7875 | 8109 | return Result{ .ty = ty, .node = try p.addNode(.{ |
| 7876 | 8110 | .tag = .implicit_static_var, |
| 7877 | 8111 | .ty = ty, |
| 7878 | 8112 | .data = .{ .decl = .{ .name = p.tok_i, .node = str_lit } }, |
| 8113 | .loc = @enumFromInt(p.tok_i), | |
| 7879 | 8114 | }) }; |
| 7880 | 8115 | } |
| 7881 | 8116 | |
| 7882 | 8117 | fn stringLiteral(p: *Parser) Error!Result { |
| 8118 | const string_start = p.tok_i; | |
| 7883 | 8119 | var string_end = p.tok_i; |
| 7884 | 8120 | var string_kind: text_literal.Kind = .char; |
| 7885 | 8121 | 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 { |
| 7894 | 8130 | return error.ParsingFailed; |
| 7895 | 8131 | } |
| 7896 | 8132 | } |
| 7897 | assert(string_end > p.tok_i); | |
| 8133 | const count = string_end - p.tok_i; | |
| 8134 | assert(count > 0); | |
| 7898 | 8135 | |
| 7899 | 8136 | const char_width = string_kind.charUnitSize(p.comp); |
| 7900 | 8137 | |
| 7901 | 8138 | const strings_top = p.strings.items.len; |
| 7902 | 8139 | defer p.strings.items.len = strings_top; |
| 7903 | 8140 | |
| 8141 | const literal_start = mem.alignForward(usize, strings_top, @intFromEnum(char_width)); | |
| 8142 | try p.strings.resize(literal_start); | |
| 8143 | ||
| 7904 | 8144 | while (p.tok_i < string_end) : (p.tok_i += 1) { |
| 7905 | 8145 | const this_kind = text_literal.Kind.classify(p.tok_ids[p.tok_i], .string_literal).?; |
| 7906 | 8146 | const slice = this_kind.contentSlice(p.tokSlice(p.tok_i)); |
| ... | ... | @@ -7940,12 +8180,18 @@ fn stringLiteral(p: *Parser) Error!Result { |
| 7940 | 8180 | }, |
| 7941 | 8181 | } |
| 7942 | 8182 | }, |
| 7943 | .improperly_encoded => |bytes| p.strings.appendSliceAssumeCapacity(bytes), | |
| 8183 | .improperly_encoded => |bytes| { | |
| 8184 | if (count > 1) { | |
| 8185 | try p.errTok(.illegal_char_encoding_error, p.tok_i); | |
| 8186 | return error.ParsingFailed; | |
| 8187 | } | |
| 8188 | p.strings.appendSliceAssumeCapacity(bytes); | |
| 8189 | }, | |
| 7944 | 8190 | .utf8_text => |view| { |
| 7945 | 8191 | switch (char_width) { |
| 7946 | 8192 | .@"1" => p.strings.appendSliceAssumeCapacity(view.bytes), |
| 7947 | 8193 | .@"2" => { |
| 7948 | const capacity_slice: []align(@alignOf(u16)) u8 = @alignCast(p.strings.unusedCapacitySlice()); | |
| 8194 | const capacity_slice: []align(@alignOf(u16)) u8 = @alignCast(p.strings.allocatedSlice()[literal_start..]); | |
| 7949 | 8195 | const dest_len = std.mem.alignBackward(usize, capacity_slice.len, 2); |
| 7950 | 8196 | const dest = std.mem.bytesAsSlice(u16, capacity_slice[0..dest_len]); |
| 7951 | 8197 | const words_written = std.unicode.utf8ToUtf16Le(dest, view.bytes) catch unreachable; |
| ... | ... | @@ -7966,7 +8212,7 @@ fn stringLiteral(p: *Parser) Error!Result { |
| 7966 | 8212 | } |
| 7967 | 8213 | } |
| 7968 | 8214 | p.strings.appendNTimesAssumeCapacity(0, @intFromEnum(char_width)); |
| 7969 | const slice = p.strings.items[strings_top..]; | |
| 8215 | const slice = p.strings.items[literal_start..]; | |
| 7970 | 8216 | |
| 7971 | 8217 | // TODO this won't do anything if there is a cache hit |
| 7972 | 8218 | const interned_align = mem.alignForward( |
| ... | ... | @@ -7987,7 +8233,7 @@ fn stringLiteral(p: *Parser) Error!Result { |
| 7987 | 8233 | }, |
| 7988 | 8234 | .val = val, |
| 7989 | 8235 | }; |
| 7990 | res.node = try p.addNode(.{ .tag = .string_literal_expr, .ty = res.ty, .data = undefined }); | |
| 8236 | res.node = try p.addNode(.{ .tag = .string_literal_expr, .ty = res.ty, .data = undefined, .loc = @enumFromInt(string_start) }); | |
| 7991 | 8237 | if (!p.in_macro) try p.value_map.put(res.node, res.val); |
| 7992 | 8238 | return res; |
| 7993 | 8239 | } |
| ... | ... | @@ -8004,7 +8250,7 @@ fn charLiteral(p: *Parser) Error!Result { |
| 8004 | 8250 | return .{ |
| 8005 | 8251 | .ty = Type.int, |
| 8006 | 8252 | .val = Value.zero, |
| 8007 | .node = try p.addNode(.{ .tag = .char_literal, .ty = Type.int, .data = undefined }), | |
| 8253 | .node = try p.addNode(.{ .tag = .char_literal, .ty = Type.int, .data = undefined, .loc = @enumFromInt(p.tok_i) }), | |
| 8008 | 8254 | }; |
| 8009 | 8255 | }; |
| 8010 | 8256 | if (char_kind == .utf_8) try p.err(.u8_char_lit); |
| ... | ... | @@ -8013,7 +8259,7 @@ fn charLiteral(p: *Parser) Error!Result { |
| 8013 | 8259 | const slice = char_kind.contentSlice(p.tokSlice(p.tok_i)); |
| 8014 | 8260 | |
| 8015 | 8261 | var is_multichar = false; |
| 8016 | if (slice.len == 1 and std.ascii.isAscii(slice[0])) { | |
| 8262 | if (slice.len == 1 and std.ascii.isASCII(slice[0])) { | |
| 8017 | 8263 | // fast path: single unescaped ASCII char |
| 8018 | 8264 | val = slice[0]; |
| 8019 | 8265 | } else { |
| ... | ... | @@ -8096,25 +8342,25 @@ fn charLiteral(p: *Parser) Error!Result { |
| 8096 | 8342 | // > that of the single character or escape sequence is converted to type int. |
| 8097 | 8343 | // This conversion only matters if `char` is signed and has a high-order bit of `1` |
| 8098 | 8344 | if (char_kind == .char and !is_multichar and val > 0x7F and p.comp.getCharSignedness() == .signed) { |
| 8099 | try value.intCast(.{ .specifier = .char }, p.comp); | |
| 8345 | _ = try value.intCast(.{ .specifier = .char }, p.comp); | |
| 8100 | 8346 | } |
| 8101 | 8347 | |
| 8102 | 8348 | const res = Result{ |
| 8103 | 8349 | .ty = if (p.in_macro) macro_ty else ty, |
| 8104 | 8350 | .val = value, |
| 8105 | .node = try p.addNode(.{ .tag = .char_literal, .ty = ty, .data = undefined }), | |
| 8351 | .node = try p.addNode(.{ .tag = .char_literal, .ty = ty, .data = undefined, .loc = @enumFromInt(p.tok_i) }), | |
| 8106 | 8352 | }; |
| 8107 | 8353 | if (!p.in_macro) try p.value_map.put(res.node, res.val); |
| 8108 | 8354 | return res; |
| 8109 | 8355 | } |
| 8110 | 8356 | |
| 8111 | fn parseFloat(p: *Parser, buf: []const u8, suffix: NumberSuffix) !Result { | |
| 8357 | fn parseFloat(p: *Parser, buf: []const u8, suffix: NumberSuffix, tok_i: TokenIndex) !Result { | |
| 8112 | 8358 | const ty = Type{ .specifier = switch (suffix) { |
| 8113 | 8359 | .None, .I => .double, |
| 8114 | 8360 | .F, .IF => .float, |
| 8115 | .F16 => .float16, | |
| 8361 | .F16, .IF16 => .float16, | |
| 8116 | 8362 | .L, .IL => .long_double, |
| 8117 | .W, .IW => .float80, | |
| 8363 | .W, .IW => p.comp.float80Type().?.specifier, | |
| 8118 | 8364 | .Q, .IQ, .F128, .IF128 => .float128, |
| 8119 | 8365 | else => unreachable, |
| 8120 | 8366 | } }; |
| ... | ... | @@ -8140,21 +8386,29 @@ fn parseFloat(p: *Parser, buf: []const u8, suffix: NumberSuffix) !Result { |
| 8140 | 8386 | }); |
| 8141 | 8387 | var res = Result{ |
| 8142 | 8388 | .ty = ty, |
| 8143 | .node = try p.addNode(.{ .tag = .float_literal, .ty = ty, .data = undefined }), | |
| 8389 | .node = try p.addNode(.{ .tag = .float_literal, .ty = ty, .data = undefined, .loc = @enumFromInt(tok_i) }), | |
| 8144 | 8390 | .val = val, |
| 8145 | 8391 | }; |
| 8146 | 8392 | if (suffix.isImaginary()) { |
| 8147 | 8393 | try p.err(.gnu_imaginary_constant); |
| 8148 | 8394 | res.ty = .{ .specifier = switch (suffix) { |
| 8149 | 8395 | .I => .complex_double, |
| 8396 | .IF16 => .complex_float16, | |
| 8150 | 8397 | .IF => .complex_float, |
| 8151 | 8398 | .IL => .complex_long_double, |
| 8152 | .IW => .complex_float80, | |
| 8399 | .IW => p.comp.float80Type().?.makeComplex().specifier, | |
| 8153 | 8400 | .IQ, .IF128 => .complex_float128, |
| 8154 | 8401 | else => unreachable, |
| 8155 | 8402 | } }; |
| 8156 | res.val = .{}; // TODO add complex values | |
| 8157 | try res.un(p, .imaginary_literal); | |
| 8403 | res.val = try Value.intern(p.comp, switch (res.ty.bitSizeof(p.comp).?) { | |
| 8404 | 32 => .{ .complex = .{ .cf16 = .{ 0.0, val.toFloat(f16, p.comp) } } }, | |
| 8405 | 64 => .{ .complex = .{ .cf32 = .{ 0.0, val.toFloat(f32, p.comp) } } }, | |
| 8406 | 128 => .{ .complex = .{ .cf64 = .{ 0.0, val.toFloat(f64, p.comp) } } }, | |
| 8407 | 160 => .{ .complex = .{ .cf80 = .{ 0.0, val.toFloat(f80, p.comp) } } }, | |
| 8408 | 256 => .{ .complex = .{ .cf128 = .{ 0.0, val.toFloat(f128, p.comp) } } }, | |
| 8409 | else => unreachable, | |
| 8410 | }); | |
| 8411 | try res.un(p, .imaginary_literal, tok_i); | |
| 8158 | 8412 | } |
| 8159 | 8413 | return res; |
| 8160 | 8414 | } |
| ... | ... | @@ -8233,12 +8487,14 @@ fn fixedSizeInt(p: *Parser, base: u8, buf: []const u8, suffix: NumberSuffix, tok |
| 8233 | 8487 | if (overflow) { |
| 8234 | 8488 | try p.errTok(.int_literal_too_big, tok_i); |
| 8235 | 8489 | res.ty = .{ .specifier = .ulong_long }; |
| 8236 | res.node = try p.addNode(.{ .tag = .int_literal, .ty = res.ty, .data = undefined }); | |
| 8490 | res.node = try p.addNode(.{ .tag = .int_literal, .ty = res.ty, .data = undefined, .loc = @enumFromInt(tok_i) }); | |
| 8237 | 8491 | if (!p.in_macro) try p.value_map.put(res.node, res.val); |
| 8238 | 8492 | return res; |
| 8239 | 8493 | } |
| 8494 | const interned_val = try Value.int(val, p.comp); | |
| 8240 | 8495 | if (suffix.isSignedInteger()) { |
| 8241 | if (val > p.comp.types.intmax.maxInt(p.comp)) { | |
| 8496 | const max_int = try Value.maxInt(p.comp.types.intmax, p.comp); | |
| 8497 | if (interned_val.compare(.gt, max_int, p.comp)) { | |
| 8242 | 8498 | try p.errTok(.implicitly_unsigned_literal, tok_i); |
| 8243 | 8499 | } |
| 8244 | 8500 | } |
| ... | ... | @@ -8266,13 +8522,23 @@ fn fixedSizeInt(p: *Parser, base: u8, buf: []const u8, suffix: NumberSuffix, tok |
| 8266 | 8522 | for (specs) |spec| { |
| 8267 | 8523 | res.ty = Type{ .specifier = spec }; |
| 8268 | 8524 | if (res.ty.compareIntegerRanks(suffix_ty, p.comp).compare(.lt)) continue; |
| 8269 | const max_int = res.ty.maxInt(p.comp); | |
| 8270 | if (val <= max_int) break; | |
| 8525 | const max_int = try Value.maxInt(res.ty, p.comp); | |
| 8526 | if (interned_val.compare(.lte, max_int, p.comp)) break; | |
| 8271 | 8527 | } else { |
| 8272 | res.ty = .{ .specifier = .ulong_long }; | |
| 8528 | res.ty = .{ .specifier = spec: { | |
| 8529 | if (p.comp.langopts.emulate == .gcc) { | |
| 8530 | if (target_util.hasInt128(p.comp.target)) { | |
| 8531 | break :spec .int128; | |
| 8532 | } else { | |
| 8533 | break :spec .long_long; | |
| 8534 | } | |
| 8535 | } else { | |
| 8536 | break :spec .ulong_long; | |
| 8537 | } | |
| 8538 | } }; | |
| 8273 | 8539 | } |
| 8274 | 8540 | |
| 8275 | res.node = try p.addNode(.{ .tag = .int_literal, .ty = res.ty, .data = undefined }); | |
| 8541 | res.node = try p.addNode(.{ .tag = .int_literal, .ty = res.ty, .data = undefined, .loc = @enumFromInt(tok_i) }); | |
| 8276 | 8542 | if (!p.in_macro) try p.value_map.put(res.node, res.val); |
| 8277 | 8543 | return res; |
| 8278 | 8544 | } |
| ... | ... | @@ -8291,7 +8557,7 @@ fn parseInt(p: *Parser, prefix: NumberPrefix, buf: []const u8, suffix: NumberSuf |
| 8291 | 8557 | try p.errTok(.gnu_imaginary_constant, tok_i); |
| 8292 | 8558 | res.ty = res.ty.makeComplex(); |
| 8293 | 8559 | res.val = .{}; |
| 8294 | try res.un(p, .imaginary_literal); | |
| 8560 | try res.un(p, .imaginary_literal, tok_i); | |
| 8295 | 8561 | } |
| 8296 | 8562 | return res; |
| 8297 | 8563 | } |
| ... | ... | @@ -8326,17 +8592,6 @@ fn bitInt(p: *Parser, base: u8, buf: []const u8, suffix: NumberSuffix, tok_i: To |
| 8326 | 8592 | // value of the constant is positive or was specified in hexadecimal or octal notation. |
| 8327 | 8593 | const sign_bits = @intFromBool(suffix.isSignedInteger()); |
| 8328 | 8594 | const bits_needed = count + sign_bits; |
| 8329 | if (bits_needed > Compilation.bit_int_max_bits) { | |
| 8330 | const specifier: Type.Builder.Specifier = switch (suffix) { | |
| 8331 | .WB => .{ .bit_int = 0 }, | |
| 8332 | .UWB => .{ .ubit_int = 0 }, | |
| 8333 | .IWB => .{ .complex_bit_int = 0 }, | |
| 8334 | .IUWB => .{ .complex_ubit_int = 0 }, | |
| 8335 | else => unreachable, | |
| 8336 | }; | |
| 8337 | try p.errStr(.bit_int_too_big, tok_i, specifier.str(p.comp.langopts).?); | |
| 8338 | return error.ParsingFailed; | |
| 8339 | } | |
| 8340 | 8595 | break :blk @intCast(bits_needed); |
| 8341 | 8596 | }; |
| 8342 | 8597 | |
| ... | ... | @@ -8347,7 +8602,7 @@ fn bitInt(p: *Parser, base: u8, buf: []const u8, suffix: NumberSuffix, tok_i: To |
| 8347 | 8602 | .data = .{ .int = .{ .bits = bits_needed, .signedness = suffix.signedness() } }, |
| 8348 | 8603 | }, |
| 8349 | 8604 | }; |
| 8350 | res.node = try p.addNode(.{ .tag = .int_literal, .ty = res.ty, .data = undefined }); | |
| 8605 | res.node = try p.addNode(.{ .tag = .int_literal, .ty = res.ty, .data = undefined, .loc = @enumFromInt(tok_i) }); | |
| 8351 | 8606 | if (!p.in_macro) try p.value_map.put(res.node, res.val); |
| 8352 | 8607 | return res; |
| 8353 | 8608 | } |
| ... | ... | @@ -8420,6 +8675,10 @@ pub fn parseNumberToken(p: *Parser, tok_i: TokenIndex) !Result { |
| 8420 | 8675 | } |
| 8421 | 8676 | return error.ParsingFailed; |
| 8422 | 8677 | }; |
| 8678 | if (suffix.isFloat80() and p.comp.float80Type() == null) { | |
| 8679 | try p.errStr(.invalid_float_suffix, tok_i, suffix_str); | |
| 8680 | return error.ParsingFailed; | |
| 8681 | } | |
| 8423 | 8682 | |
| 8424 | 8683 | if (is_float) { |
| 8425 | 8684 | assert(prefix == .hex or prefix == .decimal); |
| ... | ... | @@ -8428,7 +8687,7 @@ pub fn parseNumberToken(p: *Parser, tok_i: TokenIndex) !Result { |
| 8428 | 8687 | return error.ParsingFailed; |
| 8429 | 8688 | } |
| 8430 | 8689 | const number = buf[0 .. buf.len - suffix_str.len]; |
| 8431 | return p.parseFloat(number, suffix); | |
| 8690 | return p.parseFloat(number, suffix, tok_i); | |
| 8432 | 8691 | } else { |
| 8433 | 8692 | return p.parseInt(prefix, int_part, suffix, tok_i); |
| 8434 | 8693 | } |
| ... | ... | @@ -8444,7 +8703,6 @@ fn ppNum(p: *Parser) Error!Result { |
| 8444 | 8703 | } |
| 8445 | 8704 | res.ty = if (res.ty.isUnsignedInt(p.comp)) p.comp.types.intmax.makeIntegerUnsigned() else p.comp.types.intmax; |
| 8446 | 8705 | } else if (res.val.opt_ref != .none) { |
| 8447 | // TODO add complex values | |
| 8448 | 8706 | try p.value_map.put(res.node, res.val); |
| 8449 | 8707 | } |
| 8450 | 8708 | return res; |
| ... | ... | @@ -8465,6 +8723,7 @@ fn parseNoEval(p: *Parser, comptime func: fn (*Parser) Error!Result) Error!Resul |
| 8465 | 8723 | /// : typeName ':' assignExpr |
| 8466 | 8724 | /// | keyword_default ':' assignExpr |
| 8467 | 8725 | fn genericSelection(p: *Parser) Error!Result { |
| 8726 | const kw_generic = p.tok_i; | |
| 8468 | 8727 | p.tok_i += 1; |
| 8469 | 8728 | const l_paren = try p.expectToken(.l_paren); |
| 8470 | 8729 | const controlling_tok = p.tok_i; |
| ... | ... | @@ -8508,17 +8767,23 @@ fn genericSelection(p: *Parser) Error!Result { |
| 8508 | 8767 | try p.errStr(.generic_duplicate, start, try p.typeStr(ty)); |
| 8509 | 8768 | try p.errStr(.generic_duplicate_here, chosen_tok, try p.typeStr(ty)); |
| 8510 | 8769 | } |
| 8511 | for (p.list_buf.items[list_buf_top + 1 ..], p.decl_buf.items[decl_buf_top..]) |item, prev_tok| { | |
| 8512 | const prev_ty = p.nodes.items(.ty)[@intFromEnum(item)]; | |
| 8513 | if (prev_ty.eql(ty, p.comp, true)) { | |
| 8514 | try p.errStr(.generic_duplicate, start, try p.typeStr(ty)); | |
| 8515 | try p.errStr(.generic_duplicate_here, @intFromEnum(prev_tok), try p.typeStr(ty)); | |
| 8770 | const list_buf = p.list_buf.items[list_buf_top + 1 ..]; | |
| 8771 | const decl_buf = p.decl_buf.items[decl_buf_top..]; | |
| 8772 | if (list_buf.len == decl_buf.len) { | |
| 8773 | // If these do not have the same length, there is already an error | |
| 8774 | for (list_buf, decl_buf) |item, prev_tok| { | |
| 8775 | const prev_ty = p.nodes.items(.ty)[@intFromEnum(item)]; | |
| 8776 | if (prev_ty.eql(ty, p.comp, true)) { | |
| 8777 | try p.errStr(.generic_duplicate, start, try p.typeStr(ty)); | |
| 8778 | try p.errStr(.generic_duplicate_here, @intFromEnum(prev_tok), try p.typeStr(ty)); | |
| 8779 | } | |
| 8516 | 8780 | } |
| 8517 | 8781 | } |
| 8518 | 8782 | try p.list_buf.append(try p.addNode(.{ |
| 8519 | 8783 | .tag = .generic_association_expr, |
| 8520 | 8784 | .ty = ty, |
| 8521 | 8785 | .data = .{ .un = node.node }, |
| 8786 | .loc = @enumFromInt(start), | |
| 8522 | 8787 | })); |
| 8523 | 8788 | try p.decl_buf.append(@enumFromInt(start)); |
| 8524 | 8789 | } else if (p.eatToken(.keyword_default)) |tok| { |
| ... | ... | @@ -8542,10 +8807,12 @@ fn genericSelection(p: *Parser) Error!Result { |
| 8542 | 8807 | try p.expectClosing(l_paren, .r_paren); |
| 8543 | 8808 | |
| 8544 | 8809 | if (chosen.node == .none) { |
| 8545 | if (default_tok != null) { | |
| 8810 | if (default_tok) |tok| { | |
| 8546 | 8811 | try p.list_buf.insert(list_buf_top + 1, try p.addNode(.{ |
| 8547 | 8812 | .tag = .generic_default_expr, |
| 8548 | 8813 | .data = .{ .un = default.node }, |
| 8814 | .ty = default.ty, | |
| 8815 | .loc = @enumFromInt(tok), | |
| 8549 | 8816 | })); |
| 8550 | 8817 | chosen = default; |
| 8551 | 8818 | } else { |
| ... | ... | @@ -8556,11 +8823,15 @@ fn genericSelection(p: *Parser) Error!Result { |
| 8556 | 8823 | try p.list_buf.insert(list_buf_top + 1, try p.addNode(.{ |
| 8557 | 8824 | .tag = .generic_association_expr, |
| 8558 | 8825 | .data = .{ .un = chosen.node }, |
| 8826 | .ty = chosen.ty, | |
| 8827 | .loc = @enumFromInt(chosen_tok), | |
| 8559 | 8828 | })); |
| 8560 | if (default_tok != null) { | |
| 8829 | if (default_tok) |tok| { | |
| 8561 | 8830 | try p.list_buf.append(try p.addNode(.{ |
| 8562 | 8831 | .tag = .generic_default_expr, |
| 8563 | .data = .{ .un = chosen.node }, | |
| 8832 | .data = .{ .un = default.node }, | |
| 8833 | .ty = default.ty, | |
| 8834 | .loc = @enumFromInt(tok), | |
| 8564 | 8835 | })); |
| 8565 | 8836 | } |
| 8566 | 8837 | } |
| ... | ... | @@ -8568,7 +8839,8 @@ fn genericSelection(p: *Parser) Error!Result { |
| 8568 | 8839 | var generic_node: Tree.Node = .{ |
| 8569 | 8840 | .tag = .generic_expr_one, |
| 8570 | 8841 | .ty = chosen.ty, |
| 8571 | .data = .{ .bin = .{ .lhs = controlling.node, .rhs = chosen.node } }, | |
| 8842 | .data = .{ .two = .{ controlling.node, chosen.node } }, | |
| 8843 | .loc = @enumFromInt(kw_generic), | |
| 8572 | 8844 | }; |
| 8573 | 8845 | const associations = p.list_buf.items[list_buf_top..]; |
| 8574 | 8846 | if (associations.len > 2) { // associations[0] == controlling.node |
| ... | ... | @@ -8578,3 +8850,42 @@ fn genericSelection(p: *Parser) Error!Result { |
| 8578 | 8850 | chosen.node = try p.addNode(generic_node); |
| 8579 | 8851 | return chosen; |
| 8580 | 8852 | } |
| 8853 | ||
| 8854 | test "Node locations" { | |
| 8855 | var comp = Compilation.init(std.testing.allocator, std.fs.cwd()); | |
| 8856 | defer comp.deinit(); | |
| 8857 | ||
| 8858 | const file = try comp.addSourceFromBuffer("file.c", | |
| 8859 | \\int foo = 5; | |
| 8860 | \\int bar = 10; | |
| 8861 | \\int main(void) {} | |
| 8862 | \\ | |
| 8863 | ); | |
| 8864 | ||
| 8865 | const builtin_macros = try comp.generateBuiltinMacros(.no_system_defines); | |
| 8866 | ||
| 8867 | var pp = Preprocessor.init(&comp); | |
| 8868 | defer pp.deinit(); | |
| 8869 | try pp.addBuiltinMacros(); | |
| 8870 | ||
| 8871 | _ = try pp.preprocess(builtin_macros); | |
| 8872 | ||
| 8873 | const eof = try pp.preprocess(file); | |
| 8874 | try pp.addToken(eof); | |
| 8875 | ||
| 8876 | var tree = try Parser.parse(&pp); | |
| 8877 | defer tree.deinit(); | |
| 8878 | ||
| 8879 | try std.testing.expectEqual(0, comp.diagnostics.list.items.len); | |
| 8880 | for (tree.root_decls, 0..) |node, i| { | |
| 8881 | const tok_i = tree.nodeTok(node).?; | |
| 8882 | const slice = tree.tokSlice(tok_i); | |
| 8883 | const expected = switch (i) { | |
| 8884 | 0 => "foo", | |
| 8885 | 1 => "bar", | |
| 8886 | 2 => "main", | |
| 8887 | else => unreachable, | |
| 8888 | }; | |
| 8889 | try std.testing.expectEqualStrings(expected, slice); | |
| 8890 | } | |
| 8891 | } |
lib/compiler/aro/aro/Preprocessor.zig+162-38| ... | ... | @@ -97,6 +97,11 @@ poisoned_identifiers: std.StringHashMap(void), |
| 97 | 97 | /// Map from Source.Id to macro name in the `#ifndef` condition which guards the source, if any |
| 98 | 98 | include_guards: std.AutoHashMapUnmanaged(Source.Id, []const u8) = .{}, |
| 99 | 99 | |
| 100 | /// Store `keyword_define` and `keyword_undef` tokens. | |
| 101 | /// Used to implement preprocessor debug dump options | |
| 102 | /// Must be false unless in -E mode (parser does not handle those token types) | |
| 103 | store_macro_tokens: bool = false, | |
| 104 | ||
| 100 | 105 | /// Memory is retained to avoid allocation on every single token. |
| 101 | 106 | top_expansion_buf: ExpandBuf, |
| 102 | 107 | |
| ... | ... | @@ -622,9 +627,12 @@ fn preprocessExtra(pp: *Preprocessor, source: Source) MacroError!TokenWithExpans |
| 622 | 627 | } |
| 623 | 628 | if_level -= 1; |
| 624 | 629 | }, |
| 625 | .keyword_define => try pp.define(&tokenizer), | |
| 630 | .keyword_define => try pp.define(&tokenizer, directive), | |
| 626 | 631 | .keyword_undef => { |
| 627 | 632 | const macro_name = (try pp.expectMacroName(&tokenizer)) orelse continue; |
| 633 | if (pp.store_macro_tokens) { | |
| 634 | try pp.addToken(tokFromRaw(directive)); | |
| 635 | } | |
| 628 | 636 | |
| 629 | 637 | _ = pp.defines.remove(macro_name); |
| 630 | 638 | try pp.expectNl(&tokenizer); |
| ... | ... | @@ -975,7 +983,7 @@ fn expr(pp: *Preprocessor, tokenizer: *Tokenizer) MacroError!bool { |
| 975 | 983 | .tok_i = @intCast(token_state.tokens_len), |
| 976 | 984 | .arena = pp.arena.allocator(), |
| 977 | 985 | .in_macro = true, |
| 978 | .strings = std.ArrayList(u8).init(pp.comp.gpa), | |
| 986 | .strings = std.ArrayListAligned(u8, 4).init(pp.comp.gpa), | |
| 979 | 987 | |
| 980 | 988 | .data = undefined, |
| 981 | 989 | .value_map = undefined, |
| ... | ... | @@ -1328,19 +1336,41 @@ fn stringify(pp: *Preprocessor, tokens: []const TokenWithExpansionLocs) !void { |
| 1328 | 1336 | try pp.char_buf.append(c); |
| 1329 | 1337 | } |
| 1330 | 1338 | } |
| 1331 | if (pp.char_buf.items[pp.char_buf.items.len - 1] == '\\') { | |
| 1339 | try pp.char_buf.ensureUnusedCapacity(2); | |
| 1340 | if (pp.char_buf.items[pp.char_buf.items.len - 1] != '\\') { | |
| 1341 | pp.char_buf.appendSliceAssumeCapacity("\"\n"); | |
| 1342 | return; | |
| 1343 | } | |
| 1344 | pp.char_buf.appendAssumeCapacity('"'); | |
| 1345 | var tokenizer: Tokenizer = .{ | |
| 1346 | .buf = pp.char_buf.items, | |
| 1347 | .index = 0, | |
| 1348 | .source = .generated, | |
| 1349 | .langopts = pp.comp.langopts, | |
| 1350 | .line = 0, | |
| 1351 | }; | |
| 1352 | const item = tokenizer.next(); | |
| 1353 | if (item.id == .unterminated_string_literal) { | |
| 1332 | 1354 | const tok = tokens[tokens.len - 1]; |
| 1333 | 1355 | try pp.comp.addDiagnostic(.{ |
| 1334 | 1356 | .tag = .invalid_pp_stringify_escape, |
| 1335 | 1357 | .loc = tok.loc, |
| 1336 | 1358 | }, tok.expansionSlice()); |
| 1337 | pp.char_buf.items.len -= 1; | |
| 1359 | pp.char_buf.items.len -= 2; // erase unpaired backslash and appended end quote | |
| 1360 | pp.char_buf.appendAssumeCapacity('"'); | |
| 1338 | 1361 | } |
| 1339 | try pp.char_buf.appendSlice("\"\n"); | |
| 1362 | pp.char_buf.appendAssumeCapacity('\n'); | |
| 1340 | 1363 | } |
| 1341 | 1364 | |
| 1342 | 1365 | fn reconstructIncludeString(pp: *Preprocessor, param_toks: []const TokenWithExpansionLocs, embed_args: ?*[]const TokenWithExpansionLocs, first: TokenWithExpansionLocs) !?[]const u8 { |
| 1343 | assert(param_toks.len != 0); | |
| 1366 | if (param_toks.len == 0) { | |
| 1367 | try pp.comp.addDiagnostic(.{ | |
| 1368 | .tag = .expected_filename, | |
| 1369 | .loc = first.loc, | |
| 1370 | }, first.expansionSlice()); | |
| 1371 | return null; | |
| 1372 | } | |
| 1373 | ||
| 1344 | 1374 | const char_top = pp.char_buf.items.len; |
| 1345 | 1375 | defer pp.char_buf.items.len = char_top; |
| 1346 | 1376 | |
| ... | ... | @@ -1539,11 +1569,13 @@ fn getPasteArgs(args: []const TokenWithExpansionLocs) []const TokenWithExpansion |
| 1539 | 1569 | |
| 1540 | 1570 | fn expandFuncMacro( |
| 1541 | 1571 | pp: *Preprocessor, |
| 1542 | loc: Source.Location, | |
| 1572 | macro_tok: TokenWithExpansionLocs, | |
| 1543 | 1573 | func_macro: *const Macro, |
| 1544 | 1574 | args: *const MacroArguments, |
| 1545 | 1575 | expanded_args: *const MacroArguments, |
| 1576 | hideset_arg: Hideset.Index, | |
| 1546 | 1577 | ) MacroError!ExpandBuf { |
| 1578 | var hideset = hideset_arg; | |
| 1547 | 1579 | var buf = ExpandBuf.init(pp.gpa); |
| 1548 | 1580 | try buf.ensureTotalCapacity(func_macro.tokens.len); |
| 1549 | 1581 | errdefer buf.deinit(); |
| ... | ... | @@ -1594,16 +1626,21 @@ fn expandFuncMacro( |
| 1594 | 1626 | }, |
| 1595 | 1627 | else => &[1]TokenWithExpansionLocs{tokFromRaw(raw_next)}, |
| 1596 | 1628 | }; |
| 1597 | ||
| 1598 | 1629 | try pp.pasteTokens(&buf, next); |
| 1599 | 1630 | if (next.len != 0) break; |
| 1600 | 1631 | }, |
| 1601 | 1632 | .macro_param_no_expand => { |
| 1633 | if (tok_i + 1 < func_macro.tokens.len and func_macro.tokens[tok_i + 1].id == .hash_hash) { | |
| 1634 | hideset = pp.hideset.get(tokFromRaw(func_macro.tokens[tok_i + 1]).loc); | |
| 1635 | } | |
| 1602 | 1636 | const slice = getPasteArgs(args.items[raw.end]); |
| 1603 | 1637 | const raw_loc = Source.Location{ .id = raw.source, .byte_offset = raw.start, .line = raw.line }; |
| 1604 | 1638 | try bufCopyTokens(&buf, slice, &.{raw_loc}); |
| 1605 | 1639 | }, |
| 1606 | 1640 | .macro_param => { |
| 1641 | if (tok_i + 1 < func_macro.tokens.len and func_macro.tokens[tok_i + 1].id == .hash_hash) { | |
| 1642 | hideset = pp.hideset.get(tokFromRaw(func_macro.tokens[tok_i + 1]).loc); | |
| 1643 | } | |
| 1607 | 1644 | const arg = expanded_args.items[raw.end]; |
| 1608 | 1645 | const raw_loc = Source.Location{ .id = raw.source, .byte_offset = raw.start, .line = raw.line }; |
| 1609 | 1646 | try bufCopyTokens(&buf, arg, &.{raw_loc}); |
| ... | ... | @@ -1642,9 +1679,9 @@ fn expandFuncMacro( |
| 1642 | 1679 | const arg = expanded_args.items[0]; |
| 1643 | 1680 | const result = if (arg.len == 0) blk: { |
| 1644 | 1681 | const extra = Diagnostics.Message.Extra{ .arguments = .{ .expected = 1, .actual = 0 } }; |
| 1645 | try pp.comp.addDiagnostic(.{ .tag = .expected_arguments, .loc = loc, .extra = extra }, &.{}); | |
| 1682 | try pp.comp.addDiagnostic(.{ .tag = .expected_arguments, .loc = macro_tok.loc, .extra = extra }, &.{}); | |
| 1646 | 1683 | break :blk false; |
| 1647 | } else try pp.handleBuiltinMacro(raw.id, arg, loc); | |
| 1684 | } else try pp.handleBuiltinMacro(raw.id, arg, macro_tok.loc); | |
| 1648 | 1685 | const start = pp.comp.generated_buf.items.len; |
| 1649 | 1686 | const w = pp.comp.generated_buf.writer(pp.gpa); |
| 1650 | 1687 | try w.print("{}\n", .{@intFromBool(result)}); |
| ... | ... | @@ -1655,7 +1692,7 @@ fn expandFuncMacro( |
| 1655 | 1692 | const not_found = "0\n"; |
| 1656 | 1693 | const result = if (arg.len == 0) blk: { |
| 1657 | 1694 | const extra = Diagnostics.Message.Extra{ .arguments = .{ .expected = 1, .actual = 0 } }; |
| 1658 | try pp.comp.addDiagnostic(.{ .tag = .expected_arguments, .loc = loc, .extra = extra }, &.{}); | |
| 1695 | try pp.comp.addDiagnostic(.{ .tag = .expected_arguments, .loc = macro_tok.loc, .extra = extra }, &.{}); | |
| 1659 | 1696 | break :blk not_found; |
| 1660 | 1697 | } else res: { |
| 1661 | 1698 | var invalid: ?TokenWithExpansionLocs = null; |
| ... | ... | @@ -1687,7 +1724,7 @@ fn expandFuncMacro( |
| 1687 | 1724 | if (vendor_ident != null and attr_ident == null) { |
| 1688 | 1725 | invalid = vendor_ident; |
| 1689 | 1726 | } else if (attr_ident == null and invalid == null) { |
| 1690 | invalid = .{ .id = .eof, .loc = loc }; | |
| 1727 | invalid = .{ .id = .eof, .loc = macro_tok.loc }; | |
| 1691 | 1728 | } |
| 1692 | 1729 | if (invalid) |some| { |
| 1693 | 1730 | try pp.comp.addDiagnostic( |
| ... | ... | @@ -1731,7 +1768,7 @@ fn expandFuncMacro( |
| 1731 | 1768 | const not_found = "0\n"; |
| 1732 | 1769 | const result = if (arg.len == 0) blk: { |
| 1733 | 1770 | const extra = Diagnostics.Message.Extra{ .arguments = .{ .expected = 1, .actual = 0 } }; |
| 1734 | try pp.comp.addDiagnostic(.{ .tag = .expected_arguments, .loc = loc, .extra = extra }, &.{}); | |
| 1771 | try pp.comp.addDiagnostic(.{ .tag = .expected_arguments, .loc = macro_tok.loc, .extra = extra }, &.{}); | |
| 1735 | 1772 | break :blk not_found; |
| 1736 | 1773 | } else res: { |
| 1737 | 1774 | var embed_args: []const TokenWithExpansionLocs = &.{}; |
| ... | ... | @@ -1877,11 +1914,11 @@ fn expandFuncMacro( |
| 1877 | 1914 | break; |
| 1878 | 1915 | }, |
| 1879 | 1916 | }; |
| 1880 | if (string == null and invalid == null) invalid = .{ .loc = loc, .id = .eof }; | |
| 1917 | if (string == null and invalid == null) invalid = .{ .loc = macro_tok.loc, .id = .eof }; | |
| 1881 | 1918 | if (invalid) |some| try pp.comp.addDiagnostic( |
| 1882 | 1919 | .{ .tag = .pragma_operator_string_literal, .loc = some.loc }, |
| 1883 | 1920 | some.expansionSlice(), |
| 1884 | ) else try pp.pragmaOperator(string.?, loc); | |
| 1921 | ) else try pp.pragmaOperator(string.?, macro_tok.loc); | |
| 1885 | 1922 | }, |
| 1886 | 1923 | .comma => { |
| 1887 | 1924 | if (tok_i + 2 < func_macro.tokens.len and func_macro.tokens[tok_i + 1].id == .hash_hash) { |
| ... | ... | @@ -1930,6 +1967,15 @@ fn expandFuncMacro( |
| 1930 | 1967 | } |
| 1931 | 1968 | removePlacemarkers(&buf); |
| 1932 | 1969 | |
| 1970 | const macro_expansion_locs = macro_tok.expansionSlice(); | |
| 1971 | for (buf.items) |*tok| { | |
| 1972 | try tok.addExpansionLocation(pp.gpa, &.{macro_tok.loc}); | |
| 1973 | try tok.addExpansionLocation(pp.gpa, macro_expansion_locs); | |
| 1974 | const tok_hidelist = pp.hideset.get(tok.loc); | |
| 1975 | const new_hidelist = try pp.hideset.@"union"(tok_hidelist, hideset); | |
| 1976 | try pp.hideset.put(tok.loc, new_hidelist); | |
| 1977 | } | |
| 1978 | ||
| 1933 | 1979 | return buf; |
| 1934 | 1980 | } |
| 1935 | 1981 | |
| ... | ... | @@ -2207,8 +2253,10 @@ fn expandMacroExhaustive( |
| 2207 | 2253 | else => |e| return e, |
| 2208 | 2254 | }; |
| 2209 | 2255 | assert(r_paren.id == .r_paren); |
| 2256 | var free_arg_expansion_locs = false; | |
| 2210 | 2257 | defer { |
| 2211 | 2258 | for (args.items) |item| { |
| 2259 | if (free_arg_expansion_locs) for (item) |tok| TokenWithExpansionLocs.free(tok.expansion_locs, pp.gpa); | |
| 2212 | 2260 | pp.gpa.free(item); |
| 2213 | 2261 | } |
| 2214 | 2262 | args.deinit(); |
| ... | ... | @@ -2234,6 +2282,7 @@ fn expandMacroExhaustive( |
| 2234 | 2282 | .arguments = .{ .expected = @intCast(macro.params.len), .actual = args_count }, |
| 2235 | 2283 | }; |
| 2236 | 2284 | if (macro.var_args and args_count < macro.params.len) { |
| 2285 | free_arg_expansion_locs = true; | |
| 2237 | 2286 | try pp.comp.addDiagnostic( |
| 2238 | 2287 | .{ .tag = .expected_at_least_arguments, .loc = buf.items[idx].loc, .extra = extra }, |
| 2239 | 2288 | buf.items[idx].expansionSlice(), |
| ... | ... | @@ -2243,6 +2292,7 @@ fn expandMacroExhaustive( |
| 2243 | 2292 | continue; |
| 2244 | 2293 | } |
| 2245 | 2294 | if (!macro.var_args and args_count != macro.params.len) { |
| 2295 | free_arg_expansion_locs = true; | |
| 2246 | 2296 | try pp.comp.addDiagnostic( |
| 2247 | 2297 | .{ .tag = .expected_arguments, .loc = buf.items[idx].loc, .extra = extra }, |
| 2248 | 2298 | buf.items[idx].expansionSlice(), |
| ... | ... | @@ -2264,19 +2314,9 @@ fn expandMacroExhaustive( |
| 2264 | 2314 | expanded_args.appendAssumeCapacity(try expand_buf.toOwnedSlice()); |
| 2265 | 2315 | } |
| 2266 | 2316 | |
| 2267 | var res = try pp.expandFuncMacro(macro_tok.loc, macro, &args, &expanded_args); | |
| 2317 | var res = try pp.expandFuncMacro(macro_tok, macro, &args, &expanded_args, hs); | |
| 2268 | 2318 | defer res.deinit(); |
| 2269 | 2319 | const tokens_added = res.items.len; |
| 2270 | ||
| 2271 | const macro_expansion_locs = macro_tok.expansionSlice(); | |
| 2272 | for (res.items) |*tok| { | |
| 2273 | try tok.addExpansionLocation(pp.gpa, &.{macro_tok.loc}); | |
| 2274 | try tok.addExpansionLocation(pp.gpa, macro_expansion_locs); | |
| 2275 | const tok_hidelist = pp.hideset.get(tok.loc); | |
| 2276 | const new_hidelist = try pp.hideset.@"union"(tok_hidelist, hs); | |
| 2277 | try pp.hideset.put(tok.loc, new_hidelist); | |
| 2278 | } | |
| 2279 | ||
| 2280 | 2320 | const tokens_removed = macro_scan_idx - idx + 1; |
| 2281 | 2321 | for (buf.items[idx .. idx + tokens_removed]) |tok| TokenWithExpansionLocs.free(tok.expansion_locs, pp.gpa); |
| 2282 | 2322 | try buf.replaceRange(idx, tokens_removed, res.items); |
| ... | ... | @@ -2476,7 +2516,7 @@ fn makeGeneratedToken(pp: *Preprocessor, start: usize, id: Token.Id, source: Tok |
| 2476 | 2516 | } |
| 2477 | 2517 | |
| 2478 | 2518 | /// Defines a new macro and warns if it is a duplicate |
| 2479 | fn defineMacro(pp: *Preprocessor, name_tok: RawToken, macro: Macro) Error!void { | |
| 2519 | fn defineMacro(pp: *Preprocessor, define_tok: RawToken, name_tok: RawToken, macro: Macro) Error!void { | |
| 2480 | 2520 | const name_str = pp.tokSlice(name_tok); |
| 2481 | 2521 | const gop = try pp.defines.getOrPut(pp.gpa, name_str); |
| 2482 | 2522 | 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 { |
| 2497 | 2537 | if (pp.verbose) { |
| 2498 | 2538 | pp.verboseLog(name_tok, "macro {s} defined", .{name_str}); |
| 2499 | 2539 | } |
| 2540 | if (pp.store_macro_tokens) { | |
| 2541 | try pp.addToken(tokFromRaw(define_tok)); | |
| 2542 | } | |
| 2500 | 2543 | gop.value_ptr.* = macro; |
| 2501 | 2544 | } |
| 2502 | 2545 | |
| 2503 | 2546 | /// Handle a #define directive. |
| 2504 | fn define(pp: *Preprocessor, tokenizer: *Tokenizer) Error!void { | |
| 2547 | fn define(pp: *Preprocessor, tokenizer: *Tokenizer, define_tok: RawToken) Error!void { | |
| 2505 | 2548 | // Get macro name and validate it. |
| 2506 | 2549 | const macro_name = tokenizer.nextNoWS(); |
| 2507 | 2550 | if (macro_name.id == .keyword_defined) { |
| ... | ... | @@ -2524,7 +2567,7 @@ fn define(pp: *Preprocessor, tokenizer: *Tokenizer) Error!void { |
| 2524 | 2567 | // Check for function macros and empty defines. |
| 2525 | 2568 | var first = tokenizer.next(); |
| 2526 | 2569 | switch (first.id) { |
| 2527 | .nl, .eof => return pp.defineMacro(macro_name, .{ | |
| 2570 | .nl, .eof => return pp.defineMacro(define_tok, macro_name, .{ | |
| 2528 | 2571 | .params = &.{}, |
| 2529 | 2572 | .tokens = &.{}, |
| 2530 | 2573 | .var_args = false, |
| ... | ... | @@ -2532,7 +2575,7 @@ fn define(pp: *Preprocessor, tokenizer: *Tokenizer) Error!void { |
| 2532 | 2575 | .is_func = false, |
| 2533 | 2576 | }), |
| 2534 | 2577 | .whitespace => first = tokenizer.next(), |
| 2535 | .l_paren => return pp.defineFn(tokenizer, macro_name, first), | |
| 2578 | .l_paren => return pp.defineFn(tokenizer, define_tok, macro_name, first), | |
| 2536 | 2579 | else => try pp.err(first, .whitespace_after_macro_name), |
| 2537 | 2580 | } |
| 2538 | 2581 | if (first.id == .hash_hash) { |
| ... | ... | @@ -2591,7 +2634,7 @@ fn define(pp: *Preprocessor, tokenizer: *Tokenizer) Error!void { |
| 2591 | 2634 | } |
| 2592 | 2635 | |
| 2593 | 2636 | const list = try pp.arena.allocator().dupe(RawToken, pp.token_buf.items); |
| 2594 | try pp.defineMacro(macro_name, .{ | |
| 2637 | try pp.defineMacro(define_tok, macro_name, .{ | |
| 2595 | 2638 | .loc = tokFromRaw(macro_name).loc, |
| 2596 | 2639 | .tokens = list, |
| 2597 | 2640 | .params = undefined, |
| ... | ... | @@ -2601,7 +2644,7 @@ fn define(pp: *Preprocessor, tokenizer: *Tokenizer) Error!void { |
| 2601 | 2644 | } |
| 2602 | 2645 | |
| 2603 | 2646 | /// Handle a function like #define directive. |
| 2604 | fn defineFn(pp: *Preprocessor, tokenizer: *Tokenizer, macro_name: RawToken, l_paren: RawToken) Error!void { | |
| 2647 | fn defineFn(pp: *Preprocessor, tokenizer: *Tokenizer, define_tok: RawToken, macro_name: RawToken, l_paren: RawToken) Error!void { | |
| 2605 | 2648 | assert(macro_name.id.isMacroIdentifier()); |
| 2606 | 2649 | var params = std.ArrayList([]const u8).init(pp.gpa); |
| 2607 | 2650 | defer params.deinit(); |
| ... | ... | @@ -2778,7 +2821,7 @@ fn defineFn(pp: *Preprocessor, tokenizer: *Tokenizer, macro_name: RawToken, l_pa |
| 2778 | 2821 | |
| 2779 | 2822 | const param_list = try pp.arena.allocator().dupe([]const u8, params.items); |
| 2780 | 2823 | const token_list = try pp.arena.allocator().dupe(RawToken, pp.token_buf.items); |
| 2781 | try pp.defineMacro(macro_name, .{ | |
| 2824 | try pp.defineMacro(define_tok, macro_name, .{ | |
| 2782 | 2825 | .is_func = true, |
| 2783 | 2826 | .params = param_list, |
| 2784 | 2827 | .var_args = var_args or gnu_var_args.len != 0, |
| ... | ... | @@ -3241,8 +3284,78 @@ fn printLinemarker( |
| 3241 | 3284 | // After how many empty lines are needed to replace them with linemarkers. |
| 3242 | 3285 | const collapse_newlines = 8; |
| 3243 | 3286 | |
| 3287 | pub const DumpMode = enum { | |
| 3288 | /// Standard preprocessor output; no macros | |
| 3289 | result_only, | |
| 3290 | /// Output only #define directives for all the macros defined during the execution of the preprocessor | |
| 3291 | /// Only macros which are still defined at the end of preprocessing are printed. | |
| 3292 | /// Only the most recent definition is printed | |
| 3293 | /// Defines are printed in arbitrary order | |
| 3294 | macros_only, | |
| 3295 | /// Standard preprocessor output; but additionally output #define's and #undef's for macros as they are encountered | |
| 3296 | macros_and_result, | |
| 3297 | /// Same as macros_and_result, except only the macro name is printed for #define's | |
| 3298 | macro_names_and_result, | |
| 3299 | }; | |
| 3300 | ||
| 3301 | /// Pretty-print the macro define or undef at location `loc`. | |
| 3302 | /// We re-tokenize the directive because we are printing a macro that may have the same name as one in | |
| 3303 | /// `pp.defines` but a different definition (due to being #undef'ed and then redefined) | |
| 3304 | fn prettyPrintMacro(pp: *Preprocessor, w: anytype, loc: Source.Location, parts: enum { name_only, name_and_body }) !void { | |
| 3305 | const source = pp.comp.getSource(loc.id); | |
| 3306 | var tokenizer: Tokenizer = .{ | |
| 3307 | .buf = source.buf, | |
| 3308 | .langopts = pp.comp.langopts, | |
| 3309 | .source = source.id, | |
| 3310 | .index = loc.byte_offset, | |
| 3311 | }; | |
| 3312 | var prev_ws = false; // avoid printing multiple whitespace if /* */ comments are within the macro def | |
| 3313 | var saw_name = false; // do not print comments before the name token is seen. | |
| 3314 | while (true) { | |
| 3315 | const tok = tokenizer.next(); | |
| 3316 | switch (tok.id) { | |
| 3317 | .comment => { | |
| 3318 | if (saw_name) { | |
| 3319 | prev_ws = false; | |
| 3320 | try w.print("{s}", .{pp.tokSlice(tok)}); | |
| 3321 | } | |
| 3322 | }, | |
| 3323 | .nl, .eof => break, | |
| 3324 | .whitespace => { | |
| 3325 | if (!prev_ws) { | |
| 3326 | try w.writeByte(' '); | |
| 3327 | prev_ws = true; | |
| 3328 | } | |
| 3329 | }, | |
| 3330 | else => { | |
| 3331 | prev_ws = false; | |
| 3332 | try w.print("{s}", .{pp.tokSlice(tok)}); | |
| 3333 | }, | |
| 3334 | } | |
| 3335 | if (tok.id == .identifier or tok.id == .extended_identifier) { | |
| 3336 | if (parts == .name_only) break; | |
| 3337 | saw_name = true; | |
| 3338 | } | |
| 3339 | } | |
| 3340 | } | |
| 3341 | ||
| 3342 | fn prettyPrintMacrosOnly(pp: *Preprocessor, w: anytype) !void { | |
| 3343 | var it = pp.defines.valueIterator(); | |
| 3344 | while (it.next()) |macro| { | |
| 3345 | if (macro.is_builtin) continue; | |
| 3346 | ||
| 3347 | try w.writeAll("#define "); | |
| 3348 | try pp.prettyPrintMacro(w, macro.loc, .name_and_body); | |
| 3349 | try w.writeByte('\n'); | |
| 3350 | } | |
| 3351 | } | |
| 3352 | ||
| 3244 | 3353 | /// Pretty print tokens and try to preserve whitespace. |
| 3245 | pub fn prettyPrintTokens(pp: *Preprocessor, w: anytype) !void { | |
| 3354 | pub fn prettyPrintTokens(pp: *Preprocessor, w: anytype, macro_dump_mode: DumpMode) !void { | |
| 3355 | if (macro_dump_mode == .macros_only) { | |
| 3356 | return pp.prettyPrintMacrosOnly(w); | |
| 3357 | } | |
| 3358 | ||
| 3246 | 3359 | const tok_ids = pp.tokens.items(.id); |
| 3247 | 3360 | |
| 3248 | 3361 | var i: u32 = 0; |
| ... | ... | @@ -3334,6 +3447,17 @@ pub fn prettyPrintTokens(pp: *Preprocessor, w: anytype) !void { |
| 3334 | 3447 | try pp.printLinemarker(w, line_col.line_no, source, .@"resume"); |
| 3335 | 3448 | last_nl = true; |
| 3336 | 3449 | }, |
| 3450 | .keyword_define, .keyword_undef => { | |
| 3451 | switch (macro_dump_mode) { | |
| 3452 | .macros_and_result, .macro_names_and_result => { | |
| 3453 | try w.writeByte('#'); | |
| 3454 | try pp.prettyPrintMacro(w, cur.loc, if (macro_dump_mode == .macros_and_result) .name_and_body else .name_only); | |
| 3455 | last_nl = false; | |
| 3456 | }, | |
| 3457 | .result_only => unreachable, // `pp.store_macro_tokens` should be false for standard preprocessor output | |
| 3458 | .macros_only => unreachable, // handled by prettyPrintMacrosOnly | |
| 3459 | } | |
| 3460 | }, | |
| 3337 | 3461 | else => { |
| 3338 | 3462 | const slice = pp.expandedSlice(cur); |
| 3339 | 3463 | try w.writeAll(slice); |
| ... | ... | @@ -3350,7 +3474,7 @@ test "Preserve pragma tokens sometimes" { |
| 3350 | 3474 | var buf = std.ArrayList(u8).init(allocator); |
| 3351 | 3475 | defer buf.deinit(); |
| 3352 | 3476 | |
| 3353 | var comp = Compilation.init(allocator); | |
| 3477 | var comp = Compilation.init(allocator, std.fs.cwd()); | |
| 3354 | 3478 | defer comp.deinit(); |
| 3355 | 3479 | |
| 3356 | 3480 | try comp.addDefaultPragmaHandlers(); |
| ... | ... | @@ -3364,7 +3488,7 @@ test "Preserve pragma tokens sometimes" { |
| 3364 | 3488 | const test_runner_macros = try comp.addSourceFromBuffer("<test_runner>", source_text); |
| 3365 | 3489 | const eof = try pp.preprocess(test_runner_macros); |
| 3366 | 3490 | try pp.addToken(eof); |
| 3367 | try pp.prettyPrintTokens(buf.writer()); | |
| 3491 | try pp.prettyPrintTokens(buf.writer(), .result_only); | |
| 3368 | 3492 | return allocator.dupe(u8, buf.items); |
| 3369 | 3493 | } |
| 3370 | 3494 | |
| ... | ... | @@ -3410,7 +3534,7 @@ test "destringify" { |
| 3410 | 3534 | try std.testing.expectEqualStrings(destringified, pp.char_buf.items); |
| 3411 | 3535 | } |
| 3412 | 3536 | }; |
| 3413 | var comp = Compilation.init(allocator); | |
| 3537 | var comp = Compilation.init(allocator, std.fs.cwd()); | |
| 3414 | 3538 | defer comp.deinit(); |
| 3415 | 3539 | var pp = Preprocessor.init(&comp); |
| 3416 | 3540 | defer pp.deinit(); |
| ... | ... | @@ -3468,7 +3592,7 @@ test "Include guards" { |
| 3468 | 3592 | } |
| 3469 | 3593 | |
| 3470 | 3594 | fn testIncludeGuard(allocator: std.mem.Allocator, comptime template: []const u8, tok_id: RawToken.Id, expected_guards: u32) !void { |
| 3471 | var comp = Compilation.init(allocator); | |
| 3595 | var comp = Compilation.init(allocator, std.fs.cwd()); | |
| 3472 | 3596 | defer comp.deinit(); |
| 3473 | 3597 | var pp = Preprocessor.init(&comp); |
| 3474 | 3598 | defer pp.deinit(); |
lib/compiler/aro/aro/Source.zig+11-1| ... | ... | @@ -75,7 +75,17 @@ pub fn lineCol(source: Source, loc: Location) LineCol { |
| 75 | 75 | i += 1; |
| 76 | 76 | continue; |
| 77 | 77 | }; |
| 78 | const cp = std.unicode.utf8Decode(source.buf[i..][0..len]) catch { | |
| 78 | const slice = source.buf[i..]; | |
| 79 | if (len > slice.len) { | |
| 80 | break; | |
| 81 | } | |
| 82 | const cp = switch (len) { | |
| 83 | 1 => slice[0], | |
| 84 | 2 => std.unicode.utf8Decode2(slice[0..2].*), | |
| 85 | 3 => std.unicode.utf8Decode3(slice[0..3].*), | |
| 86 | 4 => std.unicode.utf8Decode4(slice[0..4].*), | |
| 87 | else => unreachable, | |
| 88 | } catch { | |
| 79 | 89 | i += 1; |
| 80 | 90 | continue; |
| 81 | 91 | }; |
lib/compiler/aro/aro/SymbolStack.zig+11-4| ... | ... | @@ -178,9 +178,11 @@ pub fn defineTypedef( |
| 178 | 178 | if (s.get(name, .vars)) |prev| { |
| 179 | 179 | switch (prev.kind) { |
| 180 | 180 | .typedef => { |
| 181 | if (!ty.eql(prev.ty, p.comp, true)) { | |
| 182 | try p.errStr(.redefinition_of_typedef, tok, try p.typePairStrExtra(ty, " vs ", prev.ty)); | |
| 183 | if (prev.tok != 0) try p.errTok(.previous_definition, prev.tok); | |
| 181 | if (!prev.ty.is(.invalid)) { | |
| 182 | if (!ty.eql(prev.ty, p.comp, true)) { | |
| 183 | try p.errStr(.redefinition_of_typedef, tok, try p.typePairStrExtra(ty, " vs ", prev.ty)); | |
| 184 | if (prev.tok != 0) try p.errTok(.previous_definition, prev.tok); | |
| 185 | } | |
| 184 | 186 | } |
| 185 | 187 | }, |
| 186 | 188 | .enumeration, .decl, .def, .constexpr => { |
| ... | ... | @@ -194,7 +196,12 @@ pub fn defineTypedef( |
| 194 | 196 | .kind = .typedef, |
| 195 | 197 | .name = name, |
| 196 | 198 | .tok = tok, |
| 197 | .ty = ty, | |
| 199 | .ty = .{ | |
| 200 | .name = name, | |
| 201 | .specifier = ty.specifier, | |
| 202 | .qual = ty.qual, | |
| 203 | .data = ty.data, | |
| 204 | }, | |
| 198 | 205 | .node = node, |
| 199 | 206 | .val = .{}, |
| 200 | 207 | }); |
lib/compiler/aro/aro/Tokenizer.zig+43-13| ... | ... | @@ -178,6 +178,8 @@ pub const Token = struct { |
| 178 | 178 | keyword_return, |
| 179 | 179 | keyword_short, |
| 180 | 180 | keyword_signed, |
| 181 | keyword_signed1, | |
| 182 | keyword_signed2, | |
| 181 | 183 | keyword_sizeof, |
| 182 | 184 | keyword_static, |
| 183 | 185 | keyword_struct, |
| ... | ... | @@ -258,7 +260,6 @@ pub const Token = struct { |
| 258 | 260 | keyword_asm, |
| 259 | 261 | keyword_asm1, |
| 260 | 262 | keyword_asm2, |
| 261 | keyword_float80, | |
| 262 | 263 | /// _Float128 |
| 263 | 264 | keyword_float128_1, |
| 264 | 265 | /// __float128 |
| ... | ... | @@ -369,6 +370,8 @@ pub const Token = struct { |
| 369 | 370 | .keyword_return, |
| 370 | 371 | .keyword_short, |
| 371 | 372 | .keyword_signed, |
| 373 | .keyword_signed1, | |
| 374 | .keyword_signed2, | |
| 372 | 375 | .keyword_sizeof, |
| 373 | 376 | .keyword_static, |
| 374 | 377 | .keyword_struct, |
| ... | ... | @@ -417,7 +420,6 @@ pub const Token = struct { |
| 417 | 420 | .keyword_asm, |
| 418 | 421 | .keyword_asm1, |
| 419 | 422 | .keyword_asm2, |
| 420 | .keyword_float80, | |
| 421 | 423 | .keyword_float128_1, |
| 422 | 424 | .keyword_float128_2, |
| 423 | 425 | .keyword_int128, |
| ... | ... | @@ -627,6 +629,8 @@ pub const Token = struct { |
| 627 | 629 | .keyword_return => "return", |
| 628 | 630 | .keyword_short => "short", |
| 629 | 631 | .keyword_signed => "signed", |
| 632 | .keyword_signed1 => "__signed", | |
| 633 | .keyword_signed2 => "__signed__", | |
| 630 | 634 | .keyword_sizeof => "sizeof", |
| 631 | 635 | .keyword_static => "static", |
| 632 | 636 | .keyword_struct => "struct", |
| ... | ... | @@ -702,7 +706,6 @@ pub const Token = struct { |
| 702 | 706 | .keyword_asm => "asm", |
| 703 | 707 | .keyword_asm1 => "__asm", |
| 704 | 708 | .keyword_asm2 => "__asm__", |
| 705 | .keyword_float80 => "__float80", | |
| 706 | 709 | .keyword_float128_1 => "_Float128", |
| 707 | 710 | .keyword_float128_2 => "__float128", |
| 708 | 711 | .keyword_int128 => "__int128", |
| ... | ... | @@ -732,7 +735,8 @@ pub const Token = struct { |
| 732 | 735 | |
| 733 | 736 | pub fn symbol(id: Id) []const u8 { |
| 734 | 737 | return switch (id) { |
| 735 | .macro_string, .invalid => unreachable, | |
| 738 | .macro_string => unreachable, | |
| 739 | .invalid => "invalid bytes", | |
| 736 | 740 | .identifier, |
| 737 | 741 | .extended_identifier, |
| 738 | 742 | .macro_func, |
| ... | ... | @@ -873,10 +877,7 @@ pub const Token = struct { |
| 873 | 877 | } |
| 874 | 878 | |
| 875 | 879 | const all_kws = std.StaticStringMap(Id).initComptime(.{ |
| 876 | .{ "auto", auto: { | |
| 877 | @setEvalBranchQuota(3000); | |
| 878 | break :auto .keyword_auto; | |
| 879 | } }, | |
| 880 | .{ "auto", .keyword_auto }, | |
| 880 | 881 | .{ "break", .keyword_break }, |
| 881 | 882 | .{ "case", .keyword_case }, |
| 882 | 883 | .{ "char", .keyword_char }, |
| ... | ... | @@ -898,6 +899,8 @@ pub const Token = struct { |
| 898 | 899 | .{ "return", .keyword_return }, |
| 899 | 900 | .{ "short", .keyword_short }, |
| 900 | 901 | .{ "signed", .keyword_signed }, |
| 902 | .{ "__signed", .keyword_signed1 }, | |
| 903 | .{ "__signed__", .keyword_signed2 }, | |
| 901 | 904 | .{ "sizeof", .keyword_sizeof }, |
| 902 | 905 | .{ "static", .keyword_static }, |
| 903 | 906 | .{ "struct", .keyword_struct }, |
| ... | ... | @@ -982,7 +985,6 @@ pub const Token = struct { |
| 982 | 985 | .{ "asm", .keyword_asm }, |
| 983 | 986 | .{ "__asm", .keyword_asm1 }, |
| 984 | 987 | .{ "__asm__", .keyword_asm2 }, |
| 985 | .{ "__float80", .keyword_float80 }, | |
| 986 | 988 | .{ "_Float128", .keyword_float128_1 }, |
| 987 | 989 | .{ "__float128", .keyword_float128_2 }, |
| 988 | 990 | .{ "__int128", .keyword_int128 }, |
| ... | ... | @@ -1300,11 +1302,17 @@ pub fn next(self: *Tokenizer) Token { |
| 1300 | 1302 | else => {}, |
| 1301 | 1303 | }, |
| 1302 | 1304 | .char_escape_sequence => switch (c) { |
| 1303 | '\r', '\n' => unreachable, // removed by line splicing | |
| 1305 | '\r', '\n' => { | |
| 1306 | id = .unterminated_char_literal; | |
| 1307 | break; | |
| 1308 | }, | |
| 1304 | 1309 | else => state = .char_literal, |
| 1305 | 1310 | }, |
| 1306 | 1311 | .string_escape_sequence => switch (c) { |
| 1307 | '\r', '\n' => unreachable, // removed by line splicing | |
| 1312 | '\r', '\n' => { | |
| 1313 | id = .unterminated_string_literal; | |
| 1314 | break; | |
| 1315 | }, | |
| 1308 | 1316 | else => state = .string_literal, |
| 1309 | 1317 | }, |
| 1310 | 1318 | .identifier, .extended_identifier => switch (c) { |
| ... | ... | @@ -1792,7 +1800,7 @@ pub fn nextNoWSComments(self: *Tokenizer) Token { |
| 1792 | 1800 | /// Try to tokenize a '::' even if not supported by the current language standard. |
| 1793 | 1801 | pub fn colonColon(self: *Tokenizer) Token { |
| 1794 | 1802 | var tok = self.nextNoWS(); |
| 1795 | if (tok.id == .colon and self.buf[self.index] == ':') { | |
| 1803 | if (tok.id == .colon and self.index < self.buf.len and self.buf[self.index] == ':') { | |
| 1796 | 1804 | self.index += 1; |
| 1797 | 1805 | tok.id = .colon_colon; |
| 1798 | 1806 | } |
| ... | ... | @@ -2142,8 +2150,30 @@ test "C23 keywords" { |
| 2142 | 2150 | }, .c23); |
| 2143 | 2151 | } |
| 2144 | 2152 | |
| 2153 | test "Tokenizer fuzz test" { | |
| 2154 | var comp = Compilation.init(std.testing.allocator, std.fs.cwd()); | |
| 2155 | defer comp.deinit(); | |
| 2156 | ||
| 2157 | const input_bytes = std.testing.fuzzInput(.{}); | |
| 2158 | if (input_bytes.len == 0) return; | |
| 2159 | ||
| 2160 | const source = try comp.addSourceFromBuffer("fuzz.c", input_bytes); | |
| 2161 | ||
| 2162 | var tokenizer: Tokenizer = .{ | |
| 2163 | .buf = source.buf, | |
| 2164 | .source = source.id, | |
| 2165 | .langopts = comp.langopts, | |
| 2166 | }; | |
| 2167 | while (true) { | |
| 2168 | const prev_index = tokenizer.index; | |
| 2169 | const tok = tokenizer.next(); | |
| 2170 | if (tok.id == .eof) break; | |
| 2171 | try std.testing.expect(prev_index < tokenizer.index); // ensure that the tokenizer always makes progress | |
| 2172 | } | |
| 2173 | } | |
| 2174 | ||
| 2145 | 2175 | fn expectTokensExtra(contents: []const u8, expected_tokens: []const Token.Id, standard: ?LangOpts.Standard) !void { |
| 2146 | var comp = Compilation.init(std.testing.allocator); | |
| 2176 | var comp = Compilation.init(std.testing.allocator, std.fs.cwd()); | |
| 2147 | 2177 | defer comp.deinit(); |
| 2148 | 2178 | if (standard) |provided| { |
| 2149 | 2179 | comp.langopts.standard = provided; |
lib/compiler/aro/aro/Tree.zig+172-76| ... | ... | @@ -137,15 +137,22 @@ pub const Node = struct { |
| 137 | 137 | tag: Tag, |
| 138 | 138 | ty: Type = .{ .specifier = .void }, |
| 139 | 139 | data: Data, |
| 140 | loc: Loc = .none, | |
| 140 | 141 | |
| 141 | 142 | pub const Range = struct { start: u32, end: u32 }; |
| 142 | 143 | |
| 144 | pub const Loc = enum(u32) { | |
| 145 | none = std.math.maxInt(u32), | |
| 146 | _, | |
| 147 | }; | |
| 148 | ||
| 143 | 149 | pub const Data = union { |
| 144 | 150 | decl: struct { |
| 145 | 151 | name: TokenIndex, |
| 146 | 152 | node: NodeIndex = .none, |
| 147 | 153 | }, |
| 148 | 154 | decl_ref: TokenIndex, |
| 155 | two: [2]NodeIndex, | |
| 149 | 156 | range: Range, |
| 150 | 157 | if3: struct { |
| 151 | 158 | cond: NodeIndex, |
| ... | ... | @@ -277,7 +284,8 @@ pub const Tag = enum(u8) { |
| 277 | 284 | |
| 278 | 285 | // ====== Decl ====== |
| 279 | 286 | |
| 280 | // _Static_assert | |
| 287 | /// _Static_assert | |
| 288 | /// loc is token index of _Static_assert | |
| 281 | 289 | static_assert, |
| 282 | 290 | |
| 283 | 291 | // function prototype |
| ... | ... | @@ -303,17 +311,18 @@ pub const Tag = enum(u8) { |
| 303 | 311 | threadlocal_static_var, |
| 304 | 312 | |
| 305 | 313 | /// __asm__("...") at file scope |
| 314 | /// loc is token index of __asm__ keyword | |
| 306 | 315 | file_scope_asm, |
| 307 | 316 | |
| 308 | 317 | // typedef declaration |
| 309 | 318 | typedef, |
| 310 | 319 | |
| 311 | 320 | // container declarations |
| 312 | /// { lhs; rhs; } | |
| 321 | /// { two[0]; two[1]; } | |
| 313 | 322 | struct_decl_two, |
| 314 | /// { lhs; rhs; } | |
| 323 | /// { two[0]; two[1]; } | |
| 315 | 324 | union_decl_two, |
| 316 | /// { lhs, rhs, } | |
| 325 | /// { two[0], two[1], } | |
| 317 | 326 | enum_decl_two, |
| 318 | 327 | /// { range } |
| 319 | 328 | struct_decl, |
| ... | ... | @@ -339,7 +348,7 @@ pub const Tag = enum(u8) { |
| 339 | 348 | // ====== Stmt ====== |
| 340 | 349 | |
| 341 | 350 | labeled_stmt, |
| 342 | /// { first; second; } first and second may be null | |
| 351 | /// { two[0]; two[1]; } first and second may be null | |
| 343 | 352 | compound_stmt_two, |
| 344 | 353 | /// { data } |
| 345 | 354 | compound_stmt, |
| ... | ... | @@ -476,7 +485,7 @@ pub const Tag = enum(u8) { |
| 476 | 485 | real_expr, |
| 477 | 486 | /// lhs[rhs] lhs is pointer/array type, rhs is integer type |
| 478 | 487 | array_access_expr, |
| 479 | /// first(second) second may be 0 | |
| 488 | /// two[0](two[1]) two[1] may be 0 | |
| 480 | 489 | call_expr_one, |
| 481 | 490 | /// data[0](data[1..]) |
| 482 | 491 | call_expr, |
| ... | ... | @@ -515,7 +524,7 @@ pub const Tag = enum(u8) { |
| 515 | 524 | sizeof_expr, |
| 516 | 525 | /// _Alignof(un?) |
| 517 | 526 | alignof_expr, |
| 518 | /// _Generic(controlling lhs, chosen rhs) | |
| 527 | /// _Generic(controlling two[0], chosen two[1]) | |
| 519 | 528 | generic_expr_one, |
| 520 | 529 | /// _Generic(controlling range[0], chosen range[1], rest range[2..]) |
| 521 | 530 | generic_expr, |
| ... | ... | @@ -534,28 +543,34 @@ pub const Tag = enum(u8) { |
| 534 | 543 | |
| 535 | 544 | // ====== Initializer expressions ====== |
| 536 | 545 | |
| 537 | /// { lhs, rhs } | |
| 546 | /// { two[0], two[1] } | |
| 538 | 547 | array_init_expr_two, |
| 539 | 548 | /// { range } |
| 540 | 549 | array_init_expr, |
| 541 | /// { lhs, rhs } | |
| 550 | /// { two[0], two[1] } | |
| 542 | 551 | struct_init_expr_two, |
| 543 | 552 | /// { range } |
| 544 | 553 | struct_init_expr, |
| 545 | 554 | /// { union_init } |
| 546 | 555 | union_init_expr, |
| 556 | ||
| 547 | 557 | /// (ty){ un } |
| 558 | /// loc is token index of l_paren | |
| 548 | 559 | compound_literal_expr, |
| 549 | 560 | /// (static ty){ un } |
| 561 | /// loc is token index of l_paren | |
| 550 | 562 | static_compound_literal_expr, |
| 551 | 563 | /// (thread_local ty){ un } |
| 564 | /// loc is token index of l_paren | |
| 552 | 565 | thread_local_compound_literal_expr, |
| 553 | 566 | /// (static thread_local ty){ un } |
| 567 | /// loc is token index of l_paren | |
| 554 | 568 | static_thread_local_compound_literal_expr, |
| 555 | 569 | |
| 556 | 570 | /// Inserted at the end of a function body if no return stmt is found. |
| 557 | 571 | /// ty is the functions return type |
| 558 | 572 | /// data is return_zero which is true if the function is called "main" and ty is compatible with int |
| 573 | /// loc is token index of closing r_brace of function | |
| 559 | 574 | implicit_return, |
| 560 | 575 | |
| 561 | 576 | /// 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 |
| 608 | 623 | } |
| 609 | 624 | } |
| 610 | 625 | |
| 626 | const CallableResultUsage = struct { | |
| 627 | /// name token of the thing being called, for diagnostics | |
| 628 | tok: TokenIndex, | |
| 629 | /// true if `nodiscard` attribute present | |
| 630 | nodiscard: bool, | |
| 631 | /// true if `warn_unused_result` attribute present | |
| 632 | warn_unused_result: bool, | |
| 633 | }; | |
| 634 | ||
| 635 | pub fn callableResultUsage(tree: *const Tree, node: NodeIndex) ?CallableResultUsage { | |
| 636 | const data = tree.nodes.items(.data); | |
| 637 | ||
| 638 | var cur_node = node; | |
| 639 | while (true) switch (tree.nodes.items(.tag)[@intFromEnum(cur_node)]) { | |
| 640 | .decl_ref_expr => { | |
| 641 | const tok = data[@intFromEnum(cur_node)].decl_ref; | |
| 642 | const fn_ty = tree.nodes.items(.ty)[@intFromEnum(node)].elemType(); | |
| 643 | return .{ | |
| 644 | .tok = tok, | |
| 645 | .nodiscard = fn_ty.hasAttribute(.nodiscard), | |
| 646 | .warn_unused_result = fn_ty.hasAttribute(.warn_unused_result), | |
| 647 | }; | |
| 648 | }, | |
| 649 | .paren_expr => cur_node = data[@intFromEnum(cur_node)].un, | |
| 650 | .comma_expr => cur_node = data[@intFromEnum(cur_node)].bin.rhs, | |
| 651 | ||
| 652 | .explicit_cast, .implicit_cast => cur_node = data[@intFromEnum(cur_node)].cast.operand, | |
| 653 | .addr_of_expr, .deref_expr => cur_node = data[@intFromEnum(cur_node)].un, | |
| 654 | .call_expr_one => cur_node = data[@intFromEnum(cur_node)].two[0], | |
| 655 | .call_expr => cur_node = tree.data[data[@intFromEnum(cur_node)].range.start], | |
| 656 | .member_access_expr, .member_access_ptr_expr => { | |
| 657 | const member = data[@intFromEnum(cur_node)].member; | |
| 658 | var ty = tree.nodes.items(.ty)[@intFromEnum(member.lhs)]; | |
| 659 | if (ty.isPtr()) ty = ty.elemType(); | |
| 660 | const record = ty.getRecord().?; | |
| 661 | const field = record.fields[member.index]; | |
| 662 | const attributes = if (record.field_attributes) |attrs| attrs[member.index] else &.{}; | |
| 663 | return .{ | |
| 664 | .tok = field.name_tok, | |
| 665 | .nodiscard = for (attributes) |attr| { | |
| 666 | if (attr.tag == .nodiscard) break true; | |
| 667 | } else false, | |
| 668 | .warn_unused_result = for (attributes) |attr| { | |
| 669 | if (attr.tag == .warn_unused_result) break true; | |
| 670 | } else false, | |
| 671 | }; | |
| 672 | }, | |
| 673 | else => return null, | |
| 674 | }; | |
| 675 | } | |
| 676 | ||
| 611 | 677 | pub fn isLval(tree: *const Tree, node: NodeIndex) bool { |
| 612 | 678 | var is_const: bool = undefined; |
| 613 | 679 | return tree.isLvalExtra(node, &is_const); |
| ... | ... | @@ -672,17 +738,66 @@ pub fn isLvalExtra(tree: *const Tree, node: NodeIndex, is_const: *bool) bool { |
| 672 | 738 | } |
| 673 | 739 | } |
| 674 | 740 | |
| 741 | /// This should only be used for node tags that represent AST nodes which have an arbitrary number of children | |
| 742 | /// It particular it should *not* be used for nodes with .un or .bin data types | |
| 743 | /// | |
| 744 | /// For call expressions, child_nodes[0] is the function pointer being called and child_nodes[1..] | |
| 745 | /// are the arguments | |
| 746 | /// | |
| 747 | /// For generic selection expressions, child_nodes[0] is the controlling expression, | |
| 748 | /// child_nodes[1] is the chosen expression (it is a syntax error for there to be no chosen expression), | |
| 749 | /// and child_nodes[2..] are the remaining expressions. | |
| 750 | pub fn childNodes(tree: *const Tree, node: NodeIndex) []const NodeIndex { | |
| 751 | const tags = tree.nodes.items(.tag); | |
| 752 | const data = tree.nodes.items(.data); | |
| 753 | switch (tags[@intFromEnum(node)]) { | |
| 754 | .compound_stmt_two, | |
| 755 | .array_init_expr_two, | |
| 756 | .struct_init_expr_two, | |
| 757 | .enum_decl_two, | |
| 758 | .struct_decl_two, | |
| 759 | .union_decl_two, | |
| 760 | .call_expr_one, | |
| 761 | .generic_expr_one, | |
| 762 | => { | |
| 763 | const index: u32 = @intFromEnum(node); | |
| 764 | const end = std.mem.indexOfScalar(NodeIndex, &data[index].two, .none) orelse 2; | |
| 765 | return data[index].two[0..end]; | |
| 766 | }, | |
| 767 | .compound_stmt, | |
| 768 | .array_init_expr, | |
| 769 | .struct_init_expr, | |
| 770 | .enum_decl, | |
| 771 | .struct_decl, | |
| 772 | .union_decl, | |
| 773 | .call_expr, | |
| 774 | .generic_expr, | |
| 775 | => { | |
| 776 | const range = data[@intFromEnum(node)].range; | |
| 777 | return tree.data[range.start..range.end]; | |
| 778 | }, | |
| 779 | else => unreachable, | |
| 780 | } | |
| 781 | } | |
| 782 | ||
| 675 | 783 | pub fn tokSlice(tree: *const Tree, tok_i: TokenIndex) []const u8 { |
| 676 | 784 | if (tree.tokens.items(.id)[tok_i].lexeme()) |some| return some; |
| 677 | 785 | const loc = tree.tokens.items(.loc)[tok_i]; |
| 678 | var tmp_tokenizer = Tokenizer{ | |
| 679 | .buf = tree.comp.getSource(loc.id).buf, | |
| 680 | .langopts = tree.comp.langopts, | |
| 681 | .index = loc.byte_offset, | |
| 682 | .source = .generated, | |
| 786 | return tree.comp.locSlice(loc); | |
| 787 | } | |
| 788 | ||
| 789 | pub fn nodeTok(tree: *const Tree, node: NodeIndex) ?TokenIndex { | |
| 790 | std.debug.assert(node != .none); | |
| 791 | const loc = tree.nodes.items(.loc)[@intFromEnum(node)]; | |
| 792 | return switch (loc) { | |
| 793 | .none => null, | |
| 794 | else => |tok_i| @intFromEnum(tok_i), | |
| 683 | 795 | }; |
| 684 | const tok = tmp_tokenizer.next(); | |
| 685 | return tmp_tokenizer.buf[tok.start..tok.end]; | |
| 796 | } | |
| 797 | ||
| 798 | pub fn nodeLoc(tree: *const Tree, node: NodeIndex) ?Source.Location { | |
| 799 | const tok_i = tree.nodeTok(node) orelse return null; | |
| 800 | return tree.tokens.items(.loc)[@intFromEnum(tok_i)]; | |
| 686 | 801 | } |
| 687 | 802 | |
| 688 | 803 | pub fn dump(tree: *const Tree, config: std.io.tty.Config, writer: anytype) !void { |
| ... | ... | @@ -766,6 +881,10 @@ fn dumpNode( |
| 766 | 881 | } |
| 767 | 882 | try config.setColor(w, TYPE); |
| 768 | 883 | try w.writeByte('\''); |
| 884 | const name = ty.getName(); | |
| 885 | if (name != .empty) { | |
| 886 | try w.print("{s}': '", .{mapper.lookup(name)}); | |
| 887 | } | |
| 769 | 888 | try ty.dump(mapper, tree.comp.langopts, w); |
| 770 | 889 | try w.writeByte('\''); |
| 771 | 890 | |
| ... | ... | @@ -794,7 +913,9 @@ fn dumpNode( |
| 794 | 913 | |
| 795 | 914 | if (ty.specifier == .attributed) { |
| 796 | 915 | try config.setColor(w, ATTRIBUTE); |
| 797 | for (ty.data.attributed.attributes) |attr| { | |
| 916 | var it = Attribute.Iterator.initType(ty); | |
| 917 | while (it.next()) |item| { | |
| 918 | const attr, _ = item; | |
| 798 | 919 | try w.writeByteNTimes(' ', level + half); |
| 799 | 920 | try w.print("attr: {s}", .{@tagName(attr.tag)}); |
| 800 | 921 | try tree.dumpAttribute(attr, w); |
| ... | ... | @@ -900,9 +1021,16 @@ fn dumpNode( |
| 900 | 1021 | .enum_decl, |
| 901 | 1022 | .struct_decl, |
| 902 | 1023 | .union_decl, |
| 1024 | .compound_stmt_two, | |
| 1025 | .array_init_expr_two, | |
| 1026 | .struct_init_expr_two, | |
| 1027 | .enum_decl_two, | |
| 1028 | .struct_decl_two, | |
| 1029 | .union_decl_two, | |
| 903 | 1030 | => { |
| 1031 | const child_nodes = tree.childNodes(node); | |
| 904 | 1032 | const maybe_field_attributes = if (ty.getRecord()) |record| record.field_attributes else null; |
| 905 | for (tree.data[data.range.start..data.range.end], 0..) |stmt, i| { | |
| 1033 | for (child_nodes, 0..) |stmt, i| { | |
| 906 | 1034 | if (i != 0) try w.writeByte('\n'); |
| 907 | 1035 | try tree.dumpNode(stmt, level + delta, mapper, config, w); |
| 908 | 1036 | if (maybe_field_attributes) |field_attributes| { |
| ... | ... | @@ -914,33 +1042,6 @@ fn dumpNode( |
| 914 | 1042 | } |
| 915 | 1043 | } |
| 916 | 1044 | }, |
| 917 | .compound_stmt_two, | |
| 918 | .array_init_expr_two, | |
| 919 | .struct_init_expr_two, | |
| 920 | .enum_decl_two, | |
| 921 | .struct_decl_two, | |
| 922 | .union_decl_two, | |
| 923 | => { | |
| 924 | var attr_array = [2][]const Attribute{ &.{}, &.{} }; | |
| 925 | const empty: [][]const Attribute = &attr_array; | |
| 926 | const field_attributes = if (ty.getRecord()) |record| (record.field_attributes orelse empty.ptr) else empty.ptr; | |
| 927 | if (data.bin.lhs != .none) { | |
| 928 | try tree.dumpNode(data.bin.lhs, level + delta, mapper, config, w); | |
| 929 | if (field_attributes[0].len > 0) { | |
| 930 | try config.setColor(w, ATTRIBUTE); | |
| 931 | try tree.dumpFieldAttributes(field_attributes[0], level + delta + half, w); | |
| 932 | try config.setColor(w, .reset); | |
| 933 | } | |
| 934 | } | |
| 935 | if (data.bin.rhs != .none) { | |
| 936 | try tree.dumpNode(data.bin.rhs, level + delta, mapper, config, w); | |
| 937 | if (field_attributes[1].len > 0) { | |
| 938 | try config.setColor(w, ATTRIBUTE); | |
| 939 | try tree.dumpFieldAttributes(field_attributes[1], level + delta + half, w); | |
| 940 | try config.setColor(w, .reset); | |
| 941 | } | |
| 942 | } | |
| 943 | }, | |
| 944 | 1045 | .union_init_expr => { |
| 945 | 1046 | try w.writeByteNTimes(' ', level + half); |
| 946 | 1047 | try w.writeAll("field index: "); |
| ... | ... | @@ -1130,23 +1231,21 @@ fn dumpNode( |
| 1130 | 1231 | try tree.dumpNode(data.un, level + delta, mapper, config, w); |
| 1131 | 1232 | } |
| 1132 | 1233 | }, |
| 1133 | .call_expr => { | |
| 1134 | try w.writeByteNTimes(' ', level + half); | |
| 1135 | try w.writeAll("lhs:\n"); | |
| 1136 | try tree.dumpNode(tree.data[data.range.start], level + delta, mapper, config, w); | |
| 1234 | .call_expr, .call_expr_one => { | |
| 1235 | const child_nodes = tree.childNodes(node); | |
| 1236 | const fn_ptr = child_nodes[0]; | |
| 1237 | const args = child_nodes[1..]; | |
| 1137 | 1238 | |
| 1138 | try w.writeByteNTimes(' ', level + half); | |
| 1139 | try w.writeAll("args:\n"); | |
| 1140 | for (tree.data[data.range.start + 1 .. data.range.end]) |arg| try tree.dumpNode(arg, level + delta, mapper, config, w); | |
| 1141 | }, | |
| 1142 | .call_expr_one => { | |
| 1143 | 1239 | try w.writeByteNTimes(' ', level + half); |
| 1144 | 1240 | try w.writeAll("lhs:\n"); |
| 1145 | try tree.dumpNode(data.bin.lhs, level + delta, mapper, config, w); | |
| 1146 | if (data.bin.rhs != .none) { | |
| 1241 | try tree.dumpNode(fn_ptr, level + delta, mapper, config, w); | |
| 1242 | ||
| 1243 | if (args.len > 0) { | |
| 1147 | 1244 | try w.writeByteNTimes(' ', level + half); |
| 1148 | try w.writeAll("arg:\n"); | |
| 1149 | try tree.dumpNode(data.bin.rhs, level + delta, mapper, config, w); | |
| 1245 | try w.writeAll("args:\n"); | |
| 1246 | for (args) |arg| { | |
| 1247 | try tree.dumpNode(arg, level + delta, mapper, config, w); | |
| 1248 | } | |
| 1150 | 1249 | } |
| 1151 | 1250 | }, |
| 1152 | 1251 | .builtin_call_expr => { |
| ... | ... | @@ -1295,28 +1394,25 @@ fn dumpNode( |
| 1295 | 1394 | try tree.dumpNode(data.un, level + delta, mapper, config, w); |
| 1296 | 1395 | } |
| 1297 | 1396 | }, |
| 1298 | .generic_expr_one => { | |
| 1299 | try w.writeByteNTimes(' ', level + 1); | |
| 1300 | try w.writeAll("controlling:\n"); | |
| 1301 | try tree.dumpNode(data.bin.lhs, level + delta, mapper, config, w); | |
| 1302 | try w.writeByteNTimes(' ', level + 1); | |
| 1303 | if (data.bin.rhs != .none) { | |
| 1304 | try w.writeAll("chosen:\n"); | |
| 1305 | try tree.dumpNode(data.bin.rhs, level + delta, mapper, config, w); | |
| 1306 | } | |
| 1307 | }, | |
| 1308 | .generic_expr => { | |
| 1309 | const nodes = tree.data[data.range.start..data.range.end]; | |
| 1397 | .generic_expr, .generic_expr_one => { | |
| 1398 | const child_nodes = tree.childNodes(node); | |
| 1399 | const controlling = child_nodes[0]; | |
| 1400 | const chosen = child_nodes[1]; | |
| 1401 | const rest = child_nodes[2..]; | |
| 1402 | ||
| 1310 | 1403 | try w.writeByteNTimes(' ', level + 1); |
| 1311 | 1404 | try w.writeAll("controlling:\n"); |
| 1312 | try tree.dumpNode(nodes[0], level + delta, mapper, config, w); | |
| 1405 | try tree.dumpNode(controlling, level + delta, mapper, config, w); | |
| 1313 | 1406 | try w.writeByteNTimes(' ', level + 1); |
| 1314 | 1407 | try w.writeAll("chosen:\n"); |
| 1315 | try tree.dumpNode(nodes[1], level + delta, mapper, config, w); | |
| 1316 | try w.writeByteNTimes(' ', level + 1); | |
| 1317 | try w.writeAll("rest:\n"); | |
| 1318 | for (nodes[2..]) |expr| { | |
| 1319 | try tree.dumpNode(expr, level + delta, mapper, config, w); | |
| 1408 | try tree.dumpNode(chosen, level + delta, mapper, config, w); | |
| 1409 | ||
| 1410 | if (rest.len > 0) { | |
| 1411 | try w.writeByteNTimes(' ', level + 1); | |
| 1412 | try w.writeAll("rest:\n"); | |
| 1413 | for (rest) |expr| { | |
| 1414 | try tree.dumpNode(expr, level + delta, mapper, config, w); | |
| 1415 | } | |
| 1320 | 1416 | } |
| 1321 | 1417 | }, |
| 1322 | 1418 | .generic_association_expr, .generic_default_expr, .stmt_expr, .imaginary_literal => { |
lib/compiler/aro/aro/Tree/number_affixes.zig+9-4| ... | ... | @@ -74,8 +74,8 @@ pub const Suffix = enum { |
| 74 | 74 | // float and imaginary float |
| 75 | 75 | F, IF, |
| 76 | 76 | |
| 77 | // _Float16 | |
| 78 | F16, | |
| 77 | // _Float16 and imaginary _Float16 | |
| 78 | F16, IF16, | |
| 79 | 79 | |
| 80 | 80 | // __float80 |
| 81 | 81 | W, |
| ... | ... | @@ -129,6 +129,7 @@ pub const Suffix = enum { |
| 129 | 129 | |
| 130 | 130 | .{ .I, &.{"I"} }, |
| 131 | 131 | .{ .IL, &.{ "I", "L" } }, |
| 132 | .{ .IF16, &.{ "I", "F16" } }, | |
| 132 | 133 | .{ .IF, &.{ "I", "F" } }, |
| 133 | 134 | .{ .IW, &.{ "I", "W" } }, |
| 134 | 135 | .{ .IF128, &.{ "I", "F128" } }, |
| ... | ... | @@ -161,7 +162,7 @@ pub const Suffix = enum { |
| 161 | 162 | |
| 162 | 163 | pub fn isImaginary(suffix: Suffix) bool { |
| 163 | 164 | return switch (suffix) { |
| 164 | .I, .IL, .IF, .IU, .IUL, .ILL, .IULL, .IWB, .IUWB, .IF128, .IQ, .IW => true, | |
| 165 | .I, .IL, .IF, .IU, .IUL, .ILL, .IULL, .IWB, .IUWB, .IF128, .IQ, .IW, .IF16 => true, | |
| 165 | 166 | .None, .L, .F16, .F, .U, .UL, .LL, .ULL, .WB, .UWB, .F128, .Q, .W => false, |
| 166 | 167 | }; |
| 167 | 168 | } |
| ... | ... | @@ -170,7 +171,7 @@ pub const Suffix = enum { |
| 170 | 171 | return switch (suffix) { |
| 171 | 172 | .None, .L, .LL, .I, .IL, .ILL, .WB, .IWB => true, |
| 172 | 173 | .U, .UL, .ULL, .IU, .IUL, .IULL, .UWB, .IUWB => false, |
| 173 | .F, .IF, .F16, .F128, .IF128, .Q, .IQ, .W, .IW => unreachable, | |
| 174 | .F, .IF, .F16, .F128, .IF128, .Q, .IQ, .W, .IW, .IF16 => unreachable, | |
| 174 | 175 | }; |
| 175 | 176 | } |
| 176 | 177 | |
| ... | ... | @@ -184,4 +185,8 @@ pub const Suffix = enum { |
| 184 | 185 | else => false, |
| 185 | 186 | }; |
| 186 | 187 | } |
| 188 | ||
| 189 | pub fn isFloat80(suffix: Suffix) bool { | |
| 190 | return suffix == .W or suffix == .IW; | |
| 191 | } | |
| 187 | 192 | }; |
lib/compiler/aro/aro/Type.zig+170-170| ... | ... | @@ -146,17 +146,14 @@ pub const Attributed = struct { |
| 146 | 146 | attributes: []Attribute, |
| 147 | 147 | base: Type, |
| 148 | 148 | |
| 149 | pub fn create(allocator: std.mem.Allocator, base: Type, existing_attributes: []const Attribute, attributes: []const Attribute) !*Attributed { | |
| 149 | pub fn create(allocator: std.mem.Allocator, base_ty: Type, attributes: []const Attribute) !*Attributed { | |
| 150 | 150 | const attributed_type = try allocator.create(Attributed); |
| 151 | 151 | errdefer allocator.destroy(attributed_type); |
| 152 | ||
| 153 | const all_attrs = try allocator.alloc(Attribute, existing_attributes.len + attributes.len); | |
| 154 | @memcpy(all_attrs[0..existing_attributes.len], existing_attributes); | |
| 155 | @memcpy(all_attrs[existing_attributes.len..], attributes); | |
| 152 | const duped = try allocator.dupe(Attribute, attributes); | |
| 156 | 153 | |
| 157 | 154 | attributed_type.* = .{ |
| 158 | .attributes = all_attrs, | |
| 159 | .base = base, | |
| 155 | .attributes = duped, | |
| 156 | .base = base_ty, | |
| 160 | 157 | }; |
| 161 | 158 | return attributed_type; |
| 162 | 159 | } |
| ... | ... | @@ -190,13 +187,10 @@ pub const Enum = struct { |
| 190 | 187 | } |
| 191 | 188 | }; |
| 192 | 189 | |
| 193 | // might not need all 4 of these when finished, | |
| 194 | // but currently it helps having all 4 when diff-ing | |
| 195 | // the rust code. | |
| 196 | 190 | pub const TypeLayout = struct { |
| 197 | 191 | /// The size of the type in bits. |
| 198 | 192 | /// |
| 199 | /// This is the value returned by `sizeof` and C and `std::mem::size_of` in Rust | |
| 193 | /// This is the value returned by `sizeof` in C | |
| 200 | 194 | /// (but in bits instead of bytes). This is a multiple of `pointer_alignment_bits`. |
| 201 | 195 | size_bits: u64, |
| 202 | 196 | /// The alignment of the type, in bits, when used as a field in a record. |
| ... | ... | @@ -205,9 +199,7 @@ pub const TypeLayout = struct { |
| 205 | 199 | /// cases in GCC where `_Alignof` returns a smaller value. |
| 206 | 200 | field_alignment_bits: u32, |
| 207 | 201 | /// The alignment, in bits, of valid pointers to this type. |
| 208 | /// | |
| 209 | /// This is the value returned by `std::mem::align_of` in Rust | |
| 210 | /// (but in bits instead of bytes). `size_bits` is a multiple of this value. | |
| 202 | /// `size_bits` is a multiple of this value. | |
| 211 | 203 | pointer_alignment_bits: u32, |
| 212 | 204 | /// The required alignment of the type in bits. |
| 213 | 205 | /// |
| ... | ... | @@ -301,6 +293,15 @@ pub const Record = struct { |
| 301 | 293 | } |
| 302 | 294 | return false; |
| 303 | 295 | } |
| 296 | ||
| 297 | pub fn hasField(self: *const Record, name: StringId) bool { | |
| 298 | std.debug.assert(!self.isIncomplete()); | |
| 299 | for (self.fields) |f| { | |
| 300 | if (f.isAnonymousRecord() and f.ty.getRecord().?.hasField(name)) return true; | |
| 301 | if (name == f.name) return true; | |
| 302 | } | |
| 303 | return false; | |
| 304 | } | |
| 304 | 305 | }; |
| 305 | 306 | |
| 306 | 307 | pub const Specifier = enum { |
| ... | ... | @@ -354,12 +355,11 @@ pub const Specifier = enum { |
| 354 | 355 | float, |
| 355 | 356 | double, |
| 356 | 357 | long_double, |
| 357 | float80, | |
| 358 | 358 | float128, |
| 359 | complex_float16, | |
| 359 | 360 | complex_float, |
| 360 | 361 | complex_double, |
| 361 | 362 | complex_long_double, |
| 362 | complex_float80, | |
| 363 | 363 | complex_float128, |
| 364 | 364 | |
| 365 | 365 | // data.sub_type |
| ... | ... | @@ -422,6 +422,8 @@ data: union { |
| 422 | 422 | specifier: Specifier, |
| 423 | 423 | qual: Qualifiers = .{}, |
| 424 | 424 | decayed: bool = false, |
| 425 | /// typedef name, if any | |
| 426 | name: StringId = .empty, | |
| 425 | 427 | |
| 426 | 428 | pub const int = Type{ .specifier = .int }; |
| 427 | 429 | pub const invalid = Type{ .specifier = .invalid }; |
| ... | ... | @@ -435,8 +437,8 @@ pub fn is(ty: Type, specifier: Specifier) bool { |
| 435 | 437 | |
| 436 | 438 | pub fn withAttributes(self: Type, allocator: std.mem.Allocator, attributes: []const Attribute) !Type { |
| 437 | 439 | if (attributes.len == 0) return self; |
| 438 | const attributed_type = try Type.Attributed.create(allocator, self, self.getAttributes(), attributes); | |
| 439 | return Type{ .specifier = .attributed, .data = .{ .attributed = attributed_type }, .decayed = self.decayed }; | |
| 440 | const attributed_type = try Type.Attributed.create(allocator, self, attributes); | |
| 441 | return .{ .specifier = .attributed, .data = .{ .attributed = attributed_type }, .decayed = self.decayed }; | |
| 440 | 442 | } |
| 441 | 443 | |
| 442 | 444 | pub fn isCallable(ty: Type) ?Type { |
| ... | ... | @@ -470,6 +472,23 @@ pub fn isArray(ty: Type) bool { |
| 470 | 472 | }; |
| 471 | 473 | } |
| 472 | 474 | |
| 475 | /// Must only be used to set the length of an incomplete array as determined by its initializer | |
| 476 | pub fn setIncompleteArrayLen(ty: *Type, len: u64) void { | |
| 477 | switch (ty.specifier) { | |
| 478 | .incomplete_array => { | |
| 479 | // Modifying .data is exceptionally allowed for .incomplete_array. | |
| 480 | ty.data.array.len = len; | |
| 481 | ty.specifier = .array; | |
| 482 | }, | |
| 483 | ||
| 484 | .typeof_type => ty.data.sub_type.setIncompleteArrayLen(len), | |
| 485 | .typeof_expr => ty.data.expr.ty.setIncompleteArrayLen(len), | |
| 486 | .attributed => ty.data.attributed.base.setIncompleteArrayLen(len), | |
| 487 | ||
| 488 | else => unreachable, | |
| 489 | } | |
| 490 | } | |
| 491 | ||
| 473 | 492 | /// Whether the type is promoted if used as a variadic argument or as an argument to a function with no prototype |
| 474 | 493 | fn undergoesDefaultArgPromotion(ty: Type, comp: *const Compilation) bool { |
| 475 | 494 | return switch (ty.specifier) { |
| ... | ... | @@ -536,7 +555,7 @@ pub fn isFloat(ty: Type) bool { |
| 536 | 555 | return switch (ty.specifier) { |
| 537 | 556 | // zig fmt: off |
| 538 | 557 | .float, .double, .long_double, .complex_float, .complex_double, .complex_long_double, |
| 539 | .fp16, .float16, .float80, .float128, .complex_float80, .complex_float128 => true, | |
| 558 | .fp16, .float16, .float128, .complex_float128, .complex_float16 => true, | |
| 540 | 559 | // zig fmt: on |
| 541 | 560 | .typeof_type => ty.data.sub_type.isFloat(), |
| 542 | 561 | .typeof_expr => ty.data.expr.ty.isFloat(), |
| ... | ... | @@ -548,11 +567,11 @@ pub fn isFloat(ty: Type) bool { |
| 548 | 567 | pub fn isReal(ty: Type) bool { |
| 549 | 568 | return switch (ty.specifier) { |
| 550 | 569 | // zig fmt: off |
| 551 | .complex_float, .complex_double, .complex_long_double, .complex_float80, | |
| 570 | .complex_float, .complex_double, .complex_long_double, | |
| 552 | 571 | .complex_float128, .complex_char, .complex_schar, .complex_uchar, .complex_short, |
| 553 | 572 | .complex_ushort, .complex_int, .complex_uint, .complex_long, .complex_ulong, |
| 554 | 573 | .complex_long_long, .complex_ulong_long, .complex_int128, .complex_uint128, |
| 555 | .complex_bit_int => false, | |
| 574 | .complex_bit_int, .complex_float16 => false, | |
| 556 | 575 | // zig fmt: on |
| 557 | 576 | .typeof_type => ty.data.sub_type.isReal(), |
| 558 | 577 | .typeof_expr => ty.data.expr.ty.isReal(), |
| ... | ... | @@ -564,11 +583,11 @@ pub fn isReal(ty: Type) bool { |
| 564 | 583 | pub fn isComplex(ty: Type) bool { |
| 565 | 584 | return switch (ty.specifier) { |
| 566 | 585 | // zig fmt: off |
| 567 | .complex_float, .complex_double, .complex_long_double, .complex_float80, | |
| 586 | .complex_float, .complex_double, .complex_long_double, | |
| 568 | 587 | .complex_float128, .complex_char, .complex_schar, .complex_uchar, .complex_short, |
| 569 | 588 | .complex_ushort, .complex_int, .complex_uint, .complex_long, .complex_ulong, |
| 570 | 589 | .complex_long_long, .complex_ulong_long, .complex_int128, .complex_uint128, |
| 571 | .complex_bit_int => true, | |
| 590 | .complex_bit_int, .complex_float16 => true, | |
| 572 | 591 | // zig fmt: on |
| 573 | 592 | .typeof_type => ty.data.sub_type.isComplex(), |
| 574 | 593 | .typeof_expr => ty.data.expr.ty.isComplex(), |
| ... | ... | @@ -671,11 +690,11 @@ pub fn elemType(ty: Type) Type { |
| 671 | 690 | .attributed => ty.data.attributed.base.elemType(), |
| 672 | 691 | .invalid => Type.invalid, |
| 673 | 692 | // zig fmt: off |
| 674 | .complex_float, .complex_double, .complex_long_double, .complex_float80, | |
| 693 | .complex_float, .complex_double, .complex_long_double, | |
| 675 | 694 | .complex_float128, .complex_char, .complex_schar, .complex_uchar, .complex_short, |
| 676 | 695 | .complex_ushort, .complex_int, .complex_uint, .complex_long, .complex_ulong, |
| 677 | 696 | .complex_long_long, .complex_ulong_long, .complex_int128, .complex_uint128, |
| 678 | .complex_bit_int => ty.makeReal(), | |
| 697 | .complex_bit_int, .complex_float16 => ty.makeReal(), | |
| 679 | 698 | // zig fmt: on |
| 680 | 699 | else => unreachable, |
| 681 | 700 | }; |
| ... | ... | @@ -703,6 +722,16 @@ pub fn params(ty: Type) []Func.Param { |
| 703 | 722 | }; |
| 704 | 723 | } |
| 705 | 724 | |
| 725 | /// Returns true if the return value or any param of `ty` is `.invalid` | |
| 726 | /// Asserts that ty is a function type | |
| 727 | pub fn isInvalidFunc(ty: Type) bool { | |
| 728 | if (ty.returnType().is(.invalid)) return true; | |
| 729 | for (ty.params()) |param| { | |
| 730 | if (param.ty.is(.invalid)) return true; | |
| 731 | } | |
| 732 | return false; | |
| 733 | } | |
| 734 | ||
| 706 | 735 | pub fn arrayLen(ty: Type) ?u64 { |
| 707 | 736 | return switch (ty.specifier) { |
| 708 | 737 | .array, .static_array => ty.data.array.len, |
| ... | ... | @@ -726,15 +755,6 @@ pub fn anyQual(ty: Type) bool { |
| 726 | 755 | }; |
| 727 | 756 | } |
| 728 | 757 | |
| 729 | pub fn getAttributes(ty: Type) []const Attribute { | |
| 730 | return switch (ty.specifier) { | |
| 731 | .attributed => ty.data.attributed.attributes, | |
| 732 | .typeof_type => ty.data.sub_type.getAttributes(), | |
| 733 | .typeof_expr => ty.data.expr.ty.getAttributes(), | |
| 734 | else => &.{}, | |
| 735 | }; | |
| 736 | } | |
| 737 | ||
| 738 | 758 | pub fn getRecord(ty: Type) ?*const Type.Record { |
| 739 | 759 | return switch (ty.specifier) { |
| 740 | 760 | .attributed => ty.data.attributed.base.getRecord(), |
| ... | ... | @@ -795,8 +815,8 @@ fn realIntegerConversion(a: Type, b: Type, comp: *const Compilation) Type { |
| 795 | 815 | |
| 796 | 816 | pub fn makeIntegerUnsigned(ty: Type) Type { |
| 797 | 817 | // TODO discards attributed/typeof |
| 798 | var base = ty.canonicalize(.standard); | |
| 799 | switch (base.specifier) { | |
| 818 | var base_ty = ty.canonicalize(.standard); | |
| 819 | switch (base_ty.specifier) { | |
| 800 | 820 | // zig fmt: off |
| 801 | 821 | .uchar, .ushort, .uint, .ulong, .ulong_long, .uint128, |
| 802 | 822 | .complex_uchar, .complex_ushort, .complex_uint, .complex_ulong, .complex_ulong_long, .complex_uint128, |
| ... | ... | @@ -804,21 +824,21 @@ pub fn makeIntegerUnsigned(ty: Type) Type { |
| 804 | 824 | // zig fmt: on |
| 805 | 825 | |
| 806 | 826 | .char, .complex_char => { |
| 807 | base.specifier = @enumFromInt(@intFromEnum(base.specifier) + 2); | |
| 808 | return base; | |
| 827 | base_ty.specifier = @enumFromInt(@intFromEnum(base_ty.specifier) + 2); | |
| 828 | return base_ty; | |
| 809 | 829 | }, |
| 810 | 830 | |
| 811 | 831 | // zig fmt: off |
| 812 | 832 | .schar, .short, .int, .long, .long_long, .int128, |
| 813 | 833 | .complex_schar, .complex_short, .complex_int, .complex_long, .complex_long_long, .complex_int128 => { |
| 814 | base.specifier = @enumFromInt(@intFromEnum(base.specifier) + 1); | |
| 815 | return base; | |
| 834 | base_ty.specifier = @enumFromInt(@intFromEnum(base_ty.specifier) + 1); | |
| 835 | return base_ty; | |
| 816 | 836 | }, |
| 817 | 837 | // zig fmt: on |
| 818 | 838 | |
| 819 | 839 | .bit_int, .complex_bit_int => { |
| 820 | base.data.int.signedness = .unsigned; | |
| 821 | return base; | |
| 840 | base_ty.data.int.signedness = .unsigned; | |
| 841 | return base_ty; | |
| 822 | 842 | }, |
| 823 | 843 | else => unreachable, |
| 824 | 844 | } |
| ... | ... | @@ -837,6 +857,8 @@ pub fn integerPromotion(ty: Type, comp: *Compilation) Type { |
| 837 | 857 | switch (specifier) { |
| 838 | 858 | .@"enum" => { |
| 839 | 859 | if (ty.hasIncompleteSize()) return .{ .specifier = .int }; |
| 860 | if (ty.data.@"enum".fixed) return ty.data.@"enum".tag_ty.integerPromotion(comp); | |
| 861 | ||
| 840 | 862 | specifier = ty.data.@"enum".tag_ty.specifier; |
| 841 | 863 | }, |
| 842 | 864 | .bit_int, .complex_bit_int => return .{ .specifier = specifier, .data = ty.data }, |
| ... | ... | @@ -915,53 +937,7 @@ pub fn hasUnboundVLA(ty: Type) bool { |
| 915 | 937 | } |
| 916 | 938 | |
| 917 | 939 | pub fn hasField(ty: Type, name: StringId) bool { |
| 918 | switch (ty.specifier) { | |
| 919 | .@"struct" => { | |
| 920 | std.debug.assert(!ty.data.record.isIncomplete()); | |
| 921 | for (ty.data.record.fields) |f| { | |
| 922 | if (f.isAnonymousRecord() and f.ty.hasField(name)) return true; | |
| 923 | if (name == f.name) return true; | |
| 924 | } | |
| 925 | }, | |
| 926 | .@"union" => { | |
| 927 | std.debug.assert(!ty.data.record.isIncomplete()); | |
| 928 | for (ty.data.record.fields) |f| { | |
| 929 | if (f.isAnonymousRecord() and f.ty.hasField(name)) return true; | |
| 930 | if (name == f.name) return true; | |
| 931 | } | |
| 932 | }, | |
| 933 | .typeof_type => return ty.data.sub_type.hasField(name), | |
| 934 | .typeof_expr => return ty.data.expr.ty.hasField(name), | |
| 935 | .attributed => return ty.data.attributed.base.hasField(name), | |
| 936 | .invalid => return false, | |
| 937 | else => unreachable, | |
| 938 | } | |
| 939 | return false; | |
| 940 | } | |
| 941 | ||
| 942 | // TODO handle bitints | |
| 943 | pub fn minInt(ty: Type, comp: *const Compilation) i64 { | |
| 944 | std.debug.assert(ty.isInt()); | |
| 945 | if (ty.isUnsignedInt(comp)) return 0; | |
| 946 | return switch (ty.sizeof(comp).?) { | |
| 947 | 1 => std.math.minInt(i8), | |
| 948 | 2 => std.math.minInt(i16), | |
| 949 | 4 => std.math.minInt(i32), | |
| 950 | 8 => std.math.minInt(i64), | |
| 951 | else => unreachable, | |
| 952 | }; | |
| 953 | } | |
| 954 | ||
| 955 | // TODO handle bitints | |
| 956 | pub fn maxInt(ty: Type, comp: *const Compilation) u64 { | |
| 957 | std.debug.assert(ty.isInt()); | |
| 958 | return switch (ty.sizeof(comp).?) { | |
| 959 | 1 => if (ty.isUnsignedInt(comp)) @as(u64, std.math.maxInt(u8)) else std.math.maxInt(i8), | |
| 960 | 2 => if (ty.isUnsignedInt(comp)) @as(u64, std.math.maxInt(u16)) else std.math.maxInt(i16), | |
| 961 | 4 => if (ty.isUnsignedInt(comp)) @as(u64, std.math.maxInt(u32)) else std.math.maxInt(i32), | |
| 962 | 8 => if (ty.isUnsignedInt(comp)) @as(u64, std.math.maxInt(u64)) else std.math.maxInt(i64), | |
| 963 | else => unreachable, | |
| 964 | }; | |
| 940 | return ty.getRecord().?.hasField(name); | |
| 965 | 941 | } |
| 966 | 942 | |
| 967 | 943 | const TypeSizeOrder = enum { |
| ... | ... | @@ -1004,16 +980,15 @@ pub fn sizeof(ty: Type, comp: *const Compilation) ?u64 { |
| 1004 | 980 | .fp16, .float16 => 2, |
| 1005 | 981 | .float => comp.target.cTypeByteSize(.float), |
| 1006 | 982 | .double => comp.target.cTypeByteSize(.double), |
| 1007 | .float80 => 16, | |
| 1008 | 983 | .float128 => 16, |
| 1009 | 984 | .bit_int => { |
| 1010 | return std.mem.alignForward(u64, (ty.data.int.bits + 7) / 8, ty.alignof(comp)); | |
| 985 | return std.mem.alignForward(u64, (@as(u32, ty.data.int.bits) + 7) / 8, ty.alignof(comp)); | |
| 1011 | 986 | }, |
| 1012 | 987 | // zig fmt: off |
| 1013 | 988 | .complex_char, .complex_schar, .complex_uchar, .complex_short, .complex_ushort, .complex_int, |
| 1014 | 989 | .complex_uint, .complex_long, .complex_ulong, .complex_long_long, .complex_ulong_long, |
| 1015 | 990 | .complex_int128, .complex_uint128, .complex_float, .complex_double, |
| 1016 | .complex_long_double, .complex_float80, .complex_float128, .complex_bit_int, | |
| 991 | .complex_long_double, .complex_float128, .complex_bit_int, .complex_float16, | |
| 1017 | 992 | => return 2 * ty.makeReal().sizeof(comp).?, |
| 1018 | 993 | // zig fmt: on |
| 1019 | 994 | .pointer => unreachable, |
| ... | ... | @@ -1050,7 +1025,6 @@ pub fn bitSizeof(ty: Type, comp: *const Compilation) ?u64 { |
| 1050 | 1025 | .attributed => ty.data.attributed.base.bitSizeof(comp), |
| 1051 | 1026 | .bit_int => return ty.data.int.bits, |
| 1052 | 1027 | .long_double => comp.target.cTypeBitSize(.longdouble), |
| 1053 | .float80 => return 80, | |
| 1054 | 1028 | else => 8 * (ty.sizeof(comp) orelse return null), |
| 1055 | 1029 | }; |
| 1056 | 1030 | } |
| ... | ... | @@ -1100,7 +1074,7 @@ pub fn alignof(ty: Type, comp: *const Compilation) u29 { |
| 1100 | 1074 | .complex_char, .complex_schar, .complex_uchar, .complex_short, .complex_ushort, .complex_int, |
| 1101 | 1075 | .complex_uint, .complex_long, .complex_ulong, .complex_long_long, .complex_ulong_long, |
| 1102 | 1076 | .complex_int128, .complex_uint128, .complex_float, .complex_double, |
| 1103 | .complex_long_double, .complex_float80, .complex_float128, .complex_bit_int, | |
| 1077 | .complex_long_double, .complex_float128, .complex_bit_int, .complex_float16, | |
| 1104 | 1078 | => return ty.makeReal().alignof(comp), |
| 1105 | 1079 | // zig fmt: on |
| 1106 | 1080 | |
| ... | ... | @@ -1114,10 +1088,15 @@ pub fn alignof(ty: Type, comp: *const Compilation) u29 { |
| 1114 | 1088 | .long_long => comp.target.cTypeAlignment(.longlong), |
| 1115 | 1089 | .ulong_long => comp.target.cTypeAlignment(.ulonglong), |
| 1116 | 1090 | |
| 1117 | .bit_int => @min( | |
| 1118 | std.math.ceilPowerOfTwoPromote(u16, (ty.data.int.bits + 7) / 8), | |
| 1119 | 16, // comp.target.maxIntAlignment(), please use your own logic for this value as it is implementation-defined | |
| 1120 | ), | |
| 1091 | .bit_int => { | |
| 1092 | // https://www.open-std.org/jtc1/sc22/wg14/www/docs/n2709.pdf | |
| 1093 | // _BitInt(N) types align with existing calling conventions. They have the same size and alignment as the | |
| 1094 | // smallest basic type that can contain them. Types that are larger than __int64_t are conceptually treated | |
| 1095 | // as struct of register size chunks. The number of chunks is the smallest number that can contain the type. | |
| 1096 | if (ty.data.int.bits > 64) return 8; | |
| 1097 | const basic_type = comp.intLeastN(ty.data.int.bits, ty.data.int.signedness); | |
| 1098 | return basic_type.alignof(comp); | |
| 1099 | }, | |
| 1121 | 1100 | |
| 1122 | 1101 | .float => comp.target.cTypeAlignment(.float), |
| 1123 | 1102 | .double => comp.target.cTypeAlignment(.double), |
| ... | ... | @@ -1126,7 +1105,7 @@ pub fn alignof(ty: Type, comp: *const Compilation) u29 { |
| 1126 | 1105 | .int128, .uint128 => if (comp.target.cpu.arch == .s390x and comp.target.os.tag == .linux and comp.target.isGnu()) 8 else 16, |
| 1127 | 1106 | .fp16, .float16 => 2, |
| 1128 | 1107 | |
| 1129 | .float80, .float128 => 16, | |
| 1108 | .float128 => 16, | |
| 1130 | 1109 | .pointer, |
| 1131 | 1110 | .static_array, |
| 1132 | 1111 | .nullptr_t, |
| ... | ... | @@ -1142,7 +1121,11 @@ pub fn alignof(ty: Type, comp: *const Compilation) u29 { |
| 1142 | 1121 | }; |
| 1143 | 1122 | } |
| 1144 | 1123 | |
| 1145 | pub const QualHandling = enum { standard, preserve_quals }; | |
| 1124 | // This enum should be kept public because it is used by the downstream zig translate-c | |
| 1125 | pub const QualHandling = enum { | |
| 1126 | standard, | |
| 1127 | preserve_quals, | |
| 1128 | }; | |
| 1146 | 1129 | |
| 1147 | 1130 | /// Canonicalize a possibly-typeof() type. If the type is not a typeof() type, simply |
| 1148 | 1131 | /// return it. Otherwise, determine the actual qualified type. |
| ... | ... | @@ -1151,17 +1134,12 @@ pub const QualHandling = enum { standard, preserve_quals }; |
| 1151 | 1134 | /// arrays and pointers. |
| 1152 | 1135 | pub fn canonicalize(ty: Type, qual_handling: QualHandling) Type { |
| 1153 | 1136 | var cur = ty; |
| 1154 | if (cur.specifier == .attributed) { | |
| 1155 | cur = cur.data.attributed.base; | |
| 1156 | cur.decayed = ty.decayed; | |
| 1157 | } | |
| 1158 | if (!cur.isTypeof()) return cur; | |
| 1159 | ||
| 1160 | 1137 | var qual = cur.qual; |
| 1161 | 1138 | while (true) { |
| 1162 | 1139 | switch (cur.specifier) { |
| 1163 | 1140 | .typeof_type => cur = cur.data.sub_type.*, |
| 1164 | 1141 | .typeof_expr => cur = cur.data.expr.ty, |
| 1142 | .attributed => cur = cur.data.attributed.base, | |
| 1165 | 1143 | else => break, |
| 1166 | 1144 | } |
| 1167 | 1145 | qual = qual.mergeAll(cur.qual); |
| ... | ... | @@ -1189,7 +1167,7 @@ pub fn requestedAlignment(ty: Type, comp: *const Compilation) ?u29 { |
| 1189 | 1167 | return switch (ty.specifier) { |
| 1190 | 1168 | .typeof_type => ty.data.sub_type.requestedAlignment(comp), |
| 1191 | 1169 | .typeof_expr => ty.data.expr.ty.requestedAlignment(comp), |
| 1192 | .attributed => annotationAlignment(comp, ty.data.attributed.attributes), | |
| 1170 | .attributed => annotationAlignment(comp, Attribute.Iterator.initType(ty)), | |
| 1193 | 1171 | else => null, |
| 1194 | 1172 | }; |
| 1195 | 1173 | } |
| ... | ... | @@ -1199,12 +1177,27 @@ pub fn enumIsPacked(ty: Type, comp: *const Compilation) bool { |
| 1199 | 1177 | return comp.langopts.short_enums or target_util.packAllEnums(comp.target) or ty.hasAttribute(.@"packed"); |
| 1200 | 1178 | } |
| 1201 | 1179 | |
| 1202 | pub fn annotationAlignment(comp: *const Compilation, attrs: ?[]const Attribute) ?u29 { | |
| 1203 | const a = attrs orelse return null; | |
| 1180 | pub fn getName(ty: Type) StringId { | |
| 1181 | return switch (ty.specifier) { | |
| 1182 | .typeof_type => if (ty.name == .empty) ty.data.sub_type.getName() else ty.name, | |
| 1183 | .typeof_expr => if (ty.name == .empty) ty.data.expr.ty.getName() else ty.name, | |
| 1184 | .attributed => if (ty.name == .empty) ty.data.attributed.base.getName() else ty.name, | |
| 1185 | else => ty.name, | |
| 1186 | }; | |
| 1187 | } | |
| 1204 | 1188 | |
| 1189 | pub fn annotationAlignment(comp: *const Compilation, attrs: Attribute.Iterator) ?u29 { | |
| 1190 | var it = attrs; | |
| 1205 | 1191 | var max_requested: ?u29 = null; |
| 1206 | for (a) |attribute| { | |
| 1192 | var last_aligned_index: ?usize = null; | |
| 1193 | while (it.next()) |item| { | |
| 1194 | const attribute, const index = item; | |
| 1207 | 1195 | if (attribute.tag != .aligned) continue; |
| 1196 | if (last_aligned_index) |aligned_index| { | |
| 1197 | // once we recurse into a new type, after an `aligned` attribute was found, we're done | |
| 1198 | if (index <= aligned_index) break; | |
| 1199 | } | |
| 1200 | last_aligned_index = index; | |
| 1208 | 1201 | const requested = if (attribute.args.aligned.alignment) |alignment| alignment.requested else target_util.defaultAlignment(comp.target); |
| 1209 | 1202 | if (max_requested == null or max_requested.? < requested) { |
| 1210 | 1203 | max_requested = requested; |
| ... | ... | @@ -1225,6 +1218,10 @@ pub fn eql(a_param: Type, b_param: Type, comp: *const Compilation, check_qualifi |
| 1225 | 1218 | if (!b.isFunc()) return false; |
| 1226 | 1219 | } else if (a.isArray()) { |
| 1227 | 1220 | if (!b.isArray()) return false; |
| 1221 | } else if (a.specifier == .@"enum" and b.specifier != .@"enum") { | |
| 1222 | return a.data.@"enum".tag_ty.eql(b, comp, check_qualifiers); | |
| 1223 | } else if (b.specifier == .@"enum" and a.specifier != .@"enum") { | |
| 1224 | return a.eql(b.data.@"enum".tag_ty, comp, check_qualifiers); | |
| 1228 | 1225 | } else if (a.specifier != b.specifier) return false; |
| 1229 | 1226 | |
| 1230 | 1227 | if (a.qual.atomic != b.qual.atomic) return false; |
| ... | ... | @@ -1315,6 +1312,12 @@ pub fn integerRank(ty: Type, comp: *const Compilation) usize { |
| 1315 | 1312 | .long_long, .ulong_long => 6 + (ty.bitSizeof(comp).? << 3), |
| 1316 | 1313 | .int128, .uint128 => 7 + (ty.bitSizeof(comp).? << 3), |
| 1317 | 1314 | |
| 1315 | .typeof_type => ty.data.sub_type.integerRank(comp), | |
| 1316 | .typeof_expr => ty.data.expr.ty.integerRank(comp), | |
| 1317 | .attributed => ty.data.attributed.base.integerRank(comp), | |
| 1318 | ||
| 1319 | .@"enum" => real.data.@"enum".tag_ty.integerRank(comp), | |
| 1320 | ||
| 1318 | 1321 | else => unreachable, |
| 1319 | 1322 | }); |
| 1320 | 1323 | } |
| ... | ... | @@ -1322,25 +1325,26 @@ pub fn integerRank(ty: Type, comp: *const Compilation) usize { |
| 1322 | 1325 | /// Returns true if `a` and `b` are integer types that differ only in sign |
| 1323 | 1326 | pub fn sameRankDifferentSign(a: Type, b: Type, comp: *const Compilation) bool { |
| 1324 | 1327 | if (!a.isInt() or !b.isInt()) return false; |
| 1328 | if (a.hasIncompleteSize() or b.hasIncompleteSize()) return false; | |
| 1325 | 1329 | if (a.integerRank(comp) != b.integerRank(comp)) return false; |
| 1326 | 1330 | return a.isUnsignedInt(comp) != b.isUnsignedInt(comp); |
| 1327 | 1331 | } |
| 1328 | 1332 | |
| 1329 | 1333 | pub fn makeReal(ty: Type) Type { |
| 1330 | 1334 | // TODO discards attributed/typeof |
| 1331 | var base = ty.canonicalize(.standard); | |
| 1332 | switch (base.specifier) { | |
| 1333 | .complex_float, .complex_double, .complex_long_double, .complex_float80, .complex_float128 => { | |
| 1334 | base.specifier = @enumFromInt(@intFromEnum(base.specifier) - 5); | |
| 1335 | return base; | |
| 1335 | var base_ty = ty.canonicalize(.standard); | |
| 1336 | switch (base_ty.specifier) { | |
| 1337 | .complex_float16, .complex_float, .complex_double, .complex_long_double, .complex_float128 => { | |
| 1338 | base_ty.specifier = @enumFromInt(@intFromEnum(base_ty.specifier) - 5); | |
| 1339 | return base_ty; | |
| 1336 | 1340 | }, |
| 1337 | 1341 | .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 => { |
| 1338 | base.specifier = @enumFromInt(@intFromEnum(base.specifier) - 13); | |
| 1339 | return base; | |
| 1342 | base_ty.specifier = @enumFromInt(@intFromEnum(base_ty.specifier) - 13); | |
| 1343 | return base_ty; | |
| 1340 | 1344 | }, |
| 1341 | 1345 | .complex_bit_int => { |
| 1342 | base.specifier = .bit_int; | |
| 1343 | return base; | |
| 1346 | base_ty.specifier = .bit_int; | |
| 1347 | return base_ty; | |
| 1344 | 1348 | }, |
| 1345 | 1349 | else => return ty, |
| 1346 | 1350 | } |
| ... | ... | @@ -1348,19 +1352,19 @@ pub fn makeReal(ty: Type) Type { |
| 1348 | 1352 | |
| 1349 | 1353 | pub fn makeComplex(ty: Type) Type { |
| 1350 | 1354 | // TODO discards attributed/typeof |
| 1351 | var base = ty.canonicalize(.standard); | |
| 1352 | switch (base.specifier) { | |
| 1353 | .float, .double, .long_double, .float80, .float128 => { | |
| 1354 | base.specifier = @enumFromInt(@intFromEnum(base.specifier) + 5); | |
| 1355 | return base; | |
| 1355 | var base_ty = ty.canonicalize(.standard); | |
| 1356 | switch (base_ty.specifier) { | |
| 1357 | .float, .double, .long_double, .float128 => { | |
| 1358 | base_ty.specifier = @enumFromInt(@intFromEnum(base_ty.specifier) + 5); | |
| 1359 | return base_ty; | |
| 1356 | 1360 | }, |
| 1357 | 1361 | .char, .schar, .uchar, .short, .ushort, .int, .uint, .long, .ulong, .long_long, .ulong_long, .int128, .uint128 => { |
| 1358 | base.specifier = @enumFromInt(@intFromEnum(base.specifier) + 13); | |
| 1359 | return base; | |
| 1362 | base_ty.specifier = @enumFromInt(@intFromEnum(base_ty.specifier) + 13); | |
| 1363 | return base_ty; | |
| 1360 | 1364 | }, |
| 1361 | 1365 | .bit_int => { |
| 1362 | base.specifier = .complex_bit_int; | |
| 1363 | return base; | |
| 1366 | base_ty.specifier = .complex_bit_int; | |
| 1367 | return base_ty; | |
| 1364 | 1368 | }, |
| 1365 | 1369 | else => return ty, |
| 1366 | 1370 | } |
| ... | ... | @@ -1541,13 +1545,12 @@ pub const Builder = struct { |
| 1541 | 1545 | float, |
| 1542 | 1546 | double, |
| 1543 | 1547 | long_double, |
| 1544 | float80, | |
| 1545 | 1548 | float128, |
| 1546 | 1549 | complex, |
| 1550 | complex_float16, | |
| 1547 | 1551 | complex_float, |
| 1548 | 1552 | complex_double, |
| 1549 | 1553 | complex_long_double, |
| 1550 | complex_float80, | |
| 1551 | 1554 | complex_float128, |
| 1552 | 1555 | |
| 1553 | 1556 | pointer: *Type, |
| ... | ... | @@ -1613,9 +1616,6 @@ pub const Builder = struct { |
| 1613 | 1616 | .int128 => "__int128", |
| 1614 | 1617 | .sint128 => "signed __int128", |
| 1615 | 1618 | .uint128 => "unsigned __int128", |
| 1616 | .bit_int => "_BitInt", | |
| 1617 | .sbit_int => "signed _BitInt", | |
| 1618 | .ubit_int => "unsigned _BitInt", | |
| 1619 | 1619 | .complex_char => "_Complex char", |
| 1620 | 1620 | .complex_schar => "_Complex signed char", |
| 1621 | 1621 | .complex_uchar => "_Complex unsigned char", |
| ... | ... | @@ -1645,22 +1645,18 @@ pub const Builder = struct { |
| 1645 | 1645 | .complex_int128 => "_Complex __int128", |
| 1646 | 1646 | .complex_sint128 => "_Complex signed __int128", |
| 1647 | 1647 | .complex_uint128 => "_Complex unsigned __int128", |
| 1648 | .complex_bit_int => "_Complex _BitInt", | |
| 1649 | .complex_sbit_int => "_Complex signed _BitInt", | |
| 1650 | .complex_ubit_int => "_Complex unsigned _BitInt", | |
| 1651 | 1648 | |
| 1652 | 1649 | .fp16 => "__fp16", |
| 1653 | 1650 | .float16 => "_Float16", |
| 1654 | 1651 | .float => "float", |
| 1655 | 1652 | .double => "double", |
| 1656 | 1653 | .long_double => "long double", |
| 1657 | .float80 => "__float80", | |
| 1658 | 1654 | .float128 => "__float128", |
| 1659 | 1655 | .complex => "_Complex", |
| 1656 | .complex_float16 => "_Complex _Float16", | |
| 1660 | 1657 | .complex_float => "_Complex float", |
| 1661 | 1658 | .complex_double => "_Complex double", |
| 1662 | 1659 | .complex_long_double => "_Complex long double", |
| 1663 | .complex_float80 => "_Complex __float80", | |
| 1664 | 1660 | .complex_float128 => "_Complex __float128", |
| 1665 | 1661 | |
| 1666 | 1662 | .attributed => |attributed| Builder.fromType(attributed.base).str(langopts), |
| ... | ... | @@ -1757,19 +1753,20 @@ pub const Builder = struct { |
| 1757 | 1753 | .complex_uint128 => ty.specifier = .complex_uint128, |
| 1758 | 1754 | .bit_int, .sbit_int, .ubit_int, .complex_bit_int, .complex_ubit_int, .complex_sbit_int => |bits| { |
| 1759 | 1755 | const unsigned = b.specifier == .ubit_int or b.specifier == .complex_ubit_int; |
| 1756 | const complex_str = if (b.complex_tok != null) "_Complex " else ""; | |
| 1760 | 1757 | if (unsigned) { |
| 1761 | 1758 | if (bits < 1) { |
| 1762 | try p.errStr(.unsigned_bit_int_too_small, b.bit_int_tok.?, b.specifier.str(p.comp.langopts).?); | |
| 1759 | try p.errStr(.unsigned_bit_int_too_small, b.bit_int_tok.?, complex_str); | |
| 1763 | 1760 | return Type.invalid; |
| 1764 | 1761 | } |
| 1765 | 1762 | } else { |
| 1766 | 1763 | if (bits < 2) { |
| 1767 | try p.errStr(.signed_bit_int_too_small, b.bit_int_tok.?, b.specifier.str(p.comp.langopts).?); | |
| 1764 | try p.errStr(.signed_bit_int_too_small, b.bit_int_tok.?, complex_str); | |
| 1768 | 1765 | return Type.invalid; |
| 1769 | 1766 | } |
| 1770 | 1767 | } |
| 1771 | 1768 | if (bits > Compilation.bit_int_max_bits) { |
| 1772 | try p.errStr(.bit_int_too_big, b.bit_int_tok.?, b.specifier.str(p.comp.langopts).?); | |
| 1769 | try p.errStr(if (unsigned) .unsigned_bit_int_too_big else .signed_bit_int_too_big, b.bit_int_tok.?, complex_str); | |
| 1773 | 1770 | return Type.invalid; |
| 1774 | 1771 | } |
| 1775 | 1772 | ty.specifier = if (b.complex_tok != null) .complex_bit_int else .bit_int; |
| ... | ... | @@ -1784,12 +1781,11 @@ pub const Builder = struct { |
| 1784 | 1781 | .float => ty.specifier = .float, |
| 1785 | 1782 | .double => ty.specifier = .double, |
| 1786 | 1783 | .long_double => ty.specifier = .long_double, |
| 1787 | .float80 => ty.specifier = .float80, | |
| 1788 | 1784 | .float128 => ty.specifier = .float128, |
| 1785 | .complex_float16 => ty.specifier = .complex_float16, | |
| 1789 | 1786 | .complex_float => ty.specifier = .complex_float, |
| 1790 | 1787 | .complex_double => ty.specifier = .complex_double, |
| 1791 | 1788 | .complex_long_double => ty.specifier = .complex_long_double, |
| 1792 | .complex_float80 => ty.specifier = .complex_float80, | |
| 1793 | 1789 | .complex_float128 => ty.specifier = .complex_float128, |
| 1794 | 1790 | .complex => { |
| 1795 | 1791 | try p.errTok(.plain_complex, p.tok_i - 1); |
| ... | ... | @@ -1907,6 +1903,7 @@ pub const Builder = struct { |
| 1907 | 1903 | |
| 1908 | 1904 | /// Try to combine type from typedef, returns true if successful. |
| 1909 | 1905 | pub fn combineTypedef(b: *Builder, p: *Parser, typedef_ty: Type, name_tok: TokenIndex) bool { |
| 1906 | if (typedef_ty.is(.invalid)) return false; | |
| 1910 | 1907 | b.error_on_invalid = true; |
| 1911 | 1908 | defer b.error_on_invalid = false; |
| 1912 | 1909 | |
| ... | ... | @@ -2094,6 +2091,7 @@ pub const Builder = struct { |
| 2094 | 2091 | }, |
| 2095 | 2092 | .long => b.specifier = switch (b.specifier) { |
| 2096 | 2093 | .none => .long, |
| 2094 | .double => .long_double, | |
| 2097 | 2095 | .long => .long_long, |
| 2098 | 2096 | .unsigned => .ulong, |
| 2099 | 2097 | .signed => .long, |
| ... | ... | @@ -2106,6 +2104,7 @@ pub const Builder = struct { |
| 2106 | 2104 | .complex_long => .complex_long_long, |
| 2107 | 2105 | .complex_slong => .complex_slong_long, |
| 2108 | 2106 | .complex_ulong => .complex_ulong_long, |
| 2107 | .complex_double => .complex_long_double, | |
| 2109 | 2108 | else => return b.cannotCombine(p, source_tok), |
| 2110 | 2109 | }, |
| 2111 | 2110 | .int128 => b.specifier = switch (b.specifier) { |
| ... | ... | @@ -2140,6 +2139,7 @@ pub const Builder = struct { |
| 2140 | 2139 | }, |
| 2141 | 2140 | .float16 => b.specifier = switch (b.specifier) { |
| 2142 | 2141 | .none => .float16, |
| 2142 | .complex => .complex_float16, | |
| 2143 | 2143 | else => return b.cannotCombine(p, source_tok), |
| 2144 | 2144 | }, |
| 2145 | 2145 | .float => b.specifier = switch (b.specifier) { |
| ... | ... | @@ -2154,11 +2154,6 @@ pub const Builder = struct { |
| 2154 | 2154 | .complex => .complex_double, |
| 2155 | 2155 | else => return b.cannotCombine(p, source_tok), |
| 2156 | 2156 | }, |
| 2157 | .float80 => b.specifier = switch (b.specifier) { | |
| 2158 | .none => .float80, | |
| 2159 | .complex => .complex_float80, | |
| 2160 | else => return b.cannotCombine(p, source_tok), | |
| 2161 | }, | |
| 2162 | 2157 | .float128 => b.specifier = switch (b.specifier) { |
| 2163 | 2158 | .none => .float128, |
| 2164 | 2159 | .complex => .complex_float128, |
| ... | ... | @@ -2166,10 +2161,10 @@ pub const Builder = struct { |
| 2166 | 2161 | }, |
| 2167 | 2162 | .complex => b.specifier = switch (b.specifier) { |
| 2168 | 2163 | .none => .complex, |
| 2164 | .float16 => .complex_float16, | |
| 2169 | 2165 | .float => .complex_float, |
| 2170 | 2166 | .double => .complex_double, |
| 2171 | 2167 | .long_double => .complex_long_double, |
| 2172 | .float80 => .complex_float80, | |
| 2173 | 2168 | .float128 => .complex_float128, |
| 2174 | 2169 | .char => .complex_char, |
| 2175 | 2170 | .schar => .complex_schar, |
| ... | ... | @@ -2207,7 +2202,6 @@ pub const Builder = struct { |
| 2207 | 2202 | .complex_float, |
| 2208 | 2203 | .complex_double, |
| 2209 | 2204 | .complex_long_double, |
| 2210 | .complex_float80, | |
| 2211 | 2205 | .complex_float128, |
| 2212 | 2206 | .complex_char, |
| 2213 | 2207 | .complex_schar, |
| ... | ... | @@ -2294,13 +2288,12 @@ pub const Builder = struct { |
| 2294 | 2288 | .float16 => .float16, |
| 2295 | 2289 | .float => .float, |
| 2296 | 2290 | .double => .double, |
| 2297 | .float80 => .float80, | |
| 2298 | 2291 | .float128 => .float128, |
| 2299 | 2292 | .long_double => .long_double, |
| 2293 | .complex_float16 => .complex_float16, | |
| 2300 | 2294 | .complex_float => .complex_float, |
| 2301 | 2295 | .complex_double => .complex_double, |
| 2302 | 2296 | .complex_long_double => .complex_long_double, |
| 2303 | .complex_float80 => .complex_float80, | |
| 2304 | 2297 | .complex_float128 => .complex_float128, |
| 2305 | 2298 | |
| 2306 | 2299 | .pointer => .{ .pointer = ty.data.sub_type }, |
| ... | ... | @@ -2350,22 +2343,30 @@ pub const Builder = struct { |
| 2350 | 2343 | } |
| 2351 | 2344 | }; |
| 2352 | 2345 | |
| 2346 | /// Use with caution | |
| 2347 | pub fn base(ty: *Type) *Type { | |
| 2348 | return switch (ty.specifier) { | |
| 2349 | .typeof_type => ty.data.sub_type.base(), | |
| 2350 | .typeof_expr => ty.data.expr.ty.base(), | |
| 2351 | .attributed => ty.data.attributed.base.base(), | |
| 2352 | else => ty, | |
| 2353 | }; | |
| 2354 | } | |
| 2355 | ||
| 2353 | 2356 | pub fn getAttribute(ty: Type, comptime tag: Attribute.Tag) ?Attribute.ArgumentsForTag(tag) { |
| 2354 | switch (ty.specifier) { | |
| 2355 | .typeof_type => return ty.data.sub_type.getAttribute(tag), | |
| 2356 | .typeof_expr => return ty.data.expr.ty.getAttribute(tag), | |
| 2357 | .attributed => { | |
| 2358 | for (ty.data.attributed.attributes) |attribute| { | |
| 2359 | if (attribute.tag == tag) return @field(attribute.args, @tagName(tag)); | |
| 2360 | } | |
| 2361 | return null; | |
| 2362 | }, | |
| 2363 | else => return null, | |
| 2357 | if (tag == .aligned) @compileError("use requestedAlignment"); | |
| 2358 | var it = Attribute.Iterator.initType(ty); | |
| 2359 | while (it.next()) |item| { | |
| 2360 | const attribute, _ = item; | |
| 2361 | if (attribute.tag == tag) return @field(attribute.args, @tagName(tag)); | |
| 2364 | 2362 | } |
| 2363 | return null; | |
| 2365 | 2364 | } |
| 2366 | 2365 | |
| 2367 | 2366 | pub fn hasAttribute(ty: Type, tag: Attribute.Tag) bool { |
| 2368 | for (ty.getAttributes()) |attr| { | |
| 2367 | var it = Attribute.Iterator.initType(ty); | |
| 2368 | while (it.next()) |item| { | |
| 2369 | const attr, _ = item; | |
| 2369 | 2370 | if (attr.tag == tag) return true; |
| 2370 | 2371 | } |
| 2371 | 2372 | return false; |
| ... | ... | @@ -2489,6 +2490,8 @@ fn printPrologue(ty: Type, mapper: StringInterner.TypeMapper, langopts: LangOpts |
| 2489 | 2490 | _ = try elem_ty.printPrologue(mapper, langopts, w); |
| 2490 | 2491 | try w.writeAll("' values)"); |
| 2491 | 2492 | }, |
| 2493 | .bit_int => try w.print("{s} _BitInt({d})", .{ @tagName(ty.data.int.signedness), ty.data.int.bits }), | |
| 2494 | .complex_bit_int => try w.print("_Complex {s} _BitInt({d})", .{ @tagName(ty.data.int.signedness), ty.data.int.bits }), | |
| 2492 | 2495 | else => try w.writeAll(Builder.fromType(ty).str(langopts).?), |
| 2493 | 2496 | } |
| 2494 | 2497 | return true; |
| ... | ... | @@ -2644,15 +2647,12 @@ pub fn dump(ty: Type, mapper: StringInterner.TypeMapper, langopts: LangOpts, w: |
| 2644 | 2647 | .attributed => { |
| 2645 | 2648 | if (ty.isDecayed()) try w.writeAll("*d:"); |
| 2646 | 2649 | try w.writeAll("attributed("); |
| 2647 | try ty.data.attributed.base.dump(mapper, langopts, w); | |
| 2650 | try ty.data.attributed.base.canonicalize(.standard).dump(mapper, langopts, w); | |
| 2648 | 2651 | try w.writeAll(")"); |
| 2649 | 2652 | }, |
| 2650 | else => { | |
| 2651 | try w.writeAll(Builder.fromType(ty).str(langopts).?); | |
| 2652 | if (ty.specifier == .bit_int or ty.specifier == .complex_bit_int) { | |
| 2653 | try w.print("({d})", .{ty.data.int.bits}); | |
| 2654 | } | |
| 2655 | }, | |
| 2653 | .bit_int => try w.print("{s} _BitInt({d})", .{ @tagName(ty.data.int.signedness), ty.data.int.bits }), | |
| 2654 | .complex_bit_int => try w.print("_Complex {s} _BitInt({d})", .{ @tagName(ty.data.int.signedness), ty.data.int.bits }), | |
| 2655 | else => try w.writeAll(Builder.fromType(ty).str(langopts).?), | |
| 2656 | 2656 | } |
| 2657 | 2657 | } |
| 2658 | 2658 |
lib/compiler/aro/aro/Value.zig+358-53| ... | ... | @@ -8,6 +8,7 @@ const BigIntSpace = Interner.Tag.Int.BigIntSpace; |
| 8 | 8 | const Compilation = @import("Compilation.zig"); |
| 9 | 9 | const Type = @import("Type.zig"); |
| 10 | 10 | const target_util = @import("target.zig"); |
| 11 | const annex_g = @import("annex_g.zig"); | |
| 11 | 12 | |
| 12 | 13 | const Value = @This(); |
| 13 | 14 | |
| ... | ... | @@ -41,6 +42,14 @@ pub fn is(v: Value, tag: std.meta.Tag(Interner.Key), comp: *const Compilation) b |
| 41 | 42 | return comp.interner.get(v.ref()) == tag; |
| 42 | 43 | } |
| 43 | 44 | |
| 45 | pub fn isArithmetic(v: Value, comp: *const Compilation) bool { | |
| 46 | if (v.opt_ref == .none) return false; | |
| 47 | return switch (comp.interner.get(v.ref())) { | |
| 48 | .int, .float, .complex => true, | |
| 49 | else => false, | |
| 50 | }; | |
| 51 | } | |
| 52 | ||
| 44 | 53 | /// Number of bits needed to hold `v`. |
| 45 | 54 | /// Asserts that `v` is not negative |
| 46 | 55 | pub fn minUnsignedBits(v: Value, comp: *const Compilation) usize { |
| ... | ... | @@ -58,7 +67,7 @@ test "minUnsignedBits" { |
| 58 | 67 | } |
| 59 | 68 | }; |
| 60 | 69 | |
| 61 | var comp = Compilation.init(std.testing.allocator); | |
| 70 | var comp = Compilation.init(std.testing.allocator, std.fs.cwd()); | |
| 62 | 71 | defer comp.deinit(); |
| 63 | 72 | const target_query = try std.Target.Query.parse(.{ .arch_os_abi = "x86_64-linux-gnu" }); |
| 64 | 73 | comp.target = try std.zig.system.resolveTargetQuery(target_query); |
| ... | ... | @@ -93,7 +102,7 @@ test "minSignedBits" { |
| 93 | 102 | } |
| 94 | 103 | }; |
| 95 | 104 | |
| 96 | var comp = Compilation.init(std.testing.allocator); | |
| 105 | var comp = Compilation.init(std.testing.allocator, std.fs.cwd()); | |
| 97 | 106 | defer comp.deinit(); |
| 98 | 107 | const target_query = try std.Target.Query.parse(.{ .arch_os_abi = "x86_64-linux-gnu" }); |
| 99 | 108 | comp.target = try std.zig.system.resolveTargetQuery(target_query); |
| ... | ... | @@ -134,7 +143,7 @@ pub fn floatToInt(v: *Value, dest_ty: Type, comp: *Compilation) !FloatToIntChang |
| 134 | 143 | v.* = fromBool(!was_zero); |
| 135 | 144 | if (was_zero or was_one) return .none; |
| 136 | 145 | return .value_changed; |
| 137 | } else if (dest_ty.isUnsignedInt(comp) and v.compare(.lt, zero, comp)) { | |
| 146 | } else if (dest_ty.isUnsignedInt(comp) and float_val < 0) { | |
| 138 | 147 | v.* = zero; |
| 139 | 148 | return .out_of_range; |
| 140 | 149 | } |
| ... | ... | @@ -154,7 +163,7 @@ pub fn floatToInt(v: *Value, dest_ty: Type, comp: *Compilation) !FloatToIntChang |
| 154 | 163 | }; |
| 155 | 164 | |
| 156 | 165 | // The float is reduced in rational.setFloat, so we assert that denominator is equal to one |
| 157 | const big_one = std.math.big.int.Const{ .limbs = &.{1}, .positive = true }; | |
| 166 | const big_one = BigIntConst{ .limbs = &.{1}, .positive = true }; | |
| 158 | 167 | assert(rational.q.toConst().eqlAbs(big_one)); |
| 159 | 168 | |
| 160 | 169 | if (is_negative) { |
| ... | ... | @@ -179,6 +188,20 @@ pub fn floatToInt(v: *Value, dest_ty: Type, comp: *Compilation) !FloatToIntChang |
| 179 | 188 | /// `.none` value remains unchanged. |
| 180 | 189 | pub fn intToFloat(v: *Value, dest_ty: Type, comp: *Compilation) !void { |
| 181 | 190 | if (v.opt_ref == .none) return; |
| 191 | ||
| 192 | if (dest_ty.isComplex()) { | |
| 193 | const bits = dest_ty.bitSizeof(comp).?; | |
| 194 | const cf: Interner.Key.Complex = switch (bits) { | |
| 195 | 32 => .{ .cf16 = .{ v.toFloat(f16, comp), 0 } }, | |
| 196 | 64 => .{ .cf32 = .{ v.toFloat(f32, comp), 0 } }, | |
| 197 | 128 => .{ .cf64 = .{ v.toFloat(f64, comp), 0 } }, | |
| 198 | 160 => .{ .cf80 = .{ v.toFloat(f80, comp), 0 } }, | |
| 199 | 256 => .{ .cf128 = .{ v.toFloat(f128, comp), 0 } }, | |
| 200 | else => unreachable, | |
| 201 | }; | |
| 202 | v.* = try intern(comp, .{ .complex = cf }); | |
| 203 | return; | |
| 204 | } | |
| 182 | 205 | const bits = dest_ty.bitSizeof(comp).?; |
| 183 | 206 | return switch (comp.interner.get(v.ref()).int) { |
| 184 | 207 | inline .u64, .i64 => |data| { |
| ... | ... | @@ -207,40 +230,89 @@ pub fn intToFloat(v: *Value, dest_ty: Type, comp: *Compilation) !void { |
| 207 | 230 | }; |
| 208 | 231 | } |
| 209 | 232 | |
| 233 | pub const IntCastChangeKind = enum { | |
| 234 | /// value did not change | |
| 235 | none, | |
| 236 | /// Truncation occurred (e.g., i32 to i16) | |
| 237 | truncated, | |
| 238 | /// Sign conversion occurred (e.g., i32 to u32) | |
| 239 | sign_changed, | |
| 240 | }; | |
| 241 | ||
| 210 | 242 | /// Truncates or extends bits based on type. |
| 211 | 243 | /// `.none` value remains unchanged. |
| 212 | pub fn intCast(v: *Value, dest_ty: Type, comp: *Compilation) !void { | |
| 213 | if (v.opt_ref == .none) return; | |
| 214 | const bits: usize = @intCast(dest_ty.bitSizeof(comp).?); | |
| 244 | pub fn intCast(v: *Value, dest_ty: Type, comp: *Compilation) !IntCastChangeKind { | |
| 245 | if (v.opt_ref == .none) return .none; | |
| 246 | ||
| 247 | const dest_bits: usize = @intCast(dest_ty.bitSizeof(comp).?); | |
| 248 | const dest_signed = dest_ty.signedness(comp) == .signed; | |
| 249 | ||
| 215 | 250 | var space: BigIntSpace = undefined; |
| 216 | 251 | const big = v.toBigInt(&space, comp); |
| 252 | const value_bits = big.bitCountTwosComp(); | |
| 253 | ||
| 254 | // if big is negative, then is signed. | |
| 255 | const src_signed = !big.positive; | |
| 256 | const sign_change = src_signed != dest_signed; | |
| 217 | 257 | |
| 218 | 258 | const limbs = try comp.gpa.alloc( |
| 219 | 259 | std.math.big.Limb, |
| 220 | std.math.big.int.calcTwosCompLimbCount(@max(big.bitCountTwosComp(), bits)), | |
| 260 | std.math.big.int.calcTwosCompLimbCount(@max(value_bits, dest_bits)), | |
| 221 | 261 | ); |
| 222 | 262 | defer comp.gpa.free(limbs); |
| 223 | var result_bigint = std.math.big.int.Mutable{ .limbs = limbs, .positive = undefined, .len = undefined }; | |
| 224 | result_bigint.truncate(big, dest_ty.signedness(comp), bits); | |
| 263 | ||
| 264 | var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined }; | |
| 265 | result_bigint.truncate(big, dest_ty.signedness(comp), dest_bits); | |
| 225 | 266 | |
| 226 | 267 | v.* = try intern(comp, .{ .int = .{ .big_int = result_bigint.toConst() } }); |
| 268 | ||
| 269 | const truncation_occurred = value_bits > dest_bits; | |
| 270 | if (truncation_occurred) { | |
| 271 | return .truncated; | |
| 272 | } else if (sign_change) { | |
| 273 | return .sign_changed; | |
| 274 | } else { | |
| 275 | return .none; | |
| 276 | } | |
| 227 | 277 | } |
| 228 | 278 | |
| 229 | 279 | /// Converts the stored value to a float of the specified type |
| 230 | 280 | /// `.none` value remains unchanged. |
| 231 | 281 | pub fn floatCast(v: *Value, dest_ty: Type, comp: *Compilation) !void { |
| 232 | 282 | if (v.opt_ref == .none) return; |
| 233 | // TODO complex values | |
| 234 | const bits = dest_ty.makeReal().bitSizeof(comp).?; | |
| 235 | const f: Interner.Key.Float = switch (bits) { | |
| 236 | 16 => .{ .f16 = v.toFloat(f16, comp) }, | |
| 237 | 32 => .{ .f32 = v.toFloat(f32, comp) }, | |
| 238 | 64 => .{ .f64 = v.toFloat(f64, comp) }, | |
| 239 | 80 => .{ .f80 = v.toFloat(f80, comp) }, | |
| 240 | 128 => .{ .f128 = v.toFloat(f128, comp) }, | |
| 283 | const bits = dest_ty.bitSizeof(comp).?; | |
| 284 | if (dest_ty.isComplex()) { | |
| 285 | const cf: Interner.Key.Complex = switch (bits) { | |
| 286 | 32 => .{ .cf16 = .{ v.toFloat(f16, comp), v.imag(f16, comp) } }, | |
| 287 | 64 => .{ .cf32 = .{ v.toFloat(f32, comp), v.imag(f32, comp) } }, | |
| 288 | 128 => .{ .cf64 = .{ v.toFloat(f64, comp), v.imag(f64, comp) } }, | |
| 289 | 160 => .{ .cf80 = .{ v.toFloat(f80, comp), v.imag(f80, comp) } }, | |
| 290 | 256 => .{ .cf128 = .{ v.toFloat(f128, comp), v.imag(f128, comp) } }, | |
| 291 | else => unreachable, | |
| 292 | }; | |
| 293 | v.* = try intern(comp, .{ .complex = cf }); | |
| 294 | } else { | |
| 295 | const f: Interner.Key.Float = switch (bits) { | |
| 296 | 16 => .{ .f16 = v.toFloat(f16, comp) }, | |
| 297 | 32 => .{ .f32 = v.toFloat(f32, comp) }, | |
| 298 | 64 => .{ .f64 = v.toFloat(f64, comp) }, | |
| 299 | 80 => .{ .f80 = v.toFloat(f80, comp) }, | |
| 300 | 128 => .{ .f128 = v.toFloat(f128, comp) }, | |
| 301 | else => unreachable, | |
| 302 | }; | |
| 303 | v.* = try intern(comp, .{ .float = f }); | |
| 304 | } | |
| 305 | } | |
| 306 | ||
| 307 | pub fn imag(v: Value, comptime T: type, comp: *const Compilation) T { | |
| 308 | return switch (comp.interner.get(v.ref())) { | |
| 309 | .int => 0.0, | |
| 310 | .float => 0.0, | |
| 311 | .complex => |repr| switch (repr) { | |
| 312 | inline else => |components| return @floatCast(components[1]), | |
| 313 | }, | |
| 241 | 314 | else => unreachable, |
| 242 | 315 | }; |
| 243 | v.* = try intern(comp, .{ .float = f }); | |
| 244 | 316 | } |
| 245 | 317 | |
| 246 | 318 | 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 { |
| 252 | 324 | .float => |repr| switch (repr) { |
| 253 | 325 | inline else => |data| @floatCast(data), |
| 254 | 326 | }, |
| 327 | .complex => |repr| switch (repr) { | |
| 328 | inline else => |components| @floatCast(components[0]), | |
| 329 | }, | |
| 330 | else => unreachable, | |
| 331 | }; | |
| 332 | } | |
| 333 | ||
| 334 | pub fn realPart(v: Value, comp: *Compilation) !Value { | |
| 335 | if (v.opt_ref == .none) return v; | |
| 336 | return switch (comp.interner.get(v.ref())) { | |
| 337 | .int, .float => v, | |
| 338 | .complex => |repr| Value.intern(comp, switch (repr) { | |
| 339 | .cf16 => |components| .{ .float = .{ .f16 = components[0] } }, | |
| 340 | .cf32 => |components| .{ .float = .{ .f32 = components[0] } }, | |
| 341 | .cf64 => |components| .{ .float = .{ .f64 = components[0] } }, | |
| 342 | .cf80 => |components| .{ .float = .{ .f80 = components[0] } }, | |
| 343 | .cf128 => |components| .{ .float = .{ .f128 = components[0] } }, | |
| 344 | }), | |
| 345 | else => unreachable, | |
| 346 | }; | |
| 347 | } | |
| 348 | ||
| 349 | pub fn imaginaryPart(v: Value, comp: *Compilation) !Value { | |
| 350 | if (v.opt_ref == .none) return v; | |
| 351 | return switch (comp.interner.get(v.ref())) { | |
| 352 | .int, .float => Value.zero, | |
| 353 | .complex => |repr| Value.intern(comp, switch (repr) { | |
| 354 | .cf16 => |components| .{ .float = .{ .f16 = components[1] } }, | |
| 355 | .cf32 => |components| .{ .float = .{ .f32 = components[1] } }, | |
| 356 | .cf64 => |components| .{ .float = .{ .f64 = components[1] } }, | |
| 357 | .cf80 => |components| .{ .float = .{ .f80 = components[1] } }, | |
| 358 | .cf128 => |components| .{ .float = .{ .f128 = components[1] } }, | |
| 359 | }), | |
| 255 | 360 | else => unreachable, |
| 256 | 361 | }; |
| 257 | 362 | } |
| ... | ... | @@ -298,11 +403,56 @@ pub fn isZero(v: Value, comp: *const Compilation) bool { |
| 298 | 403 | inline .i64, .u64 => |data| return data == 0, |
| 299 | 404 | .big_int => |data| return data.eqlZero(), |
| 300 | 405 | }, |
| 406 | .complex => |repr| switch (repr) { | |
| 407 | inline else => |data| return data[0] == 0.0 and data[1] == 0.0, | |
| 408 | }, | |
| 301 | 409 | .bytes => return false, |
| 302 | 410 | else => unreachable, |
| 303 | 411 | } |
| 304 | 412 | } |
| 305 | 413 | |
| 414 | const IsInfKind = enum(i32) { | |
| 415 | negative = -1, | |
| 416 | finite = 0, | |
| 417 | positive = 1, | |
| 418 | unknown = std.math.maxInt(i32), | |
| 419 | }; | |
| 420 | ||
| 421 | pub fn isInfSign(v: Value, comp: *const Compilation) IsInfKind { | |
| 422 | if (v.opt_ref == .none) return .unknown; | |
| 423 | return switch (comp.interner.get(v.ref())) { | |
| 424 | .float => |repr| switch (repr) { | |
| 425 | inline else => |data| if (std.math.isPositiveInf(data)) .positive else if (std.math.isNegativeInf(data)) .negative else .finite, | |
| 426 | }, | |
| 427 | else => .unknown, | |
| 428 | }; | |
| 429 | } | |
| 430 | pub fn isInf(v: Value, comp: *const Compilation) bool { | |
| 431 | if (v.opt_ref == .none) return false; | |
| 432 | return switch (comp.interner.get(v.ref())) { | |
| 433 | .float => |repr| switch (repr) { | |
| 434 | inline else => |data| std.math.isInf(data), | |
| 435 | }, | |
| 436 | .complex => |repr| switch (repr) { | |
| 437 | inline else => |components| std.math.isInf(components[0]) or std.math.isInf(components[1]), | |
| 438 | }, | |
| 439 | else => false, | |
| 440 | }; | |
| 441 | } | |
| 442 | ||
| 443 | pub fn isNan(v: Value, comp: *const Compilation) bool { | |
| 444 | if (v.opt_ref == .none) return false; | |
| 445 | return switch (comp.interner.get(v.ref())) { | |
| 446 | .float => |repr| switch (repr) { | |
| 447 | inline else => |data| std.math.isNan(data), | |
| 448 | }, | |
| 449 | .complex => |repr| switch (repr) { | |
| 450 | inline else => |components| std.math.isNan(components[0]) or std.math.isNan(components[1]), | |
| 451 | }, | |
| 452 | else => false, | |
| 453 | }; | |
| 454 | } | |
| 455 | ||
| 306 | 456 | /// Converts value to zero or one; |
| 307 | 457 | /// `.none` value remains unchanged. |
| 308 | 458 | 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 { |
| 326 | 476 | return big_int.to(T) catch null; |
| 327 | 477 | } |
| 328 | 478 | |
| 479 | const ComplexOp = enum { | |
| 480 | add, | |
| 481 | sub, | |
| 482 | }; | |
| 483 | ||
| 484 | fn complexAddSub(lhs: Value, rhs: Value, comptime T: type, op: ComplexOp, comp: *Compilation) !Value { | |
| 485 | const res_re = switch (op) { | |
| 486 | .add => lhs.toFloat(T, comp) + rhs.toFloat(T, comp), | |
| 487 | .sub => lhs.toFloat(T, comp) - rhs.toFloat(T, comp), | |
| 488 | }; | |
| 489 | const res_im = switch (op) { | |
| 490 | .add => lhs.imag(T, comp) + rhs.imag(T, comp), | |
| 491 | .sub => lhs.imag(T, comp) - rhs.imag(T, comp), | |
| 492 | }; | |
| 493 | ||
| 494 | return switch (T) { | |
| 495 | f16 => intern(comp, .{ .complex = .{ .cf16 = .{ res_re, res_im } } }), | |
| 496 | f32 => intern(comp, .{ .complex = .{ .cf32 = .{ res_re, res_im } } }), | |
| 497 | f64 => intern(comp, .{ .complex = .{ .cf64 = .{ res_re, res_im } } }), | |
| 498 | f80 => intern(comp, .{ .complex = .{ .cf80 = .{ res_re, res_im } } }), | |
| 499 | f128 => intern(comp, .{ .complex = .{ .cf128 = .{ res_re, res_im } } }), | |
| 500 | else => unreachable, | |
| 501 | }; | |
| 502 | } | |
| 503 | ||
| 329 | 504 | pub fn add(res: *Value, lhs: Value, rhs: Value, ty: Type, comp: *Compilation) !bool { |
| 330 | 505 | const bits: usize = @intCast(ty.bitSizeof(comp).?); |
| 331 | 506 | if (ty.isFloat()) { |
| 507 | if (ty.isComplex()) { | |
| 508 | res.* = switch (bits) { | |
| 509 | 32 => try complexAddSub(lhs, rhs, f16, .add, comp), | |
| 510 | 64 => try complexAddSub(lhs, rhs, f32, .add, comp), | |
| 511 | 128 => try complexAddSub(lhs, rhs, f64, .add, comp), | |
| 512 | 160 => try complexAddSub(lhs, rhs, f80, .add, comp), | |
| 513 | 256 => try complexAddSub(lhs, rhs, f128, .add, comp), | |
| 514 | else => unreachable, | |
| 515 | }; | |
| 516 | return false; | |
| 517 | } | |
| 332 | 518 | const f: Interner.Key.Float = switch (bits) { |
| 333 | 519 | 16 => .{ .f16 = lhs.toFloat(f16, comp) + rhs.toFloat(f16, comp) }, |
| 334 | 520 | 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 |
| 350 | 536 | std.math.big.int.calcTwosCompLimbCount(bits), |
| 351 | 537 | ); |
| 352 | 538 | defer comp.gpa.free(limbs); |
| 353 | var result_bigint = std.math.big.int.Mutable{ .limbs = limbs, .positive = undefined, .len = undefined }; | |
| 539 | var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined }; | |
| 354 | 540 | |
| 355 | 541 | const overflowed = result_bigint.addWrap(lhs_bigint, rhs_bigint, ty.signedness(comp), bits); |
| 356 | 542 | 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 |
| 361 | 547 | pub fn sub(res: *Value, lhs: Value, rhs: Value, ty: Type, comp: *Compilation) !bool { |
| 362 | 548 | const bits: usize = @intCast(ty.bitSizeof(comp).?); |
| 363 | 549 | if (ty.isFloat()) { |
| 550 | if (ty.isComplex()) { | |
| 551 | res.* = switch (bits) { | |
| 552 | 32 => try complexAddSub(lhs, rhs, f16, .sub, comp), | |
| 553 | 64 => try complexAddSub(lhs, rhs, f32, .sub, comp), | |
| 554 | 128 => try complexAddSub(lhs, rhs, f64, .sub, comp), | |
| 555 | 160 => try complexAddSub(lhs, rhs, f80, .sub, comp), | |
| 556 | 256 => try complexAddSub(lhs, rhs, f128, .sub, comp), | |
| 557 | else => unreachable, | |
| 558 | }; | |
| 559 | return false; | |
| 560 | } | |
| 364 | 561 | const f: Interner.Key.Float = switch (bits) { |
| 365 | 562 | 16 => .{ .f16 = lhs.toFloat(f16, comp) - rhs.toFloat(f16, comp) }, |
| 366 | 563 | 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 |
| 382 | 579 | std.math.big.int.calcTwosCompLimbCount(bits), |
| 383 | 580 | ); |
| 384 | 581 | defer comp.gpa.free(limbs); |
| 385 | var result_bigint = std.math.big.int.Mutable{ .limbs = limbs, .positive = undefined, .len = undefined }; | |
| 582 | var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined }; | |
| 386 | 583 | |
| 387 | 584 | const overflowed = result_bigint.subWrap(lhs_bigint, rhs_bigint, ty.signedness(comp), bits); |
| 388 | 585 | 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 |
| 393 | 590 | pub fn mul(res: *Value, lhs: Value, rhs: Value, ty: Type, comp: *Compilation) !bool { |
| 394 | 591 | const bits: usize = @intCast(ty.bitSizeof(comp).?); |
| 395 | 592 | if (ty.isFloat()) { |
| 593 | if (ty.isComplex()) { | |
| 594 | const cf: Interner.Key.Complex = switch (bits) { | |
| 595 | 32 => .{ .cf16 = annex_g.complexFloatMul(f16, lhs.toFloat(f16, comp), lhs.imag(f16, comp), rhs.toFloat(f16, comp), rhs.imag(f16, comp)) }, | |
| 596 | 64 => .{ .cf32 = annex_g.complexFloatMul(f32, lhs.toFloat(f32, comp), lhs.imag(f32, comp), rhs.toFloat(f32, comp), rhs.imag(f32, comp)) }, | |
| 597 | 128 => .{ .cf64 = annex_g.complexFloatMul(f64, lhs.toFloat(f64, comp), lhs.imag(f64, comp), rhs.toFloat(f64, comp), rhs.imag(f64, comp)) }, | |
| 598 | 160 => .{ .cf80 = annex_g.complexFloatMul(f80, lhs.toFloat(f80, comp), lhs.imag(f80, comp), rhs.toFloat(f80, comp), rhs.imag(f80, comp)) }, | |
| 599 | 256 => .{ .cf128 = annex_g.complexFloatMul(f128, lhs.toFloat(f128, comp), lhs.imag(f128, comp), rhs.toFloat(f128, comp), rhs.imag(f128, comp)) }, | |
| 600 | else => unreachable, | |
| 601 | }; | |
| 602 | res.* = try intern(comp, .{ .complex = cf }); | |
| 603 | return false; | |
| 604 | } | |
| 396 | 605 | const f: Interner.Key.Float = switch (bits) { |
| 397 | 606 | 16 => .{ .f16 = lhs.toFloat(f16, comp) * rhs.toFloat(f16, comp) }, |
| 398 | 607 | 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 |
| 438 | 647 | pub fn div(res: *Value, lhs: Value, rhs: Value, ty: Type, comp: *Compilation) !bool { |
| 439 | 648 | const bits: usize = @intCast(ty.bitSizeof(comp).?); |
| 440 | 649 | if (ty.isFloat()) { |
| 650 | if (ty.isComplex()) { | |
| 651 | const cf: Interner.Key.Complex = switch (bits) { | |
| 652 | 32 => .{ .cf16 = annex_g.complexFloatDiv(f16, lhs.toFloat(f16, comp), lhs.imag(f16, comp), rhs.toFloat(f16, comp), rhs.imag(f16, comp)) }, | |
| 653 | 64 => .{ .cf32 = annex_g.complexFloatDiv(f32, lhs.toFloat(f32, comp), lhs.imag(f32, comp), rhs.toFloat(f32, comp), rhs.imag(f32, comp)) }, | |
| 654 | 128 => .{ .cf64 = annex_g.complexFloatDiv(f64, lhs.toFloat(f64, comp), lhs.imag(f64, comp), rhs.toFloat(f64, comp), rhs.imag(f64, comp)) }, | |
| 655 | 160 => .{ .cf80 = annex_g.complexFloatDiv(f80, lhs.toFloat(f80, comp), lhs.imag(f80, comp), rhs.toFloat(f80, comp), rhs.imag(f80, comp)) }, | |
| 656 | 256 => .{ .cf128 = annex_g.complexFloatDiv(f128, lhs.toFloat(f128, comp), lhs.imag(f128, comp), rhs.toFloat(f128, comp), rhs.imag(f128, comp)) }, | |
| 657 | else => unreachable, | |
| 658 | }; | |
| 659 | res.* = try intern(comp, .{ .complex = cf }); | |
| 660 | return false; | |
| 661 | } | |
| 441 | 662 | const f: Interner.Key.Float = switch (bits) { |
| 442 | 663 | 16 => .{ .f16 = lhs.toFloat(f16, comp) / rhs.toFloat(f16, comp) }, |
| 443 | 664 | 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 { |
| 491 | 712 | |
| 492 | 713 | const signedness = ty.signedness(comp); |
| 493 | 714 | if (signedness == .signed) { |
| 494 | var spaces: [3]BigIntSpace = undefined; | |
| 495 | const min_val = BigIntMutable.init(&spaces[0].limbs, ty.minInt(comp)).toConst(); | |
| 496 | const negative = BigIntMutable.init(&spaces[1].limbs, -1).toConst(); | |
| 497 | const big_one = BigIntMutable.init(&spaces[2].limbs, 1).toConst(); | |
| 498 | if (lhs_bigint.eql(min_val) and rhs_bigint.eql(negative)) { | |
| 715 | var spaces: [2]BigIntSpace = undefined; | |
| 716 | const min_val = try Value.minInt(ty, comp); | |
| 717 | const negative = BigIntMutable.init(&spaces[0].limbs, -1).toConst(); | |
| 718 | const big_one = BigIntMutable.init(&spaces[1].limbs, 1).toConst(); | |
| 719 | if (lhs.compare(.eq, min_val, comp) and rhs_bigint.eql(negative)) { | |
| 499 | 720 | return .{}; |
| 500 | 721 | } else if (rhs_bigint.order(big_one).compare(.lt)) { |
| 501 | 722 | // lhs - @divTrunc(lhs, rhs) * rhs |
| ... | ... | @@ -542,7 +763,7 @@ pub fn bitOr(lhs: Value, rhs: Value, comp: *Compilation) !Value { |
| 542 | 763 | @max(lhs_bigint.limbs.len, rhs_bigint.limbs.len), |
| 543 | 764 | ); |
| 544 | 765 | defer comp.gpa.free(limbs); |
| 545 | var result_bigint = std.math.big.int.Mutable{ .limbs = limbs, .positive = undefined, .len = undefined }; | |
| 766 | var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined }; | |
| 546 | 767 | |
| 547 | 768 | result_bigint.bitOr(lhs_bigint, rhs_bigint); |
| 548 | 769 | return intern(comp, .{ .int = .{ .big_int = result_bigint.toConst() } }); |
| ... | ... | @@ -554,12 +775,13 @@ pub fn bitXor(lhs: Value, rhs: Value, comp: *Compilation) !Value { |
| 554 | 775 | const lhs_bigint = lhs.toBigInt(&lhs_space, comp); |
| 555 | 776 | const rhs_bigint = rhs.toBigInt(&rhs_space, comp); |
| 556 | 777 | |
| 778 | const extra = @intFromBool(lhs_bigint.positive != rhs_bigint.positive); | |
| 557 | 779 | const limbs = try comp.gpa.alloc( |
| 558 | 780 | std.math.big.Limb, |
| 559 | @max(lhs_bigint.limbs.len, rhs_bigint.limbs.len), | |
| 781 | @max(lhs_bigint.limbs.len, rhs_bigint.limbs.len) + extra, | |
| 560 | 782 | ); |
| 561 | 783 | defer comp.gpa.free(limbs); |
| 562 | var result_bigint = std.math.big.int.Mutable{ .limbs = limbs, .positive = undefined, .len = undefined }; | |
| 784 | var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined }; | |
| 563 | 785 | |
| 564 | 786 | result_bigint.bitXor(lhs_bigint, rhs_bigint); |
| 565 | 787 | return intern(comp, .{ .int = .{ .big_int = result_bigint.toConst() } }); |
| ... | ... | @@ -571,12 +793,18 @@ pub fn bitAnd(lhs: Value, rhs: Value, comp: *Compilation) !Value { |
| 571 | 793 | const lhs_bigint = lhs.toBigInt(&lhs_space, comp); |
| 572 | 794 | const rhs_bigint = rhs.toBigInt(&rhs_space, comp); |
| 573 | 795 | |
| 574 | const limbs = try comp.gpa.alloc( | |
| 575 | std.math.big.Limb, | |
| 576 | @max(lhs_bigint.limbs.len, rhs_bigint.limbs.len), | |
| 577 | ); | |
| 796 | const limb_count = if (lhs_bigint.positive and rhs_bigint.positive) | |
| 797 | @min(lhs_bigint.limbs.len, rhs_bigint.limbs.len) | |
| 798 | else if (lhs_bigint.positive) | |
| 799 | lhs_bigint.limbs.len | |
| 800 | else if (rhs_bigint.positive) | |
| 801 | rhs_bigint.limbs.len | |
| 802 | else | |
| 803 | @max(lhs_bigint.limbs.len, rhs_bigint.limbs.len) + 1; | |
| 804 | ||
| 805 | const limbs = try comp.gpa.alloc(std.math.big.Limb, limb_count); | |
| 578 | 806 | defer comp.gpa.free(limbs); |
| 579 | var result_bigint = std.math.big.int.Mutable{ .limbs = limbs, .positive = undefined, .len = undefined }; | |
| 807 | var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined }; | |
| 580 | 808 | |
| 581 | 809 | result_bigint.bitAnd(lhs_bigint, rhs_bigint); |
| 582 | 810 | return intern(comp, .{ .int = .{ .big_int = result_bigint.toConst() } }); |
| ... | ... | @@ -592,7 +820,7 @@ pub fn bitNot(val: Value, ty: Type, comp: *Compilation) !Value { |
| 592 | 820 | std.math.big.int.calcTwosCompLimbCount(bits), |
| 593 | 821 | ); |
| 594 | 822 | defer comp.gpa.free(limbs); |
| 595 | var result_bigint = std.math.big.int.Mutable{ .limbs = limbs, .positive = undefined, .len = undefined }; | |
| 823 | var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined }; | |
| 596 | 824 | |
| 597 | 825 | result_bigint.bitNotWrap(val_bigint, ty.signedness(comp), bits); |
| 598 | 826 | 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 |
| 606 | 834 | const bits: usize = @intCast(ty.bitSizeof(comp).?); |
| 607 | 835 | if (shift > bits) { |
| 608 | 836 | if (lhs_bigint.positive) { |
| 609 | res.* = try intern(comp, .{ .int = .{ .u64 = ty.maxInt(comp) } }); | |
| 837 | res.* = try Value.maxInt(ty, comp); | |
| 610 | 838 | } else { |
| 611 | res.* = try intern(comp, .{ .int = .{ .i64 = ty.minInt(comp) } }); | |
| 839 | res.* = try Value.minInt(ty, comp); | |
| 612 | 840 | } |
| 613 | 841 | return true; |
| 614 | 842 | } |
| ... | ... | @@ -618,7 +846,7 @@ pub fn shl(res: *Value, lhs: Value, rhs: Value, ty: Type, comp: *Compilation) !b |
| 618 | 846 | lhs_bigint.limbs.len + (shift / (@sizeOf(std.math.big.Limb) * 8)) + 1, |
| 619 | 847 | ); |
| 620 | 848 | defer comp.gpa.free(limbs); |
| 621 | var result_bigint = std.math.big.int.Mutable{ .limbs = limbs, .positive = undefined, .len = undefined }; | |
| 849 | var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined }; | |
| 622 | 850 | |
| 623 | 851 | result_bigint.shiftLeft(lhs_bigint, shift); |
| 624 | 852 | const signedness = ty.signedness(comp); |
| ... | ... | @@ -652,12 +880,25 @@ pub fn shr(lhs: Value, rhs: Value, ty: Type, comp: *Compilation) !Value { |
| 652 | 880 | std.math.big.int.calcTwosCompLimbCount(bits), |
| 653 | 881 | ); |
| 654 | 882 | defer comp.gpa.free(limbs); |
| 655 | var result_bigint = std.math.big.int.Mutable{ .limbs = limbs, .positive = undefined, .len = undefined }; | |
| 883 | var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined }; | |
| 656 | 884 | |
| 657 | 885 | result_bigint.shiftRight(lhs_bigint, shift); |
| 658 | 886 | return intern(comp, .{ .int = .{ .big_int = result_bigint.toConst() } }); |
| 659 | 887 | } |
| 660 | 888 | |
| 889 | pub fn complexConj(val: Value, ty: Type, comp: *Compilation) !Value { | |
| 890 | const bits = ty.bitSizeof(comp).?; | |
| 891 | const cf: Interner.Key.Complex = switch (bits) { | |
| 892 | 32 => .{ .cf16 = .{ val.toFloat(f16, comp), -val.imag(f16, comp) } }, | |
| 893 | 64 => .{ .cf32 = .{ val.toFloat(f32, comp), -val.imag(f32, comp) } }, | |
| 894 | 128 => .{ .cf64 = .{ val.toFloat(f64, comp), -val.imag(f64, comp) } }, | |
| 895 | 160 => .{ .cf80 = .{ val.toFloat(f80, comp), -val.imag(f80, comp) } }, | |
| 896 | 256 => .{ .cf128 = .{ val.toFloat(f128, comp), -val.imag(f128, comp) } }, | |
| 897 | else => unreachable, | |
| 898 | }; | |
| 899 | return intern(comp, .{ .complex = cf }); | |
| 900 | } | |
| 901 | ||
| 661 | 902 | pub fn compare(lhs: Value, op: std.math.CompareOperator, rhs: Value, comp: *const Compilation) bool { |
| 662 | 903 | if (op == .eq) { |
| 663 | 904 | return lhs.opt_ref == rhs.opt_ref; |
| ... | ... | @@ -672,6 +913,12 @@ pub fn compare(lhs: Value, op: std.math.CompareOperator, rhs: Value, comp: *cons |
| 672 | 913 | const rhs_f128 = rhs.toFloat(f128, comp); |
| 673 | 914 | return std.math.compare(lhs_f128, op, rhs_f128); |
| 674 | 915 | } |
| 916 | if (lhs_key == .complex or rhs_key == .complex) { | |
| 917 | assert(op == .neq); | |
| 918 | const real_equal = std.math.compare(lhs.toFloat(f128, comp), .eq, rhs.toFloat(f128, comp)); | |
| 919 | const imag_equal = std.math.compare(lhs.imag(f128, comp), .eq, rhs.imag(f128, comp)); | |
| 920 | return !real_equal or !imag_equal; | |
| 921 | } | |
| 675 | 922 | |
| 676 | 923 | var lhs_bigint_space: BigIntSpace = undefined; |
| 677 | 924 | var rhs_bigint_space: BigIntSpace = undefined; |
| ... | ... | @@ -680,6 +927,42 @@ pub fn compare(lhs: Value, op: std.math.CompareOperator, rhs: Value, comp: *cons |
| 680 | 927 | return lhs_bigint.order(rhs_bigint).compare(op); |
| 681 | 928 | } |
| 682 | 929 | |
| 930 | fn twosCompIntLimit(limit: std.math.big.int.TwosCompIntLimit, ty: Type, comp: *Compilation) !Value { | |
| 931 | const signedness = ty.signedness(comp); | |
| 932 | if (limit == .min and signedness == .unsigned) return Value.zero; | |
| 933 | const mag_bits: usize = @intCast(ty.bitSizeof(comp).?); | |
| 934 | switch (mag_bits) { | |
| 935 | inline 8, 16, 32, 64 => |bits| { | |
| 936 | if (limit == .min) return Value.int(@as(i64, std.math.minInt(std.meta.Int(.signed, bits))), comp); | |
| 937 | return switch (signedness) { | |
| 938 | inline else => |sign| Value.int(std.math.maxInt(std.meta.Int(sign, bits)), comp), | |
| 939 | }; | |
| 940 | }, | |
| 941 | else => {}, | |
| 942 | } | |
| 943 | ||
| 944 | const sign_bits = @intFromBool(signedness == .signed); | |
| 945 | const total_bits = mag_bits + sign_bits; | |
| 946 | ||
| 947 | const limbs = try comp.gpa.alloc( | |
| 948 | std.math.big.Limb, | |
| 949 | std.math.big.int.calcTwosCompLimbCount(total_bits), | |
| 950 | ); | |
| 951 | defer comp.gpa.free(limbs); | |
| 952 | ||
| 953 | var result_bigint: BigIntMutable = .{ .limbs = limbs, .positive = undefined, .len = undefined }; | |
| 954 | result_bigint.setTwosCompIntLimit(limit, signedness, mag_bits); | |
| 955 | return Value.intern(comp, .{ .int = .{ .big_int = result_bigint.toConst() } }); | |
| 956 | } | |
| 957 | ||
| 958 | pub fn minInt(ty: Type, comp: *Compilation) !Value { | |
| 959 | return twosCompIntLimit(.min, ty, comp); | |
| 960 | } | |
| 961 | ||
| 962 | pub fn maxInt(ty: Type, comp: *Compilation) !Value { | |
| 963 | return twosCompIntLimit(.max, ty, comp); | |
| 964 | } | |
| 965 | ||
| 683 | 966 | pub fn print(v: Value, ty: Type, comp: *const Compilation, w: anytype) @TypeOf(w).Error!void { |
| 684 | 967 | if (ty.is(.bool)) { |
| 685 | 968 | 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 |
| 696 | 979 | inline else => |x| return w.print("{d}", .{@as(f64, @floatCast(x))}), |
| 697 | 980 | }, |
| 698 | 981 | .bytes => |b| return printString(b, ty, comp, w), |
| 982 | .complex => |repr| switch (repr) { | |
| 983 | .cf32 => |components| return w.print("{d} + {d}i", .{ @round(@as(f64, @floatCast(components[0])) * 1000000) / 1000000, @round(@as(f64, @floatCast(components[1])) * 1000000) / 1000000 }), | |
| 984 | inline else => |components| return w.print("{d} + {d}i", .{ @as(f64, @floatCast(components[0])), @as(f64, @floatCast(components[1])) }), | |
| 985 | }, | |
| 699 | 986 | else => unreachable, // not a value |
| 700 | 987 | } |
| 701 | 988 | } |
| ... | ... | @@ -703,26 +990,44 @@ pub fn print(v: Value, ty: Type, comp: *const Compilation, w: anytype) @TypeOf(w |
| 703 | 990 | pub fn printString(bytes: []const u8, ty: Type, comp: *const Compilation, w: anytype) @TypeOf(w).Error!void { |
| 704 | 991 | const size: Compilation.CharUnitSize = @enumFromInt(ty.elemType().sizeof(comp).?); |
| 705 | 992 | const without_null = bytes[0 .. bytes.len - @intFromEnum(size)]; |
| 993 | try w.writeByte('"'); | |
| 706 | 994 | switch (size) { |
| 707 | inline .@"1", .@"2" => |sz| { | |
| 708 | const data_slice: []const sz.Type() = @alignCast(std.mem.bytesAsSlice(sz.Type(), without_null)); | |
| 709 | const formatter = if (sz == .@"1") std.zig.fmtEscapes(data_slice) else std.unicode.fmtUtf16Le(data_slice); | |
| 710 | try w.print("\"{}\"", .{formatter}); | |
| 995 | .@"1" => try w.print("{}", .{std.zig.fmtEscapes(without_null)}), | |
| 996 | .@"2" => { | |
| 997 | var items: [2]u16 = undefined; | |
| 998 | var i: usize = 0; | |
| 999 | while (i < without_null.len) { | |
| 1000 | @memcpy(std.mem.sliceAsBytes(items[0..1]), without_null[i..][0..2]); | |
| 1001 | i += 2; | |
| 1002 | const is_surrogate = std.unicode.utf16IsHighSurrogate(items[0]); | |
| 1003 | if (is_surrogate and i < without_null.len) { | |
| 1004 | @memcpy(std.mem.sliceAsBytes(items[1..2]), without_null[i..][0..2]); | |
| 1005 | if (std.unicode.utf16DecodeSurrogatePair(&items)) |decoded| { | |
| 1006 | i += 2; | |
| 1007 | try w.print("{u}", .{decoded}); | |
| 1008 | } else |_| { | |
| 1009 | try w.print("\\x{x}", .{items[0]}); | |
| 1010 | } | |
| 1011 | } else if (is_surrogate) { | |
| 1012 | try w.print("\\x{x}", .{items[0]}); | |
| 1013 | } else { | |
| 1014 | try w.print("{u}", .{items[0]}); | |
| 1015 | } | |
| 1016 | } | |
| 711 | 1017 | }, |
| 712 | 1018 | .@"4" => { |
| 713 | try w.writeByte('"'); | |
| 714 | const data_slice = std.mem.bytesAsSlice(u32, without_null); | |
| 715 | var buf: [4]u8 = undefined; | |
| 716 | for (data_slice) |item| { | |
| 717 | if (item <= std.math.maxInt(u21) and std.unicode.utf8ValidCodepoint(@intCast(item))) { | |
| 718 | const codepoint: u21 = @intCast(item); | |
| 719 | const written = std.unicode.utf8Encode(codepoint, &buf) catch unreachable; | |
| 720 | try w.print("{s}", .{buf[0..written]}); | |
| 1019 | var item: [1]u32 = undefined; | |
| 1020 | const data_slice = std.mem.sliceAsBytes(item[0..1]); | |
| 1021 | for (0..@divExact(without_null.len, 4)) |n| { | |
| 1022 | @memcpy(data_slice, without_null[n * 4 ..][0..4]); | |
| 1023 | if (item[0] <= std.math.maxInt(u21) and std.unicode.utf8ValidCodepoint(@intCast(item[0]))) { | |
| 1024 | const codepoint: u21 = @intCast(item[0]); | |
| 1025 | try w.print("{u}", .{codepoint}); | |
| 721 | 1026 | } else { |
| 722 | try w.print("\\x{x}", .{item}); | |
| 1027 | try w.print("\\x{x}", .{item[0]}); | |
| 723 | 1028 | } |
| 724 | 1029 | } |
| 725 | try w.writeByte('"'); | |
| 726 | 1030 | }, |
| 727 | 1031 | } |
| 1032 | try w.writeByte('"'); | |
| 728 | 1033 | } |
lib/compiler/aro/aro/annex_g.zig created+118| ... | ... | @@ -0,0 +1,118 @@ |
| 1 | //! Complex arithmetic algorithms from C99 Annex G | |
| 2 | ||
| 3 | const std = @import("std"); | |
| 4 | const copysign = std.math.copysign; | |
| 5 | const ilogb = std.math.ilogb; | |
| 6 | const inf = std.math.inf; | |
| 7 | const isFinite = std.math.isFinite; | |
| 8 | const isInf = std.math.isInf; | |
| 9 | const isNan = std.math.isNan; | |
| 10 | const isPositiveZero = std.math.isPositiveZero; | |
| 11 | const scalbn = std.math.scalbn; | |
| 12 | ||
| 13 | /// 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 | |
| 14 | pub fn complexFloatMul(comptime T: type, a_param: T, b_param: T, c_param: T, d_param: T) [2]T { | |
| 15 | var a = a_param; | |
| 16 | var b = b_param; | |
| 17 | var c = c_param; | |
| 18 | var d = d_param; | |
| 19 | ||
| 20 | const ac = a * c; | |
| 21 | const bd = b * d; | |
| 22 | const ad = a * d; | |
| 23 | const bc = b * c; | |
| 24 | var x = ac - bd; | |
| 25 | var y = ad + bc; | |
| 26 | if (isNan(x) and isNan(y)) { | |
| 27 | var recalc = false; | |
| 28 | if (isInf(a) or isInf(b)) { | |
| 29 | // lhs infinite | |
| 30 | // Box the infinity and change NaNs in the other factor to 0 | |
| 31 | a = copysign(if (isInf(a)) @as(T, 1.0) else @as(T, 0.0), a); | |
| 32 | b = copysign(if (isInf(b)) @as(T, 1.0) else @as(T, 0.0), b); | |
| 33 | if (isNan(c)) c = copysign(@as(T, 0.0), c); | |
| 34 | if (isNan(d)) d = copysign(@as(T, 0.0), d); | |
| 35 | recalc = true; | |
| 36 | } | |
| 37 | if (isInf(c) or isInf(d)) { | |
| 38 | // rhs infinite | |
| 39 | // Box the infinity and change NaNs in the other factor to 0 | |
| 40 | c = copysign(if (isInf(c)) @as(T, 1.0) else @as(T, 0.0), c); | |
| 41 | d = copysign(if (isInf(d)) @as(T, 1.0) else @as(T, 0.0), d); | |
| 42 | if (isNan(a)) a = copysign(@as(T, 0.0), a); | |
| 43 | if (isNan(b)) b = copysign(@as(T, 0.0), b); | |
| 44 | recalc = true; | |
| 45 | } | |
| 46 | if (!recalc and (isInf(ac) or isInf(bd) or isInf(ad) or isInf(bc))) { | |
| 47 | // Recover infinities from overflow by changing NaN's to 0 | |
| 48 | if (isNan(a)) a = copysign(@as(T, 0.0), a); | |
| 49 | if (isNan(b)) b = copysign(@as(T, 0.0), b); | |
| 50 | if (isNan(c)) c = copysign(@as(T, 0.0), c); | |
| 51 | if (isNan(d)) d = copysign(@as(T, 0.0), d); | |
| 52 | } | |
| 53 | if (recalc) { | |
| 54 | x = inf(T) * (a * c - b * d); | |
| 55 | y = inf(T) * (a * d + b * c); | |
| 56 | } | |
| 57 | } | |
| 58 | return .{ x, y }; | |
| 59 | } | |
| 60 | ||
| 61 | /// 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 | |
| 62 | pub fn complexFloatDiv(comptime T: type, a_param: T, b_param: T, c_param: T, d_param: T) [2]T { | |
| 63 | var a = a_param; | |
| 64 | var b = b_param; | |
| 65 | var c = c_param; | |
| 66 | var d = d_param; | |
| 67 | var denom_logb: i32 = 0; | |
| 68 | const max_cd = @max(@abs(c), @abs(d)); | |
| 69 | if (isFinite(max_cd)) { | |
| 70 | if (max_cd == 0) { | |
| 71 | denom_logb = std.math.minInt(i32) + 1; | |
| 72 | c = 0; | |
| 73 | d = 0; | |
| 74 | } else { | |
| 75 | denom_logb = ilogb(max_cd); | |
| 76 | c = scalbn(c, -denom_logb); | |
| 77 | d = scalbn(d, -denom_logb); | |
| 78 | } | |
| 79 | } | |
| 80 | const denom = c * c + d * d; | |
| 81 | var x = scalbn((a * c + b * d) / denom, -denom_logb); | |
| 82 | var y = scalbn((b * c - a * d) / denom, -denom_logb); | |
| 83 | if (isNan(x) and isNan(y)) { | |
| 84 | if (isPositiveZero(denom) and (!isNan(a) or !isNan(b))) { | |
| 85 | x = copysign(inf(T), c) * a; | |
| 86 | y = copysign(inf(T), c) * b; | |
| 87 | } else if ((isInf(a) or isInf(b)) and isFinite(c) and isFinite(d)) { | |
| 88 | a = copysign(if (isInf(a)) @as(T, 1.0) else @as(T, 0.0), a); | |
| 89 | b = copysign(if (isInf(b)) @as(T, 1.0) else @as(T, 0.0), b); | |
| 90 | x = inf(T) * (a * c + b * d); | |
| 91 | y = inf(T) * (b * c - a * d); | |
| 92 | } else if (isInf(max_cd) and isFinite(a) and isFinite(b)) { | |
| 93 | c = copysign(if (isInf(c)) @as(T, 1.0) else @as(T, 0.0), c); | |
| 94 | d = copysign(if (isInf(d)) @as(T, 1.0) else @as(T, 0.0), d); | |
| 95 | x = 0.0 * (a * c + b * d); | |
| 96 | y = 0.0 * (b * c - a * d); | |
| 97 | } | |
| 98 | } | |
| 99 | return .{ x, y }; | |
| 100 | } | |
| 101 | ||
| 102 | test complexFloatMul { | |
| 103 | // Naive algorithm would produce NaN + NaNi instead of inf + NaNi | |
| 104 | const result = complexFloatMul(f64, inf(f64), std.math.nan(f64), 2, 0); | |
| 105 | try std.testing.expect(isInf(result[0])); | |
| 106 | try std.testing.expect(isNan(result[1])); | |
| 107 | } | |
| 108 | ||
| 109 | test complexFloatDiv { | |
| 110 | // Naive algorithm would produce NaN + NaNi instead of inf + NaNi | |
| 111 | var result = complexFloatDiv(f64, inf(f64), std.math.nan(f64), 2, 0); | |
| 112 | try std.testing.expect(isInf(result[0])); | |
| 113 | try std.testing.expect(isNan(result[1])); | |
| 114 | ||
| 115 | result = complexFloatDiv(f64, 2.0, 2.0, 0.0, 0.0); | |
| 116 | try std.testing.expect(isInf(result[0])); | |
| 117 | try std.testing.expect(isInf(result[1])); | |
| 118 | } |
lib/compiler/aro/aro/features.zig+2-2| ... | ... | @@ -45,7 +45,7 @@ pub fn hasFeature(comp: *Compilation, ext: []const u8) bool { |
| 45 | 45 | .c_static_assert = comp.langopts.standard.atLeast(.c11), |
| 46 | 46 | .c_thread_local = comp.langopts.standard.atLeast(.c11) and target_util.isTlsSupported(comp.target), |
| 47 | 47 | }; |
| 48 | inline for (std.meta.fields(@TypeOf(list))) |f| { | |
| 48 | inline for (@typeInfo(@TypeOf(list)).@"struct".fields) |f| { | |
| 49 | 49 | if (std.mem.eql(u8, f.name, ext)) return @field(list, f.name); |
| 50 | 50 | } |
| 51 | 51 | return false; |
| ... | ... | @@ -69,7 +69,7 @@ pub fn hasExtension(comp: *Compilation, ext: []const u8) bool { |
| 69 | 69 | .matrix_types = false, // TODO |
| 70 | 70 | .matrix_types_scalar_division = false, // TODO |
| 71 | 71 | }; |
| 72 | inline for (std.meta.fields(@TypeOf(list))) |f| { | |
| 72 | inline for (@typeInfo(@TypeOf(list)).@"struct".fields) |f| { | |
| 73 | 73 | if (std.mem.eql(u8, f.name, ext)) return @field(list, f.name); |
| 74 | 74 | } |
| 75 | 75 | return false; |
lib/compiler/aro/aro/record_layout.zig+44-46| ... | ... | @@ -19,6 +19,13 @@ const OngoingBitfield = struct { |
| 19 | 19 | unused_size_bits: u64, |
| 20 | 20 | }; |
| 21 | 21 | |
| 22 | pub const Error = error{Overflow}; | |
| 23 | ||
| 24 | fn alignForward(addr: u64, alignment: u64) !u64 { | |
| 25 | const forward_addr = try std.math.add(u64, addr, alignment - 1); | |
| 26 | return std.mem.alignBackward(u64, forward_addr, alignment); | |
| 27 | } | |
| 28 | ||
| 22 | 29 | const SysVContext = struct { |
| 23 | 30 | /// Does the record have an __attribute__((packed)) annotation. |
| 24 | 31 | attr_packed: bool, |
| ... | ... | @@ -36,14 +43,8 @@ const SysVContext = struct { |
| 36 | 43 | comp: *const Compilation, |
| 37 | 44 | |
| 38 | 45 | fn init(ty: Type, comp: *const Compilation, pragma_pack: ?u8) SysVContext { |
| 39 | var pack_value: ?u64 = null; | |
| 40 | if (pragma_pack) |pak| { | |
| 41 | pack_value = pak * BITS_PER_BYTE; | |
| 42 | } | |
| 43 | var req_align: u29 = BITS_PER_BYTE; | |
| 44 | if (ty.requestedAlignment(comp)) |aln| { | |
| 45 | req_align = aln * BITS_PER_BYTE; | |
| 46 | } | |
| 46 | const pack_value: ?u64 = if (pragma_pack) |pak| @as(u64, pak) * BITS_PER_BYTE else null; | |
| 47 | const req_align = @as(u32, (ty.requestedAlignment(comp) orelse 1)) * BITS_PER_BYTE; | |
| 47 | 48 | return SysVContext{ |
| 48 | 49 | .attr_packed = ty.hasAttribute(.@"packed"), |
| 49 | 50 | .max_field_align_bits = pack_value, |
| ... | ... | @@ -55,7 +56,7 @@ const SysVContext = struct { |
| 55 | 56 | }; |
| 56 | 57 | } |
| 57 | 58 | |
| 58 | fn layoutFields(self: *SysVContext, rec: *const Record) void { | |
| 59 | fn layoutFields(self: *SysVContext, rec: *const Record) !void { | |
| 59 | 60 | for (rec.fields, 0..) |*fld, fld_indx| { |
| 60 | 61 | if (fld.ty.specifier == .invalid) continue; |
| 61 | 62 | const type_layout = computeLayout(fld.ty, self.comp); |
| ... | ... | @@ -65,12 +66,12 @@ const SysVContext = struct { |
| 65 | 66 | field_attrs = attrs[fld_indx]; |
| 66 | 67 | } |
| 67 | 68 | if (self.comp.target.isMinGW()) { |
| 68 | fld.layout = self.layoutMinGWField(fld, field_attrs, type_layout); | |
| 69 | fld.layout = try self.layoutMinGWField(fld, field_attrs, type_layout); | |
| 69 | 70 | } else { |
| 70 | 71 | if (fld.isRegularField()) { |
| 71 | fld.layout = self.layoutRegularField(field_attrs, type_layout); | |
| 72 | fld.layout = try self.layoutRegularField(field_attrs, type_layout); | |
| 72 | 73 | } else { |
| 73 | fld.layout = self.layoutBitField(field_attrs, type_layout, fld.isNamed(), fld.specifiedBitWidth()); | |
| 74 | fld.layout = try self.layoutBitField(field_attrs, type_layout, fld.isNamed(), fld.specifiedBitWidth()); | |
| 74 | 75 | } |
| 75 | 76 | } |
| 76 | 77 | } |
| ... | ... | @@ -99,8 +100,8 @@ const SysVContext = struct { |
| 99 | 100 | field: *const Field, |
| 100 | 101 | field_attrs: ?[]const Attribute, |
| 101 | 102 | field_layout: TypeLayout, |
| 102 | ) FieldLayout { | |
| 103 | const annotation_alignment_bits = BITS_PER_BYTE * (Type.annotationAlignment(self.comp, field_attrs) orelse 1); | |
| 103 | ) !FieldLayout { | |
| 104 | const annotation_alignment_bits = BITS_PER_BYTE * @as(u32, (Type.annotationAlignment(self.comp, Attribute.Iterator.initSlice(field_attrs)) orelse 1)); | |
| 104 | 105 | const is_attr_packed = self.attr_packed or isPacked(field_attrs); |
| 105 | 106 | const ignore_type_alignment = ignoreTypeAlignment(is_attr_packed, field.bit_width, self.ongoing_bitfield, field_layout); |
| 106 | 107 | |
| ... | ... | @@ -157,7 +158,7 @@ const SysVContext = struct { |
| 157 | 158 | field_alignment_bits: u64, |
| 158 | 159 | is_named: bool, |
| 159 | 160 | width: u64, |
| 160 | ) FieldLayout { | |
| 161 | ) !FieldLayout { | |
| 161 | 162 | std.debug.assert(width <= ty_size_bits); // validated in parser |
| 162 | 163 | |
| 163 | 164 | // In a union, the size of the underlying type does not affect the size of the union. |
| ... | ... | @@ -194,8 +195,8 @@ const SysVContext = struct { |
| 194 | 195 | .unused_size_bits = ty_size_bits - width, |
| 195 | 196 | }; |
| 196 | 197 | } |
| 197 | const offset_bits = std.mem.alignForward(u64, self.size_bits, field_alignment_bits); | |
| 198 | self.size_bits = if (width == 0) offset_bits else offset_bits + ty_size_bits; | |
| 198 | const offset_bits = try alignForward(self.size_bits, field_alignment_bits); | |
| 199 | self.size_bits = if (width == 0) offset_bits else try std.math.add(u64, offset_bits, ty_size_bits); | |
| 199 | 200 | if (!is_named) return .{}; |
| 200 | 201 | return .{ |
| 201 | 202 | .offset_bits = offset_bits, |
| ... | ... | @@ -207,16 +208,16 @@ const SysVContext = struct { |
| 207 | 208 | self: *SysVContext, |
| 208 | 209 | ty_size_bits: u64, |
| 209 | 210 | field_alignment_bits: u64, |
| 210 | ) FieldLayout { | |
| 211 | ) !FieldLayout { | |
| 211 | 212 | self.ongoing_bitfield = null; |
| 212 | 213 | // A struct field starts at the next offset in the struct that is properly |
| 213 | 214 | // aligned with respect to the start of the struct. See test case 0033. |
| 214 | 215 | // A union field always starts at offset 0. |
| 215 | const offset_bits = if (self.is_union) 0 else std.mem.alignForward(u64, self.size_bits, field_alignment_bits); | |
| 216 | const offset_bits = if (self.is_union) 0 else try alignForward(self.size_bits, field_alignment_bits); | |
| 216 | 217 | |
| 217 | 218 | // Set the size of the record to the maximum of the current size and the end of |
| 218 | 219 | // the field. See test case 0034. |
| 219 | self.size_bits = @max(self.size_bits, offset_bits + ty_size_bits); | |
| 220 | self.size_bits = @max(self.size_bits, try std.math.add(u64, offset_bits, ty_size_bits)); | |
| 220 | 221 | |
| 221 | 222 | return .{ |
| 222 | 223 | .offset_bits = offset_bits, |
| ... | ... | @@ -228,7 +229,7 @@ const SysVContext = struct { |
| 228 | 229 | self: *SysVContext, |
| 229 | 230 | fld_attrs: ?[]const Attribute, |
| 230 | 231 | fld_layout: TypeLayout, |
| 231 | ) FieldLayout { | |
| 232 | ) !FieldLayout { | |
| 232 | 233 | var fld_align_bits = fld_layout.field_alignment_bits; |
| 233 | 234 | |
| 234 | 235 | // If the struct or the field is packed, then the alignment of the underlying type is |
| ... | ... | @@ -239,8 +240,8 @@ const SysVContext = struct { |
| 239 | 240 | |
| 240 | 241 | // The field alignment can be increased by __attribute__((aligned)) annotations on the |
| 241 | 242 | // field. See test case 0085. |
| 242 | if (Type.annotationAlignment(self.comp, fld_attrs)) |anno| { | |
| 243 | fld_align_bits = @max(fld_align_bits, anno * BITS_PER_BYTE); | |
| 243 | if (Type.annotationAlignment(self.comp, Attribute.Iterator.initSlice(fld_attrs))) |anno| { | |
| 244 | fld_align_bits = @max(fld_align_bits, @as(u32, anno) * BITS_PER_BYTE); | |
| 244 | 245 | } |
| 245 | 246 | |
| 246 | 247 | // #pragma pack takes precedence over all other attributes. See test cases 0084 and |
| ... | ... | @@ -251,12 +252,12 @@ const SysVContext = struct { |
| 251 | 252 | |
| 252 | 253 | // A struct field starts at the next offset in the struct that is properly |
| 253 | 254 | // aligned with respect to the start of the struct. |
| 254 | const offset_bits = if (self.is_union) 0 else std.mem.alignForward(u64, self.size_bits, fld_align_bits); | |
| 255 | const offset_bits = if (self.is_union) 0 else try alignForward(self.size_bits, fld_align_bits); | |
| 255 | 256 | const size_bits = fld_layout.size_bits; |
| 256 | 257 | |
| 257 | 258 | // The alignment of a record is the maximum of its field alignments. See test cases |
| 258 | 259 | // 0084, 0085, 0086. |
| 259 | self.size_bits = @max(self.size_bits, offset_bits + size_bits); | |
| 260 | self.size_bits = @max(self.size_bits, try std.math.add(u64, offset_bits, size_bits)); | |
| 260 | 261 | self.aligned_bits = @max(self.aligned_bits, fld_align_bits); |
| 261 | 262 | |
| 262 | 263 | return .{ |
| ... | ... | @@ -271,7 +272,7 @@ const SysVContext = struct { |
| 271 | 272 | fld_layout: TypeLayout, |
| 272 | 273 | is_named: bool, |
| 273 | 274 | bit_width: u64, |
| 274 | ) FieldLayout { | |
| 275 | ) !FieldLayout { | |
| 275 | 276 | const ty_size_bits = fld_layout.size_bits; |
| 276 | 277 | var ty_fld_algn_bits: u32 = fld_layout.field_alignment_bits; |
| 277 | 278 | |
| ... | ... | @@ -301,7 +302,7 @@ const SysVContext = struct { |
| 301 | 302 | const attr_packed = self.attr_packed or isPacked(fld_attrs); |
| 302 | 303 | const has_packing_annotation = attr_packed or self.max_field_align_bits != null; |
| 303 | 304 | |
| 304 | const annotation_alignment: u32 = if (Type.annotationAlignment(self.comp, fld_attrs)) |anno| anno * BITS_PER_BYTE else 1; | |
| 305 | const annotation_alignment = if (Type.annotationAlignment(self.comp, Attribute.Iterator.initSlice(fld_attrs))) |anno| @as(u32, anno) * BITS_PER_BYTE else 1; | |
| 305 | 306 | |
| 306 | 307 | const first_unused_bit: u64 = if (self.is_union) 0 else self.size_bits; |
| 307 | 308 | var field_align_bits: u64 = 1; |
| ... | ... | @@ -322,7 +323,7 @@ const SysVContext = struct { |
| 322 | 323 | // - the alignment of the type is larger than its size, |
| 323 | 324 | // then it is aligned to the type's field alignment. See test case 0083. |
| 324 | 325 | if (!has_packing_annotation) { |
| 325 | const start_bit = std.mem.alignForward(u64, first_unused_bit, field_align_bits); | |
| 326 | const start_bit = try alignForward(first_unused_bit, field_align_bits); | |
| 326 | 327 | |
| 327 | 328 | const does_field_cross_boundary = start_bit % ty_fld_algn_bits + bit_width > ty_size_bits; |
| 328 | 329 | |
| ... | ... | @@ -349,8 +350,8 @@ const SysVContext = struct { |
| 349 | 350 | } |
| 350 | 351 | } |
| 351 | 352 | |
| 352 | const offset_bits = std.mem.alignForward(u64, first_unused_bit, field_align_bits); | |
| 353 | self.size_bits = @max(self.size_bits, offset_bits + bit_width); | |
| 353 | const offset_bits = try alignForward(first_unused_bit, field_align_bits); | |
| 354 | self.size_bits = @max(self.size_bits, try std.math.add(u64, offset_bits, bit_width)); | |
| 354 | 355 | |
| 355 | 356 | // Unnamed fields do not contribute to the record alignment except on a few targets. |
| 356 | 357 | // See test case 0079. |
| ... | ... | @@ -419,10 +420,7 @@ const MsvcContext = struct { |
| 419 | 420 | |
| 420 | 421 | // The required alignment can be increased by adding a __declspec(align) |
| 421 | 422 | // annotation. See test case 0023. |
| 422 | var must_align: u29 = BITS_PER_BYTE; | |
| 423 | if (ty.requestedAlignment(comp)) |req_align| { | |
| 424 | must_align = req_align * BITS_PER_BYTE; | |
| 425 | } | |
| 423 | const must_align = @as(u32, (ty.requestedAlignment(comp) orelse 1)) * BITS_PER_BYTE; | |
| 426 | 424 | return MsvcContext{ |
| 427 | 425 | .req_align_bits = must_align, |
| 428 | 426 | .pointer_align_bits = must_align, |
| ... | ... | @@ -436,15 +434,15 @@ const MsvcContext = struct { |
| 436 | 434 | }; |
| 437 | 435 | } |
| 438 | 436 | |
| 439 | fn layoutField(self: *MsvcContext, fld: *const Field, fld_attrs: ?[]const Attribute) FieldLayout { | |
| 437 | fn layoutField(self: *MsvcContext, fld: *const Field, fld_attrs: ?[]const Attribute) !FieldLayout { | |
| 440 | 438 | const type_layout = computeLayout(fld.ty, self.comp); |
| 441 | 439 | |
| 442 | 440 | // The required alignment of the field is the maximum of the required alignment of the |
| 443 | 441 | // underlying type and the __declspec(align) annotation on the field itself. |
| 444 | 442 | // See test case 0028. |
| 445 | 443 | var req_align = type_layout.required_alignment_bits; |
| 446 | if (Type.annotationAlignment(self.comp, fld_attrs)) |anno| { | |
| 447 | req_align = @max(anno * BITS_PER_BYTE, req_align); | |
| 444 | if (Type.annotationAlignment(self.comp, Attribute.Iterator.initSlice(fld_attrs))) |anno| { | |
| 445 | req_align = @max(@as(u32, anno) * BITS_PER_BYTE, req_align); | |
| 448 | 446 | } |
| 449 | 447 | |
| 450 | 448 | // The required alignment of a record is the maximum of the required alignments of its |
| ... | ... | @@ -480,7 +478,7 @@ const MsvcContext = struct { |
| 480 | 478 | } |
| 481 | 479 | } |
| 482 | 480 | |
| 483 | fn layoutBitField(self: *MsvcContext, ty_size_bits: u64, field_align: u32, bit_width: u32) FieldLayout { | |
| 481 | fn layoutBitField(self: *MsvcContext, ty_size_bits: u64, field_align: u32, bit_width: u32) !FieldLayout { | |
| 484 | 482 | if (bit_width == 0) { |
| 485 | 483 | // A zero-sized bit-field that does not follow a non-zero-sized bit-field does not affect |
| 486 | 484 | // the overall layout of the record. Even in a union where the order would otherwise |
| ... | ... | @@ -522,7 +520,7 @@ const MsvcContext = struct { |
| 522 | 520 | self.pointer_align_bits = @max(self.pointer_align_bits, p_align); |
| 523 | 521 | self.field_align_bits = @max(self.field_align_bits, field_align); |
| 524 | 522 | |
| 525 | const offset_bits = std.mem.alignForward(u64, self.size_bits, field_align); | |
| 523 | const offset_bits = try alignForward(self.size_bits, field_align); | |
| 526 | 524 | self.size_bits = if (bit_width == 0) offset_bits else offset_bits + ty_size_bits; |
| 527 | 525 | |
| 528 | 526 | break :bits offset_bits; |
| ... | ... | @@ -534,7 +532,7 @@ const MsvcContext = struct { |
| 534 | 532 | return .{ .offset_bits = offset_bits, .size_bits = bit_width }; |
| 535 | 533 | } |
| 536 | 534 | |
| 537 | fn layoutRegularField(self: *MsvcContext, size_bits: u64, field_align: u32) FieldLayout { | |
| 535 | fn layoutRegularField(self: *MsvcContext, size_bits: u64, field_align: u32) !FieldLayout { | |
| 538 | 536 | self.contains_non_bitfield = true; |
| 539 | 537 | self.ongoing_bitfield = null; |
| 540 | 538 | // The alignment of the field affects both the pointer alignment and the field |
| ... | ... | @@ -543,7 +541,7 @@ const MsvcContext = struct { |
| 543 | 541 | self.field_align_bits = @max(self.field_align_bits, field_align); |
| 544 | 542 | const offset_bits = switch (self.is_union) { |
| 545 | 543 | true => 0, |
| 546 | false => std.mem.alignForward(u64, self.size_bits, field_align), | |
| 544 | false => try alignForward(self.size_bits, field_align), | |
| 547 | 545 | }; |
| 548 | 546 | self.size_bits = @max(self.size_bits, offset_bits + size_bits); |
| 549 | 547 | return .{ .offset_bits = offset_bits, .size_bits = size_bits }; |
| ... | ... | @@ -569,14 +567,14 @@ const MsvcContext = struct { |
| 569 | 567 | } |
| 570 | 568 | }; |
| 571 | 569 | |
| 572 | pub fn compute(rec: *Type.Record, ty: Type, comp: *const Compilation, pragma_pack: ?u8) void { | |
| 570 | pub fn compute(rec: *Type.Record, ty: Type, comp: *const Compilation, pragma_pack: ?u8) Error!void { | |
| 573 | 571 | switch (comp.langopts.emulate) { |
| 574 | 572 | .gcc, .clang => { |
| 575 | 573 | var context = SysVContext.init(ty, comp, pragma_pack); |
| 576 | 574 | |
| 577 | context.layoutFields(rec); | |
| 575 | try context.layoutFields(rec); | |
| 578 | 576 | |
| 579 | context.size_bits = std.mem.alignForward(u64, context.size_bits, context.aligned_bits); | |
| 577 | context.size_bits = try alignForward(context.size_bits, context.aligned_bits); | |
| 580 | 578 | |
| 581 | 579 | rec.type_layout = .{ |
| 582 | 580 | .size_bits = context.size_bits, |
| ... | ... | @@ -594,7 +592,7 @@ pub fn compute(rec: *Type.Record, ty: Type, comp: *const Compilation, pragma_pac |
| 594 | 592 | field_attrs = attrs[fld_indx]; |
| 595 | 593 | } |
| 596 | 594 | |
| 597 | fld.layout = context.layoutField(fld, field_attrs); | |
| 595 | fld.layout = try context.layoutField(fld, field_attrs); | |
| 598 | 596 | } |
| 599 | 597 | if (context.size_bits == 0) { |
| 600 | 598 | // 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 |
| 602 | 600 | // ensure that there are no zero-sized records. |
| 603 | 601 | context.handleZeroSizedRecord(); |
| 604 | 602 | } |
| 605 | context.size_bits = std.mem.alignForward(u64, context.size_bits, context.pointer_align_bits); | |
| 603 | context.size_bits = try alignForward(context.size_bits, context.pointer_align_bits); | |
| 606 | 604 | rec.type_layout = .{ |
| 607 | 605 | .size_bits = context.size_bits, |
| 608 | 606 | .field_alignment_bits = context.field_align_bits, |
lib/compiler/aro/aro/target.zig+15-5| ... | ... | @@ -35,10 +35,7 @@ pub fn intMaxType(target: std.Target) Type { |
| 35 | 35 | |
| 36 | 36 | /// intptr_t for this target |
| 37 | 37 | pub fn intPtrType(target: std.Target) Type { |
| 38 | switch (target.os.tag) { | |
| 39 | .haiku => return .{ .specifier = .long }, | |
| 40 | else => {}, | |
| 41 | } | |
| 38 | if (target.os.tag == .haiku) return .{ .specifier = .long }; | |
| 42 | 39 | |
| 43 | 40 | switch (target.cpu.arch) { |
| 44 | 41 | .aarch64, .aarch64_be => switch (target.os.tag) { |
| ... | ... | @@ -127,6 +124,14 @@ pub fn int64Type(target: std.Target) Type { |
| 127 | 124 | return .{ .specifier = .long_long }; |
| 128 | 125 | } |
| 129 | 126 | |
| 127 | pub fn float80Type(target: std.Target) ?Type { | |
| 128 | switch (target.cpu.arch) { | |
| 129 | .x86, .x86_64 => return .{ .specifier = .long_double }, | |
| 130 | else => {}, | |
| 131 | } | |
| 132 | return null; | |
| 133 | } | |
| 134 | ||
| 130 | 135 | /// This function returns 1 if function alignment is not observable or settable. |
| 131 | 136 | pub fn defaultFunctionAlignment(target: std.Target) u8 { |
| 132 | 137 | return switch (target.cpu.arch) { |
| ... | ... | @@ -474,6 +479,7 @@ pub fn get32BitArchVariant(target: std.Target) ?std.Target { |
| 474 | 479 | .kalimba, |
| 475 | 480 | .lanai, |
| 476 | 481 | .wasm32, |
| 482 | .spirv, | |
| 477 | 483 | .spirv32, |
| 478 | 484 | .loongarch32, |
| 479 | 485 | .dxil, |
| ... | ... | @@ -544,6 +550,7 @@ pub fn get64BitArchVariant(target: std.Target) ?std.Target { |
| 544 | 550 | .powerpcle => copy.cpu.arch = .powerpc64le, |
| 545 | 551 | .riscv32 => copy.cpu.arch = .riscv64, |
| 546 | 552 | .sparc => copy.cpu.arch = .sparc64, |
| 553 | .spirv => copy.cpu.arch = .spirv64, | |
| 547 | 554 | .spirv32 => copy.cpu.arch = .spirv64, |
| 548 | 555 | .thumb => copy.cpu.arch = .aarch64, |
| 549 | 556 | .thumbeb => copy.cpu.arch = .aarch64_be, |
| ... | ... | @@ -599,6 +606,7 @@ pub fn toLLVMTriple(target: std.Target, buf: []u8) []const u8 { |
| 599 | 606 | .xtensa => "xtensa", |
| 600 | 607 | .nvptx => "nvptx", |
| 601 | 608 | .nvptx64 => "nvptx64", |
| 609 | .spirv => "spirv", | |
| 602 | 610 | .spirv32 => "spirv32", |
| 603 | 611 | .spirv64 => "spirv64", |
| 604 | 612 | .kalimba => "kalimba", |
| ... | ... | @@ -646,9 +654,10 @@ pub fn toLLVMTriple(target: std.Target, buf: []u8) []const u8 { |
| 646 | 654 | .ios => "ios", |
| 647 | 655 | .tvos => "tvos", |
| 648 | 656 | .watchos => "watchos", |
| 649 | .visionos => "xros", | |
| 650 | 657 | .driverkit => "driverkit", |
| 651 | 658 | .shadermodel => "shadermodel", |
| 659 | .visionos => "xros", | |
| 660 | .serenity => "serenity", | |
| 652 | 661 | .opencl, |
| 653 | 662 | .opengl, |
| 654 | 663 | .vulkan, |
| ... | ... | @@ -707,6 +716,7 @@ pub fn toLLVMTriple(target: std.Target, buf: []u8) []const u8 { |
| 707 | 716 | .callable => "callable", |
| 708 | 717 | .mesh => "mesh", |
| 709 | 718 | .amplification => "amplification", |
| 719 | .ohos => "openhos", | |
| 710 | 720 | }; |
| 711 | 721 | writer.writeAll(llvm_abi) catch unreachable; |
| 712 | 722 | return stream.getWritten(); |
lib/compiler/aro/aro/text_literal.zig+2-2| ... | ... | @@ -71,7 +71,7 @@ pub const Kind = enum { |
| 71 | 71 | pub fn maxCodepoint(kind: Kind, comp: *const Compilation) u21 { |
| 72 | 72 | return @intCast(switch (kind) { |
| 73 | 73 | .char => std.math.maxInt(u7), |
| 74 | .wide => @min(0x10FFFF, comp.types.wchar.maxInt(comp)), | |
| 74 | .wide => @min(0x10FFFF, comp.wcharMax()), | |
| 75 | 75 | .utf_8 => std.math.maxInt(u7), |
| 76 | 76 | .utf_16 => std.math.maxInt(u16), |
| 77 | 77 | .utf_32 => 0x10FFFF, |
| ... | ... | @@ -83,7 +83,7 @@ pub const Kind = enum { |
| 83 | 83 | pub fn maxInt(kind: Kind, comp: *const Compilation) u32 { |
| 84 | 84 | return @intCast(switch (kind) { |
| 85 | 85 | .char, .utf_8 => std.math.maxInt(u8), |
| 86 | .wide => comp.types.wchar.maxInt(comp), | |
| 86 | .wide => comp.wcharMax(), | |
| 87 | 87 | .utf_16 => std.math.maxInt(u16), |
| 88 | 88 | .utf_32 => std.math.maxInt(u32), |
| 89 | 89 | .unterminated => unreachable, |
lib/compiler/aro/aro/toolchains/Linux.zig+1-1| ... | ... | @@ -423,7 +423,7 @@ test Linux { |
| 423 | 423 | defer arena_instance.deinit(); |
| 424 | 424 | const arena = arena_instance.allocator(); |
| 425 | 425 | |
| 426 | var comp = Compilation.init(std.testing.allocator); | |
| 426 | var comp = Compilation.init(std.testing.allocator, std.fs.cwd()); | |
| 427 | 427 | defer comp.deinit(); |
| 428 | 428 | comp.environment = .{ |
| 429 | 429 | .path = "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", |
lib/compiler/aro/backend/Interner.zig+226| ... | ... | @@ -34,6 +34,7 @@ const KeyAdapter = struct { |
| 34 | 34 | pub const Key = union(enum) { |
| 35 | 35 | int_ty: u16, |
| 36 | 36 | float_ty: u16, |
| 37 | complex_ty: u16, | |
| 37 | 38 | ptr_ty, |
| 38 | 39 | noreturn_ty, |
| 39 | 40 | void_ty, |
| ... | ... | @@ -62,6 +63,7 @@ pub const Key = union(enum) { |
| 62 | 63 | } |
| 63 | 64 | }, |
| 64 | 65 | float: Float, |
| 66 | complex: Complex, | |
| 65 | 67 | bytes: []const u8, |
| 66 | 68 | |
| 67 | 69 | pub const Float = union(enum) { |
| ... | ... | @@ -71,6 +73,13 @@ pub const Key = union(enum) { |
| 71 | 73 | f80: f80, |
| 72 | 74 | f128: f128, |
| 73 | 75 | }; |
| 76 | pub const Complex = union(enum) { | |
| 77 | cf16: [2]f16, | |
| 78 | cf32: [2]f32, | |
| 79 | cf64: [2]f64, | |
| 80 | cf80: [2]f80, | |
| 81 | cf128: [2]f128, | |
| 82 | }; | |
| 74 | 83 | |
| 75 | 84 | pub fn hash(key: Key) u32 { |
| 76 | 85 | var hasher = Hash.init(0); |
| ... | ... | @@ -89,6 +98,12 @@ pub const Key = union(enum) { |
| 89 | 98 | @as(std.meta.Int(.unsigned, @bitSizeOf(@TypeOf(data))), @bitCast(data)), |
| 90 | 99 | ), |
| 91 | 100 | }, |
| 101 | .complex => |repr| switch (repr) { | |
| 102 | inline else => |data| std.hash.autoHash( | |
| 103 | &hasher, | |
| 104 | @as(std.meta.Int(.unsigned, @bitSizeOf(@TypeOf(data))), @bitCast(data)), | |
| 105 | ), | |
| 106 | }, | |
| 92 | 107 | .int => |repr| { |
| 93 | 108 | var space: Tag.Int.BigIntSpace = undefined; |
| 94 | 109 | const big = repr.toBigInt(&space); |
| ... | ... | @@ -154,6 +169,14 @@ pub const Key = union(enum) { |
| 154 | 169 | 128 => return .f128, |
| 155 | 170 | else => unreachable, |
| 156 | 171 | }, |
| 172 | .complex_ty => |bits| switch (bits) { | |
| 173 | 16 => return .cf16, | |
| 174 | 32 => return .cf32, | |
| 175 | 64 => return .cf64, | |
| 176 | 80 => return .cf80, | |
| 177 | 128 => return .cf128, | |
| 178 | else => unreachable, | |
| 179 | }, | |
| 157 | 180 | .ptr_ty => return .ptr, |
| 158 | 181 | .func_ty => return .func, |
| 159 | 182 | .noreturn_ty => return .noreturn, |
| ... | ... | @@ -199,6 +222,11 @@ pub const Ref = enum(u32) { |
| 199 | 222 | zero = max - 16, |
| 200 | 223 | one = max - 17, |
| 201 | 224 | null = max - 18, |
| 225 | cf16 = max - 19, | |
| 226 | cf32 = max - 20, | |
| 227 | cf64 = max - 21, | |
| 228 | cf80 = max - 22, | |
| 229 | cf128 = max - 23, | |
| 202 | 230 | _, |
| 203 | 231 | }; |
| 204 | 232 | |
| ... | ... | @@ -224,6 +252,11 @@ pub const OptRef = enum(u32) { |
| 224 | 252 | zero = max - 16, |
| 225 | 253 | one = max - 17, |
| 226 | 254 | null = max - 18, |
| 255 | cf16 = max - 19, | |
| 256 | cf32 = max - 20, | |
| 257 | cf64 = max - 21, | |
| 258 | cf80 = max - 22, | |
| 259 | cf128 = max - 23, | |
| 227 | 260 | _, |
| 228 | 261 | }; |
| 229 | 262 | |
| ... | ... | @@ -232,6 +265,8 @@ pub const Tag = enum(u8) { |
| 232 | 265 | int_ty, |
| 233 | 266 | /// `data` is `u16` |
| 234 | 267 | float_ty, |
| 268 | /// `data` is `u16` | |
| 269 | complex_ty, | |
| 235 | 270 | /// `data` is index to `Array` |
| 236 | 271 | array_ty, |
| 237 | 272 | /// `data` is index to `Vector` |
| ... | ... | @@ -254,6 +289,16 @@ pub const Tag = enum(u8) { |
| 254 | 289 | f80, |
| 255 | 290 | /// `data` is `F128` |
| 256 | 291 | f128, |
| 292 | /// `data` is `CF16` | |
| 293 | cf16, | |
| 294 | /// `data` is `CF32` | |
| 295 | cf32, | |
| 296 | /// `data` is `CF64` | |
| 297 | cf64, | |
| 298 | /// `data` is `CF80` | |
| 299 | cf80, | |
| 300 | /// `data` is `CF128` | |
| 301 | cf128, | |
| 257 | 302 | /// `data` is `Bytes` |
| 258 | 303 | bytes, |
| 259 | 304 | /// `data` is `Record` |
| ... | ... | @@ -354,6 +399,134 @@ pub const Tag = enum(u8) { |
| 354 | 399 | } |
| 355 | 400 | }; |
| 356 | 401 | |
| 402 | pub const CF16 = struct { | |
| 403 | piece0: u32, | |
| 404 | ||
| 405 | pub fn get(self: CF16) [2]f16 { | |
| 406 | const real: f16 = @bitCast(@as(u16, @truncate(self.piece0 >> 16))); | |
| 407 | const imag: f16 = @bitCast(@as(u16, @truncate(self.piece0))); | |
| 408 | return .{ | |
| 409 | real, | |
| 410 | imag, | |
| 411 | }; | |
| 412 | } | |
| 413 | ||
| 414 | fn pack(val: [2]f16) CF16 { | |
| 415 | const real: u16 = @bitCast(val[0]); | |
| 416 | const imag: u16 = @bitCast(val[1]); | |
| 417 | return .{ | |
| 418 | .piece0 = (@as(u32, real) << 16) | @as(u32, imag), | |
| 419 | }; | |
| 420 | } | |
| 421 | }; | |
| 422 | ||
| 423 | pub const CF32 = struct { | |
| 424 | piece0: u32, | |
| 425 | piece1: u32, | |
| 426 | ||
| 427 | pub fn get(self: CF32) [2]f32 { | |
| 428 | return .{ | |
| 429 | @bitCast(self.piece0), | |
| 430 | @bitCast(self.piece1), | |
| 431 | }; | |
| 432 | } | |
| 433 | ||
| 434 | fn pack(val: [2]f32) CF32 { | |
| 435 | return .{ | |
| 436 | .piece0 = @bitCast(val[0]), | |
| 437 | .piece1 = @bitCast(val[1]), | |
| 438 | }; | |
| 439 | } | |
| 440 | }; | |
| 441 | ||
| 442 | pub const CF64 = struct { | |
| 443 | piece0: u32, | |
| 444 | piece1: u32, | |
| 445 | piece2: u32, | |
| 446 | piece3: u32, | |
| 447 | ||
| 448 | pub fn get(self: CF64) [2]f64 { | |
| 449 | return .{ | |
| 450 | (F64{ .piece0 = self.piece0, .piece1 = self.piece1 }).get(), | |
| 451 | (F64{ .piece0 = self.piece2, .piece1 = self.piece3 }).get(), | |
| 452 | }; | |
| 453 | } | |
| 454 | ||
| 455 | fn pack(val: [2]f64) CF64 { | |
| 456 | const real = F64.pack(val[0]); | |
| 457 | const imag = F64.pack(val[1]); | |
| 458 | return .{ | |
| 459 | .piece0 = real.piece0, | |
| 460 | .piece1 = real.piece1, | |
| 461 | .piece2 = imag.piece0, | |
| 462 | .piece3 = imag.piece1, | |
| 463 | }; | |
| 464 | } | |
| 465 | }; | |
| 466 | ||
| 467 | /// TODO pack into 5 pieces | |
| 468 | pub const CF80 = struct { | |
| 469 | piece0: u32, | |
| 470 | piece1: u32, | |
| 471 | piece2: u32, // u16 part, top bits | |
| 472 | piece3: u32, | |
| 473 | piece4: u32, | |
| 474 | piece5: u32, // u16 part, top bits | |
| 475 | ||
| 476 | pub fn get(self: CF80) [2]f80 { | |
| 477 | return .{ | |
| 478 | (F80{ .piece0 = self.piece0, .piece1 = self.piece1, .piece2 = self.piece2 }).get(), | |
| 479 | (F80{ .piece0 = self.piece3, .piece1 = self.piece4, .piece2 = self.piece5 }).get(), | |
| 480 | }; | |
| 481 | } | |
| 482 | ||
| 483 | fn pack(val: [2]f80) CF80 { | |
| 484 | const real = F80.pack(val[0]); | |
| 485 | const imag = F80.pack(val[1]); | |
| 486 | return .{ | |
| 487 | .piece0 = real.piece0, | |
| 488 | .piece1 = real.piece1, | |
| 489 | .piece2 = real.piece2, | |
| 490 | .piece3 = imag.piece0, | |
| 491 | .piece4 = imag.piece1, | |
| 492 | .piece5 = imag.piece2, | |
| 493 | }; | |
| 494 | } | |
| 495 | }; | |
| 496 | ||
| 497 | pub const CF128 = struct { | |
| 498 | piece0: u32, | |
| 499 | piece1: u32, | |
| 500 | piece2: u32, | |
| 501 | piece3: u32, | |
| 502 | piece4: u32, | |
| 503 | piece5: u32, | |
| 504 | piece6: u32, | |
| 505 | piece7: u32, | |
| 506 | ||
| 507 | pub fn get(self: CF128) [2]f128 { | |
| 508 | return .{ | |
| 509 | (F128{ .piece0 = self.piece0, .piece1 = self.piece1, .piece2 = self.piece2, .piece3 = self.piece3 }).get(), | |
| 510 | (F128{ .piece0 = self.piece4, .piece1 = self.piece5, .piece2 = self.piece6, .piece3 = self.piece7 }).get(), | |
| 511 | }; | |
| 512 | } | |
| 513 | ||
| 514 | fn pack(val: [2]f128) CF128 { | |
| 515 | const real = F128.pack(val[0]); | |
| 516 | const imag = F128.pack(val[1]); | |
| 517 | return .{ | |
| 518 | .piece0 = real.piece0, | |
| 519 | .piece1 = real.piece1, | |
| 520 | .piece2 = real.piece2, | |
| 521 | .piece3 = real.piece3, | |
| 522 | .piece4 = imag.piece0, | |
| 523 | .piece5 = imag.piece1, | |
| 524 | .piece6 = imag.piece2, | |
| 525 | .piece7 = imag.piece3, | |
| 526 | }; | |
| 527 | } | |
| 528 | }; | |
| 529 | ||
| 357 | 530 | pub const Bytes = struct { |
| 358 | 531 | strings_index: u32, |
| 359 | 532 | len: u32, |
| ... | ... | @@ -407,6 +580,12 @@ pub fn put(i: *Interner, gpa: Allocator, key: Key) !Ref { |
| 407 | 580 | .data = bits, |
| 408 | 581 | }); |
| 409 | 582 | }, |
| 583 | .complex_ty => |bits| { | |
| 584 | i.items.appendAssumeCapacity(.{ | |
| 585 | .tag = .complex_ty, | |
| 586 | .data = bits, | |
| 587 | }); | |
| 588 | }, | |
| 410 | 589 | .array_ty => |info| { |
| 411 | 590 | const split_len = PackedU64.init(info.len); |
| 412 | 591 | i.items.appendAssumeCapacity(.{ |
| ... | ... | @@ -493,6 +672,28 @@ pub fn put(i: *Interner, gpa: Allocator, key: Key) !Ref { |
| 493 | 672 | .data = try i.addExtra(gpa, Tag.F128.pack(data)), |
| 494 | 673 | }), |
| 495 | 674 | }, |
| 675 | .complex => |repr| switch (repr) { | |
| 676 | .cf16 => |data| i.items.appendAssumeCapacity(.{ | |
| 677 | .tag = .cf16, | |
| 678 | .data = try i.addExtra(gpa, Tag.CF16.pack(data)), | |
| 679 | }), | |
| 680 | .cf32 => |data| i.items.appendAssumeCapacity(.{ | |
| 681 | .tag = .cf32, | |
| 682 | .data = try i.addExtra(gpa, Tag.CF32.pack(data)), | |
| 683 | }), | |
| 684 | .cf64 => |data| i.items.appendAssumeCapacity(.{ | |
| 685 | .tag = .cf64, | |
| 686 | .data = try i.addExtra(gpa, Tag.CF64.pack(data)), | |
| 687 | }), | |
| 688 | .cf80 => |data| i.items.appendAssumeCapacity(.{ | |
| 689 | .tag = .cf80, | |
| 690 | .data = try i.addExtra(gpa, Tag.CF80.pack(data)), | |
| 691 | }), | |
| 692 | .cf128 => |data| i.items.appendAssumeCapacity(.{ | |
| 693 | .tag = .cf128, | |
| 694 | .data = try i.addExtra(gpa, Tag.CF128.pack(data)), | |
| 695 | }), | |
| 696 | }, | |
| 496 | 697 | .bytes => |bytes| { |
| 497 | 698 | const strings_index: u32 = @intCast(i.strings.items.len); |
| 498 | 699 | try i.strings.appendSlice(gpa, bytes); |
| ... | ... | @@ -564,6 +765,10 @@ pub fn get(i: *const Interner, ref: Ref) Key { |
| 564 | 765 | .zero => return .{ .int = .{ .u64 = 0 } }, |
| 565 | 766 | .one => return .{ .int = .{ .u64 = 1 } }, |
| 566 | 767 | .null => return .null, |
| 768 | .cf16 => return .{ .complex_ty = 16 }, | |
| 769 | .cf32 => return .{ .complex_ty = 32 }, | |
| 770 | .cf64 => return .{ .complex_ty = 64 }, | |
| 771 | .cf80 => return .{ .complex_ty = 80 }, | |
| 567 | 772 | else => {}, |
| 568 | 773 | } |
| 569 | 774 | |
| ... | ... | @@ -572,6 +777,7 @@ pub fn get(i: *const Interner, ref: Ref) Key { |
| 572 | 777 | return switch (item.tag) { |
| 573 | 778 | .int_ty => .{ .int_ty = @intCast(data) }, |
| 574 | 779 | .float_ty => .{ .float_ty = @intCast(data) }, |
| 780 | .complex_ty => .{ .complex_ty = @intCast(data) }, | |
| 575 | 781 | .array_ty => { |
| 576 | 782 | const array_ty = i.extraData(Tag.Array, data); |
| 577 | 783 | return .{ .array_ty = .{ |
| ... | ... | @@ -612,6 +818,26 @@ pub fn get(i: *const Interner, ref: Ref) Key { |
| 612 | 818 | const float = i.extraData(Tag.F128, data); |
| 613 | 819 | return .{ .float = .{ .f128 = float.get() } }; |
| 614 | 820 | }, |
| 821 | .cf16 => { | |
| 822 | const components = i.extraData(Tag.CF16, data); | |
| 823 | return .{ .complex = .{ .cf16 = components.get() } }; | |
| 824 | }, | |
| 825 | .cf32 => { | |
| 826 | const components = i.extraData(Tag.CF32, data); | |
| 827 | return .{ .complex = .{ .cf32 = components.get() } }; | |
| 828 | }, | |
| 829 | .cf64 => { | |
| 830 | const components = i.extraData(Tag.CF64, data); | |
| 831 | return .{ .complex = .{ .cf64 = components.get() } }; | |
| 832 | }, | |
| 833 | .cf80 => { | |
| 834 | const components = i.extraData(Tag.CF80, data); | |
| 835 | return .{ .complex = .{ .cf80 = components.get() } }; | |
| 836 | }, | |
| 837 | .cf128 => { | |
| 838 | const components = i.extraData(Tag.CF128, data); | |
| 839 | return .{ .complex = .{ .cf128 = components.get() } }; | |
| 840 | }, | |
| 615 | 841 | .bytes => { |
| 616 | 842 | const bytes = i.extraData(Tag.Bytes, data); |
| 617 | 843 | return .{ .bytes = i.strings.items[bytes.strings_index..][0..bytes.len] }; |
lib/compiler/aro/backend/Ir.zig+1| ... | ... | @@ -37,6 +37,7 @@ pub const Builder = struct { |
| 37 | 37 | for (b.decls.values()) |*decl| { |
| 38 | 38 | decl.deinit(b.gpa); |
| 39 | 39 | } |
| 40 | b.decls.deinit(b.gpa); | |
| 40 | 41 | b.arena.deinit(); |
| 41 | 42 | b.instructions.deinit(b.gpa); |
| 42 | 43 | b.body.deinit(b.gpa); |
lib/compiler/aro/backend/Object.zig+5-5| ... | ... | @@ -16,7 +16,7 @@ pub fn create(gpa: Allocator, target: std.Target) !*Object { |
| 16 | 16 | |
| 17 | 17 | pub fn deinit(obj: *Object) void { |
| 18 | 18 | switch (obj.format) { |
| 19 | .elf => @as(*Elf, @fieldParentPtr("obj", obj)).deinit(), | |
| 19 | .elf => @as(*Elf, @alignCast(@fieldParentPtr("obj", obj))).deinit(), | |
| 20 | 20 | else => unreachable, |
| 21 | 21 | } |
| 22 | 22 | } |
| ... | ... | @@ -32,7 +32,7 @@ pub const Section = union(enum) { |
| 32 | 32 | |
| 33 | 33 | pub fn getSection(obj: *Object, section: Section) !*std.ArrayList(u8) { |
| 34 | 34 | switch (obj.format) { |
| 35 | .elf => return @as(*Elf, @fieldParentPtr("obj", obj)).getSection(section), | |
| 35 | .elf => return @as(*Elf, @alignCast(@fieldParentPtr("obj", obj))).getSection(section), | |
| 36 | 36 | else => unreachable, |
| 37 | 37 | } |
| 38 | 38 | } |
| ... | ... | @@ -53,21 +53,21 @@ pub fn declareSymbol( |
| 53 | 53 | size: u64, |
| 54 | 54 | ) ![]const u8 { |
| 55 | 55 | switch (obj.format) { |
| 56 | .elf => return @as(*Elf, @fieldParentPtr("obj", obj)).declareSymbol(section, name, linkage, @"type", offset, size), | |
| 56 | .elf => return @as(*Elf, @alignCast(@fieldParentPtr("obj", obj))).declareSymbol(section, name, linkage, @"type", offset, size), | |
| 57 | 57 | else => unreachable, |
| 58 | 58 | } |
| 59 | 59 | } |
| 60 | 60 | |
| 61 | 61 | pub fn addRelocation(obj: *Object, name: []const u8, section: Section, address: u64, addend: i64) !void { |
| 62 | 62 | switch (obj.format) { |
| 63 | .elf => return @as(*Elf, @fieldParentPtr("obj", obj)).addRelocation(name, section, address, addend), | |
| 63 | .elf => return @as(*Elf, @alignCast(@fieldParentPtr("obj", obj))).addRelocation(name, section, address, addend), | |
| 64 | 64 | else => unreachable, |
| 65 | 65 | } |
| 66 | 66 | } |
| 67 | 67 | |
| 68 | 68 | pub fn finish(obj: *Object, file: std.fs.File) !void { |
| 69 | 69 | switch (obj.format) { |
| 70 | .elf => return @as(*Elf, @fieldParentPtr("obj", obj)).finish(file), | |
| 70 | .elf => return @as(*Elf, @alignCast(@fieldParentPtr("obj", obj))).finish(file), | |
| 71 | 71 | else => unreachable, |
| 72 | 72 | } |
| 73 | 73 | } |
lib/compiler/aro_translate_c.zig+1-2| ... | ... | @@ -731,7 +731,6 @@ fn transType(c: *Context, scope: *Scope, raw_ty: Type, qual_handling: Type.QualH |
| 731 | 731 | .float => return ZigTag.type.create(c.arena, "f32"), |
| 732 | 732 | .double => return ZigTag.type.create(c.arena, "f64"), |
| 733 | 733 | .long_double => return ZigTag.type.create(c.arena, "c_longdouble"), |
| 734 | .float80 => return ZigTag.type.create(c.arena, "f80"), | |
| 735 | 734 | .float128 => return ZigTag.type.create(c.arena, "f128"), |
| 736 | 735 | .@"enum" => { |
| 737 | 736 | const enum_decl = ty.data.@"enum"; |
| ... | ... | @@ -1799,7 +1798,7 @@ pub fn main() !void { |
| 1799 | 1798 | |
| 1800 | 1799 | const args = try std.process.argsAlloc(arena); |
| 1801 | 1800 | |
| 1802 | var aro_comp = aro.Compilation.init(gpa); | |
| 1801 | var aro_comp = aro.Compilation.init(gpa, std.fs.cwd()); | |
| 1803 | 1802 | defer aro_comp.deinit(); |
| 1804 | 1803 | |
| 1805 | 1804 | var tree = translate(gpa, &aro_comp, args) catch |err| switch (err) { |
lib/compiler/resinator/main.zig+1-1| ... | ... | @@ -126,7 +126,7 @@ pub fn main() !void { |
| 126 | 126 | defer aro_arena_state.deinit(); |
| 127 | 127 | const aro_arena = aro_arena_state.allocator(); |
| 128 | 128 | |
| 129 | var comp = aro.Compilation.init(aro_arena); | |
| 129 | var comp = aro.Compilation.init(aro_arena, std.fs.cwd()); | |
| 130 | 130 | defer comp.deinit(); |
| 131 | 131 | |
| 132 | 132 | var argv = std.ArrayList([]const u8).init(comp.gpa); |
lib/compiler/resinator/preprocess.zig+1-1| ... | ... | @@ -59,7 +59,7 @@ pub fn preprocess( |
| 59 | 59 | |
| 60 | 60 | if (hasAnyErrors(comp)) return error.PreprocessError; |
| 61 | 61 | |
| 62 | try pp.prettyPrintTokens(writer); | |
| 62 | try pp.prettyPrintTokens(writer, .result_only); | |
| 63 | 63 | |
| 64 | 64 | if (maybe_dependencies_list) |dependencies_list| { |
| 65 | 65 | for (comp.sources.values()) |comp_source| { |
src/mingw.zig+2-2| ... | ... | @@ -230,7 +230,7 @@ pub fn buildImportLib(comp: *Compilation, lib_name: []const u8) !void { |
| 230 | 230 | }; |
| 231 | 231 | |
| 232 | 232 | const aro = @import("aro"); |
| 233 | var aro_comp = aro.Compilation.init(comp.gpa); | |
| 233 | var aro_comp = aro.Compilation.init(comp.gpa, std.fs.cwd()); | |
| 234 | 234 | defer aro_comp.deinit(); |
| 235 | 235 | |
| 236 | 236 | 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 { |
| 268 | 268 | // new scope to ensure definition file is written before passing the path to WriteImportLibrary |
| 269 | 269 | const def_final_file = try o_dir.createFile(final_def_basename, .{ .truncate = true }); |
| 270 | 270 | defer def_final_file.close(); |
| 271 | try pp.prettyPrintTokens(def_final_file.writer()); | |
| 271 | try pp.prettyPrintTokens(def_final_file.writer(), .result_only); | |
| 272 | 272 | } |
| 273 | 273 | |
| 274 | 274 | const lib_final_path = try comp.global_cache_directory.join(comp.gpa, &[_][]const u8{ |
src/translate_c.zig+1-1| ... | ... | @@ -5258,7 +5258,7 @@ fn getMacroText(unit: *const clang.ASTUnit, c: *const Context, macro: *const cla |
| 5258 | 5258 | const end_c = c.source_manager.getCharacterData(end_loc); |
| 5259 | 5259 | const slice_len = @intFromPtr(end_c) - @intFromPtr(begin_c); |
| 5260 | 5260 | |
| 5261 | var comp = aro.Compilation.init(c.gpa); | |
| 5261 | var comp = aro.Compilation.init(c.gpa, std.fs.cwd()); | |
| 5262 | 5262 | defer comp.deinit(); |
| 5263 | 5263 | const result = comp.addSourceFromBuffer("", begin_c[0..slice_len]) catch return error.OutOfMemory; |
| 5264 | 5264 |