| author | |
| committer | |
| log | 376d443ae314e6e999fb58fd6d8e8a4adc4fbdeb |
| tree | fdf08175822edd14d1a985691fe3bc4ed9c8b497 |
| parent | 04f379dd414184a42412f4497b0573d7612d6730 |
Problems to solve in this branch before merging:
* Liveness and AIR printing want a ZIR instance. This issue is
addressed by #10784.
* linker: updateFunc / updateDecl want a Module instance which
currently represents only Zig code. These functions should be changed
to accept a Compilation, not a Module. The relevant state that it
needed to touch on Module should be moved to Compilation instead.
* `Decl` needs to be shared by both the C frontend and the Zig
frontend. It currently has Zig-only fields and the functions to
create Decl objects don't really fit the API that the C frontend
needs.
* A lot of the incremental compilation infrastructure doesn't quite
match up, for example Decl objects being owned by Namespaces which is
a Zig concept. Maybe there could be 1 Namespace globally across all C
files and 1 more Namespace per C file for functions and globals
declared `static`.
* There is common code to be extracted from Sema that is shared with
Aro frontend. All the helper functions having to do with the
air_instructions and air_extra arrays.
There are many instances of calling `@panic("TODO")` that need to be
cleaned up.
There is a lot of compiler options that Zig is not communicating to Aro,
for example we do not pass the -D switches that we pass to Clang yet.
Need to audit `addCCArgs` and do the equivalent things for setting up
the Aro Compilation.
The Aro code is copied from https://github.com/Vexu/arocc, commit
7a0e54227e1ea2b2df8aa7bd2b7075f735109317. The only patches are to delete
Codegen and replace it with our own, and to delete main.zig, and add
Type and Value to lib.zig.
I believe the current plan is for main Aro development to happen
upstream on the arocc repository and periodically sync it downstream
with the Zig repository. It's up to @Vexu whether to keep that process
or change it in the future.25 files changed, 19598 insertions(+), 19 deletions(-)
src/Compilation.zig+91-14| ... | ... | @@ -35,6 +35,7 @@ const WaitGroup = @import("WaitGroup.zig"); |
| 35 | 35 | const libtsan = @import("libtsan.zig"); |
| 36 | 36 | const Zir = @import("Zir.zig"); |
| 37 | 37 | const Color = @import("main.zig").Color; |
| 38 | const aro = @import("aro/lib.zig"); | |
| 38 | 39 | |
| 39 | 40 | /// General-purpose allocator. Used for both temporary and long-term storage. |
| 40 | 41 | gpa: Allocator, |
| ... | ... | @@ -237,6 +238,10 @@ const Job = union(enum) { |
| 237 | 238 | |
| 238 | 239 | /// The value is the index into `link.File.Options.system_libs`. |
| 239 | 240 | windows_import_lib: usize, |
| 241 | ||
| 242 | /// Compile a C source file with Aro. | |
| 243 | /// The value is the index into `c_source_files`. | |
| 244 | arocc: usize, | |
| 240 | 245 | }; |
| 241 | 246 | |
| 242 | 247 | pub const CObject = struct { |
| ... | ... | @@ -1677,17 +1682,20 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation { |
| 1677 | 1682 | try comp.astgen_wait_group.init(); |
| 1678 | 1683 | errdefer comp.astgen_wait_group.deinit(); |
| 1679 | 1684 | |
| 1680 | // Add a `CObject` for each `c_source_files`. | |
| 1681 | try comp.c_object_table.ensureTotalCapacity(gpa, options.c_source_files.len); | |
| 1682 | for (options.c_source_files) |c_source_file| { | |
| 1683 | const c_object = try gpa.create(CObject); | |
| 1684 | errdefer gpa.destroy(c_object); | |
| 1685 | ||
| 1686 | c_object.* = .{ | |
| 1687 | .status = .{ .new = {} }, | |
| 1688 | .src = c_source_file, | |
| 1689 | }; | |
| 1690 | comp.c_object_table.putAssumeCapacityNoClobber(c_object, {}); | |
| 1685 | // When using Clang, add a `CObject` for each `c_source_files`. | |
| 1686 | const use_clang = build_options.have_llvm; | |
| 1687 | if (use_clang) { | |
| 1688 | try comp.c_object_table.ensureTotalCapacity(gpa, options.c_source_files.len); | |
| 1689 | for (options.c_source_files) |c_source_file| { | |
| 1690 | const c_object = try gpa.create(CObject); | |
| 1691 | errdefer gpa.destroy(c_object); | |
| 1692 | ||
| 1693 | c_object.* = .{ | |
| 1694 | .status = .{ .new = {} }, | |
| 1695 | .src = c_source_file, | |
| 1696 | }; | |
| 1697 | comp.c_object_table.putAssumeCapacityNoClobber(c_object, {}); | |
| 1698 | } | |
| 1691 | 1699 | } |
| 1692 | 1700 | |
| 1693 | 1701 | const have_bin_emit = comp.bin_file.options.emit != null or comp.whole_bin_sub_path != null; |
| ... | ... | @@ -2025,9 +2033,17 @@ pub fn update(comp: *Compilation) !void { |
| 2025 | 2033 | |
| 2026 | 2034 | // For compiling C objects, we rely on the cache hash system to avoid duplicating work. |
| 2027 | 2035 | // Add a Job for each C object. |
| 2028 | try comp.c_object_work_queue.ensureUnusedCapacity(comp.c_object_table.count()); | |
| 2029 | for (comp.c_object_table.keys()) |key| { | |
| 2030 | comp.c_object_work_queue.writeItemAssumeCapacity(key); | |
| 2036 | // Note that when using Aro frontend instead of Clang, `c_object_work_queue` is always empty. | |
| 2037 | const use_clang = build_options.have_llvm; | |
| 2038 | if (use_clang) { | |
| 2039 | try comp.c_object_work_queue.ensureUnusedCapacity(comp.c_object_table.count()); | |
| 2040 | for (comp.c_object_table.keys()) |key| { | |
| 2041 | comp.c_object_work_queue.writeItemAssumeCapacity(key); | |
| 2042 | } | |
| 2043 | } else { | |
| 2044 | for (comp.c_source_files) |_, i| { | |
| 2045 | try comp.work_queue.writeItem(.{ .arocc = i }); | |
| 2046 | } | |
| 2031 | 2047 | } |
| 2032 | 2048 | |
| 2033 | 2049 | const use_stage1 = build_options.is_stage1 and comp.bin_file.options.use_stage1; |
| ... | ... | @@ -2811,6 +2827,15 @@ fn processOneJob(comp: *Compilation, job: Job, main_progress_node: *std.Progress |
| 2811 | 2827 | return; |
| 2812 | 2828 | }, |
| 2813 | 2829 | }, |
| 2830 | .arocc => |c_source_file_index| { | |
| 2831 | const c_source_file = comp.c_source_files[c_source_file_index]; | |
| 2832 | comp.compileWithAro(c_source_file) catch |err| switch (err) { | |
| 2833 | error.OutOfMemory => return error.OutOfMemory, | |
| 2834 | else => { | |
| 2835 | @panic("properly handle arocc errors"); | |
| 2836 | }, | |
| 2837 | }; | |
| 2838 | }, | |
| 2814 | 2839 | .emit_h_decl => |decl| switch (decl.analysis) { |
| 2815 | 2840 | .unreferenced => unreachable, |
| 2816 | 2841 | .in_progress => unreachable, |
| ... | ... | @@ -3145,6 +3170,58 @@ fn processOneJob(comp: *Compilation, job: Job, main_progress_node: *std.Progress |
| 3145 | 3170 | } |
| 3146 | 3171 | } |
| 3147 | 3172 | |
| 3173 | fn compileWithAro(comp: *Compilation, c_source_file: CSourceFile) !void { | |
| 3174 | const src_path = c_source_file.src_path; | |
| 3175 | ||
| 3176 | var arena_allocator = std.heap.ArenaAllocator.init(comp.gpa); | |
| 3177 | defer arena_allocator.deinit(); | |
| 3178 | const arena = arena_allocator.allocator(); | |
| 3179 | ||
| 3180 | var aro_comp = aro.Compilation.init(comp.gpa); | |
| 3181 | defer aro_comp.deinit(); | |
| 3182 | ||
| 3183 | aro_comp.target = comp.getTarget(); | |
| 3184 | ||
| 3185 | try aro_comp.addDefaultPragmaHandlers(); | |
| 3186 | ||
| 3187 | const c_headers_dir = try std.fs.path.join(arena, &[_][]const u8{ comp.zig_lib_directory.path.?, "include" }); | |
| 3188 | try aro_comp.system_include_dirs.append(c_headers_dir); | |
| 3189 | ||
| 3190 | for (comp.libc_include_dir_list) |include_dir| { | |
| 3191 | try aro_comp.system_include_dirs.append(include_dir); | |
| 3192 | } | |
| 3193 | ||
| 3194 | var macro_buf = std.ArrayList(u8).init(comp.gpa); | |
| 3195 | defer macro_buf.deinit(); | |
| 3196 | ||
| 3197 | const builtin_macros = try aro_comp.generateBuiltinMacros(); | |
| 3198 | const user_macros = try aro_comp.addSourceFromBuffer("<command line>", macro_buf.items); | |
| 3199 | ||
| 3200 | const source = try aro_comp.addSourceFromPath(src_path); | |
| 3201 | ||
| 3202 | aro_comp.generated_buf.items.len = 0; | |
| 3203 | var pp = aro.Preprocessor.init(&aro_comp); | |
| 3204 | defer pp.deinit(); | |
| 3205 | try pp.addBuiltinMacros(); | |
| 3206 | ||
| 3207 | _ = try pp.preprocess(builtin_macros); | |
| 3208 | _ = try pp.preprocess(user_macros); | |
| 3209 | const eof = try pp.preprocess(source); | |
| 3210 | try pp.tokens.append(comp.gpa, eof); | |
| 3211 | ||
| 3212 | var tree = try aro.Parser.parse(&pp); | |
| 3213 | defer tree.deinit(); | |
| 3214 | ||
| 3215 | aro_comp.renderErrors(); // populates aro_comp.diag.errors | |
| 3216 | ||
| 3217 | if (aro_comp.diag.errors != 0) { | |
| 3218 | // errors occurred | |
| 3219 | @panic("report aro errors"); | |
| 3220 | } | |
| 3221 | ||
| 3222 | try aro.Codegen.generateTree(comp, &aro_comp, tree, arena); | |
| 3223 | } | |
| 3224 | ||
| 3148 | 3225 | const AstGenSrc = union(enum) { |
| 3149 | 3226 | root, |
| 3150 | 3227 | import: struct { |
src/Module.zig+25| ... | ... | @@ -4709,6 +4709,31 @@ pub fn createAnonymousDecl(mod: *Module, block: *Sema.Block, typed_value: TypedV |
| 4709 | 4709 | return mod.createAnonymousDeclFromDecl(block.src_decl, block.namespace, block.wip_capture_scope, typed_value); |
| 4710 | 4710 | } |
| 4711 | 4711 | |
| 4712 | /// TODO cleanup | |
| 4713 | pub fn createAnonymousDecl2(mod: *Module, typed_value: TypedValue, src_name: []const u8) !*Decl { | |
| 4714 | const name_index = mod.getNextAnonNameIndex(); | |
| 4715 | const name = try std.fmt.allocPrintZ(mod.gpa, "{s}__anon_{d}", .{ | |
| 4716 | src_name, name_index, | |
| 4717 | }); | |
| 4718 | errdefer mod.gpa.free(name); | |
| 4719 | ||
| 4720 | const new_decl = try mod.allocateNewDecl(name, undefined, 0, null); | |
| 4721 | ||
| 4722 | new_decl.src_line = 0; | |
| 4723 | new_decl.ty = typed_value.ty; | |
| 4724 | new_decl.val = typed_value.val; | |
| 4725 | new_decl.align_val = Value.@"null"; | |
| 4726 | new_decl.linksection_val = Value.@"null"; | |
| 4727 | new_decl.has_tv = true; | |
| 4728 | new_decl.analysis = .complete; | |
| 4729 | new_decl.generation = mod.generation; | |
| 4730 | ||
| 4731 | try mod.comp.bin_file.allocateDeclIndexes(new_decl); | |
| 4732 | try mod.comp.anon_work_queue.writeItem(.{ .codegen_decl = new_decl }); | |
| 4733 | ||
| 4734 | return new_decl; | |
| 4735 | } | |
| 4736 | ||
| 4712 | 4737 | pub fn createAnonymousDeclFromDecl( |
| 4713 | 4738 | mod: *Module, |
| 4714 | 4739 | src_decl: *Decl, |
src/Sema.zig-5| ... | ... | @@ -164,11 +164,6 @@ pub const Block = struct { |
| 164 | 164 | br_list: std.ArrayListUnmanaged(Air.Inst.Index), |
| 165 | 165 | }; |
| 166 | 166 | |
| 167 | /// For debugging purposes. | |
| 168 | pub fn dump(block: *Block, mod: Module) void { | |
| 169 | Zir.dumpBlock(mod, block); | |
| 170 | } | |
| 171 | ||
| 172 | 167 | pub fn makeSubBlock(parent: *Block) Block { |
| 173 | 168 | return .{ |
| 174 | 169 | .parent = parent, |
src/aro/Attribute.zig created+978| ... | ... | @@ -0,0 +1,978 @@ |
| 1 | const std = @import("std"); | |
| 2 | const mem = std.mem; | |
| 3 | const Tree = @import("Tree.zig"); | |
| 4 | const Diagnostics = @import("Diagnostics.zig"); | |
| 5 | const Value = @import("Value.zig"); | |
| 6 | const Compilation = @import("Compilation.zig"); | |
| 7 | const Type = @import("Type.zig"); | |
| 8 | const NodeIndex = Tree.NodeIndex; | |
| 9 | const TokenIndex = Tree.TokenIndex; | |
| 10 | const TypeInfo = std.builtin.TypeInfo; | |
| 11 | ||
| 12 | const Attribute = @This(); | |
| 13 | ||
| 14 | tag: Tag, | |
| 15 | args: Arguments, | |
| 16 | ||
| 17 | pub const Kind = enum { | |
| 18 | c2x, | |
| 19 | declspec, | |
| 20 | gnu, | |
| 21 | }; | |
| 22 | ||
| 23 | pub const ArgumentType = enum { | |
| 24 | string, | |
| 25 | identifier, | |
| 26 | int, | |
| 27 | alignment, | |
| 28 | float, | |
| 29 | array, | |
| 30 | expression, | |
| 31 | ||
| 32 | pub fn toString(self: ArgumentType) []const u8 { | |
| 33 | return switch (self) { | |
| 34 | .string => "a string", | |
| 35 | .identifier => "an identifier", | |
| 36 | .int, .alignment => "an integer constant", | |
| 37 | .float => "a floating point number", | |
| 38 | .array => "an array", | |
| 39 | .expression => "an expression", | |
| 40 | }; | |
| 41 | } | |
| 42 | ||
| 43 | fn fromType(comptime T: type) ArgumentType { | |
| 44 | return switch (T) { | |
| 45 | []const u8 => .string, | |
| 46 | Identifier => .identifier, | |
| 47 | u32 => .int, | |
| 48 | Alignment => .alignment, | |
| 49 | else => switch (@typeInfo(T)) { | |
| 50 | .Enum => if (T.opts.enum_kind == .string) .string else .identifier, | |
| 51 | else => unreachable, | |
| 52 | }, | |
| 53 | }; | |
| 54 | } | |
| 55 | ||
| 56 | fn fromVal(value: Value) ArgumentType { | |
| 57 | return switch (value.tag) { | |
| 58 | .int => .int, | |
| 59 | .bytes => .string, | |
| 60 | .unavailable => .expression, | |
| 61 | .float => .float, | |
| 62 | .array => .array, | |
| 63 | }; | |
| 64 | } | |
| 65 | }; | |
| 66 | ||
| 67 | fn getArguments(comptime descriptor: type) []const TypeInfo.StructField { | |
| 68 | return if (@hasDecl(descriptor, "Args")) std.meta.fields(descriptor.Args) else &.{}; | |
| 69 | } | |
| 70 | ||
| 71 | /// number of required arguments | |
| 72 | pub fn requiredArgCount(attr: Tag) u32 { | |
| 73 | inline for (@typeInfo(Tag).Enum.fields) |field, i| { | |
| 74 | if (field.value == @enumToInt(attr)) comptime { | |
| 75 | var needed = 0; | |
| 76 | const decl = @typeInfo(attributes).Struct.decls[i]; | |
| 77 | const fields = getArguments(@field(attributes, decl.name)); | |
| 78 | for (fields) |arg_field| { | |
| 79 | if (!mem.eql(u8, arg_field.name, "__name_tok") and @typeInfo(arg_field.field_type) != .Optional) needed += 1; | |
| 80 | } | |
| 81 | return needed; | |
| 82 | }; | |
| 83 | } | |
| 84 | unreachable; | |
| 85 | } | |
| 86 | ||
| 87 | /// maximum number of args that can be passed | |
| 88 | pub fn maxArgCount(attr: Tag) u32 { | |
| 89 | inline for (@typeInfo(Tag).Enum.fields) |field, i| { | |
| 90 | if (field.value == @enumToInt(attr)) comptime { | |
| 91 | const decl = @typeInfo(attributes).Struct.decls[i]; | |
| 92 | const fields = getArguments(@field(attributes, decl.name)); | |
| 93 | var max = 0; | |
| 94 | for (fields) |arg_field| { | |
| 95 | if (!mem.eql(u8, arg_field.name, "__name_tok")) max += 1; | |
| 96 | } | |
| 97 | return max; | |
| 98 | }; | |
| 99 | } | |
| 100 | unreachable; | |
| 101 | } | |
| 102 | ||
| 103 | fn UnwrapOptional(comptime T: type) type { | |
| 104 | return switch (@typeInfo(T)) { | |
| 105 | .Optional => |optional| optional.child, | |
| 106 | else => T, | |
| 107 | }; | |
| 108 | } | |
| 109 | ||
| 110 | pub const Formatting = struct { | |
| 111 | /// The quote char (single or double) to use when printing identifiers/strings corresponding | |
| 112 | /// to the enum in the first field of the Args of `attr`. Identifier enums use single quotes, string enums | |
| 113 | /// use double quotes | |
| 114 | fn quoteChar(attr: Tag) []const u8 { | |
| 115 | inline for (@typeInfo(Tag).Enum.fields) |field, i| { | |
| 116 | if (field.value == @enumToInt(attr)) { | |
| 117 | const decl = @typeInfo(attributes).Struct.decls[i]; | |
| 118 | const fields = getArguments(@field(attributes, decl.name)); | |
| 119 | ||
| 120 | if (fields.len == 0) unreachable; | |
| 121 | const Unwrapped = UnwrapOptional(fields[0].field_type); | |
| 122 | if (@typeInfo(Unwrapped) != .Enum) unreachable; | |
| 123 | ||
| 124 | return if (Unwrapped.opts.enum_kind == .identifier) "'" else "\""; | |
| 125 | } | |
| 126 | } | |
| 127 | unreachable; | |
| 128 | } | |
| 129 | ||
| 130 | /// returns a comma-separated string of quoted enum values, representing the valid | |
| 131 | /// choices for the string or identifier enum of the first field of the Args of `attr`. | |
| 132 | pub fn choices(attr: Tag) []const u8 { | |
| 133 | inline for (@typeInfo(Tag).Enum.fields) |field, i| { | |
| 134 | if (field.value == @enumToInt(attr)) { | |
| 135 | const decl = @typeInfo(attributes).Struct.decls[i]; | |
| 136 | const fields = getArguments(@field(attributes, decl.name)); | |
| 137 | ||
| 138 | if (fields.len == 0) unreachable; | |
| 139 | const Unwrapped = UnwrapOptional(fields[0].field_type); | |
| 140 | if (@typeInfo(Unwrapped) != .Enum) unreachable; | |
| 141 | ||
| 142 | const enum_fields = @typeInfo(Unwrapped).Enum.fields; | |
| 143 | @setEvalBranchQuota(3000); | |
| 144 | const quote = comptime quoteChar(@intToEnum(Tag, field.value)); | |
| 145 | comptime var values: []const u8 = quote ++ enum_fields[0].name ++ quote; | |
| 146 | inline for (enum_fields[1..]) |enum_field| { | |
| 147 | values = values ++ ", "; | |
| 148 | values = values ++ quote ++ enum_field.name ++ quote; | |
| 149 | } | |
| 150 | return values; | |
| 151 | } | |
| 152 | } | |
| 153 | unreachable; | |
| 154 | } | |
| 155 | }; | |
| 156 | ||
| 157 | /// Checks if the first argument (if it exists) is an identifier enum | |
| 158 | pub fn wantsIdentEnum(attr: Tag) bool { | |
| 159 | inline for (@typeInfo(Tag).Enum.fields) |field, i| { | |
| 160 | if (field.value == @enumToInt(attr)) { | |
| 161 | const decl = @typeInfo(attributes).Struct.decls[i]; | |
| 162 | const fields = getArguments(@field(attributes, decl.name)); | |
| 163 | ||
| 164 | if (fields.len == 0) return false; | |
| 165 | const Unwrapped = UnwrapOptional(fields[0].field_type); | |
| 166 | if (@typeInfo(Unwrapped) != .Enum) return false; | |
| 167 | ||
| 168 | return Unwrapped.opts.enum_kind == .identifier; | |
| 169 | } | |
| 170 | } | |
| 171 | unreachable; | |
| 172 | } | |
| 173 | ||
| 174 | pub fn diagnoseIdent(attr: Tag, arguments: *Arguments, ident: []const u8) ?Diagnostics.Message { | |
| 175 | inline for (@typeInfo(Tag).Enum.fields) |field, i| { | |
| 176 | if (field.value == @enumToInt(attr)) { | |
| 177 | const decl = @typeInfo(attributes).Struct.decls[i]; | |
| 178 | const fields = getArguments(@field(attributes, decl.name)); | |
| 179 | if (fields.len == 0) unreachable; | |
| 180 | const Unwrapped = UnwrapOptional(fields[0].field_type); | |
| 181 | if (@typeInfo(Unwrapped) != .Enum) unreachable; | |
| 182 | if (std.meta.stringToEnum(Unwrapped, normalize(ident))) |enum_val| { | |
| 183 | @field(@field(arguments, decl.name), fields[0].name) = enum_val; | |
| 184 | return null; | |
| 185 | } | |
| 186 | return Diagnostics.Message{ | |
| 187 | .tag = .unknown_attr_enum, | |
| 188 | .extra = .{ .attr_enum = .{ .tag = attr } }, | |
| 189 | }; | |
| 190 | } | |
| 191 | } | |
| 192 | unreachable; | |
| 193 | } | |
| 194 | ||
| 195 | pub fn wantsAlignment(attr: Tag, idx: usize) bool { | |
| 196 | inline for (@typeInfo(Tag).Enum.fields) |field, i| { | |
| 197 | if (field.value == @enumToInt(attr)) { | |
| 198 | const decl = @typeInfo(attributes).Struct.decls[i]; | |
| 199 | const fields = getArguments(@field(attributes, decl.name)); | |
| 200 | ||
| 201 | if (idx >= fields.len) return false; | |
| 202 | inline for (fields) |arg_field, field_idx| { | |
| 203 | if (field_idx == idx) { | |
| 204 | return UnwrapOptional(arg_field.field_type) == Alignment; | |
| 205 | } | |
| 206 | } | |
| 207 | } | |
| 208 | } | |
| 209 | unreachable; | |
| 210 | } | |
| 211 | ||
| 212 | pub fn diagnoseAlignment(attr: Tag, arguments: *Arguments, arg_idx: u32, val: Value, ty: Type, comp: *Compilation) ?Diagnostics.Message { | |
| 213 | inline for (@typeInfo(Tag).Enum.fields) |field, i| { | |
| 214 | if (field.value == @enumToInt(attr)) { | |
| 215 | const decl = @typeInfo(attributes).Struct.decls[i]; | |
| 216 | const arg_fields = getArguments(@field(attributes, decl.name)); | |
| 217 | inline for (arg_fields) |arg_field, arg_i| { | |
| 218 | if (arg_idx == arg_i) { | |
| 219 | if (UnwrapOptional(arg_field.field_type) != Alignment) unreachable; | |
| 220 | ||
| 221 | if (val.tag == .unavailable) return Diagnostics.Message{ .tag = .alignas_unavailable }; | |
| 222 | if (val.compare(.lt, Value.int(0), ty, comp)) { | |
| 223 | return Diagnostics.Message{ .tag = .negative_alignment, .extra = .{ .signed = val.signExtend(ty, comp) } }; | |
| 224 | } | |
| 225 | const requested = std.math.cast(u29, val.data.int) catch { | |
| 226 | return Diagnostics.Message{ .tag = .maximum_alignment, .extra = .{ .unsigned = val.data.int } }; | |
| 227 | }; | |
| 228 | if (!std.mem.isValidAlign(requested)) return Diagnostics.Message{ .tag = .non_pow2_align }; | |
| 229 | ||
| 230 | @field(@field(arguments, decl.name), arg_field.name) = Alignment{ .requested = requested }; | |
| 231 | return null; | |
| 232 | } | |
| 233 | } | |
| 234 | unreachable; | |
| 235 | } | |
| 236 | } | |
| 237 | unreachable; | |
| 238 | } | |
| 239 | ||
| 240 | fn diagnoseField( | |
| 241 | comptime decl: TypeInfo.Declaration, | |
| 242 | comptime field: TypeInfo.StructField, | |
| 243 | comptime wanted: type, | |
| 244 | arguments: *Arguments, | |
| 245 | val: Value, | |
| 246 | node: Tree.Node, | |
| 247 | ) ?Diagnostics.Message { | |
| 248 | switch (val.tag) { | |
| 249 | .int => { | |
| 250 | if (@typeInfo(wanted) == .Int) { | |
| 251 | @field(@field(arguments, decl.name), field.name) = val.getInt(wanted); | |
| 252 | return null; | |
| 253 | } | |
| 254 | }, | |
| 255 | .bytes => { | |
| 256 | const bytes = @as([]const u8, val.data.bytes[0 .. val.data.bytes.len - 1]); | |
| 257 | if (wanted == []const u8) { | |
| 258 | @field(@field(arguments, decl.name), field.name) = bytes; | |
| 259 | return null; | |
| 260 | } else if (@typeInfo(wanted) == .Enum and wanted.opts.enum_kind == .string) { | |
| 261 | if (std.meta.stringToEnum(wanted, bytes)) |enum_val| { | |
| 262 | @field(@field(arguments, decl.name), field.name) = enum_val; | |
| 263 | return null; | |
| 264 | } else { | |
| 265 | @setEvalBranchQuota(3000); | |
| 266 | return Diagnostics.Message{ | |
| 267 | .tag = .unknown_attr_enum, | |
| 268 | .extra = .{ .attr_enum = .{ .tag = std.meta.stringToEnum(Tag, decl.name).? } }, | |
| 269 | }; | |
| 270 | } | |
| 271 | } | |
| 272 | }, | |
| 273 | else => { | |
| 274 | if (wanted == Identifier and node.tag == .decl_ref_expr) { | |
| 275 | @field(@field(arguments, decl.name), field.name) = Identifier{ .tok = node.data.decl_ref }; | |
| 276 | return null; | |
| 277 | } | |
| 278 | }, | |
| 279 | } | |
| 280 | return Diagnostics.Message{ | |
| 281 | .tag = .attribute_arg_invalid, | |
| 282 | .extra = .{ .attr_arg_type = .{ .expected = ArgumentType.fromType(wanted), .actual = ArgumentType.fromVal(val) } }, | |
| 283 | }; | |
| 284 | } | |
| 285 | ||
| 286 | pub fn diagnose(attr: Tag, arguments: *Arguments, arg_idx: u32, val: Value, node: Tree.Node) ?Diagnostics.Message { | |
| 287 | inline for (@typeInfo(Tag).Enum.fields) |field, i| { | |
| 288 | if (field.value == @enumToInt(attr)) { | |
| 289 | const decl = @typeInfo(attributes).Struct.decls[i]; | |
| 290 | const max_arg_count = maxArgCount(attr); | |
| 291 | if (arg_idx >= max_arg_count) return Diagnostics.Message{ | |
| 292 | .tag = .attribute_too_many_args, | |
| 293 | .extra = .{ .attr_arg_count = .{ .attribute = attr, .expected = max_arg_count } }, | |
| 294 | }; | |
| 295 | const arg_fields = getArguments(@field(attributes, decl.name)); | |
| 296 | inline for (arg_fields) |arg_field, arg_i| { | |
| 297 | if (arg_idx == arg_i) { | |
| 298 | return diagnoseField(decl, arg_field, UnwrapOptional(arg_field.field_type), arguments, val, node); | |
| 299 | } | |
| 300 | } | |
| 301 | unreachable; | |
| 302 | } | |
| 303 | } | |
| 304 | unreachable; | |
| 305 | } | |
| 306 | ||
| 307 | const EnumTypes = enum { | |
| 308 | string, | |
| 309 | identifier, | |
| 310 | }; | |
| 311 | pub const Alignment = struct { | |
| 312 | node: NodeIndex = .none, | |
| 313 | requested: u29, | |
| 314 | alignas: bool = false, | |
| 315 | }; | |
| 316 | pub const Identifier = struct { | |
| 317 | tok: TokenIndex = 0, | |
| 318 | }; | |
| 319 | ||
| 320 | const attributes = struct { | |
| 321 | const access = struct { | |
| 322 | const gnu = "access"; | |
| 323 | ||
| 324 | const Args = struct { | |
| 325 | access_mode: enum { | |
| 326 | read_only, | |
| 327 | read_write, | |
| 328 | write_only, | |
| 329 | none, | |
| 330 | ||
| 331 | const opts = struct { | |
| 332 | const enum_kind = .identifier; | |
| 333 | }; | |
| 334 | }, | |
| 335 | ref_index: u32, | |
| 336 | size_index: ?u32 = null, | |
| 337 | }; | |
| 338 | }; | |
| 339 | const alias = struct { | |
| 340 | const gnu = "alias"; | |
| 341 | const Args = struct { | |
| 342 | alias: []const u8, | |
| 343 | }; | |
| 344 | }; | |
| 345 | const aligned = struct { | |
| 346 | const gnu = "aligned"; | |
| 347 | const declspec = "align"; | |
| 348 | ||
| 349 | const Args = struct { | |
| 350 | alignment: ?Alignment = null, | |
| 351 | __name_tok: TokenIndex = undefined, | |
| 352 | }; | |
| 353 | }; | |
| 354 | const alloc_align = struct { | |
| 355 | const gnu = "alloc_align"; | |
| 356 | ||
| 357 | const Args = struct { | |
| 358 | position: u32, | |
| 359 | }; | |
| 360 | }; | |
| 361 | const alloc_size = struct { | |
| 362 | const gnu = "alloc_size"; | |
| 363 | ||
| 364 | const Args = struct { | |
| 365 | position_1: u32, | |
| 366 | position_2: ?u32 = null, | |
| 367 | }; | |
| 368 | }; | |
| 369 | const allocate = struct { | |
| 370 | const declspec = "allocate"; | |
| 371 | ||
| 372 | const Args = struct { | |
| 373 | segname: []const u8, | |
| 374 | }; | |
| 375 | }; | |
| 376 | const allocator = struct { | |
| 377 | const declspec = "allocator"; | |
| 378 | }; | |
| 379 | const always_inline = struct { | |
| 380 | const gnu = "always_inline"; | |
| 381 | }; | |
| 382 | const appdomain = struct { | |
| 383 | const declspec = "appdomain"; | |
| 384 | }; | |
| 385 | const artificial = struct { | |
| 386 | const gnu = "artificial"; | |
| 387 | }; | |
| 388 | const assume_aligned = struct { | |
| 389 | const gnu = "assume_aligned"; | |
| 390 | const Args = struct { | |
| 391 | alignment: Alignment, | |
| 392 | offset: ?u32 = null, | |
| 393 | }; | |
| 394 | }; | |
| 395 | const cleanup = struct { | |
| 396 | const gnu = "cleanup"; | |
| 397 | const Args = struct { | |
| 398 | function: Identifier, | |
| 399 | }; | |
| 400 | }; | |
| 401 | const code_seg = struct { | |
| 402 | const declspec = "code_seg"; | |
| 403 | const Args = struct { | |
| 404 | segname: []const u8, | |
| 405 | }; | |
| 406 | }; | |
| 407 | const cold = struct { | |
| 408 | const gnu = "cold"; | |
| 409 | }; | |
| 410 | const common = struct { | |
| 411 | const gnu = "common"; | |
| 412 | }; | |
| 413 | const @"const" = struct { | |
| 414 | const gnu = "const"; | |
| 415 | }; | |
| 416 | const constructor = struct { | |
| 417 | const gnu = "constructor"; | |
| 418 | const Args = struct { | |
| 419 | priority: ?u32 = null, | |
| 420 | }; | |
| 421 | }; | |
| 422 | const copy = struct { | |
| 423 | const gnu = "copy"; | |
| 424 | const Args = struct { | |
| 425 | function: Identifier, | |
| 426 | }; | |
| 427 | }; | |
| 428 | const deprecated = struct { | |
| 429 | const gnu = "deprecated"; | |
| 430 | const declspec = "deprecated"; | |
| 431 | const c2x = "deprecated"; | |
| 432 | ||
| 433 | const Args = struct { | |
| 434 | msg: ?[]const u8 = null, | |
| 435 | __name_tok: TokenIndex = undefined, | |
| 436 | }; | |
| 437 | }; | |
| 438 | const designated_init = struct { | |
| 439 | const gnu = "designated_init"; | |
| 440 | }; | |
| 441 | const destructor = struct { | |
| 442 | const gnu = "destructor"; | |
| 443 | const Args = struct { | |
| 444 | priority: ?u32 = null, | |
| 445 | }; | |
| 446 | }; | |
| 447 | const dllexport = struct { | |
| 448 | const declspec = "dllexport"; | |
| 449 | }; | |
| 450 | const dllimport = struct { | |
| 451 | const declspec = "dllimport"; | |
| 452 | }; | |
| 453 | const @"error" = struct { | |
| 454 | const gnu = "error"; | |
| 455 | const Args = struct { | |
| 456 | message: []const u8, | |
| 457 | }; | |
| 458 | }; | |
| 459 | const externally_visible = struct { | |
| 460 | const gnu = "externally_visible"; | |
| 461 | }; | |
| 462 | const fallthrough = struct { | |
| 463 | const gnu = "fallthrough"; | |
| 464 | const c2x = "fallthrough"; | |
| 465 | }; | |
| 466 | const flatten = struct { | |
| 467 | const gnu = "flatten"; | |
| 468 | }; | |
| 469 | const format = struct { | |
| 470 | const gnu = "format"; | |
| 471 | const Args = struct { | |
| 472 | archetype: enum { | |
| 473 | printf, | |
| 474 | scanf, | |
| 475 | strftime, | |
| 476 | strfmon, | |
| 477 | ||
| 478 | const opts = struct { | |
| 479 | const enum_kind = .identifier; | |
| 480 | }; | |
| 481 | }, | |
| 482 | string_index: u32, | |
| 483 | first_to_check: u32, | |
| 484 | }; | |
| 485 | }; | |
| 486 | const format_arg = struct { | |
| 487 | const gnu = "format_arg"; | |
| 488 | const Args = struct { | |
| 489 | string_index: u32, | |
| 490 | }; | |
| 491 | }; | |
| 492 | const gnu_inline = struct { | |
| 493 | const gnu = "gnu_inline"; | |
| 494 | }; | |
| 495 | const hot = struct { | |
| 496 | const gnu = "hot"; | |
| 497 | }; | |
| 498 | const ifunc = struct { | |
| 499 | const gnu = "ifunc"; | |
| 500 | const Args = struct { | |
| 501 | resolver: []const u8, | |
| 502 | }; | |
| 503 | }; | |
| 504 | const interrupt = struct { | |
| 505 | const gnu = "interrupt"; | |
| 506 | }; | |
| 507 | const interrupt_handler = struct { | |
| 508 | const gnu = "interrupt_handler"; | |
| 509 | }; | |
| 510 | const jitintrinsic = struct { | |
| 511 | const declspec = "jitintrinsic"; | |
| 512 | }; | |
| 513 | const leaf = struct { | |
| 514 | const gnu = "leaf"; | |
| 515 | }; | |
| 516 | const malloc = struct { | |
| 517 | const gnu = "malloc"; | |
| 518 | }; | |
| 519 | const may_alias = struct { | |
| 520 | const gnu = "may_alias"; | |
| 521 | }; | |
| 522 | const mode = struct { | |
| 523 | const gnu = "mode"; | |
| 524 | const Args = struct { | |
| 525 | mode: enum { | |
| 526 | // zig fmt: off | |
| 527 | byte, word, pointer, | |
| 528 | BI, QI, HI, | |
| 529 | PSI, SI, PDI, | |
| 530 | DI, TI, OI, | |
| 531 | XI, QF, HF, | |
| 532 | TQF, SF, DF, | |
| 533 | XF, SD, DD, | |
| 534 | TD, TF, QQ, | |
| 535 | HQ, SQ, DQ, | |
| 536 | TQ, UQQ, UHQ, | |
| 537 | USQ, UDQ, UTQ, | |
| 538 | HA, SA, DA, | |
| 539 | TA, UHA, USA, | |
| 540 | UDA, UTA, CC, | |
| 541 | BLK, VOID, QC, | |
| 542 | HC, SC, DC, | |
| 543 | XC, TC, CQI, | |
| 544 | CHI, CSI, CDI, | |
| 545 | CTI, COI, CPSI, | |
| 546 | BND32, BND64, | |
| 547 | // zig fmt: on | |
| 548 | ||
| 549 | const opts = struct { | |
| 550 | const enum_kind = .identifier; | |
| 551 | }; | |
| 552 | }, | |
| 553 | }; | |
| 554 | }; | |
| 555 | const naked = struct { | |
| 556 | const declspec = "naked"; | |
| 557 | }; | |
| 558 | const no_address_safety_analysis = struct { | |
| 559 | const gnu = "no_address_safety_analysise"; | |
| 560 | }; | |
| 561 | const no_icf = struct { | |
| 562 | const gnu = "no_icf"; | |
| 563 | }; | |
| 564 | const no_instrument_function = struct { | |
| 565 | const gnu = "no_instrument_function"; | |
| 566 | }; | |
| 567 | const no_profile_instrument_function = struct { | |
| 568 | const gnu = "no_profile_instrument_function"; | |
| 569 | }; | |
| 570 | const no_reorder = struct { | |
| 571 | const gnu = "no_reorder"; | |
| 572 | }; | |
| 573 | const no_sanitize = struct { | |
| 574 | const gnu = "no_sanitize"; | |
| 575 | /// Todo: represent args as union? | |
| 576 | const Args = struct { | |
| 577 | alignment: []const u8, | |
| 578 | object_size: ?[]const u8 = null, | |
| 579 | }; | |
| 580 | }; | |
| 581 | const no_sanitize_address = struct { | |
| 582 | const gnu = "no_sanitize_address"; | |
| 583 | const declspec = "no_sanitize_address"; | |
| 584 | }; | |
| 585 | const no_sanitize_coverage = struct { | |
| 586 | const gnu = "no_sanitize_coverage"; | |
| 587 | }; | |
| 588 | const no_sanitize_thread = struct { | |
| 589 | const gnu = "no_sanitize_thread"; | |
| 590 | }; | |
| 591 | const no_sanitize_undefined = struct { | |
| 592 | const gnu = "no_sanitize_undefined"; | |
| 593 | }; | |
| 594 | const no_split_stack = struct { | |
| 595 | const gnu = "no_split_stack"; | |
| 596 | }; | |
| 597 | const no_stack_limit = struct { | |
| 598 | const gnu = "no_stack_limit"; | |
| 599 | }; | |
| 600 | const no_stack_protector = struct { | |
| 601 | const gnu = "no_stack_protector"; | |
| 602 | }; | |
| 603 | const @"noalias" = struct { | |
| 604 | const declspec = "noalias"; | |
| 605 | }; | |
| 606 | const noclone = struct { | |
| 607 | const gnu = "noclone"; | |
| 608 | }; | |
| 609 | const nocommon = struct { | |
| 610 | const gnu = "nocommon"; | |
| 611 | }; | |
| 612 | const nodiscard = struct { | |
| 613 | const c2x = "nodiscard"; | |
| 614 | }; | |
| 615 | const noinit = struct { | |
| 616 | const gnu = "noinit"; | |
| 617 | }; | |
| 618 | const @"noinline" = struct { | |
| 619 | const gnu = "noinline"; | |
| 620 | const declspec = "noinline"; | |
| 621 | }; | |
| 622 | const noipa = struct { | |
| 623 | const gnu = "noipa"; | |
| 624 | }; | |
| 625 | // TODO: arbitrary number of arguments | |
| 626 | // const nonnull = struct { | |
| 627 | // const gnu = "nonnull"; | |
| 628 | // const Args = struct { | |
| 629 | // arg_index: []const u32, | |
| 630 | // }; | |
| 631 | // }; | |
| 632 | const nonstring = struct { | |
| 633 | const gnu = "nonstring"; | |
| 634 | }; | |
| 635 | const noplt = struct { | |
| 636 | const gnu = "noplt"; | |
| 637 | }; | |
| 638 | const @"noreturn" = struct { | |
| 639 | const gnu = "noreturn"; | |
| 640 | const c2x = "noreturn"; | |
| 641 | const declspec = "noreturn"; | |
| 642 | }; | |
| 643 | const nothrow = struct { | |
| 644 | const gnu = "nothrow"; | |
| 645 | const declspec = "nothrow"; | |
| 646 | }; | |
| 647 | const novtable = struct { | |
| 648 | const declspec = "novtable"; | |
| 649 | }; | |
| 650 | // TODO: union args ? | |
| 651 | // const optimize = struct { | |
| 652 | // const gnu = "optimize"; | |
| 653 | // const Args = struct { | |
| 654 | // optimize, // u32 | []const u8 -- optimize? | |
| 655 | // }; | |
| 656 | // }; | |
| 657 | const @"packed" = struct { | |
| 658 | const gnu = "packed"; | |
| 659 | }; | |
| 660 | const patchable_function_entry = struct { | |
| 661 | const gnu = "patchable_function_entry"; | |
| 662 | }; | |
| 663 | const persistent = struct { | |
| 664 | const gnu = "persistent"; | |
| 665 | }; | |
| 666 | const process = struct { | |
| 667 | const declspec = "process"; | |
| 668 | }; | |
| 669 | const pure = struct { | |
| 670 | const gnu = "pure"; | |
| 671 | }; | |
| 672 | const restrict = struct { | |
| 673 | const declspec = "restrict"; | |
| 674 | }; | |
| 675 | const retain = struct { | |
| 676 | const gnu = "retain"; | |
| 677 | }; | |
| 678 | const returns_nonnull = struct { | |
| 679 | const gnu = "returns_nonnull"; | |
| 680 | }; | |
| 681 | const returns_twice = struct { | |
| 682 | const gnu = "returns_twice"; | |
| 683 | }; | |
| 684 | const safebuffers = struct { | |
| 685 | const declspec = "safebuffers"; | |
| 686 | }; | |
| 687 | const scalar_storage_order = struct { | |
| 688 | const gnu = "scalar_storage_order"; | |
| 689 | const Args = struct { | |
| 690 | order: enum { | |
| 691 | @"little-endian", | |
| 692 | @"big-endian", | |
| 693 | ||
| 694 | const opts = struct { | |
| 695 | const enum_kind = .string; | |
| 696 | }; | |
| 697 | }, | |
| 698 | }; | |
| 699 | }; | |
| 700 | const section = struct { | |
| 701 | const gnu = "section"; | |
| 702 | const Args = struct { | |
| 703 | name: []const u8, | |
| 704 | }; | |
| 705 | }; | |
| 706 | const selectany = struct { | |
| 707 | const declspec = "selectany"; | |
| 708 | }; | |
| 709 | const sentinel = struct { | |
| 710 | const gnu = "sentinel"; | |
| 711 | const Args = struct { | |
| 712 | position: ?u32 = null, | |
| 713 | }; | |
| 714 | }; | |
| 715 | const simd = struct { | |
| 716 | const gnu = "simd"; | |
| 717 | const Args = struct { | |
| 718 | mask: ?enum { | |
| 719 | notinbranch, | |
| 720 | inbranch, | |
| 721 | ||
| 722 | const opts = struct { | |
| 723 | const enum_kind = .string; | |
| 724 | }; | |
| 725 | } = null, | |
| 726 | }; | |
| 727 | }; | |
| 728 | const spectre = struct { | |
| 729 | const declspec = "spectre"; | |
| 730 | const Args = struct { | |
| 731 | arg: enum { | |
| 732 | nomitigation, | |
| 733 | ||
| 734 | const opts = struct { | |
| 735 | const enum_kind = .identifier; | |
| 736 | }; | |
| 737 | }, | |
| 738 | }; | |
| 739 | }; | |
| 740 | const stack_protect = struct { | |
| 741 | const gnu = "stack_protect"; | |
| 742 | }; | |
| 743 | const symver = struct { | |
| 744 | const gnu = "symver"; | |
| 745 | const Args = struct { | |
| 746 | version: []const u8, // TODO: validate format "name2@nodename" | |
| 747 | }; | |
| 748 | }; | |
| 749 | const target = struct { | |
| 750 | const gnu = "target"; | |
| 751 | const Args = struct { | |
| 752 | options: []const u8, // TODO: multiple arguments | |
| 753 | }; | |
| 754 | }; | |
| 755 | const target_clones = struct { | |
| 756 | const gnu = "target_clones"; | |
| 757 | const Args = struct { | |
| 758 | options: []const u8, // TODO: multiple arguments | |
| 759 | }; | |
| 760 | }; | |
| 761 | const thread = struct { | |
| 762 | const declspec = "thread"; | |
| 763 | }; | |
| 764 | const tls_model = struct { | |
| 765 | const gnu = "tls_model"; | |
| 766 | const Args = struct { | |
| 767 | model: enum { | |
| 768 | @"global-dynamic", | |
| 769 | @"local-dynamic", | |
| 770 | @"initial-exec", | |
| 771 | @"local-exec", | |
| 772 | ||
| 773 | const opts = struct { | |
| 774 | const enum_kind = .string; | |
| 775 | }; | |
| 776 | }, | |
| 777 | }; | |
| 778 | }; | |
| 779 | const transparent_union = struct { | |
| 780 | const gnu = "transparent_union"; | |
| 781 | }; | |
| 782 | const unavailable = struct { | |
| 783 | const gnu = "unavailable"; | |
| 784 | const Args = struct { | |
| 785 | msg: ?[]const u8 = null, | |
| 786 | __name_tok: TokenIndex = undefined, | |
| 787 | }; | |
| 788 | }; | |
| 789 | const uninitialized = struct { | |
| 790 | const gnu = "uninitialized"; | |
| 791 | }; | |
| 792 | const unused = struct { | |
| 793 | const gnu = "unused"; | |
| 794 | const c2x = "maybe_unused"; | |
| 795 | }; | |
| 796 | const used = struct { | |
| 797 | const gnu = "used"; | |
| 798 | }; | |
| 799 | const uuid = struct { | |
| 800 | const declspec = "uuid"; | |
| 801 | const Args = struct { | |
| 802 | uuid: []const u8, | |
| 803 | }; | |
| 804 | }; | |
| 805 | const vector_size = struct { | |
| 806 | const gnu = "vector_size"; | |
| 807 | const Args = struct { | |
| 808 | bytes: u32, // TODO: validate "The bytes argument must be a positive power-of-two multiple of the base type size" | |
| 809 | }; | |
| 810 | }; | |
| 811 | const visibility = struct { | |
| 812 | const gnu = "visibility"; | |
| 813 | const Args = struct { | |
| 814 | visibility_type: enum { | |
| 815 | default, | |
| 816 | hidden, | |
| 817 | internal, | |
| 818 | protected, | |
| 819 | ||
| 820 | const opts = struct { | |
| 821 | const enum_kind = .string; | |
| 822 | }; | |
| 823 | }, | |
| 824 | }; | |
| 825 | }; | |
| 826 | const warn_if_not_aligned = struct { | |
| 827 | const gnu = "warn_if_not_aligned"; | |
| 828 | const Args = struct { | |
| 829 | alignment: Alignment, | |
| 830 | }; | |
| 831 | }; | |
| 832 | const warn_unused_result = struct { | |
| 833 | const gnu = "warn_unused_result"; | |
| 834 | }; | |
| 835 | const warning = struct { | |
| 836 | const gnu = "warning"; | |
| 837 | const Args = struct { | |
| 838 | message: []const u8, | |
| 839 | }; | |
| 840 | }; | |
| 841 | const weak = struct { | |
| 842 | const gnu = "weak"; | |
| 843 | }; | |
| 844 | const weakref = struct { | |
| 845 | const gnu = "weakref"; | |
| 846 | const Args = struct { | |
| 847 | target: ?[]const u8 = null, | |
| 848 | }; | |
| 849 | }; | |
| 850 | const zero_call_used_regs = struct { | |
| 851 | const gnu = "zero_call_used_regs"; | |
| 852 | const Args = struct { | |
| 853 | choice: enum { | |
| 854 | skip, | |
| 855 | used, | |
| 856 | @"used-gpr", | |
| 857 | @"used-arg", | |
| 858 | @"used-gpr-arg", | |
| 859 | all, | |
| 860 | @"all-gpr", | |
| 861 | @"all-arg", | |
| 862 | @"all-gpr-arg", | |
| 863 | ||
| 864 | const opts = struct { | |
| 865 | const enum_kind = .string; | |
| 866 | }; | |
| 867 | }, | |
| 868 | }; | |
| 869 | }; | |
| 870 | const asm_label = struct { | |
| 871 | const Args = struct { | |
| 872 | name: []const u8, | |
| 873 | }; | |
| 874 | }; | |
| 875 | }; | |
| 876 | ||
| 877 | pub const Tag = std.meta.DeclEnum(attributes); | |
| 878 | ||
| 879 | pub const Arguments = blk: { | |
| 880 | const decls = @typeInfo(attributes).Struct.decls; | |
| 881 | var union_fields: [decls.len]std.builtin.TypeInfo.UnionField = undefined; | |
| 882 | inline for (decls) |decl, i| { | |
| 883 | union_fields[i] = .{ | |
| 884 | .name = decl.name, | |
| 885 | .field_type = if (@hasDecl(@field(attributes, decl.name), "Args")) @field(attributes, decl.name).Args else void, | |
| 886 | .alignment = 0, | |
| 887 | }; | |
| 888 | } | |
| 889 | ||
| 890 | break :blk @Type(.{ | |
| 891 | .Union = .{ | |
| 892 | .layout = .Auto, | |
| 893 | .tag_type = null, | |
| 894 | .fields = &union_fields, | |
| 895 | .decls = &.{}, | |
| 896 | }, | |
| 897 | }); | |
| 898 | }; | |
| 899 | ||
| 900 | pub fn ArgumentsForTag(comptime tag: Tag) type { | |
| 901 | const decl = @typeInfo(attributes).Struct.decls[@enumToInt(tag)]; | |
| 902 | return if (@hasDecl(@field(attributes, decl.name), "Args")) @field(attributes, decl.name).Args else void; | |
| 903 | } | |
| 904 | ||
| 905 | pub fn initArguments(tag: Tag, name_tok: TokenIndex) Arguments { | |
| 906 | inline for (@typeInfo(Tag).Enum.fields) |field| { | |
| 907 | if (@enumToInt(tag) == field.value) { | |
| 908 | var args = @unionInit(Arguments, field.name, undefined); | |
| 909 | const decl = @typeInfo(attributes).Struct.decls[field.value]; | |
| 910 | if (@hasDecl(@field(attributes, decl.name), "Args") and @hasField(@field(attributes, decl.name).Args, "__name_tok")) { | |
| 911 | @field(@field(args, field.name), "__name_tok") = name_tok; | |
| 912 | } | |
| 913 | return args; | |
| 914 | } | |
| 915 | } | |
| 916 | unreachable; | |
| 917 | } | |
| 918 | ||
| 919 | pub fn fromString(kind: Kind, namespace: ?[]const u8, name: []const u8) ?Tag { | |
| 920 | return switch (kind) { | |
| 921 | .c2x => fromStringC2X(namespace, name), | |
| 922 | .declspec => fromStringDeclspec(name), | |
| 923 | .gnu => fromStringGnu(name), | |
| 924 | }; | |
| 925 | } | |
| 926 | ||
| 927 | fn fromStringGnu(name: []const u8) ?Tag { | |
| 928 | const normalized = normalize(name); | |
| 929 | const decls = @typeInfo(attributes).Struct.decls; | |
| 930 | @setEvalBranchQuota(3000); | |
| 931 | inline for (decls) |decl, i| { | |
| 932 | if (@hasDecl(@field(attributes, decl.name), "gnu")) { | |
| 933 | if (mem.eql(u8, @field(attributes, decl.name).gnu, normalized)) { | |
| 934 | return @intToEnum(Tag, i); | |
| 935 | } | |
| 936 | } | |
| 937 | } | |
| 938 | return null; | |
| 939 | } | |
| 940 | ||
| 941 | fn fromStringC2X(namespace: ?[]const u8, name: []const u8) ?Tag { | |
| 942 | const normalized = normalize(name); | |
| 943 | if (namespace) |ns| { | |
| 944 | const normalized_ns = normalize(ns); | |
| 945 | if (mem.eql(u8, normalized_ns, "gnu")) { | |
| 946 | return fromStringGnu(normalized); | |
| 947 | } | |
| 948 | return null; | |
| 949 | } | |
| 950 | const decls = @typeInfo(attributes).Struct.decls; | |
| 951 | inline for (decls) |decl, i| { | |
| 952 | if (@hasDecl(@field(attributes, decl.name), "c2x")) { | |
| 953 | if (mem.eql(u8, @field(attributes, decl.name).c2x, normalized)) { | |
| 954 | return @intToEnum(Tag, i); | |
| 955 | } | |
| 956 | } | |
| 957 | } | |
| 958 | return null; | |
| 959 | } | |
| 960 | ||
| 961 | fn fromStringDeclspec(name: []const u8) ?Tag { | |
| 962 | const decls = @typeInfo(attributes).Struct.decls; | |
| 963 | inline for (decls) |decl, i| { | |
| 964 | if (@hasDecl(@field(attributes, decl.name), "declspec")) { | |
| 965 | if (mem.eql(u8, @field(attributes, decl.name).declspec, name)) { | |
| 966 | return @intToEnum(Tag, i); | |
| 967 | } | |
| 968 | } | |
| 969 | } | |
| 970 | return null; | |
| 971 | } | |
| 972 | ||
| 973 | fn normalize(name: []const u8) []const u8 { | |
| 974 | if (name.len >= 4 and mem.startsWith(u8, name, "__") and mem.endsWith(u8, name, "__")) { | |
| 975 | return name[2 .. name.len - 2]; | |
| 976 | } | |
| 977 | return name; | |
| 978 | } |
src/aro/Builtins.zig created+90| ... | ... | @@ -0,0 +1,90 @@ |
| 1 | const std = @import("std"); | |
| 2 | const Compilation = @import("Compilation.zig"); | |
| 3 | const Type = @import("Type.zig"); | |
| 4 | ||
| 5 | const Builtins = @This(); | |
| 6 | ||
| 7 | const Builtin = struct { | |
| 8 | spec: Type.Specifier, | |
| 9 | func_ty: Type.Func, | |
| 10 | attrs: Attributes, | |
| 11 | ||
| 12 | const Attributes = packed struct { | |
| 13 | printf_like: u8 = 0, | |
| 14 | vprintf_like: u8 = 0, | |
| 15 | noreturn: bool = false, | |
| 16 | libm: bool = false, | |
| 17 | libc: bool = false, | |
| 18 | returns_twice: bool = false, | |
| 19 | eval_args: bool = true, | |
| 20 | }; | |
| 21 | }; | |
| 22 | const BuiltinMap = std.StringHashMapUnmanaged(Builtin); | |
| 23 | ||
| 24 | _builtins: BuiltinMap = .{}, | |
| 25 | _params: []Type.Func.Param = &.{}, | |
| 26 | ||
| 27 | pub fn deinit(b: *Builtins, gpa: std.mem.Allocator) void { | |
| 28 | b._builtins.deinit(gpa); | |
| 29 | gpa.free(b._params); | |
| 30 | } | |
| 31 | ||
| 32 | fn add( | |
| 33 | a: std.mem.Allocator, | |
| 34 | b: *BuiltinMap, | |
| 35 | name: []const u8, | |
| 36 | ret_ty: Type, | |
| 37 | param_types: []const Type, | |
| 38 | spec: Type.Specifier, | |
| 39 | attrs: Builtin.Attributes, | |
| 40 | ) void { | |
| 41 | var params = a.alloc(Type.Func.Param, param_types.len) catch unreachable; // fib | |
| 42 | for (param_types) |param_ty, i| { | |
| 43 | params[i] = .{ .name_tok = 0, .ty = param_ty, .name = "" }; | |
| 44 | } | |
| 45 | b.putAssumeCapacity(name, .{ | |
| 46 | .spec = spec, | |
| 47 | .func_ty = .{ | |
| 48 | .return_type = ret_ty, | |
| 49 | .params = params, | |
| 50 | }, | |
| 51 | .attrs = attrs, | |
| 52 | }); | |
| 53 | } | |
| 54 | ||
| 55 | pub fn create(comp: *Compilation) !Builtins { | |
| 56 | const builtin_count = 3; | |
| 57 | const param_count = 5; | |
| 58 | ||
| 59 | var b = BuiltinMap{}; | |
| 60 | try b.ensureTotalCapacity(comp.gpa, builtin_count); | |
| 61 | errdefer b.deinit(comp.gpa); | |
| 62 | var _params = try comp.gpa.alloc(Type.Func.Param, param_count); | |
| 63 | errdefer comp.gpa.free(_params); | |
| 64 | var fib_state = std.heap.FixedBufferAllocator.init(std.mem.sliceAsBytes(_params)); | |
| 65 | const a = fib_state.allocator(); | |
| 66 | ||
| 67 | const void_ty = Type{ .specifier = .void }; | |
| 68 | var va_list = comp.types.va_list; | |
| 69 | if (va_list.isArray()) va_list.decayArray(); | |
| 70 | ||
| 71 | add(a, &b, "__builtin_va_start", void_ty, &.{ va_list, .{ .specifier = .special_va_start } }, .func, .{}); | |
| 72 | add(a, &b, "__builtin_va_end", void_ty, &.{va_list}, .func, .{}); | |
| 73 | add(a, &b, "__builtin_va_copy", void_ty, &.{ va_list, va_list }, .func, .{}); | |
| 74 | ||
| 75 | return Builtins{ ._builtins = b, ._params = _params }; | |
| 76 | } | |
| 77 | ||
| 78 | pub fn hasBuiltin(b: Builtins, name: []const u8) bool { | |
| 79 | if (std.mem.eql(u8, name, "__builtin_va_arg") or | |
| 80 | std.mem.eql(u8, name, "__builtin_choose_expr")) return true; | |
| 81 | return b._builtins.getPtr(name) != null; | |
| 82 | } | |
| 83 | ||
| 84 | pub fn get(b: Builtins, name: []const u8) ?Type { | |
| 85 | const builtin = b._builtins.getPtr(name) orelse return null; | |
| 86 | return Type{ | |
| 87 | .specifier = builtin.spec, | |
| 88 | .data = .{ .func = &builtin.func_ty }, | |
| 89 | }; | |
| 90 | } |
src/aro/CharInfo.zig created+487| ... | ... | @@ -0,0 +1,487 @@ |
| 1 | //! This module provides functions for classifying characters according to | |
| 2 | //! various C standards. All classification routines *do not* consider | |
| 3 | //! characters from the basic character set; it is assumed those will be | |
| 4 | //! checked separately | |
| 5 | ||
| 6 | const assert = @import("std").debug.assert; | |
| 7 | ||
| 8 | /// C11 Standard Annex D | |
| 9 | pub fn isC11IdChar(codepoint: u21) bool { | |
| 10 | assert(codepoint > 0x7F); | |
| 11 | return switch (codepoint) { | |
| 12 | // 1 | |
| 13 | 0x00A8, | |
| 14 | 0x00AA, | |
| 15 | 0x00AD, | |
| 16 | 0x00AF, | |
| 17 | 0x00B2...0x00B5, | |
| 18 | 0x00B7...0x00BA, | |
| 19 | 0x00BC...0x00BE, | |
| 20 | 0x00C0...0x00D6, | |
| 21 | 0x00D8...0x00F6, | |
| 22 | 0x00F8...0x00FF, | |
| 23 | ||
| 24 | // 2 | |
| 25 | 0x0100...0x167F, | |
| 26 | 0x1681...0x180D, | |
| 27 | 0x180F...0x1FFF, | |
| 28 | ||
| 29 | // 3 | |
| 30 | 0x200B...0x200D, | |
| 31 | 0x202A...0x202E, | |
| 32 | 0x203F...0x2040, | |
| 33 | 0x2054, | |
| 34 | 0x2060...0x206F, | |
| 35 | ||
| 36 | // 4 | |
| 37 | 0x2070...0x218F, | |
| 38 | 0x2460...0x24FF, | |
| 39 | 0x2776...0x2793, | |
| 40 | 0x2C00...0x2DFF, | |
| 41 | 0x2E80...0x2FFF, | |
| 42 | ||
| 43 | // 5 | |
| 44 | 0x3004...0x3007, | |
| 45 | 0x3021...0x302F, | |
| 46 | 0x3031...0x303F, | |
| 47 | ||
| 48 | // 6 | |
| 49 | 0x3040...0xD7FF, | |
| 50 | ||
| 51 | // 7 | |
| 52 | 0xF900...0xFD3D, | |
| 53 | 0xFD40...0xFDCF, | |
| 54 | 0xFDF0...0xFE44, | |
| 55 | 0xFE47...0xFFFD, | |
| 56 | ||
| 57 | // 8 | |
| 58 | 0x10000...0x1FFFD, | |
| 59 | 0x20000...0x2FFFD, | |
| 60 | 0x30000...0x3FFFD, | |
| 61 | 0x40000...0x4FFFD, | |
| 62 | 0x50000...0x5FFFD, | |
| 63 | 0x60000...0x6FFFD, | |
| 64 | 0x70000...0x7FFFD, | |
| 65 | 0x80000...0x8FFFD, | |
| 66 | 0x90000...0x9FFFD, | |
| 67 | 0xA0000...0xAFFFD, | |
| 68 | 0xB0000...0xBFFFD, | |
| 69 | 0xC0000...0xCFFFD, | |
| 70 | 0xD0000...0xDFFFD, | |
| 71 | 0xE0000...0xEFFFD, | |
| 72 | => true, | |
| 73 | else => false, | |
| 74 | }; | |
| 75 | } | |
| 76 | ||
| 77 | /// C99 Standard Annex D | |
| 78 | pub fn isC99IdChar(codepoint: u21) bool { | |
| 79 | assert(codepoint > 0x7F); | |
| 80 | return switch (codepoint) { | |
| 81 | // Latin | |
| 82 | 0x00AA, | |
| 83 | 0x00BA, | |
| 84 | 0x00C0...0x00D6, | |
| 85 | 0x00D8...0x00F6, | |
| 86 | 0x00F8...0x01F5, | |
| 87 | 0x01FA...0x0217, | |
| 88 | 0x0250...0x02A8, | |
| 89 | 0x1E00...0x1E9B, | |
| 90 | 0x1EA0...0x1EF9, | |
| 91 | 0x207F, | |
| 92 | ||
| 93 | // Greek | |
| 94 | 0x0386, | |
| 95 | 0x0388...0x038A, | |
| 96 | 0x038C, | |
| 97 | 0x038E...0x03A1, | |
| 98 | 0x03A3...0x03CE, | |
| 99 | 0x03D0...0x03D6, | |
| 100 | 0x03DA, | |
| 101 | 0x03DC, | |
| 102 | 0x03DE, | |
| 103 | 0x03E0, | |
| 104 | 0x03E2...0x03F3, | |
| 105 | 0x1F00...0x1F15, | |
| 106 | 0x1F18...0x1F1D, | |
| 107 | 0x1F20...0x1F45, | |
| 108 | 0x1F48...0x1F4D, | |
| 109 | 0x1F50...0x1F57, | |
| 110 | 0x1F59, | |
| 111 | 0x1F5B, | |
| 112 | 0x1F5D, | |
| 113 | 0x1F5F...0x1F7D, | |
| 114 | 0x1F80...0x1FB4, | |
| 115 | 0x1FB6...0x1FBC, | |
| 116 | 0x1FC2...0x1FC4, | |
| 117 | 0x1FC6...0x1FCC, | |
| 118 | 0x1FD0...0x1FD3, | |
| 119 | 0x1FD6...0x1FDB, | |
| 120 | 0x1FE0...0x1FEC, | |
| 121 | 0x1FF2...0x1FF4, | |
| 122 | 0x1FF6...0x1FFC, | |
| 123 | ||
| 124 | // Cyrillic | |
| 125 | 0x0401...0x040C, | |
| 126 | 0x040E...0x044F, | |
| 127 | 0x0451...0x045C, | |
| 128 | 0x045E...0x0481, | |
| 129 | 0x0490...0x04C4, | |
| 130 | 0x04C7...0x04C8, | |
| 131 | 0x04CB...0x04CC, | |
| 132 | 0x04D0...0x04EB, | |
| 133 | 0x04EE...0x04F5, | |
| 134 | 0x04F8...0x04F9, | |
| 135 | ||
| 136 | // Armenian | |
| 137 | 0x0531...0x0556, | |
| 138 | 0x0561...0x0587, | |
| 139 | ||
| 140 | // Hebrew | |
| 141 | 0x05B0...0x05B9, | |
| 142 | 0x05BB...0x05BD, | |
| 143 | 0x05BF, | |
| 144 | 0x05C1...0x05C2, | |
| 145 | 0x05D0...0x05EA, | |
| 146 | 0x05F0...0x05F2, | |
| 147 | ||
| 148 | // Arabic | |
| 149 | 0x0621...0x063A, | |
| 150 | 0x0640...0x0652, | |
| 151 | 0x0670...0x06B7, | |
| 152 | 0x06BA...0x06BE, | |
| 153 | 0x06C0...0x06CE, | |
| 154 | 0x06D0...0x06DC, | |
| 155 | 0x06E5...0x06E8, | |
| 156 | 0x06EA...0x06ED, | |
| 157 | ||
| 158 | // Devanagari | |
| 159 | 0x0901...0x0903, | |
| 160 | 0x0905...0x0939, | |
| 161 | 0x093E...0x094D, | |
| 162 | 0x0950...0x0952, | |
| 163 | 0x0958...0x0963, | |
| 164 | ||
| 165 | // Bengali | |
| 166 | 0x0981...0x0983, | |
| 167 | 0x0985...0x098C, | |
| 168 | 0x098F...0x0990, | |
| 169 | 0x0993...0x09A8, | |
| 170 | 0x09AA...0x09B0, | |
| 171 | 0x09B2, | |
| 172 | 0x09B6...0x09B9, | |
| 173 | 0x09BE...0x09C4, | |
| 174 | 0x09C7...0x09C8, | |
| 175 | 0x09CB...0x09CD, | |
| 176 | 0x09DC...0x09DD, | |
| 177 | 0x09DF...0x09E3, | |
| 178 | 0x09F0...0x09F1, | |
| 179 | ||
| 180 | // Gurmukhi | |
| 181 | 0x0A02, | |
| 182 | 0x0A05...0x0A0A, | |
| 183 | 0x0A0F...0x0A10, | |
| 184 | 0x0A13...0x0A28, | |
| 185 | 0x0A2A...0x0A30, | |
| 186 | 0x0A32...0x0A33, | |
| 187 | 0x0A35...0x0A36, | |
| 188 | 0x0A38...0x0A39, | |
| 189 | 0x0A3E...0x0A42, | |
| 190 | 0x0A47...0x0A48, | |
| 191 | 0x0A4B...0x0A4D, | |
| 192 | 0x0A59...0x0A5C, | |
| 193 | 0x0A5E, | |
| 194 | 0x0A74, | |
| 195 | ||
| 196 | // Gujarati | |
| 197 | 0x0A81...0x0A83, | |
| 198 | 0x0A85...0x0A8B, | |
| 199 | 0x0A8D, | |
| 200 | 0x0A8F...0x0A91, | |
| 201 | 0x0A93...0x0AA8, | |
| 202 | 0x0AAA...0x0AB0, | |
| 203 | 0x0AB2...0x0AB3, | |
| 204 | 0x0AB5...0x0AB9, | |
| 205 | 0x0ABD...0x0AC5, | |
| 206 | 0x0AC7...0x0AC9, | |
| 207 | 0x0ACB...0x0ACD, | |
| 208 | 0x0AD0, | |
| 209 | 0x0AE0, | |
| 210 | ||
| 211 | // Oriya | |
| 212 | 0x0B01...0x0B03, | |
| 213 | 0x0B05...0x0B0C, | |
| 214 | 0x0B0F...0x0B10, | |
| 215 | 0x0B13...0x0B28, | |
| 216 | 0x0B2A...0x0B30, | |
| 217 | 0x0B32...0x0B33, | |
| 218 | 0x0B36...0x0B39, | |
| 219 | 0x0B3E...0x0B43, | |
| 220 | 0x0B47...0x0B48, | |
| 221 | 0x0B4B...0x0B4D, | |
| 222 | 0x0B5C...0x0B5D, | |
| 223 | 0x0B5F...0x0B61, | |
| 224 | ||
| 225 | // Tamil | |
| 226 | 0x0B82...0x0B83, | |
| 227 | 0x0B85...0x0B8A, | |
| 228 | 0x0B8E...0x0B90, | |
| 229 | 0x0B92...0x0B95, | |
| 230 | 0x0B99...0x0B9A, | |
| 231 | 0x0B9C, | |
| 232 | 0x0B9E...0x0B9F, | |
| 233 | 0x0BA3...0x0BA4, | |
| 234 | 0x0BA8...0x0BAA, | |
| 235 | 0x0BAE...0x0BB5, | |
| 236 | 0x0BB7...0x0BB9, | |
| 237 | 0x0BBE...0x0BC2, | |
| 238 | 0x0BC6...0x0BC8, | |
| 239 | 0x0BCA...0x0BCD, | |
| 240 | ||
| 241 | // Telugu | |
| 242 | 0x0C01...0x0C03, | |
| 243 | 0x0C05...0x0C0C, | |
| 244 | 0x0C0E...0x0C10, | |
| 245 | 0x0C12...0x0C28, | |
| 246 | 0x0C2A...0x0C33, | |
| 247 | 0x0C35...0x0C39, | |
| 248 | 0x0C3E...0x0C44, | |
| 249 | 0x0C46...0x0C48, | |
| 250 | 0x0C4A...0x0C4D, | |
| 251 | 0x0C60...0x0C61, | |
| 252 | ||
| 253 | // Kannada | |
| 254 | 0x0C82...0x0C83, | |
| 255 | 0x0C85...0x0C8C, | |
| 256 | 0x0C8E...0x0C90, | |
| 257 | 0x0C92...0x0CA8, | |
| 258 | 0x0CAA...0x0CB3, | |
| 259 | 0x0CB5...0x0CB9, | |
| 260 | 0x0CBE...0x0CC4, | |
| 261 | 0x0CC6...0x0CC8, | |
| 262 | 0x0CCA...0x0CCD, | |
| 263 | 0x0CDE, | |
| 264 | 0x0CE0...0x0CE1, | |
| 265 | ||
| 266 | // Malayalam | |
| 267 | 0x0D02...0x0D03, | |
| 268 | 0x0D05...0x0D0C, | |
| 269 | 0x0D0E...0x0D10, | |
| 270 | 0x0D12...0x0D28, | |
| 271 | 0x0D2A...0x0D39, | |
| 272 | 0x0D3E...0x0D43, | |
| 273 | 0x0D46...0x0D48, | |
| 274 | 0x0D4A...0x0D4D, | |
| 275 | 0x0D60...0x0D61, | |
| 276 | ||
| 277 | // Thai (excluding digits 0x0E50...0x0E59; originally 0x0E01...0x0E3A and 0x0E40...0x0E5B | |
| 278 | 0x0E01...0x0E3A, | |
| 279 | 0x0E40...0x0E4F, | |
| 280 | 0x0E5A...0x0E5B, | |
| 281 | ||
| 282 | // Lao | |
| 283 | 0x0E81...0x0E82, | |
| 284 | 0x0E84, | |
| 285 | 0x0E87...0x0E88, | |
| 286 | 0x0E8A, | |
| 287 | 0x0E8D, | |
| 288 | 0x0E94...0x0E97, | |
| 289 | 0x0E99...0x0E9F, | |
| 290 | 0x0EA1...0x0EA3, | |
| 291 | 0x0EA5, | |
| 292 | 0x0EA7, | |
| 293 | 0x0EAA...0x0EAB, | |
| 294 | 0x0EAD...0x0EAE, | |
| 295 | 0x0EB0...0x0EB9, | |
| 296 | 0x0EBB...0x0EBD, | |
| 297 | 0x0EC0...0x0EC4, | |
| 298 | 0x0EC6, | |
| 299 | 0x0EC8...0x0ECD, | |
| 300 | 0x0EDC...0x0EDD, | |
| 301 | ||
| 302 | // Tibetan | |
| 303 | 0x0F00, | |
| 304 | 0x0F18...0x0F19, | |
| 305 | 0x0F35, | |
| 306 | 0x0F37, | |
| 307 | 0x0F39, | |
| 308 | 0x0F3E...0x0F47, | |
| 309 | 0x0F49...0x0F69, | |
| 310 | 0x0F71...0x0F84, | |
| 311 | 0x0F86...0x0F8B, | |
| 312 | 0x0F90...0x0F95, | |
| 313 | 0x0F97, | |
| 314 | 0x0F99...0x0FAD, | |
| 315 | 0x0FB1...0x0FB7, | |
| 316 | 0x0FB9, | |
| 317 | ||
| 318 | // Georgian | |
| 319 | 0x10A0...0x10C5, | |
| 320 | 0x10D0...0x10F6, | |
| 321 | ||
| 322 | // Hiragana | |
| 323 | 0x3041...0x3093, | |
| 324 | 0x309B...0x309C, | |
| 325 | ||
| 326 | // Katakana | |
| 327 | 0x30A1...0x30F6, | |
| 328 | 0x30FB...0x30FC, | |
| 329 | ||
| 330 | // Bopomofo | |
| 331 | 0x3105...0x312C, | |
| 332 | ||
| 333 | // CJK Unified Ideographs | |
| 334 | 0x4E00...0x9FA5, | |
| 335 | ||
| 336 | // Hangul | |
| 337 | 0xAC00...0xD7A3, | |
| 338 | ||
| 339 | // Digits | |
| 340 | 0x0660...0x0669, | |
| 341 | 0x06F0...0x06F9, | |
| 342 | 0x0966...0x096F, | |
| 343 | 0x09E6...0x09EF, | |
| 344 | 0x0A66...0x0A6F, | |
| 345 | 0x0AE6...0x0AEF, | |
| 346 | 0x0B66...0x0B6F, | |
| 347 | 0x0BE7...0x0BEF, | |
| 348 | 0x0C66...0x0C6F, | |
| 349 | 0x0CE6...0x0CEF, | |
| 350 | 0x0D66...0x0D6F, | |
| 351 | 0x0E50...0x0E59, | |
| 352 | 0x0ED0...0x0ED9, | |
| 353 | 0x0F20...0x0F33, | |
| 354 | ||
| 355 | // Special characters | |
| 356 | 0x00B5, | |
| 357 | 0x00B7, | |
| 358 | 0x02B0...0x02B8, | |
| 359 | 0x02BB, | |
| 360 | 0x02BD...0x02C1, | |
| 361 | 0x02D0...0x02D1, | |
| 362 | 0x02E0...0x02E4, | |
| 363 | 0x037A, | |
| 364 | 0x0559, | |
| 365 | 0x093D, | |
| 366 | 0x0B3D, | |
| 367 | 0x1FBE, | |
| 368 | 0x203F...0x2040, | |
| 369 | 0x2102, | |
| 370 | 0x2107, | |
| 371 | 0x210A...0x2113, | |
| 372 | 0x2115, | |
| 373 | 0x2118...0x211D, | |
| 374 | 0x2124, | |
| 375 | 0x2126, | |
| 376 | 0x2128, | |
| 377 | 0x212A...0x2131, | |
| 378 | 0x2133...0x2138, | |
| 379 | 0x2160...0x2182, | |
| 380 | 0x3005...0x3007, | |
| 381 | 0x3021...0x3029, | |
| 382 | => true, | |
| 383 | else => false, | |
| 384 | }; | |
| 385 | } | |
| 386 | ||
| 387 | /// C11 standard Annex D | |
| 388 | pub fn isC11DisallowedInitialIdChar(codepoint: u21) bool { | |
| 389 | assert(codepoint > 0x7F); | |
| 390 | return switch (codepoint) { | |
| 391 | 0x0300...0x036F, | |
| 392 | 0x1DC0...0x1DFF, | |
| 393 | 0x20D0...0x20FF, | |
| 394 | 0xFE20...0xFE2F, | |
| 395 | => true, | |
| 396 | else => false, | |
| 397 | }; | |
| 398 | } | |
| 399 | ||
| 400 | /// These are "digit" characters; C99 disallows them as the first | |
| 401 | /// character of an identifier | |
| 402 | pub fn isC99DisallowedInitialIDChar(codepoint: u21) bool { | |
| 403 | assert(codepoint > 0x7F); | |
| 404 | return switch (codepoint) { | |
| 405 | 0x0660...0x0669, | |
| 406 | 0x06F0...0x06F9, | |
| 407 | 0x0966...0x096F, | |
| 408 | 0x09E6...0x09EF, | |
| 409 | 0x0A66...0x0A6F, | |
| 410 | 0x0AE6...0x0AEF, | |
| 411 | 0x0B66...0x0B6F, | |
| 412 | 0x0BE7...0x0BEF, | |
| 413 | 0x0C66...0x0C6F, | |
| 414 | 0x0CE6...0x0CEF, | |
| 415 | 0x0D66...0x0D6F, | |
| 416 | 0x0E50...0x0E59, | |
| 417 | 0x0ED0...0x0ED9, | |
| 418 | 0x0F20...0x0F33, | |
| 419 | => true, | |
| 420 | else => false, | |
| 421 | }; | |
| 422 | } | |
| 423 | ||
| 424 | pub fn isInvisible(codepoint: u21) bool { | |
| 425 | assert(codepoint > 0x7F); | |
| 426 | return switch (codepoint) { | |
| 427 | 0x00ad, // SOFT HYPHEN | |
| 428 | 0x200b, // ZERO WIDTH SPACE | |
| 429 | 0x200c, // ZERO WIDTH NON-JOINER | |
| 430 | 0x200d, // ZERO WIDTH JOINER | |
| 431 | 0x2060, // WORD JOINER | |
| 432 | 0x2061, // FUNCTION APPLICATION | |
| 433 | 0x2062, // INVISIBLE TIMES | |
| 434 | 0x2063, // INVISIBLE SEPARATOR | |
| 435 | 0x2064, // INVISIBLE PLUS | |
| 436 | 0xfeff, // ZERO WIDTH NO-BREAK SPACE | |
| 437 | => true, | |
| 438 | else => false, | |
| 439 | }; | |
| 440 | } | |
| 441 | ||
| 442 | /// Checks for identifier characters which resemble non-identifier characters | |
| 443 | pub fn homoglyph(codepoint: u21) ?u21 { | |
| 444 | assert(codepoint > 0x7F); | |
| 445 | return switch (codepoint) { | |
| 446 | 0x01c3 => '!', // LATIN LETTER RETROFLEX CLICK | |
| 447 | 0x037e => ';', // GREEK QUESTION MARK | |
| 448 | 0x2212 => '-', // MINUS SIGN | |
| 449 | 0x2215 => '/', // DIVISION SLASH | |
| 450 | 0x2216 => '\\', // SET MINUS | |
| 451 | 0x2217 => '*', // ASTERISK OPERATOR | |
| 452 | 0x2223 => '|', // DIVIDES | |
| 453 | 0x2227 => '^', // LOGICAL AND | |
| 454 | 0x2236 => ':', // RATIO | |
| 455 | 0x223c => '~', // TILDE OPERATOR | |
| 456 | 0xa789 => ':', // MODIFIER LETTER COLON | |
| 457 | 0xff01 => '!', // FULLWIDTH EXCLAMATION MARK | |
| 458 | 0xff03 => '#', // FULLWIDTH NUMBER SIGN | |
| 459 | 0xff04 => '$', // FULLWIDTH DOLLAR SIGN | |
| 460 | 0xff05 => '%', // FULLWIDTH PERCENT SIGN | |
| 461 | 0xff06 => '&', // FULLWIDTH AMPERSAND | |
| 462 | 0xff08 => '(', // FULLWIDTH LEFT PARENTHESIS | |
| 463 | 0xff09 => ')', // FULLWIDTH RIGHT PARENTHESIS | |
| 464 | 0xff0a => '*', // FULLWIDTH ASTERISK | |
| 465 | 0xff0b => '+', // FULLWIDTH ASTERISK | |
| 466 | 0xff0c => ',', // FULLWIDTH COMMA | |
| 467 | 0xff0d => '-', // FULLWIDTH HYPHEN-MINUS | |
| 468 | 0xff0e => '.', // FULLWIDTH FULL STOP | |
| 469 | 0xff0f => '/', // FULLWIDTH SOLIDUS | |
| 470 | 0xff1a => ':', // FULLWIDTH COLON | |
| 471 | 0xff1b => ';', // FULLWIDTH SEMICOLON | |
| 472 | 0xff1c => '<', // FULLWIDTH LESS-THAN SIGN | |
| 473 | 0xff1d => '=', // FULLWIDTH EQUALS SIGN | |
| 474 | 0xff1e => '>', // FULLWIDTH GREATER-THAN SIGN | |
| 475 | 0xff1f => '?', // FULLWIDTH QUESTION MARK | |
| 476 | 0xff20 => '@', // FULLWIDTH COMMERCIAL AT | |
| 477 | 0xff3b => '[', // FULLWIDTH LEFT SQUARE BRACKET | |
| 478 | 0xff3c => '\\', // FULLWIDTH REVERSE SOLIDUS | |
| 479 | 0xff3d => ']', // FULLWIDTH RIGHT SQUARE BRACKET | |
| 480 | 0xff3e => '^', // FULLWIDTH CIRCUMFLEX ACCENT | |
| 481 | 0xff5b => '{', // FULLWIDTH LEFT CURLY BRACKET | |
| 482 | 0xff5c => '|', // FULLWIDTH VERTICAL LINE | |
| 483 | 0xff5d => '}', // FULLWIDTH RIGHT CURLY BRACKET | |
| 484 | 0xff5e => '~', // FULLWIDTH TILDE | |
| 485 | else => null, | |
| 486 | }; | |
| 487 | } |
src/aro/Codegen.zig created+768| ... | ... | @@ -0,0 +1,768 @@ |
| 1 | aro_comp: *aro.Compilation, | |
| 2 | tree: aro.Tree, | |
| 3 | bin_file: *link.File, | |
| 4 | arena: Allocator, | |
| 5 | gpa: Allocator, | |
| 6 | verbose_air: bool, | |
| 7 | ||
| 8 | const builtin = @import("builtin"); | |
| 9 | const std = @import("std"); | |
| 10 | const Allocator = std.mem.Allocator; | |
| 11 | const log = std.log.scoped(.aro); | |
| 12 | ||
| 13 | const Codegen = @This(); | |
| 14 | const aro = @import("lib.zig"); | |
| 15 | const link = @import("../link.zig"); | |
| 16 | const NodeIndex = aro.Tree.NodeIndex; | |
| 17 | const Value = @import("../value.zig").Value; | |
| 18 | const Type = @import("../type.zig").Type; | |
| 19 | const TypedValue = @import("../TypedValue.zig"); | |
| 20 | const Air = @import("../Air.zig"); | |
| 21 | const Compilation = @import("../Compilation.zig"); | |
| 22 | const Module = @import("../Module.zig"); | |
| 23 | const Liveness = @import("../Liveness.zig"); | |
| 24 | ||
| 25 | pub fn generateTree(comp: *Compilation, aro_comp: *aro.Compilation, tree: aro.Tree, arena: Allocator) !void { | |
| 26 | var c = Codegen{ | |
| 27 | .bin_file = comp.bin_file, | |
| 28 | .aro_comp = aro_comp, | |
| 29 | .tree = tree, | |
| 30 | .arena = arena, | |
| 31 | .gpa = comp.gpa, | |
| 32 | .verbose_air = comp.verbose_air, | |
| 33 | }; | |
| 34 | ||
| 35 | const node_tags = tree.nodes.items(.tag); | |
| 36 | const node_datas = tree.nodes.items(.data); | |
| 37 | for (tree.root_decls) |decl| { | |
| 38 | switch (node_tags[@enumToInt(decl)]) { | |
| 39 | // these produce no code | |
| 40 | .static_assert, | |
| 41 | .typedef, | |
| 42 | .struct_decl_two, | |
| 43 | .union_decl_two, | |
| 44 | .enum_decl_two, | |
| 45 | .struct_decl, | |
| 46 | .union_decl, | |
| 47 | .enum_decl, | |
| 48 | => {}, | |
| 49 | ||
| 50 | // define symbol | |
| 51 | .fn_proto, | |
| 52 | .static_fn_proto, | |
| 53 | .inline_fn_proto, | |
| 54 | .inline_static_fn_proto, | |
| 55 | .extern_var, | |
| 56 | .threadlocal_extern_var, | |
| 57 | => { | |
| 58 | const name = c.tree.tokSlice(node_datas[@enumToInt(decl)].decl.name); | |
| 59 | log.debug("ignoring the opportunity to define a symbol named {s}", .{name}); | |
| 60 | //_ = try c.obj.declareSymbol(.@"undefined", name, .Strong, .external, 0, 0); | |
| 61 | }, | |
| 62 | ||
| 63 | // function definition | |
| 64 | .fn_def, | |
| 65 | .static_fn_def, | |
| 66 | .inline_fn_def, | |
| 67 | .inline_static_fn_def, | |
| 68 | => try c.genFn(decl), | |
| 69 | ||
| 70 | .@"var", | |
| 71 | .static_var, | |
| 72 | .threadlocal_var, | |
| 73 | .threadlocal_static_var, | |
| 74 | => try c.genVar(decl), | |
| 75 | ||
| 76 | else => unreachable, | |
| 77 | } | |
| 78 | } | |
| 79 | } | |
| 80 | ||
| 81 | const Func = struct { | |
| 82 | codegen: *Codegen, | |
| 83 | name: []const u8, | |
| 84 | ||
| 85 | air_instructions: std.MultiArrayList(Air.Inst) = .{}, | |
| 86 | air_extra: std.ArrayListUnmanaged(u32) = .{}, | |
| 87 | air_values: std.ArrayListUnmanaged(Value) = .{}, | |
| 88 | ||
| 89 | fn deinit(func: *Func) void { | |
| 90 | const gpa = func.codegen.gpa; | |
| 91 | func.air_instructions.deinit(gpa); | |
| 92 | func.air_extra.deinit(gpa); | |
| 93 | func.air_values.deinit(gpa); | |
| 94 | func.* = undefined; | |
| 95 | } | |
| 96 | ||
| 97 | /// Reminder to refactor this out with the equivalent Sema function. | |
| 98 | fn addConstant(func: *Func, ty: Type, val: Value) !Air.Inst.Ref { | |
| 99 | const gpa = func.codegen.gpa; | |
| 100 | const ty_inst = try func.addType(ty); | |
| 101 | try func.air_values.append(gpa, val); | |
| 102 | try func.air_instructions.append(gpa, .{ | |
| 103 | .tag = .constant, | |
| 104 | .data = .{ .ty_pl = .{ | |
| 105 | .ty = ty_inst, | |
| 106 | .payload = @intCast(u32, func.air_values.items.len - 1), | |
| 107 | } }, | |
| 108 | }); | |
| 109 | return Air.indexToRef(@intCast(u32, func.air_instructions.len - 1)); | |
| 110 | } | |
| 111 | ||
| 112 | /// Reminder to refactor this out with the equivalent Sema function. | |
| 113 | fn addType(func: *Func, ty: Type) !Air.Inst.Ref { | |
| 114 | switch (ty.tag()) { | |
| 115 | .u1 => return .u1_type, | |
| 116 | .u8 => return .u8_type, | |
| 117 | .i8 => return .i8_type, | |
| 118 | .u16 => return .u16_type, | |
| 119 | .i16 => return .i16_type, | |
| 120 | .u32 => return .u32_type, | |
| 121 | .i32 => return .i32_type, | |
| 122 | .u64 => return .u64_type, | |
| 123 | .i64 => return .i64_type, | |
| 124 | .u128 => return .u128_type, | |
| 125 | .i128 => return .i128_type, | |
| 126 | .usize => return .usize_type, | |
| 127 | .isize => return .isize_type, | |
| 128 | .c_short => return .c_short_type, | |
| 129 | .c_ushort => return .c_ushort_type, | |
| 130 | .c_int => return .c_int_type, | |
| 131 | .c_uint => return .c_uint_type, | |
| 132 | .c_long => return .c_long_type, | |
| 133 | .c_ulong => return .c_ulong_type, | |
| 134 | .c_longlong => return .c_longlong_type, | |
| 135 | .c_ulonglong => return .c_ulonglong_type, | |
| 136 | .c_longdouble => return .c_longdouble_type, | |
| 137 | .f16 => return .f16_type, | |
| 138 | .f32 => return .f32_type, | |
| 139 | .f64 => return .f64_type, | |
| 140 | .f80 => return .f80_type, | |
| 141 | .f128 => return .f128_type, | |
| 142 | .anyopaque => return .anyopaque_type, | |
| 143 | .bool => return .bool_type, | |
| 144 | .void => return .void_type, | |
| 145 | .type => return .type_type, | |
| 146 | .anyerror => return .anyerror_type, | |
| 147 | .comptime_int => return .comptime_int_type, | |
| 148 | .comptime_float => return .comptime_float_type, | |
| 149 | .noreturn => return .noreturn_type, | |
| 150 | .@"anyframe" => return .anyframe_type, | |
| 151 | .@"null" => return .null_type, | |
| 152 | .@"undefined" => return .undefined_type, | |
| 153 | .enum_literal => return .enum_literal_type, | |
| 154 | .atomic_order => return .atomic_order_type, | |
| 155 | .atomic_rmw_op => return .atomic_rmw_op_type, | |
| 156 | .calling_convention => return .calling_convention_type, | |
| 157 | .address_space => return .address_space_type, | |
| 158 | .float_mode => return .float_mode_type, | |
| 159 | .reduce_op => return .reduce_op_type, | |
| 160 | .call_options => return .call_options_type, | |
| 161 | .prefetch_options => return .prefetch_options_type, | |
| 162 | .export_options => return .export_options_type, | |
| 163 | .extern_options => return .extern_options_type, | |
| 164 | .type_info => return .type_info_type, | |
| 165 | .manyptr_u8 => return .manyptr_u8_type, | |
| 166 | .manyptr_const_u8 => return .manyptr_const_u8_type, | |
| 167 | .fn_noreturn_no_args => return .fn_noreturn_no_args_type, | |
| 168 | .fn_void_no_args => return .fn_void_no_args_type, | |
| 169 | .fn_naked_noreturn_no_args => return .fn_naked_noreturn_no_args_type, | |
| 170 | .fn_ccc_void_no_args => return .fn_ccc_void_no_args_type, | |
| 171 | .single_const_pointer_to_comptime_int => return .single_const_pointer_to_comptime_int_type, | |
| 172 | .const_slice_u8 => return .const_slice_u8_type, | |
| 173 | .anyerror_void_error_union => return .anyerror_void_error_union_type, | |
| 174 | .generic_poison => return .generic_poison_type, | |
| 175 | else => {}, | |
| 176 | } | |
| 177 | try func.air_instructions.append(func.codegen.gpa, .{ | |
| 178 | .tag = .const_ty, | |
| 179 | .data = .{ .ty = ty }, | |
| 180 | }); | |
| 181 | return Air.indexToRef(@intCast(u32, func.air_instructions.len - 1)); | |
| 182 | } | |
| 183 | ||
| 184 | fn getTmpAir(func: Func) Air { | |
| 185 | return .{ | |
| 186 | .instructions = func.air_instructions.slice(), | |
| 187 | .extra = func.air_extra.items, | |
| 188 | .values = func.air_values.items, | |
| 189 | }; | |
| 190 | } | |
| 191 | ||
| 192 | fn addExtra(func: *Func, extra: anytype) Allocator.Error!u32 { | |
| 193 | const fields = std.meta.fields(@TypeOf(extra)); | |
| 194 | try func.air_extra.ensureUnusedCapacity(func.gpa, fields.len); | |
| 195 | return addExtraAssumeCapacity(func, extra); | |
| 196 | } | |
| 197 | ||
| 198 | fn addExtraAssumeCapacity(func: *Func, extra: anytype) u32 { | |
| 199 | const fields = std.meta.fields(@TypeOf(extra)); | |
| 200 | const result = @intCast(u32, func.air_extra.items.len); | |
| 201 | inline for (fields) |field| { | |
| 202 | func.air_extra.appendAssumeCapacity(switch (field.field_type) { | |
| 203 | u32 => @field(extra, field.name), | |
| 204 | Air.Inst.Ref => @enumToInt(@field(extra, field.name)), | |
| 205 | i32 => @bitCast(u32, @field(extra, field.name)), | |
| 206 | else => @compileError("bad field type"), | |
| 207 | }); | |
| 208 | } | |
| 209 | return result; | |
| 210 | } | |
| 211 | ||
| 212 | fn appendRefsAssumeCapacity(func: *Func, refs: []const Air.Inst.Ref) void { | |
| 213 | const coerced = @bitCast([]const u32, refs); | |
| 214 | func.air_extra.appendSliceAssumeCapacity(coerced); | |
| 215 | } | |
| 216 | }; | |
| 217 | ||
| 218 | fn genFn(c: *Codegen, decl_node: NodeIndex) !void { | |
| 219 | const node_datas = c.tree.nodes.items(.data); | |
| 220 | const node_data = node_datas[@enumToInt(decl_node)].decl; | |
| 221 | const name = c.tree.tokSlice(node_data.name); | |
| 222 | log.debug("genFn {s}", .{name}); | |
| 223 | const body_node = node_data.node; | |
| 224 | ||
| 225 | var func: Func = .{ | |
| 226 | .codegen = c, | |
| 227 | .name = name, | |
| 228 | }; | |
| 229 | defer func.deinit(); | |
| 230 | ||
| 231 | // First few indexes of extra are reserved and set at the end. | |
| 232 | const reserved_count = @typeInfo(Air.ExtraIndex).Enum.fields.len; | |
| 233 | try func.air_extra.ensureTotalCapacity(c.gpa, reserved_count); | |
| 234 | func.air_extra.items.len += reserved_count; | |
| 235 | ||
| 236 | var block: Block = .{ | |
| 237 | .func = &func, | |
| 238 | .instructions = .{}, | |
| 239 | }; | |
| 240 | defer block.instructions.deinit(c.gpa); | |
| 241 | ||
| 242 | _ = try genNode(&func, &block, body_node); | |
| 243 | ||
| 244 | try func.air_extra.ensureUnusedCapacity(c.gpa, @typeInfo(Air.Block).Struct.fields.len + | |
| 245 | block.instructions.items.len); | |
| 246 | const main_block_index = func.addExtraAssumeCapacity(Air.Block{ | |
| 247 | .body_len = @intCast(u32, block.instructions.items.len), | |
| 248 | }); | |
| 249 | func.air_extra.appendSliceAssumeCapacity(block.instructions.items); | |
| 250 | func.air_extra.items[@enumToInt(Air.ExtraIndex.main_block)] = main_block_index; | |
| 251 | ||
| 252 | var air = func.getTmpAir(); | |
| 253 | ||
| 254 | var liveness = try Liveness.analyze(c.gpa, air, undefined); | |
| 255 | defer liveness.deinit(c.gpa); | |
| 256 | ||
| 257 | if (builtin.mode == .Debug and c.verbose_air) { | |
| 258 | std.debug.print("# Begin Function AIR: {s}:\n", .{name}); | |
| 259 | @import("../print_air.zig").dump(c.gpa, air, undefined, liveness); | |
| 260 | std.debug.print("# End Function AIR: {s}\n\n", .{name}); | |
| 261 | } | |
| 262 | ||
| 263 | @panic("TODO make a Decl and Fn"); | |
| 264 | //c.bin_file.updateFunc(module, module_fn, air, liveness) catch |err| switch (err) { | |
| 265 | // error.OutOfMemory => return error.OutOfMemory, | |
| 266 | // error.AnalysisFail => { | |
| 267 | // decl.analysis = .codegen_failure; | |
| 268 | // return; | |
| 269 | // }, | |
| 270 | // else => { | |
| 271 | // try module.failed_decls.ensureUnusedCapacity(gpa, 1); | |
| 272 | // module.failed_decls.putAssumeCapacityNoClobber(decl, try Module.ErrorMsg.create( | |
| 273 | // gpa, | |
| 274 | // decl.srcLoc(), | |
| 275 | // "unable to codegen: {s}", | |
| 276 | // .{@errorName(err)}, | |
| 277 | // )); | |
| 278 | // decl.analysis = .codegen_failure_retryable; | |
| 279 | // return; | |
| 280 | // }, | |
| 281 | //}; | |
| 282 | } | |
| 283 | ||
| 284 | fn lowerType(c: *Codegen, aro_ty: aro.Type) Allocator.Error!Type { | |
| 285 | _ = c; | |
| 286 | switch (aro_ty.specifier) { | |
| 287 | .void => return Type.void, | |
| 288 | .bool => return Type.bool, | |
| 289 | .char, .schar => return Type.initTag(.i8), | |
| 290 | .uchar => return Type.initTag(.u8), | |
| 291 | .short => return Type.initTag(.c_short), | |
| 292 | .ushort => return Type.initTag(.c_ushort), | |
| 293 | .int => return Type.initTag(.c_int), | |
| 294 | .uint => return Type.initTag(.c_uint), | |
| 295 | .long => return Type.initTag(.c_long), | |
| 296 | .ulong => return Type.initTag(.c_ulong), | |
| 297 | .long_long => return Type.initTag(.c_longlong), | |
| 298 | .ulong_long => return Type.initTag(.c_ulonglong), | |
| 299 | ||
| 300 | .float => return Type.initTag(.f32), | |
| 301 | .double => return Type.initTag(.f64), | |
| 302 | .long_double => return Type.initTag(.c_longdouble), | |
| 303 | ||
| 304 | .complex_float, | |
| 305 | .complex_double, | |
| 306 | .complex_long_double, | |
| 307 | ||
| 308 | // data.sub_type | |
| 309 | .pointer, | |
| 310 | .unspecified_variable_len_array, | |
| 311 | .decayed_unspecified_variable_len_array, | |
| 312 | // data.func | |
| 313 | // int foo(int bar, char baz) and int (void) | |
| 314 | .func, | |
| 315 | // int foo(int bar, char baz, ...) | |
| 316 | .var_args_func, | |
| 317 | // int foo(bar, baz) and int foo() | |
| 318 | // is also var args, but we can give warnings about incorrect amounts of parameters | |
| 319 | .old_style_func, | |
| 320 | ||
| 321 | // data.array | |
| 322 | .array, | |
| 323 | .decayed_array, | |
| 324 | .static_array, | |
| 325 | .decayed_static_array, | |
| 326 | .incomplete_array, | |
| 327 | .decayed_incomplete_array, | |
| 328 | // data.expr | |
| 329 | .variable_len_array, | |
| 330 | .decayed_variable_len_array, | |
| 331 | ||
| 332 | // data.record | |
| 333 | .@"struct", | |
| 334 | .@"union", | |
| 335 | ||
| 336 | // data.enum | |
| 337 | .@"enum", | |
| 338 | ||
| 339 | // typeof(type-name) | |
| 340 | .typeof_type, | |
| 341 | // decayed array created with typeof(type-name) | |
| 342 | .decayed_typeof_type, | |
| 343 | ||
| 344 | // typeof(expression) | |
| 345 | .typeof_expr, | |
| 346 | // decayed array created with typeof(expression) | |
| 347 | .decayed_typeof_expr, | |
| 348 | ||
| 349 | // data.attributed | |
| 350 | .attributed, | |
| 351 | ||
| 352 | // special type used to implement __builtin_va_start | |
| 353 | .special_va_start, | |
| 354 | => std.debug.panic("TODO handle {s}", .{@tagName(aro_ty.specifier)}), | |
| 355 | } | |
| 356 | } | |
| 357 | ||
| 358 | fn lowerValue(c: *Codegen, aro_ty: aro.Type, aro_val: aro.Value) Allocator.Error!TypedValue { | |
| 359 | const zig_ty = try c.lowerType(aro_ty); | |
| 360 | switch (aro_val.tag) { | |
| 361 | .unavailable => unreachable, | |
| 362 | .int => { | |
| 363 | const is_signed = zig_ty.isSignedInt(); | |
| 364 | if (is_signed) @panic("TODO"); | |
| 365 | return TypedValue{ | |
| 366 | .ty = zig_ty, | |
| 367 | .val = try Value.Tag.int_u64.create(c.arena, aro_val.data.int), | |
| 368 | }; | |
| 369 | }, | |
| 370 | .float => @panic("TODO"), | |
| 371 | .array => @panic("TODO"), | |
| 372 | .bytes => @panic("TODO"), | |
| 373 | } | |
| 374 | } | |
| 375 | ||
| 376 | const Error = error{OutOfMemory}; | |
| 377 | ||
| 378 | fn genNode(func: *Func, block: *Block, node: NodeIndex) Error!Air.Inst.Ref { | |
| 379 | const tree = func.codegen.tree; | |
| 380 | const node_tys = tree.nodes.items(.ty); | |
| 381 | const node_datas = tree.nodes.items(.data); | |
| 382 | const node_tags = tree.nodes.items(.tag); | |
| 383 | ||
| 384 | if (tree.value_map.get(node)) |some| { | |
| 385 | if (some.tag == .int) { | |
| 386 | const zig_tv = try func.codegen.lowerValue(node_tys[@enumToInt(node)], some); | |
| 387 | return func.addConstant(zig_tv.ty, zig_tv.val); | |
| 388 | } | |
| 389 | } | |
| 390 | ||
| 391 | const data = node_datas[@enumToInt(node)]; | |
| 392 | switch (node_tags[@enumToInt(node)]) { | |
| 393 | .static_assert => return Air.Inst.Ref.void_value, | |
| 394 | .compound_stmt_two => { | |
| 395 | if (data.bin.lhs != .none) _ = try genNode(func, block, data.bin.lhs); | |
| 396 | if (data.bin.rhs != .none) _ = try genNode(func, block, data.bin.rhs); | |
| 397 | return Air.Inst.Ref.void_value; | |
| 398 | }, | |
| 399 | .compound_stmt => { | |
| 400 | const body = tree.data[data.range.start..data.range.end]; | |
| 401 | for (body) |stmt| { | |
| 402 | _ = try genNode(func, block, stmt); | |
| 403 | } | |
| 404 | return Air.Inst.Ref.void_value; | |
| 405 | }, | |
| 406 | .call_expr_one => if (data.bin.rhs != .none) | |
| 407 | return genCall(func, block, data.bin.lhs, &.{data.bin.rhs}) | |
| 408 | else | |
| 409 | return genCall(func, block, data.bin.lhs, &.{}), | |
| 410 | .call_expr => return genCall(func, block, tree.data[data.range.start], tree.data[data.range.start + 1 .. data.range.end]), | |
| 411 | .function_to_pointer => return genNode(func, block, data.un), // no-op | |
| 412 | .array_to_pointer => { | |
| 413 | const operand = try genNode(func, block, data.un); | |
| 414 | const array_val = func.getTmpAir().value(operand).?; | |
| 415 | const tmp_bytes = array_val.castTag(.bytes).?.data; | |
| 416 | ||
| 417 | var anon_decl = try block.startAnonDecl(); | |
| 418 | defer anon_decl.deinit(); | |
| 419 | ||
| 420 | const bytes = try anon_decl.arena().dupeZ(u8, tmp_bytes); | |
| 421 | ||
| 422 | const new_decl = try anon_decl.finish( | |
| 423 | try Type.Tag.array_u8_sentinel_0.create(anon_decl.arena(), bytes.len), | |
| 424 | try Value.Tag.bytes.create(anon_decl.arena(), bytes[0 .. bytes.len + 1]), | |
| 425 | ); | |
| 426 | ||
| 427 | return func.addConstant( | |
| 428 | try Type.ptr(func.codegen.arena, .{ | |
| 429 | .pointee_type = new_decl.ty, | |
| 430 | .mutable = false, | |
| 431 | .@"addrspace" = new_decl.@"addrspace", | |
| 432 | }), | |
| 433 | try Value.Tag.decl_ref.create(func.codegen.arena, new_decl), | |
| 434 | ); | |
| 435 | }, | |
| 436 | .decl_ref_expr => { | |
| 437 | // TODO locals and arguments | |
| 438 | const name = tree.tokSlice(data.decl_ref); | |
| 439 | std.debug.panic("TODO decl_ref_expr {s}", .{name}); | |
| 440 | }, | |
| 441 | .return_stmt => { | |
| 442 | const operand = try genNode(func, block, data.un); | |
| 443 | _ = try block.addUnOp(.ret, operand); | |
| 444 | return Air.Inst.Ref.unreachable_value; | |
| 445 | }, | |
| 446 | .implicit_return => { | |
| 447 | _ = try block.addUnOp(.ret, .void_value); | |
| 448 | return Air.Inst.Ref.void_value; | |
| 449 | }, | |
| 450 | .int_literal => { | |
| 451 | const zig_ty = try func.codegen.lowerType(node_tys[@enumToInt(node)]); | |
| 452 | if (zig_ty.isSignedInt()) { | |
| 453 | @panic("TODO"); | |
| 454 | } | |
| 455 | const zig_val = try Value.Tag.int_u64.create(func.codegen.arena, data.int); | |
| 456 | return func.addConstant(zig_ty, zig_val); | |
| 457 | }, | |
| 458 | .string_literal_expr => { | |
| 459 | const ast_bytes = tree.value_map.get(node).?.data.bytes; | |
| 460 | const array_val = try Value.Tag.bytes.create(func.codegen.arena, ast_bytes); | |
| 461 | const array_ty = try Type.Tag.array_u8.create(func.codegen.arena, ast_bytes.len); | |
| 462 | return func.addConstant(array_ty, array_val); | |
| 463 | }, | |
| 464 | else => return std.debug.panic("TODO lower Aro AST tag {}\n", .{node_tags[@enumToInt(node)]}), | |
| 465 | } | |
| 466 | } | |
| 467 | ||
| 468 | fn genCall(func: *Func, block: *Block, lhs: NodeIndex, args: []const NodeIndex) Error!Air.Inst.Ref { | |
| 469 | const callee = try genNode(func, block, lhs); | |
| 470 | ||
| 471 | const air_args = try func.codegen.arena.alloc(Air.Inst.Ref, args.len); | |
| 472 | for (args) |arg_node, i| { | |
| 473 | air_args[i] = try genNode(func, block, arg_node); | |
| 474 | } | |
| 475 | ||
| 476 | try func.air_extra.ensureUnusedCapacity(func.codegen.gpa, @typeInfo(Air.Call).Struct.fields.len + args.len); | |
| 477 | ||
| 478 | const func_inst = try block.addInst(.{ | |
| 479 | .tag = .call, | |
| 480 | .data = .{ .pl_op = .{ | |
| 481 | .operand = callee, | |
| 482 | .payload = func.addExtraAssumeCapacity(Air.Call{ | |
| 483 | .args_len = @intCast(u32, args.len), | |
| 484 | }), | |
| 485 | } }, | |
| 486 | }); | |
| 487 | func.appendRefsAssumeCapacity(air_args); | |
| 488 | ||
| 489 | return func_inst; | |
| 490 | } | |
| 491 | ||
| 492 | fn genVar(c: *Codegen, decl: NodeIndex) !void { | |
| 493 | const node_datas = c.tree.nodes.items(.data); | |
| 494 | const name = c.tree.tokSlice(node_datas[@enumToInt(decl)].decl.name); | |
| 495 | log.debug("genVar {s}", .{name}); | |
| 496 | } | |
| 497 | ||
| 498 | pub const Block = struct { | |
| 499 | func: *Func, | |
| 500 | /// The AIR instructions generated for this block. | |
| 501 | instructions: std.ArrayListUnmanaged(Air.Inst.Index), | |
| 502 | ||
| 503 | pub fn addTy( | |
| 504 | block: *Block, | |
| 505 | tag: Air.Inst.Tag, | |
| 506 | ty: Type, | |
| 507 | ) error{OutOfMemory}!Air.Inst.Ref { | |
| 508 | return block.addInst(.{ | |
| 509 | .tag = tag, | |
| 510 | .data = .{ .ty = ty }, | |
| 511 | }); | |
| 512 | } | |
| 513 | ||
| 514 | pub fn addTyOp( | |
| 515 | block: *Block, | |
| 516 | tag: Air.Inst.Tag, | |
| 517 | ty: Type, | |
| 518 | operand: Air.Inst.Ref, | |
| 519 | ) error{OutOfMemory}!Air.Inst.Ref { | |
| 520 | return block.addInst(.{ | |
| 521 | .tag = tag, | |
| 522 | .data = .{ .ty_op = .{ | |
| 523 | .ty = try block.func.addType(ty), | |
| 524 | .operand = operand, | |
| 525 | } }, | |
| 526 | }); | |
| 527 | } | |
| 528 | ||
| 529 | pub fn addBitCast(block: *Block, ty: Type, operand: Air.Inst.Ref) Allocator.Error!Air.Inst.Ref { | |
| 530 | return block.addInst(.{ | |
| 531 | .tag = .bitcast, | |
| 532 | .data = .{ .ty_op = .{ | |
| 533 | .ty = try block.func.addType(ty), | |
| 534 | .operand = operand, | |
| 535 | } }, | |
| 536 | }); | |
| 537 | } | |
| 538 | ||
| 539 | pub fn addNoOp(block: *Block, tag: Air.Inst.Tag) error{OutOfMemory}!Air.Inst.Ref { | |
| 540 | return block.addInst(.{ | |
| 541 | .tag = tag, | |
| 542 | .data = .{ .no_op = {} }, | |
| 543 | }); | |
| 544 | } | |
| 545 | ||
| 546 | pub fn addUnOp( | |
| 547 | block: *Block, | |
| 548 | tag: Air.Inst.Tag, | |
| 549 | operand: Air.Inst.Ref, | |
| 550 | ) error{OutOfMemory}!Air.Inst.Ref { | |
| 551 | return block.addInst(.{ | |
| 552 | .tag = tag, | |
| 553 | .data = .{ .un_op = operand }, | |
| 554 | }); | |
| 555 | } | |
| 556 | ||
| 557 | pub fn addBr( | |
| 558 | block: *Block, | |
| 559 | target_block: Air.Inst.Index, | |
| 560 | operand: Air.Inst.Ref, | |
| 561 | ) error{OutOfMemory}!Air.Inst.Ref { | |
| 562 | return block.addInst(.{ | |
| 563 | .tag = .br, | |
| 564 | .data = .{ .br = .{ | |
| 565 | .block_inst = target_block, | |
| 566 | .operand = operand, | |
| 567 | } }, | |
| 568 | }); | |
| 569 | } | |
| 570 | ||
| 571 | fn addBinOp( | |
| 572 | block: *Block, | |
| 573 | tag: Air.Inst.Tag, | |
| 574 | lhs: Air.Inst.Ref, | |
| 575 | rhs: Air.Inst.Ref, | |
| 576 | ) error{OutOfMemory}!Air.Inst.Ref { | |
| 577 | return block.addInst(.{ | |
| 578 | .tag = tag, | |
| 579 | .data = .{ .bin_op = .{ | |
| 580 | .lhs = lhs, | |
| 581 | .rhs = rhs, | |
| 582 | } }, | |
| 583 | }); | |
| 584 | } | |
| 585 | ||
| 586 | fn addArg(block: *Block, ty: Type, name: u32) error{OutOfMemory}!Air.Inst.Ref { | |
| 587 | return block.addInst(.{ | |
| 588 | .tag = .arg, | |
| 589 | .data = .{ .ty_str = .{ | |
| 590 | .ty = try block.func.addType(ty), | |
| 591 | .str = name, | |
| 592 | } }, | |
| 593 | }); | |
| 594 | } | |
| 595 | ||
| 596 | fn addStructFieldPtr( | |
| 597 | block: *Block, | |
| 598 | struct_ptr: Air.Inst.Ref, | |
| 599 | field_index: u32, | |
| 600 | ptr_field_ty: Type, | |
| 601 | ) !Air.Inst.Ref { | |
| 602 | const ty = try block.func.addType(ptr_field_ty); | |
| 603 | const tag: Air.Inst.Tag = switch (field_index) { | |
| 604 | 0 => .struct_field_ptr_index_0, | |
| 605 | 1 => .struct_field_ptr_index_1, | |
| 606 | 2 => .struct_field_ptr_index_2, | |
| 607 | 3 => .struct_field_ptr_index_3, | |
| 608 | else => { | |
| 609 | return block.addInst(.{ | |
| 610 | .tag = .struct_field_ptr, | |
| 611 | .data = .{ .ty_pl = .{ | |
| 612 | .ty = ty, | |
| 613 | .payload = try block.func.addExtra(Air.StructField{ | |
| 614 | .struct_operand = struct_ptr, | |
| 615 | .field_index = field_index, | |
| 616 | }), | |
| 617 | } }, | |
| 618 | }); | |
| 619 | }, | |
| 620 | }; | |
| 621 | return block.addInst(.{ | |
| 622 | .tag = tag, | |
| 623 | .data = .{ .ty_op = .{ | |
| 624 | .ty = ty, | |
| 625 | .operand = struct_ptr, | |
| 626 | } }, | |
| 627 | }); | |
| 628 | } | |
| 629 | ||
| 630 | pub fn addStructFieldVal( | |
| 631 | block: *Block, | |
| 632 | struct_val: Air.Inst.Ref, | |
| 633 | field_index: u32, | |
| 634 | field_ty: Type, | |
| 635 | ) !Air.Inst.Ref { | |
| 636 | return block.addInst(.{ | |
| 637 | .tag = .struct_field_val, | |
| 638 | .data = .{ .ty_pl = .{ | |
| 639 | .ty = try block.func.addType(field_ty), | |
| 640 | .payload = try block.func.addExtra(Air.StructField{ | |
| 641 | .struct_operand = struct_val, | |
| 642 | .field_index = field_index, | |
| 643 | }), | |
| 644 | } }, | |
| 645 | }); | |
| 646 | } | |
| 647 | ||
| 648 | pub fn addSliceElemPtr( | |
| 649 | block: *Block, | |
| 650 | slice: Air.Inst.Ref, | |
| 651 | elem_index: Air.Inst.Ref, | |
| 652 | elem_ptr_ty: Type, | |
| 653 | ) !Air.Inst.Ref { | |
| 654 | return block.addInst(.{ | |
| 655 | .tag = .slice_elem_ptr, | |
| 656 | .data = .{ .ty_pl = .{ | |
| 657 | .ty = try block.func.addType(elem_ptr_ty), | |
| 658 | .payload = try block.func.addExtra(Air.Bin{ | |
| 659 | .lhs = slice, | |
| 660 | .rhs = elem_index, | |
| 661 | }), | |
| 662 | } }, | |
| 663 | }); | |
| 664 | } | |
| 665 | ||
| 666 | pub fn addPtrElemPtr( | |
| 667 | block: *Block, | |
| 668 | array_ptr: Air.Inst.Ref, | |
| 669 | elem_index: Air.Inst.Ref, | |
| 670 | elem_ptr_ty: Type, | |
| 671 | ) !Air.Inst.Ref { | |
| 672 | const ty_ref = try block.func.addType(elem_ptr_ty); | |
| 673 | return block.addPtrElemPtrTypeRef(array_ptr, elem_index, ty_ref); | |
| 674 | } | |
| 675 | ||
| 676 | pub fn addPtrElemPtrTypeRef( | |
| 677 | block: *Block, | |
| 678 | array_ptr: Air.Inst.Ref, | |
| 679 | elem_index: Air.Inst.Ref, | |
| 680 | elem_ptr_ty: Air.Inst.Ref, | |
| 681 | ) !Air.Inst.Ref { | |
| 682 | return block.addInst(.{ | |
| 683 | .tag = .ptr_elem_ptr, | |
| 684 | .data = .{ .ty_pl = .{ | |
| 685 | .ty = elem_ptr_ty, | |
| 686 | .payload = try block.func.addExtra(Air.Bin{ | |
| 687 | .lhs = array_ptr, | |
| 688 | .rhs = elem_index, | |
| 689 | }), | |
| 690 | } }, | |
| 691 | }); | |
| 692 | } | |
| 693 | ||
| 694 | pub fn addVectorInit( | |
| 695 | block: *Block, | |
| 696 | vector_ty: Type, | |
| 697 | elements: []const Air.Inst.Ref, | |
| 698 | ) !Air.Inst.Ref { | |
| 699 | const func = block.func; | |
| 700 | const ty_ref = try func.addType(vector_ty); | |
| 701 | try func.air_extra.ensureUnusedCapacity(func.gpa, elements.len); | |
| 702 | const extra_index = @intCast(u32, func.air_extra.items.len); | |
| 703 | func.appendRefsAssumeCapacity(elements); | |
| 704 | ||
| 705 | return block.addInst(.{ | |
| 706 | .tag = .vector_init, | |
| 707 | .data = .{ .ty_pl = .{ | |
| 708 | .ty = ty_ref, | |
| 709 | .payload = extra_index, | |
| 710 | } }, | |
| 711 | }); | |
| 712 | } | |
| 713 | ||
| 714 | pub fn addInst(block: *Block, inst: Air.Inst) error{OutOfMemory}!Air.Inst.Ref { | |
| 715 | return Air.indexToRef(try block.addInstAsIndex(inst)); | |
| 716 | } | |
| 717 | ||
| 718 | pub fn addInstAsIndex(block: *Block, inst: Air.Inst) error{OutOfMemory}!Air.Inst.Index { | |
| 719 | const func = block.func; | |
| 720 | const gpa = func.codegen.gpa; | |
| 721 | ||
| 722 | try func.air_instructions.ensureUnusedCapacity(gpa, 1); | |
| 723 | try block.instructions.ensureUnusedCapacity(gpa, 1); | |
| 724 | ||
| 725 | const result_index = @intCast(Air.Inst.Index, func.air_instructions.len); | |
| 726 | func.air_instructions.appendAssumeCapacity(inst); | |
| 727 | block.instructions.appendAssumeCapacity(result_index); | |
| 728 | return result_index; | |
| 729 | } | |
| 730 | ||
| 731 | pub fn startAnonDecl(block: *Block) !WipAnonDecl { | |
| 732 | return WipAnonDecl{ | |
| 733 | .block = block, | |
| 734 | .new_decl_arena = std.heap.ArenaAllocator.init(block.func.codegen.gpa), | |
| 735 | .finished = false, | |
| 736 | }; | |
| 737 | } | |
| 738 | ||
| 739 | pub const WipAnonDecl = struct { | |
| 740 | block: *Block, | |
| 741 | new_decl_arena: std.heap.ArenaAllocator, | |
| 742 | finished: bool, | |
| 743 | ||
| 744 | pub fn arena(wad: *WipAnonDecl) Allocator { | |
| 745 | return wad.new_decl_arena.allocator(); | |
| 746 | } | |
| 747 | ||
| 748 | pub fn deinit(wad: *WipAnonDecl) void { | |
| 749 | if (!wad.finished) { | |
| 750 | wad.new_decl_arena.deinit(); | |
| 751 | } | |
| 752 | wad.* = undefined; | |
| 753 | } | |
| 754 | ||
| 755 | pub fn finish(wad: *WipAnonDecl, ty: Type, val: Value) !*Module.Decl { | |
| 756 | const func = wad.block.func; | |
| 757 | const mod = func.codegen.bin_file.options.module.?; | |
| 758 | const new_decl = try mod.createAnonymousDecl2(.{ | |
| 759 | .ty = ty, | |
| 760 | .val = val, | |
| 761 | }, func.name); | |
| 762 | errdefer mod.abortAnonDecl(new_decl); | |
| 763 | try new_decl.finalizeNewArena(&wad.new_decl_arena); | |
| 764 | wad.finished = true; | |
| 765 | return new_decl; | |
| 766 | } | |
| 767 | }; | |
| 768 | }; |
src/aro/Compilation.zig created+834| ... | ... | @@ -0,0 +1,834 @@ |
| 1 | const std = @import("std"); | |
| 2 | const assert = std.debug.assert; | |
| 3 | const mem = std.mem; | |
| 4 | const Allocator = mem.Allocator; | |
| 5 | const EpochSeconds = std.time.epoch.EpochSeconds; | |
| 6 | const Builtins = @import("Builtins.zig"); | |
| 7 | const Diagnostics = @import("Diagnostics.zig"); | |
| 8 | const LangOpts = @import("LangOpts.zig"); | |
| 9 | const Source = @import("Source.zig"); | |
| 10 | const Tokenizer = @import("Tokenizer.zig"); | |
| 11 | const Token = Tokenizer.Token; | |
| 12 | const Type = @import("Type.zig"); | |
| 13 | const Pragma = @import("Pragma.zig"); | |
| 14 | ||
| 15 | const Compilation = @This(); | |
| 16 | ||
| 17 | pub const Error = error{ | |
| 18 | /// A fatal error has ocurred and compilation has stopped. | |
| 19 | FatalError, | |
| 20 | } || Allocator.Error; | |
| 21 | ||
| 22 | gpa: Allocator, | |
| 23 | sources: std.StringArrayHashMap(Source), | |
| 24 | diag: Diagnostics, | |
| 25 | include_dirs: std.ArrayList([]const u8), | |
| 26 | system_include_dirs: std.ArrayList([]const u8), | |
| 27 | output_name: ?[]const u8 = null, | |
| 28 | builtin_header_path: ?[]u8 = null, | |
| 29 | target: std.Target = @import("builtin").target, | |
| 30 | pragma_handlers: std.StringArrayHashMap(*Pragma), | |
| 31 | only_preprocess: bool = false, | |
| 32 | only_compile: bool = false, | |
| 33 | verbose_ast: bool = false, | |
| 34 | langopts: LangOpts = .{}, | |
| 35 | generated_buf: std.ArrayList(u8), | |
| 36 | builtins: Builtins = .{}, | |
| 37 | types: struct { | |
| 38 | wchar: Type, | |
| 39 | ptrdiff: Type, | |
| 40 | size: Type, | |
| 41 | va_list: Type, | |
| 42 | } = undefined, | |
| 43 | ||
| 44 | pub fn init(gpa: Allocator) Compilation { | |
| 45 | return .{ | |
| 46 | .gpa = gpa, | |
| 47 | .sources = std.StringArrayHashMap(Source).init(gpa), | |
| 48 | .diag = Diagnostics.init(gpa), | |
| 49 | .include_dirs = std.ArrayList([]const u8).init(gpa), | |
| 50 | .system_include_dirs = std.ArrayList([]const u8).init(gpa), | |
| 51 | .pragma_handlers = std.StringArrayHashMap(*Pragma).init(gpa), | |
| 52 | .generated_buf = std.ArrayList(u8).init(gpa), | |
| 53 | }; | |
| 54 | } | |
| 55 | ||
| 56 | pub fn deinit(comp: *Compilation) void { | |
| 57 | for (comp.pragma_handlers.values()) |pragma| { | |
| 58 | pragma.deinit(pragma, comp); | |
| 59 | } | |
| 60 | for (comp.sources.values()) |source| { | |
| 61 | comp.gpa.free(source.path); | |
| 62 | comp.gpa.free(source.buf); | |
| 63 | comp.gpa.free(source.splice_locs); | |
| 64 | } | |
| 65 | comp.sources.deinit(); | |
| 66 | comp.diag.deinit(); | |
| 67 | comp.include_dirs.deinit(); | |
| 68 | comp.system_include_dirs.deinit(); | |
| 69 | comp.pragma_handlers.deinit(); | |
| 70 | if (comp.builtin_header_path) |some| comp.gpa.free(some); | |
| 71 | comp.generated_buf.deinit(); | |
| 72 | comp.builtins.deinit(comp.gpa); | |
| 73 | } | |
| 74 | ||
| 75 | fn generateDateAndTime(w: anytype) !void { | |
| 76 | // TODO take timezone into account here once it is supported in Zig std | |
| 77 | const timestamp = std.math.clamp(std.time.timestamp(), 0, std.math.maxInt(i64)); | |
| 78 | const epoch_seconds = EpochSeconds{ .secs = @intCast(u64, timestamp) }; | |
| 79 | const epoch_day = epoch_seconds.getEpochDay(); | |
| 80 | const day_seconds = epoch_seconds.getDaySeconds(); | |
| 81 | const year_day = epoch_day.calculateYearDay(); | |
| 82 | const month_day = year_day.calculateMonthDay(); | |
| 83 | ||
| 84 | const month_names = [_][]const u8{ "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; | |
| 85 | std.debug.assert(std.time.epoch.Month.jan.numeric() == 1); | |
| 86 | ||
| 87 | const month_name = month_names[month_day.month.numeric() - 1]; | |
| 88 | try w.print("#define __DATE__ \"{s} {d: >2} {d}\"\n", .{ | |
| 89 | month_name, | |
| 90 | month_day.day_index + 1, | |
| 91 | year_day.year, | |
| 92 | }); | |
| 93 | try w.print("#define __TIME__ \"{d:0>2}:{d:0>2}:{d:0>2}\"\n", .{ | |
| 94 | day_seconds.getHoursIntoDay(), | |
| 95 | day_seconds.getMinutesIntoHour(), | |
| 96 | day_seconds.getSecondsIntoMinute(), | |
| 97 | }); | |
| 98 | ||
| 99 | const day_names = [_][]const u8{ "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun" }; | |
| 100 | // days since Thu Oct 1 1970 | |
| 101 | const day_name = day_names[(epoch_day.day + 3) % 7]; | |
| 102 | try w.print("#define __TIMESTAMP__ \"{s} {s} {d: >2} {d:0>2}:{d:0>2}:{d:0>2} {d}\"\n", .{ | |
| 103 | day_name, | |
| 104 | month_name, | |
| 105 | month_day.day_index + 1, | |
| 106 | day_seconds.getHoursIntoDay(), | |
| 107 | day_seconds.getMinutesIntoHour(), | |
| 108 | day_seconds.getSecondsIntoMinute(), | |
| 109 | year_day.year, | |
| 110 | }); | |
| 111 | } | |
| 112 | ||
| 113 | /// Generate builtin macros that will be available to each source file. | |
| 114 | pub fn generateBuiltinMacros(comp: *Compilation) !Source { | |
| 115 | try comp.generateBuiltinTypes(); | |
| 116 | comp.builtins = try Builtins.create(comp); | |
| 117 | ||
| 118 | var buf = std.ArrayList(u8).init(comp.gpa); | |
| 119 | defer buf.deinit(); | |
| 120 | const w = buf.writer(); | |
| 121 | ||
| 122 | // standard macros | |
| 123 | try w.writeAll( | |
| 124 | \\#define __VERSION__ "Aro | |
| 125 | ++ @import("lib.zig").version_str ++ "\"\n" ++ | |
| 126 | \\#define __Aro__ | |
| 127 | \\#define __STDC__ 1 | |
| 128 | \\#define __STDC_HOSTED__ 1 | |
| 129 | \\#define __STDC_NO_ATOMICS__ 1 | |
| 130 | \\#define __STDC_NO_COMPLEX__ 1 | |
| 131 | \\#define __STDC_NO_THREADS__ 1 | |
| 132 | \\#define __STDC_NO_VLA__ 1 | |
| 133 | \\ | |
| 134 | ); | |
| 135 | if (comp.langopts.standard.StdCVersionMacro()) |stdc_version| { | |
| 136 | try w.print("#define __STDC_VERSION__ {s}\n", .{stdc_version}); | |
| 137 | } | |
| 138 | ||
| 139 | // os macros | |
| 140 | switch (comp.target.os.tag) { | |
| 141 | .linux => try w.writeAll( | |
| 142 | \\#define linux 1 | |
| 143 | \\#define __linux 1 | |
| 144 | \\#define __linux__ 1 | |
| 145 | \\ | |
| 146 | ), | |
| 147 | .windows => if (comp.target.cpu.arch.ptrBitWidth() == 32) try w.writeAll( | |
| 148 | \\#define WIN32 1 | |
| 149 | \\#define _WIN32 1 | |
| 150 | \\#define __WIN32 1 | |
| 151 | \\#define __WIN32__ 1 | |
| 152 | \\ | |
| 153 | ) else try w.writeAll( | |
| 154 | \\#define WIN32 1 | |
| 155 | \\#define WIN64 1 | |
| 156 | \\#define _WIN32 1 | |
| 157 | \\#define _WIN64 1 | |
| 158 | \\#define __WIN32 1 | |
| 159 | \\#define __WIN64 1 | |
| 160 | \\#define __WIN32__ 1 | |
| 161 | \\#define __WIN64__ 1 | |
| 162 | \\ | |
| 163 | ), | |
| 164 | .freebsd => try w.print("#define __FreeBSD__ {d}\n", .{comp.target.os.version_range.semver.min.major}), | |
| 165 | .netbsd => try w.writeAll("#define __NetBSD__ 1\n"), | |
| 166 | .openbsd => try w.writeAll("#define __OpenBSD__ 1\n"), | |
| 167 | .dragonfly => try w.writeAll("#define __DragonFly__ 1\n"), | |
| 168 | .solaris => try w.writeAll( | |
| 169 | \\#define sun 1 | |
| 170 | \\#define __sun 1 | |
| 171 | \\ | |
| 172 | ), | |
| 173 | .macos => try w.writeAll( | |
| 174 | \\#define __APPLE__ 1 | |
| 175 | \\#define __MACH__ 1 | |
| 176 | \\ | |
| 177 | ), | |
| 178 | else => {}, | |
| 179 | } | |
| 180 | ||
| 181 | // unix and other additional os macros | |
| 182 | switch (comp.target.os.tag) { | |
| 183 | .freebsd, | |
| 184 | .netbsd, | |
| 185 | .openbsd, | |
| 186 | .dragonfly, | |
| 187 | .linux, | |
| 188 | => try w.writeAll( | |
| 189 | \\#define unix 1 | |
| 190 | \\#define __unix 1 | |
| 191 | \\#define __unix__ 1 | |
| 192 | \\ | |
| 193 | ), | |
| 194 | else => {}, | |
| 195 | } | |
| 196 | if (comp.target.abi == .android) { | |
| 197 | try w.writeAll("#define __ANDROID__ 1\n"); | |
| 198 | } | |
| 199 | ||
| 200 | // architecture macros | |
| 201 | switch (comp.target.cpu.arch) { | |
| 202 | .x86_64 => try w.writeAll( | |
| 203 | \\#define __amd64__ 1 | |
| 204 | \\#define __amd64 1 | |
| 205 | \\#define __x86_64 1 | |
| 206 | \\#define __x86_64__ 1 | |
| 207 | \\ | |
| 208 | ), | |
| 209 | .i386 => try w.writeAll( | |
| 210 | \\#define i386 1 | |
| 211 | \\#define __i386 1 | |
| 212 | \\#define __i386__ 1 | |
| 213 | \\ | |
| 214 | ), | |
| 215 | .mips, | |
| 216 | .mipsel, | |
| 217 | .mips64, | |
| 218 | .mips64el, | |
| 219 | => try w.writeAll( | |
| 220 | \\#define __mips__ 1 | |
| 221 | \\#define mips 1 | |
| 222 | \\ | |
| 223 | ), | |
| 224 | .powerpc, | |
| 225 | .powerpcle, | |
| 226 | => try w.writeAll( | |
| 227 | \\#define __powerpc__ 1 | |
| 228 | \\#define __POWERPC__ 1 | |
| 229 | \\#define __ppc__ 1 | |
| 230 | \\#define __PPC__ 1 | |
| 231 | \\#define _ARCH_PPC 1 | |
| 232 | \\ | |
| 233 | ), | |
| 234 | .powerpc64, | |
| 235 | .powerpc64le, | |
| 236 | => try w.writeAll( | |
| 237 | \\#define __powerpc 1 | |
| 238 | \\#define __powerpc__ 1 | |
| 239 | \\#define __powerpc64__ 1 | |
| 240 | \\#define __POWERPC__ 1 | |
| 241 | \\#define __ppc__ 1 | |
| 242 | \\#define __ppc64__ 1 | |
| 243 | \\#define __PPC__ 1 | |
| 244 | \\#define __PPC64__ 1 | |
| 245 | \\#define _ARCH_PPC 1 | |
| 246 | \\#define _ARCH_PPC64 1 | |
| 247 | \\ | |
| 248 | ), | |
| 249 | .sparcv9 => try w.writeAll( | |
| 250 | \\#define __sparc__ 1 | |
| 251 | \\#define __sparc 1 | |
| 252 | \\#define __sparc_v9__ 1 | |
| 253 | \\ | |
| 254 | ), | |
| 255 | .sparc, .sparcel => try w.writeAll( | |
| 256 | \\#define __sparc__ 1 | |
| 257 | \\#define __sparc 1 | |
| 258 | \\ | |
| 259 | ), | |
| 260 | .arm, .armeb => try w.writeAll( | |
| 261 | \\#define __arm__ 1 | |
| 262 | \\#define __arm 1 | |
| 263 | \\ | |
| 264 | ), | |
| 265 | .thumb, .thumbeb => try w.writeAll( | |
| 266 | \\#define __arm__ 1 | |
| 267 | \\#define __arm 1 | |
| 268 | \\#define __thumb__ 1 | |
| 269 | \\ | |
| 270 | ), | |
| 271 | .aarch64, .aarch64_be => try w.writeAll("#define __aarch64__ 1\n"), | |
| 272 | else => {}, | |
| 273 | } | |
| 274 | ||
| 275 | if (comp.target.os.tag != .windows) switch (comp.target.cpu.arch.ptrBitWidth()) { | |
| 276 | 64 => try w.writeAll( | |
| 277 | \\#define _LP64 1 | |
| 278 | \\#define __LP64__ 1 | |
| 279 | \\ | |
| 280 | ), | |
| 281 | 32 => try w.writeAll("#define _ILP32 1\n"), | |
| 282 | else => {}, | |
| 283 | }; | |
| 284 | ||
| 285 | try w.writeAll( | |
| 286 | \\#define __ORDER_LITTLE_ENDIAN__ 1234 | |
| 287 | \\#define __ORDER_BIG_ENDIAN__ 4321 | |
| 288 | \\#define __ORDER_PDP_ENDIAN__ 3412 | |
| 289 | \\ | |
| 290 | ); | |
| 291 | if (comp.target.cpu.arch.endian() == .Little) try w.writeAll( | |
| 292 | \\#define __BYTE_ORDER__ __ORDER_LITTLE_ENDIAN__ | |
| 293 | \\#define __LITTLE_ENDIAN__ 1 | |
| 294 | \\ | |
| 295 | ) else try w.writeAll( | |
| 296 | \\#define __BYTE_ORDER__ __ORDER_BIG_ENDIAN__; | |
| 297 | \\#define __BIG_ENDIAN__ 1 | |
| 298 | \\ | |
| 299 | ); | |
| 300 | ||
| 301 | // timestamps | |
| 302 | try generateDateAndTime(w); | |
| 303 | ||
| 304 | // types | |
| 305 | if (Type.getCharSignedness(comp) == .unsigned) try w.writeAll("#define __CHAR_UNSIGNED__ 1\n"); | |
| 306 | try w.writeAll("#define __CHAR_BIT__ 8\n"); | |
| 307 | ||
| 308 | // int maxs | |
| 309 | try comp.generateIntMax(w, "__SCHAR_MAX__", .{ .specifier = .schar }); | |
| 310 | try comp.generateIntMax(w, "__SHRT_MAX__", .{ .specifier = .short }); | |
| 311 | try comp.generateIntMax(w, "__INT_MAX__", .{ .specifier = .int }); | |
| 312 | try comp.generateIntMax(w, "__LONG_MAX__", .{ .specifier = .long }); | |
| 313 | try comp.generateIntMax(w, "__LONG_LONG_MAX__", .{ .specifier = .long_long }); | |
| 314 | try comp.generateIntMax(w, "__WCHAR_MAX__", comp.types.wchar); | |
| 315 | // try comp.generateIntMax(w, "__WINT_MAX__", comp.types.wchar); | |
| 316 | // try comp.generateIntMax(w, "__INTMAX_MAX__", comp.types.wchar); | |
| 317 | try comp.generateIntMax(w, "__SIZE_MAX__", comp.types.size); | |
| 318 | // try comp.generateIntMax(w, "__UINTMAX_MAX__", comp.types.wchar); | |
| 319 | try comp.generateIntMax(w, "__PTRDIFF_MAX__", comp.types.ptrdiff); | |
| 320 | // try comp.generateIntMax(w, "__INTPTR_MAX__", comp.types.wchar); | |
| 321 | // try comp.generateIntMax(w, "__UINTPTR_MAX__", comp.types.size); | |
| 322 | ||
| 323 | // sizeof types | |
| 324 | try comp.generateSizeofType(w, "__SIZEOF_FLOAT__", .{ .specifier = .float }); | |
| 325 | try comp.generateSizeofType(w, "__SIZEOF_DOUBLE__", .{ .specifier = .double }); | |
| 326 | try comp.generateSizeofType(w, "__SIZEOF_LONG_DOUBLE__", .{ .specifier = .long_double }); | |
| 327 | try comp.generateSizeofType(w, "__SIZEOF_SHORT__", .{ .specifier = .short }); | |
| 328 | try comp.generateSizeofType(w, "__SIZEOF_INT__", .{ .specifier = .int }); | |
| 329 | try comp.generateSizeofType(w, "__SIZEOF_LONG__", .{ .specifier = .long }); | |
| 330 | try comp.generateSizeofType(w, "__SIZEOF_LONG_LONG__", .{ .specifier = .long_long }); | |
| 331 | try comp.generateSizeofType(w, "__SIZEOF_POINTER__", .{ .specifier = .pointer }); | |
| 332 | try comp.generateSizeofType(w, "__SIZEOF_PTRDIFF_T__", comp.types.ptrdiff); | |
| 333 | try comp.generateSizeofType(w, "__SIZEOF_SIZE_T__", comp.types.size); | |
| 334 | try comp.generateSizeofType(w, "__SIZEOF_WCHAR_T__", comp.types.wchar); | |
| 335 | // try comp.generateSizeofType(w, "__SIZEOF_WINT_T__", .{ .specifier = .pointer }); | |
| 336 | ||
| 337 | // various int types | |
| 338 | try generateTypeMacro(w, "__PTRDIFF_TYPE__", comp.types.ptrdiff); | |
| 339 | try generateTypeMacro(w, "__SIZE_TYPE__", comp.types.size); | |
| 340 | try generateTypeMacro(w, "__WCHAR_TYPE__", comp.types.wchar); | |
| 341 | ||
| 342 | return comp.addSourceFromBuffer("<builtin>", buf.items); | |
| 343 | } | |
| 344 | ||
| 345 | fn generateTypeMacro(w: anytype, name: []const u8, ty: Type) !void { | |
| 346 | try w.print("#define {s} ", .{name}); | |
| 347 | try ty.print(w); | |
| 348 | try w.writeByte('\n'); | |
| 349 | } | |
| 350 | ||
| 351 | fn generateBuiltinTypes(comp: *Compilation) !void { | |
| 352 | const os = comp.target.os.tag; | |
| 353 | const wchar: Type = switch (comp.target.cpu.arch) { | |
| 354 | .xcore => .{ .specifier = .uchar }, | |
| 355 | .ve => .{ .specifier = .uint }, | |
| 356 | .arm, .armeb, .thumb, .thumbeb => .{ | |
| 357 | .specifier = if (os != .windows and os != .netbsd and os != .openbsd) .uint else .int, | |
| 358 | }, | |
| 359 | .aarch64, .aarch64_be, .aarch64_32 => .{ | |
| 360 | .specifier = if (!os.isDarwin() and os != .netbsd) .uint else .int, | |
| 361 | }, | |
| 362 | .x86_64, .i386 => .{ .specifier = if (os == .windows) .ushort else .int }, | |
| 363 | else => .{ .specifier = .int }, | |
| 364 | }; | |
| 365 | ||
| 366 | const ptrdiff = if (os == .windows and comp.target.cpu.arch.ptrBitWidth() == 64) | |
| 367 | Type{ .specifier = .long_long } | |
| 368 | else switch (comp.target.cpu.arch.ptrBitWidth()) { | |
| 369 | 32 => Type{ .specifier = .int }, | |
| 370 | 64 => Type{ .specifier = .long }, | |
| 371 | else => unreachable, | |
| 372 | }; | |
| 373 | ||
| 374 | const size = if (os == .windows and comp.target.cpu.arch.ptrBitWidth() == 64) | |
| 375 | Type{ .specifier = .ulong_long } | |
| 376 | else switch (comp.target.cpu.arch.ptrBitWidth()) { | |
| 377 | 32 => Type{ .specifier = .uint }, | |
| 378 | 64 => Type{ .specifier = .ulong }, | |
| 379 | else => unreachable, | |
| 380 | }; | |
| 381 | ||
| 382 | const va_list = try comp.generateVaListType(); | |
| 383 | ||
| 384 | comp.types = .{ | |
| 385 | .wchar = wchar, | |
| 386 | .ptrdiff = ptrdiff, | |
| 387 | .size = size, | |
| 388 | .va_list = va_list, | |
| 389 | }; | |
| 390 | } | |
| 391 | ||
| 392 | fn generateVaListType(comp: *Compilation) !Type { | |
| 393 | const Kind = enum { char_ptr, void_ptr, aarch64_va_list, x86_64_va_list }; | |
| 394 | const kind: Kind = switch (comp.target.cpu.arch) { | |
| 395 | .aarch64 => switch (comp.target.os.tag) { | |
| 396 | .windows => @as(Kind, .char_ptr), | |
| 397 | .ios, .macos, .tvos, .watchos => .char_ptr, | |
| 398 | else => .aarch64_va_list, | |
| 399 | }, | |
| 400 | .sparc, .wasm32, .wasm64, .bpfel, .bpfeb, .riscv32, .riscv64, .avr, .spirv32, .spirv64 => .void_ptr, | |
| 401 | .powerpc => switch (comp.target.os.tag) { | |
| 402 | .ios, .macos, .tvos, .watchos, .aix => @as(Kind, .char_ptr), | |
| 403 | else => return Type{ .specifier = .void }, // unknown | |
| 404 | }, | |
| 405 | .i386 => .char_ptr, | |
| 406 | .x86_64 => switch (comp.target.os.tag) { | |
| 407 | .windows => @as(Kind, .char_ptr), | |
| 408 | else => .x86_64_va_list, | |
| 409 | }, | |
| 410 | else => return Type{ .specifier = .void }, // unknown | |
| 411 | }; | |
| 412 | ||
| 413 | // TODO this might be bad? | |
| 414 | const arena = comp.diag.arena.allocator(); | |
| 415 | ||
| 416 | var ty: Type = undefined; | |
| 417 | switch (kind) { | |
| 418 | .char_ptr => ty = .{ .specifier = .char }, | |
| 419 | .void_ptr => ty = .{ .specifier = .void }, | |
| 420 | .aarch64_va_list => { | |
| 421 | const record_ty = try arena.create(Type.Record); | |
| 422 | record_ty.* = .{ | |
| 423 | .name = "__va_list_tag", | |
| 424 | .fields = try arena.alloc(Type.Record.Field, 5), | |
| 425 | .size = 32, | |
| 426 | .alignment = 8, | |
| 427 | }; | |
| 428 | const void_ty = try arena.create(Type); | |
| 429 | void_ty.* = .{ .specifier = .void }; | |
| 430 | const void_ptr = Type{ .specifier = .pointer, .data = .{ .sub_type = void_ty } }; | |
| 431 | record_ty.fields[0] = .{ .name = "__stack", .ty = void_ptr }; | |
| 432 | record_ty.fields[1] = .{ .name = "__gr_top", .ty = void_ptr }; | |
| 433 | record_ty.fields[2] = .{ .name = "__vr_top", .ty = void_ptr }; | |
| 434 | record_ty.fields[3] = .{ .name = "__gr_offs", .ty = .{ .specifier = .int } }; | |
| 435 | record_ty.fields[4] = .{ .name = "__vr_offs", .ty = .{ .specifier = .int } }; | |
| 436 | ty = .{ .specifier = .@"struct", .data = .{ .record = record_ty } }; | |
| 437 | }, | |
| 438 | .x86_64_va_list => { | |
| 439 | const record_ty = try arena.create(Type.Record); | |
| 440 | record_ty.* = .{ | |
| 441 | .name = "__va_list_tag", | |
| 442 | .fields = try arena.alloc(Type.Record.Field, 4), | |
| 443 | .size = 24, | |
| 444 | .alignment = 8, | |
| 445 | }; | |
| 446 | const void_ty = try arena.create(Type); | |
| 447 | void_ty.* = .{ .specifier = .void }; | |
| 448 | const void_ptr = Type{ .specifier = .pointer, .data = .{ .sub_type = void_ty } }; | |
| 449 | record_ty.fields[0] = .{ .name = "gp_offset", .ty = .{ .specifier = .uint } }; | |
| 450 | record_ty.fields[1] = .{ .name = "fp_offset", .ty = .{ .specifier = .uint } }; | |
| 451 | record_ty.fields[2] = .{ .name = "overflow_arg_area", .ty = void_ptr }; | |
| 452 | record_ty.fields[3] = .{ .name = "reg_save_area", .ty = void_ptr }; | |
| 453 | ty = .{ .specifier = .@"struct", .data = .{ .record = record_ty } }; | |
| 454 | }, | |
| 455 | } | |
| 456 | if (kind == .char_ptr or kind == .void_ptr) { | |
| 457 | const elem_ty = try arena.create(Type); | |
| 458 | elem_ty.* = ty; | |
| 459 | ty = Type{ .specifier = .pointer, .data = .{ .sub_type = elem_ty } }; | |
| 460 | } else { | |
| 461 | const arr_ty = try arena.create(Type.Array); | |
| 462 | arr_ty.* = .{ .len = 1, .elem = ty }; | |
| 463 | ty = Type{ .specifier = .array, .data = .{ .array = arr_ty } }; | |
| 464 | } | |
| 465 | ||
| 466 | return ty; | |
| 467 | } | |
| 468 | ||
| 469 | fn generateIntMax(comp: *Compilation, w: anytype, name: []const u8, ty: Type) !void { | |
| 470 | const bit_count = @intCast(u8, ty.sizeof(comp).? * 8); | |
| 471 | const unsigned = ty.isUnsignedInt(comp); | |
| 472 | const max = if (bit_count == 128) | |
| 473 | @as(u128, if (unsigned) std.math.maxInt(u128) else std.math.maxInt(u128)) | |
| 474 | else | |
| 475 | (@as(u64, 1) << @truncate(u6, bit_count - @boolToInt(!unsigned))) - 1; | |
| 476 | try w.print("#define {s} {d}\n", .{ name, max }); | |
| 477 | } | |
| 478 | ||
| 479 | fn generateSizeofType(comp: *Compilation, w: anytype, name: []const u8, ty: Type) !void { | |
| 480 | try w.print("#define {s} {d}\n", .{ name, ty.sizeof(comp).? }); | |
| 481 | } | |
| 482 | ||
| 483 | pub fn defineSystemIncludes(comp: *Compilation) !void { | |
| 484 | var buf: [std.fs.MAX_PATH_BYTES]u8 = undefined; | |
| 485 | var search_path: []const u8 = std.fs.selfExePath(&buf) catch return error.SelfExeNotFound; | |
| 486 | while (std.fs.path.dirname(search_path)) |dirname| : (search_path = dirname) { | |
| 487 | var base_dir = std.fs.cwd().openDir(dirname, .{}) catch continue; | |
| 488 | defer base_dir.close(); | |
| 489 | ||
| 490 | base_dir.access("include/stddef.h", .{}) catch continue; | |
| 491 | const path = try std.fs.path.join(comp.gpa, &.{ dirname, "include" }); | |
| 492 | comp.builtin_header_path = path; | |
| 493 | try comp.system_include_dirs.append(path); | |
| 494 | break; | |
| 495 | } else return error.AroIncludeNotFound; | |
| 496 | ||
| 497 | try comp.system_include_dirs.append("/usr/include"); | |
| 498 | } | |
| 499 | ||
| 500 | pub fn getSource(comp: *Compilation, id: Source.Id) Source { | |
| 501 | if (id == .generated) return .{ | |
| 502 | .path = "<scratch space>", | |
| 503 | .buf = comp.generated_buf.items, | |
| 504 | .id = .generated, | |
| 505 | .splice_locs = &.{}, | |
| 506 | }; | |
| 507 | return comp.sources.values()[@enumToInt(id) - 2]; | |
| 508 | } | |
| 509 | ||
| 510 | /// Write bytes from `reader` into `contents`, performing newline splicing, | |
| 511 | /// line-ending normalization (convert line endings to \n), and UTF-8 validation. | |
| 512 | /// Creates a Source with `contents` as the buf and adds it to the Compilation. | |
| 513 | /// `contents` is assumed to be large enough to hold the entire content of `reader`. | |
| 514 | /// `contents` must have been allocated by `comp`'s allocator since it will be reallocated | |
| 515 | /// if splicing occurred. | |
| 516 | /// Compilation owns `contents` if and only if this call succeeds; caller always retains | |
| 517 | /// ownership of `path`. | |
| 518 | pub fn addSourceFromReader(comp: *Compilation, reader: anytype, path: []const u8, contents: []u8) !Source { | |
| 519 | const duped_path = try comp.gpa.dupe(u8, path); | |
| 520 | errdefer comp.gpa.free(duped_path); | |
| 521 | ||
| 522 | var splice_list = std.ArrayList(u32).init(comp.gpa); | |
| 523 | defer splice_list.deinit(); | |
| 524 | ||
| 525 | const source_id = @intToEnum(Source.Id, comp.sources.count() + 2); | |
| 526 | ||
| 527 | var i: u32 = 0; | |
| 528 | var backslash_loc: u32 = undefined; | |
| 529 | var state: enum { start, back_slash, cr, back_slash_cr, trailing_ws } = .start; | |
| 530 | var line: u32 = 1; | |
| 531 | ||
| 532 | while (true) { | |
| 533 | const byte = reader.readByte() catch break; | |
| 534 | contents[i] = byte; | |
| 535 | ||
| 536 | switch (byte) { | |
| 537 | '\r' => { | |
| 538 | switch (state) { | |
| 539 | .start, .cr => { | |
| 540 | line += 1; | |
| 541 | state = .cr; | |
| 542 | contents[i] = '\n'; | |
| 543 | i += 1; | |
| 544 | }, | |
| 545 | .back_slash, .trailing_ws, .back_slash_cr => { | |
| 546 | i = backslash_loc; | |
| 547 | try splice_list.append(i); | |
| 548 | if (state == .trailing_ws) { | |
| 549 | try comp.diag.add(.{ | |
| 550 | .tag = .backslash_newline_escape, | |
| 551 | .loc = .{ .id = source_id, .byte_offset = i, .line = line }, | |
| 552 | }, &.{}); | |
| 553 | } | |
| 554 | state = if (state == .back_slash_cr) .cr else .back_slash_cr; | |
| 555 | }, | |
| 556 | } | |
| 557 | }, | |
| 558 | '\n' => { | |
| 559 | switch (state) { | |
| 560 | .start => { | |
| 561 | line += 1; | |
| 562 | i += 1; | |
| 563 | }, | |
| 564 | .cr, .back_slash_cr => {}, | |
| 565 | .back_slash, .trailing_ws => { | |
| 566 | i = backslash_loc; | |
| 567 | if (state == .back_slash or state == .trailing_ws) { | |
| 568 | try splice_list.append(i); | |
| 569 | } | |
| 570 | if (state == .trailing_ws) { | |
| 571 | try comp.diag.add(.{ | |
| 572 | .tag = .backslash_newline_escape, | |
| 573 | .loc = .{ .id = source_id, .byte_offset = i, .line = line }, | |
| 574 | }, &.{}); | |
| 575 | } | |
| 576 | }, | |
| 577 | } | |
| 578 | state = .start; | |
| 579 | }, | |
| 580 | '\\' => { | |
| 581 | backslash_loc = i; | |
| 582 | state = .back_slash; | |
| 583 | i += 1; | |
| 584 | }, | |
| 585 | '\t', '\x0B', '\x0C', ' ' => { | |
| 586 | switch (state) { | |
| 587 | .start, .trailing_ws => {}, | |
| 588 | .cr, .back_slash_cr => state = .start, | |
| 589 | .back_slash => state = .trailing_ws, | |
| 590 | } | |
| 591 | i += 1; | |
| 592 | }, | |
| 593 | else => { | |
| 594 | i += 1; | |
| 595 | state = .start; | |
| 596 | }, | |
| 597 | } | |
| 598 | } | |
| 599 | ||
| 600 | const splice_locs = splice_list.toOwnedSlice(); | |
| 601 | errdefer comp.gpa.free(splice_locs); | |
| 602 | ||
| 603 | var source = Source{ | |
| 604 | .id = source_id, | |
| 605 | .path = duped_path, | |
| 606 | .buf = if (i == contents.len) contents else try comp.gpa.realloc(contents, i), | |
| 607 | .splice_locs = splice_locs, | |
| 608 | }; | |
| 609 | ||
| 610 | source.checkUtf8(); | |
| 611 | try comp.sources.put(path, source); | |
| 612 | return source; | |
| 613 | } | |
| 614 | ||
| 615 | /// Caller retains ownership of `path` and `buf`. | |
| 616 | pub fn addSourceFromBuffer(comp: *Compilation, path: []const u8, buf: []const u8) !Source { | |
| 617 | if (comp.sources.get(path)) |some| return some; | |
| 618 | ||
| 619 | if (buf.len > std.math.maxInt(u32)) return error.StreamTooLong; | |
| 620 | ||
| 621 | const reader = std.io.fixedBufferStream(buf).reader(); | |
| 622 | const contents = try comp.gpa.alloc(u8, buf.len); | |
| 623 | errdefer comp.gpa.free(contents); | |
| 624 | ||
| 625 | return comp.addSourceFromReader(reader, path, contents); | |
| 626 | } | |
| 627 | ||
| 628 | /// Caller retains ownership of `path` | |
| 629 | pub fn addSourceFromPath(comp: *Compilation, path: []const u8) !Source { | |
| 630 | if (comp.sources.get(path)) |some| return some; | |
| 631 | ||
| 632 | if (mem.indexOfScalar(u8, path, 0) != null) { | |
| 633 | return error.FileNotFound; | |
| 634 | } | |
| 635 | ||
| 636 | const file = try std.fs.cwd().openFile(path, .{}); | |
| 637 | defer file.close(); | |
| 638 | ||
| 639 | const size = std.math.cast(u32, try file.getEndPos()) catch return error.StreamTooLong; | |
| 640 | ||
| 641 | var reader = std.io.bufferedReader(file.reader()).reader(); | |
| 642 | const contents = try comp.gpa.alloc(u8, size); | |
| 643 | errdefer comp.gpa.free(contents); | |
| 644 | ||
| 645 | return comp.addSourceFromReader(reader, path, contents); | |
| 646 | } | |
| 647 | ||
| 648 | pub fn findInclude(comp: *Compilation, tok: Token, filename: []const u8, search_cwd: bool) !?Source { | |
| 649 | var path_buf: [std.fs.MAX_PATH_BYTES]u8 = undefined; | |
| 650 | var fib = std.heap.FixedBufferAllocator.init(&path_buf); | |
| 651 | if (search_cwd) blk: { | |
| 652 | const source = comp.getSource(tok.source); | |
| 653 | const path = if (std.fs.path.dirname(source.path)) |some| | |
| 654 | std.fs.path.join(fib.allocator(), &.{ some, filename }) catch break :blk | |
| 655 | else | |
| 656 | std.fs.path.join(fib.allocator(), &.{ ".", filename }) catch break :blk; | |
| 657 | if (comp.addSourceFromPath(path)) |some| | |
| 658 | return some | |
| 659 | else |err| switch (err) { | |
| 660 | error.OutOfMemory => return error.OutOfMemory, | |
| 661 | else => {}, | |
| 662 | } | |
| 663 | } | |
| 664 | for (comp.include_dirs.items) |dir| { | |
| 665 | fib.end_index = 0; | |
| 666 | const path = std.fs.path.join(fib.allocator(), &.{ dir, filename }) catch continue; | |
| 667 | if (comp.addSourceFromPath(path)) |some| | |
| 668 | return some | |
| 669 | else |err| switch (err) { | |
| 670 | error.OutOfMemory => return error.OutOfMemory, | |
| 671 | else => {}, | |
| 672 | } | |
| 673 | } | |
| 674 | for (comp.system_include_dirs.items) |dir| { | |
| 675 | fib.end_index = 0; | |
| 676 | const path = std.fs.path.join(fib.allocator(), &.{ dir, filename }) catch continue; | |
| 677 | if (comp.addSourceFromPath(path)) |some| | |
| 678 | return some | |
| 679 | else |err| switch (err) { | |
| 680 | error.OutOfMemory => return error.OutOfMemory, | |
| 681 | else => {}, | |
| 682 | } | |
| 683 | } | |
| 684 | return null; | |
| 685 | } | |
| 686 | ||
| 687 | pub fn addPragmaHandler(comp: *Compilation, name: []const u8, handler: *Pragma) Allocator.Error!void { | |
| 688 | try comp.pragma_handlers.putNoClobber(name, handler); | |
| 689 | } | |
| 690 | ||
| 691 | pub fn addDefaultPragmaHandlers(comp: *Compilation) Allocator.Error!void { | |
| 692 | const GCC = @import("pragmas/gcc.zig"); | |
| 693 | var gcc = try GCC.init(comp.gpa); | |
| 694 | errdefer gcc.deinit(gcc, comp); | |
| 695 | ||
| 696 | const Once = @import("pragmas/once.zig"); | |
| 697 | var once = try Once.init(comp.gpa); | |
| 698 | errdefer once.deinit(once, comp); | |
| 699 | ||
| 700 | const Message = @import("pragmas/message.zig"); | |
| 701 | var message = try Message.init(comp.gpa); | |
| 702 | errdefer message.deinit(message, comp); | |
| 703 | ||
| 704 | try comp.addPragmaHandler("GCC", gcc); | |
| 705 | try comp.addPragmaHandler("once", once); | |
| 706 | try comp.addPragmaHandler("message", message); | |
| 707 | } | |
| 708 | ||
| 709 | pub fn getPragma(comp: *Compilation, name: []const u8) ?*Pragma { | |
| 710 | return comp.pragma_handlers.get(name); | |
| 711 | } | |
| 712 | ||
| 713 | const PragmaEvent = enum { | |
| 714 | before_preprocess, | |
| 715 | before_parse, | |
| 716 | after_parse, | |
| 717 | }; | |
| 718 | ||
| 719 | pub fn pragmaEvent(comp: *Compilation, event: PragmaEvent) void { | |
| 720 | for (comp.pragma_handlers.values()) |pragma| { | |
| 721 | const maybe_func = switch (event) { | |
| 722 | .before_preprocess => pragma.beforePreprocess, | |
| 723 | .before_parse => pragma.beforeParse, | |
| 724 | .after_parse => pragma.afterParse, | |
| 725 | }; | |
| 726 | if (maybe_func) |func| func(pragma, comp); | |
| 727 | } | |
| 728 | } | |
| 729 | ||
| 730 | pub const renderErrors = Diagnostics.render; | |
| 731 | ||
| 732 | pub fn isTlsSupported(comp: *Compilation) bool { | |
| 733 | if (comp.target.isDarwin()) { | |
| 734 | var supported = false; | |
| 735 | switch (comp.target.os.tag) { | |
| 736 | .macos => supported = !(comp.target.os.isAtLeast(.macos, .{ .major = 10, .minor = 7 }) orelse false), | |
| 737 | else => {}, | |
| 738 | } | |
| 739 | return supported; | |
| 740 | } | |
| 741 | return switch (comp.target.cpu.arch) { | |
| 742 | .tce, .tcele, .bpfel, .bpfeb, .msp430, .nvptx, .nvptx64, .i386, .arm, .armeb, .thumb, .thumbeb => false, | |
| 743 | else => true, | |
| 744 | }; | |
| 745 | } | |
| 746 | ||
| 747 | /// Default alignment (in bytes) for __attribute__((aligned)) when no alignment is specified | |
| 748 | pub fn defaultAlignment(comp: *const Compilation) u29 { | |
| 749 | switch (comp.target.cpu.arch) { | |
| 750 | .avr => return 1, | |
| 751 | .arm, | |
| 752 | .armeb, | |
| 753 | .thumb, | |
| 754 | .thumbeb, | |
| 755 | => switch (comp.target.abi) { | |
| 756 | .gnueabi, .gnueabihf, .eabi, .eabihf, .musleabi, .musleabihf => return 8, | |
| 757 | else => {}, | |
| 758 | }, | |
| 759 | else => {}, | |
| 760 | } | |
| 761 | return 16; | |
| 762 | } | |
| 763 | ||
| 764 | test "addSourceFromReader" { | |
| 765 | const Test = struct { | |
| 766 | fn addSourceFromReader(str: []const u8, expected: []const u8, warning_count: u32, splices: []const u32) !void { | |
| 767 | var comp = Compilation.init(std.testing.allocator); | |
| 768 | defer comp.deinit(); | |
| 769 | ||
| 770 | const contents = try comp.gpa.alloc(u8, 1024); | |
| 771 | ||
| 772 | var reader = std.io.fixedBufferStream(str).reader(); | |
| 773 | const source = try comp.addSourceFromReader(reader, "path", contents); | |
| 774 | ||
| 775 | try std.testing.expectEqualStrings(expected, source.buf); | |
| 776 | try std.testing.expectEqual(warning_count, @intCast(u32, comp.diag.list.items.len)); | |
| 777 | try std.testing.expectEqualSlices(u32, splices, source.splice_locs); | |
| 778 | } | |
| 779 | }; | |
| 780 | try Test.addSourceFromReader("ab\\\nc", "abc", 0, &.{2}); | |
| 781 | try Test.addSourceFromReader("ab\\\rc", "abc", 0, &.{2}); | |
| 782 | try Test.addSourceFromReader("ab\\\r\nc", "abc", 0, &.{2}); | |
| 783 | try Test.addSourceFromReader("ab\\ \nc", "abc", 1, &.{2}); | |
| 784 | try Test.addSourceFromReader("ab\\\t\nc", "abc", 1, &.{2}); | |
| 785 | try Test.addSourceFromReader("ab\\ \t\nc", "abc", 1, &.{2}); | |
| 786 | try Test.addSourceFromReader("ab\\\r \nc", "ab \nc", 0, &.{2}); | |
| 787 | try Test.addSourceFromReader("ab\\\\\nc", "ab\\c", 0, &.{3}); | |
| 788 | try Test.addSourceFromReader("ab\\ \r\nc", "abc", 1, &.{2}); | |
| 789 | try Test.addSourceFromReader("ab\\ \\\nc", "ab\\ c", 0, &.{4}); | |
| 790 | try Test.addSourceFromReader("ab\\\r\\\nc", "abc", 0, &.{ 2, 2 }); | |
| 791 | try Test.addSourceFromReader("ab\\ \rc", "abc", 1, &.{2}); | |
| 792 | try Test.addSourceFromReader("ab\\", "ab\\", 0, &.{}); | |
| 793 | try Test.addSourceFromReader("ab\\\\", "ab\\\\", 0, &.{}); | |
| 794 | try Test.addSourceFromReader("ab\\ ", "ab\\ ", 0, &.{}); | |
| 795 | try Test.addSourceFromReader("ab\\\n", "ab", 0, &.{2}); | |
| 796 | try Test.addSourceFromReader("ab\\\r\n", "ab", 0, &.{2}); | |
| 797 | try Test.addSourceFromReader("ab\\\r", "ab", 0, &.{2}); | |
| 798 | ||
| 799 | // carriage return normalization | |
| 800 | try Test.addSourceFromReader("ab\r", "ab\n", 0, &.{}); | |
| 801 | try Test.addSourceFromReader("ab\r\r", "ab\n\n", 0, &.{}); | |
| 802 | try Test.addSourceFromReader("ab\r\r\n", "ab\n\n", 0, &.{}); | |
| 803 | try Test.addSourceFromReader("ab\r\r\n\r", "ab\n\n\n", 0, &.{}); | |
| 804 | try Test.addSourceFromReader("\r\\", "\n\\", 0, &.{}); | |
| 805 | try Test.addSourceFromReader("\\\r\\", "\\", 0, &.{0}); | |
| 806 | } | |
| 807 | ||
| 808 | test "addSourceFromReader - exhaustive check for carriage return elimination" { | |
| 809 | const alphabet = [_]u8{ '\r', '\n', ' ', '\\', 'a' }; | |
| 810 | const alen = alphabet.len; | |
| 811 | var buf: [alphabet.len]u8 = [1]u8{alphabet[0]} ** alen; | |
| 812 | ||
| 813 | var comp = Compilation.init(std.testing.allocator); | |
| 814 | defer comp.deinit(); | |
| 815 | ||
| 816 | var source_count: u32 = 0; | |
| 817 | ||
| 818 | while (true) { | |
| 819 | const source = try comp.addSourceFromBuffer(&buf, &buf); | |
| 820 | source_count += 1; | |
| 821 | try std.testing.expect(std.mem.indexOfScalar(u8, source.buf, '\r') == null); | |
| 822 | ||
| 823 | if (std.mem.allEqual(u8, &buf, alphabet[alen - 1])) break; | |
| 824 | ||
| 825 | var idx = std.mem.indexOfScalar(u8, &alphabet, buf[buf.len - 1]).?; | |
| 826 | buf[buf.len - 1] = alphabet[(idx + 1) % alen]; | |
| 827 | var j = buf.len - 1; | |
| 828 | while (j > 0) : (j -= 1) { | |
| 829 | idx = std.mem.indexOfScalar(u8, &alphabet, buf[j - 1]).?; | |
| 830 | if (buf[j] == alphabet[0]) buf[j - 1] = alphabet[(idx + 1) % alen] else break; | |
| 831 | } | |
| 832 | } | |
| 833 | try std.testing.expect(source_count == std.math.powi(usize, alen, alen) catch unreachable); | |
| 834 | } |
src/aro/Diagnostics.zig created+1943| ... | ... | @@ -0,0 +1,1943 @@ |
| 1 | const std = @import("std"); | |
| 2 | const mem = std.mem; | |
| 3 | const Allocator = mem.Allocator; | |
| 4 | const Source = @import("Source.zig"); | |
| 5 | const Compilation = @import("Compilation.zig"); | |
| 6 | const Attribute = @import("Attribute.zig"); | |
| 7 | const Tree = @import("Tree.zig"); | |
| 8 | const util = @import("util.zig"); | |
| 9 | const is_windows = @import("builtin").os.tag == .windows; | |
| 10 | ||
| 11 | const Diagnostics = @This(); | |
| 12 | ||
| 13 | pub const Message = struct { | |
| 14 | tag: Tag, | |
| 15 | kind: Kind = undefined, | |
| 16 | loc: Source.Location = .{}, | |
| 17 | extra: Extra = .{ .none = {} }, | |
| 18 | ||
| 19 | pub const Extra = union { | |
| 20 | str: []const u8, | |
| 21 | tok_id: struct { | |
| 22 | expected: Tree.Token.Id, | |
| 23 | actual: Tree.Token.Id, | |
| 24 | }, | |
| 25 | tok_id_expected: Tree.Token.Id, | |
| 26 | arguments: struct { | |
| 27 | expected: u32, | |
| 28 | actual: u32, | |
| 29 | }, | |
| 30 | codepoints: struct { | |
| 31 | actual: u21, | |
| 32 | resembles: u21, | |
| 33 | }, | |
| 34 | attr_arg_count: struct { | |
| 35 | attribute: Attribute.Tag, | |
| 36 | expected: u32, | |
| 37 | }, | |
| 38 | attr_arg_type: struct { | |
| 39 | expected: Attribute.ArgumentType, | |
| 40 | actual: Attribute.ArgumentType, | |
| 41 | }, | |
| 42 | attr_enum: struct { | |
| 43 | tag: Attribute.Tag, | |
| 44 | }, | |
| 45 | ignored_record_attr: struct { | |
| 46 | tag: Attribute.Tag, | |
| 47 | specifier: enum { @"struct", @"union", @"enum" }, | |
| 48 | }, | |
| 49 | actual_codepoint: u21, | |
| 50 | unsigned: u64, | |
| 51 | signed: i64, | |
| 52 | none: void, | |
| 53 | }; | |
| 54 | }; | |
| 55 | ||
| 56 | pub const Tag = std.meta.DeclEnum(messages); | |
| 57 | ||
| 58 | // u4 to avoid any possible packed struct issues | |
| 59 | pub const Kind = enum(u4) { @"fatal error", @"error", note, warning, off, default }; | |
| 60 | ||
| 61 | pub const Options = packed struct { | |
| 62 | // do not directly use these, instead add `const NAME = true;` | |
| 63 | all: Kind = .default, | |
| 64 | extra: Kind = .default, | |
| 65 | pedantic: Kind = .default, | |
| 66 | ||
| 67 | @"unsupported-pragma": Kind = .default, | |
| 68 | @"c99-extensions": Kind = .default, | |
| 69 | @"implicit-int": Kind = .default, | |
| 70 | @"duplicate-decl-specifier": Kind = .default, | |
| 71 | @"missing-declaration": Kind = .default, | |
| 72 | @"extern-initializer": Kind = .default, | |
| 73 | @"implicit-function-declaration": Kind = .default, | |
| 74 | @"unused-value": Kind = .default, | |
| 75 | @"unreachable-code": Kind = .default, | |
| 76 | @"unknown-warning-option": Kind = .default, | |
| 77 | @"gnu-empty-struct": Kind = .default, | |
| 78 | @"gnu-alignof-expression": Kind = .default, | |
| 79 | @"macro-redefined": Kind = .default, | |
| 80 | @"generic-qual-type": Kind = .default, | |
| 81 | multichar: Kind = .default, | |
| 82 | @"pointer-integer-compare": Kind = .default, | |
| 83 | @"compare-distinct-pointer-types": Kind = .default, | |
| 84 | @"literal-conversion": Kind = .default, | |
| 85 | @"cast-qualifiers": Kind = .default, | |
| 86 | @"array-bounds": Kind = .default, | |
| 87 | @"int-conversion": Kind = .default, | |
| 88 | @"pointer-type-mismatch": Kind = .default, | |
| 89 | @"c2x-extensions": Kind = .default, | |
| 90 | @"incompatible-pointer-types": Kind = .default, | |
| 91 | @"excess-initializers": Kind = .default, | |
| 92 | @"division-by-zero": Kind = .default, | |
| 93 | @"initializer-overrides": Kind = .default, | |
| 94 | @"incompatible-pointer-types-discards-qualifiers": Kind = .default, | |
| 95 | @"unknown-attributes": Kind = .default, | |
| 96 | @"ignored-attributes": Kind = .default, | |
| 97 | @"builtin-macro-redefined": Kind = .default, | |
| 98 | @"gnu-label-as-value": Kind = .default, | |
| 99 | @"malformed-warning-check": Kind = .default, | |
| 100 | @"#pragma-messages": Kind = .default, | |
| 101 | @"newline-eof": Kind = .default, | |
| 102 | @"empty-translation-unit": Kind = .default, | |
| 103 | @"implicitly-unsigned-literal": Kind = .default, | |
| 104 | @"c99-compat": Kind = .default, | |
| 105 | @"unicode-zero-width": Kind = .default, | |
| 106 | @"unicode-homoglyph": Kind = .default, | |
| 107 | @"return-type": Kind = .default, | |
| 108 | @"dollar-in-identifier-extension": Kind = .default, | |
| 109 | @"unknown-pragmas": Kind = .default, | |
| 110 | @"predefined-identifier-outside-function": Kind = .default, | |
| 111 | @"many-braces-around-scalar-init": Kind = .default, | |
| 112 | uninitialized: Kind = .default, | |
| 113 | @"gnu-statement-expression": Kind = .default, | |
| 114 | @"gnu-imaginary-constant": Kind = .default, | |
| 115 | @"ignored-qualifiers": Kind = .default, | |
| 116 | @"integer-overflow": Kind = .default, | |
| 117 | @"extra-semi": Kind = .default, | |
| 118 | @"gnu-binary-literal": Kind = .default, | |
| 119 | @"variadic-macros": Kind = .default, | |
| 120 | varargs: Kind = .default, | |
| 121 | @"#warnings": Kind = .default, | |
| 122 | @"deprecated-declarations": Kind = .default, | |
| 123 | @"backslash-newline-escape": Kind = .default, | |
| 124 | }; | |
| 125 | ||
| 126 | const messages = struct { | |
| 127 | const todo = struct { // Maybe someday this will no longer be needed. | |
| 128 | const msg = "TODO: {s}"; | |
| 129 | const extra = .str; | |
| 130 | const kind = .@"error"; | |
| 131 | }; | |
| 132 | const error_directive = struct { | |
| 133 | const msg = "{s}"; | |
| 134 | const extra = .str; | |
| 135 | const kind = .@"error"; | |
| 136 | }; | |
| 137 | const warning_directive = struct { | |
| 138 | const msg = "{s}"; | |
| 139 | const opt = "#warnings"; | |
| 140 | const extra = .str; | |
| 141 | const kind = .@"warning"; | |
| 142 | }; | |
| 143 | const elif_without_if = struct { | |
| 144 | const msg = "#elif without #if"; | |
| 145 | const kind = .@"error"; | |
| 146 | }; | |
| 147 | const elif_after_else = struct { | |
| 148 | const msg = "#elif after #else"; | |
| 149 | const kind = .@"error"; | |
| 150 | }; | |
| 151 | const else_without_if = struct { | |
| 152 | const msg = "#else without #if"; | |
| 153 | const kind = .@"error"; | |
| 154 | }; | |
| 155 | const else_after_else = struct { | |
| 156 | const msg = "#else after #else"; | |
| 157 | const kind = .@"error"; | |
| 158 | }; | |
| 159 | const endif_without_if = struct { | |
| 160 | const msg = "#endif without #if"; | |
| 161 | const kind = .@"error"; | |
| 162 | }; | |
| 163 | const unknown_pragma = struct { | |
| 164 | const msg = "unknown pragma ignored"; | |
| 165 | const opt = "unknown-pragmas"; | |
| 166 | const kind = .off; | |
| 167 | const all = true; | |
| 168 | }; | |
| 169 | const line_simple_digit = struct { | |
| 170 | const msg = "#line directive requires a simple digit sequence"; | |
| 171 | const kind = .@"error"; | |
| 172 | }; | |
| 173 | const line_invalid_filename = struct { | |
| 174 | const msg = "invalid filename for #line directive"; | |
| 175 | const kind = .@"error"; | |
| 176 | }; | |
| 177 | const unterminated_conditional_directive = struct { | |
| 178 | const msg = "unterminated conditional directive"; | |
| 179 | const kind = .@"error"; | |
| 180 | }; | |
| 181 | const invalid_preprocessing_directive = struct { | |
| 182 | const msg = "invalid preprocessing directive"; | |
| 183 | const kind = .@"error"; | |
| 184 | }; | |
| 185 | const macro_name_missing = struct { | |
| 186 | const msg = "macro name missing"; | |
| 187 | const kind = .@"error"; | |
| 188 | }; | |
| 189 | const extra_tokens_directive_end = struct { | |
| 190 | const msg = "extra tokens at end of macro directive"; | |
| 191 | const kind = .@"error"; | |
| 192 | }; | |
| 193 | const expected_value_in_expr = struct { | |
| 194 | const msg = "expected value in expression"; | |
| 195 | const kind = .@"error"; | |
| 196 | }; | |
| 197 | const closing_paren = struct { | |
| 198 | const msg = "expected closing ')'"; | |
| 199 | const kind = .@"error"; | |
| 200 | }; | |
| 201 | const to_match_paren = struct { | |
| 202 | const msg = "to match this '('"; | |
| 203 | const kind = .note; | |
| 204 | }; | |
| 205 | const to_match_brace = struct { | |
| 206 | const msg = "to match this '{'"; | |
| 207 | const kind = .note; | |
| 208 | }; | |
| 209 | const to_match_bracket = struct { | |
| 210 | const msg = "to match this '['"; | |
| 211 | const kind = .note; | |
| 212 | }; | |
| 213 | const header_str_closing = struct { | |
| 214 | const msg = "expected closing '>'"; | |
| 215 | const kind = .@"error"; | |
| 216 | }; | |
| 217 | const header_str_match = struct { | |
| 218 | const msg = "to match this '<'"; | |
| 219 | const kind = .note; | |
| 220 | }; | |
| 221 | const string_literal_in_pp_expr = struct { | |
| 222 | const msg = "string literal in preprocessor expression"; | |
| 223 | const kind = .@"error"; | |
| 224 | }; | |
| 225 | const float_literal_in_pp_expr = struct { | |
| 226 | const msg = "floating point literal in preprocessor expression"; | |
| 227 | const kind = .@"error"; | |
| 228 | }; | |
| 229 | const defined_as_macro_name = struct { | |
| 230 | const msg = "'defined' cannot be used as a macro name"; | |
| 231 | const kind = .@"error"; | |
| 232 | }; | |
| 233 | const macro_name_must_be_identifier = struct { | |
| 234 | const msg = "macro name must be an identifier"; | |
| 235 | const kind = .@"error"; | |
| 236 | }; | |
| 237 | const whitespace_after_macro_name = struct { | |
| 238 | const msg = "ISO C99 requires whitespace after the macro name"; | |
| 239 | const opt = "c99-extensions"; | |
| 240 | const kind = .warning; | |
| 241 | }; | |
| 242 | const hash_hash_at_start = struct { | |
| 243 | const msg = "'##' cannot appear at the start of a macro expansion"; | |
| 244 | const kind = .@"error"; | |
| 245 | }; | |
| 246 | const hash_hash_at_end = struct { | |
| 247 | const msg = "'##' cannot appear at the end of a macro expansion"; | |
| 248 | const kind = .@"error"; | |
| 249 | }; | |
| 250 | const pasting_formed_invalid = struct { | |
| 251 | const msg = "pasting formed '{s}', an invalid preprocessing token"; | |
| 252 | const extra = .str; | |
| 253 | const kind = .@"error"; | |
| 254 | }; | |
| 255 | const missing_paren_param_list = struct { | |
| 256 | const msg = "missing ')' in macro parameter list"; | |
| 257 | const kind = .@"error"; | |
| 258 | }; | |
| 259 | const unterminated_macro_param_list = struct { | |
| 260 | const msg = "unterminated macro param list"; | |
| 261 | const kind = .@"error"; | |
| 262 | }; | |
| 263 | const invalid_token_param_list = struct { | |
| 264 | const msg = "invalid token in macro parameter list"; | |
| 265 | const kind = .@"error"; | |
| 266 | }; | |
| 267 | const expected_comma_param_list = struct { | |
| 268 | const msg = "expected comma in macro parameter list"; | |
| 269 | const kind = .@"error"; | |
| 270 | }; | |
| 271 | const hash_not_followed_param = struct { | |
| 272 | const msg = "'#' is not followed by a macro parameter"; | |
| 273 | const kind = .@"error"; | |
| 274 | }; | |
| 275 | const expected_filename = struct { | |
| 276 | const msg = "expected \"FILENAME\" or <FILENAME>"; | |
| 277 | const kind = .@"error"; | |
| 278 | }; | |
| 279 | const empty_filename = struct { | |
| 280 | const msg = "empty filename"; | |
| 281 | const kind = .@"error"; | |
| 282 | }; | |
| 283 | const expected_invalid = struct { | |
| 284 | const msg = "expected '{s}', found invalid bytes"; | |
| 285 | const extra = .tok_id_expected; | |
| 286 | const kind = .@"error"; | |
| 287 | }; | |
| 288 | const expected_eof = struct { | |
| 289 | const msg = "expected '{s}' before end of file"; | |
| 290 | const extra = .tok_id_expected; | |
| 291 | const kind = .@"error"; | |
| 292 | }; | |
| 293 | const expected_token = struct { | |
| 294 | const msg = "expected '{s}', found '{s}'"; | |
| 295 | const extra = .tok_id; | |
| 296 | const kind = .@"error"; | |
| 297 | }; | |
| 298 | const expected_expr = struct { | |
| 299 | const msg = "expected expression"; | |
| 300 | const kind = .@"error"; | |
| 301 | }; | |
| 302 | const expected_integer_constant_expr = struct { | |
| 303 | const msg = "expression is not an integer constant expression"; | |
| 304 | const kind = .@"error"; | |
| 305 | }; | |
| 306 | const missing_type_specifier = struct { | |
| 307 | const msg = "type specifier missing, defaults to 'int'"; | |
| 308 | const opt = "implicit-int"; | |
| 309 | const kind = .warning; | |
| 310 | const all = true; | |
| 311 | }; | |
| 312 | const multiple_storage_class = struct { | |
| 313 | const msg = "cannot combine with previous '{s}' declaration specifier"; | |
| 314 | const extra = .str; | |
| 315 | const kind = .@"error"; | |
| 316 | }; | |
| 317 | const static_assert_failure = struct { | |
| 318 | const msg = "static assertion failed"; | |
| 319 | const kind = .@"error"; | |
| 320 | }; | |
| 321 | const static_assert_failure_message = struct { | |
| 322 | const msg = "static assertion failed {s}"; | |
| 323 | const extra = .str; | |
| 324 | const kind = .@"error"; | |
| 325 | }; | |
| 326 | const expected_type = struct { | |
| 327 | const msg = "expected a type"; | |
| 328 | const kind = .@"error"; | |
| 329 | }; | |
| 330 | const cannot_combine_spec = struct { | |
| 331 | const msg = "cannot combine with previous '{s}' specifier"; | |
| 332 | const extra = .str; | |
| 333 | const kind = .@"error"; | |
| 334 | }; | |
| 335 | const duplicate_decl_spec = struct { | |
| 336 | const msg = "duplicate '{s}' declaration specifier"; | |
| 337 | const extra = .str; | |
| 338 | const opt = "duplicate-decl-specifier"; | |
| 339 | const kind = .warning; | |
| 340 | const all = true; | |
| 341 | }; | |
| 342 | const restrict_non_pointer = struct { | |
| 343 | const msg = "restrict requires a pointer or reference ('{s}' is invalid)"; | |
| 344 | const extra = .str; | |
| 345 | const kind = .@"error"; | |
| 346 | }; | |
| 347 | const expected_external_decl = struct { | |
| 348 | const msg = "expected external declaration"; | |
| 349 | const kind = .@"error"; | |
| 350 | }; | |
| 351 | const expected_ident_or_l_paren = struct { | |
| 352 | const msg = "expected identifier or '('"; | |
| 353 | const kind = .@"error"; | |
| 354 | }; | |
| 355 | const missing_declaration = struct { | |
| 356 | const msg = "declaration does not declare anything"; | |
| 357 | const opt = "missing-declaration"; | |
| 358 | const kind = .warning; | |
| 359 | }; | |
| 360 | const func_not_in_root = struct { | |
| 361 | const msg = "function definition is not allowed here"; | |
| 362 | const kind = .@"error"; | |
| 363 | }; | |
| 364 | const illegal_initializer = struct { | |
| 365 | const msg = "illegal initializer (only variables can be initialized)"; | |
| 366 | const kind = .@"error"; | |
| 367 | }; | |
| 368 | const extern_initializer = struct { | |
| 369 | const msg = "extern variable has initializer"; | |
| 370 | const opt = "extern-initializer"; | |
| 371 | const kind = .warning; | |
| 372 | }; | |
| 373 | const spec_from_typedef = struct { | |
| 374 | const msg = "'{s}' came from typedef"; | |
| 375 | const extra = .str; | |
| 376 | const kind = .note; | |
| 377 | }; | |
| 378 | const type_is_invalid = struct { | |
| 379 | const msg = "'{s}' is invalid"; | |
| 380 | const extra = .str; | |
| 381 | const kind = .@"error"; | |
| 382 | }; | |
| 383 | const param_before_var_args = struct { | |
| 384 | const msg = "ISO C requires a named parameter before '...'"; | |
| 385 | const kind = .@"error"; | |
| 386 | }; | |
| 387 | const void_only_param = struct { | |
| 388 | const msg = "'void' must be the only parameter if specified"; | |
| 389 | const kind = .@"error"; | |
| 390 | }; | |
| 391 | const void_param_qualified = struct { | |
| 392 | const msg = "'void' parameter cannot be qualified"; | |
| 393 | const kind = .@"error"; | |
| 394 | }; | |
| 395 | const void_must_be_first_param = struct { | |
| 396 | const msg = "'void' must be the first parameter if specified"; | |
| 397 | const kind = .@"error"; | |
| 398 | }; | |
| 399 | const invalid_storage_on_param = struct { | |
| 400 | const msg = "invalid storage class on function parameter"; | |
| 401 | const kind = .@"error"; | |
| 402 | }; | |
| 403 | const threadlocal_non_var = struct { | |
| 404 | const msg = "_Thread_local only allowed on variables"; | |
| 405 | const kind = .@"error"; | |
| 406 | }; | |
| 407 | const func_spec_non_func = struct { | |
| 408 | const msg = "'{s}' can only appear on functions"; | |
| 409 | const extra = .str; | |
| 410 | const kind = .@"error"; | |
| 411 | }; | |
| 412 | const illegal_storage_on_func = struct { | |
| 413 | const msg = "illegal storage class on function"; | |
| 414 | const kind = .@"error"; | |
| 415 | }; | |
| 416 | const illegal_storage_on_global = struct { | |
| 417 | const msg = "illegal storage class on global variable"; | |
| 418 | const kind = .@"error"; | |
| 419 | }; | |
| 420 | const expected_stmt = struct { | |
| 421 | const msg = "expected statement"; | |
| 422 | const kind = .@"error"; | |
| 423 | }; | |
| 424 | const func_cannot_return_func = struct { | |
| 425 | const msg = "function cannot return a function"; | |
| 426 | const kind = .@"error"; | |
| 427 | }; | |
| 428 | const func_cannot_return_array = struct { | |
| 429 | const msg = "function cannot return an array"; | |
| 430 | const kind = .@"error"; | |
| 431 | }; | |
| 432 | const undeclared_identifier = struct { | |
| 433 | const msg = "use of undeclared identifier '{s}'"; | |
| 434 | const extra = .str; | |
| 435 | const kind = .@"error"; | |
| 436 | }; | |
| 437 | const not_callable = struct { | |
| 438 | const msg = "cannot call non function type '{s}'"; | |
| 439 | const extra = .str; | |
| 440 | const kind = .@"error"; | |
| 441 | }; | |
| 442 | const unsupported_str_cat = struct { | |
| 443 | const msg = "unsupported string literal concatenation"; | |
| 444 | const kind = .@"error"; | |
| 445 | }; | |
| 446 | const static_func_not_global = struct { | |
| 447 | const msg = "static functions must be global"; | |
| 448 | const kind = .@"error"; | |
| 449 | }; | |
| 450 | const implicit_func_decl = struct { | |
| 451 | const msg = "implicit declaration of function '{s}' is invalid in C99"; | |
| 452 | const extra = .str; | |
| 453 | const opt = "implicit-function-declaration"; | |
| 454 | const kind = .warning; | |
| 455 | const all = true; | |
| 456 | }; | |
| 457 | const unknown_builtin = struct { | |
| 458 | const msg = "use of unknown builtin '{s}'"; | |
| 459 | const extra = .str; | |
| 460 | const opt = "implicit-function-declaration"; | |
| 461 | const kind = .@"error"; | |
| 462 | const all = true; | |
| 463 | }; | |
| 464 | const expected_param_decl = struct { | |
| 465 | const msg = "expected parameter declaration"; | |
| 466 | const kind = .@"error"; | |
| 467 | }; | |
| 468 | const invalid_old_style_params = struct { | |
| 469 | const msg = "identifier parameter lists are only allowed in function definitions"; | |
| 470 | const kind = .@"error"; | |
| 471 | }; | |
| 472 | const expected_fn_body = struct { | |
| 473 | const msg = "expected function body after function declaration"; | |
| 474 | const kind = .@"error"; | |
| 475 | }; | |
| 476 | const invalid_void_param = struct { | |
| 477 | const msg = "parameter cannot have void type"; | |
| 478 | const kind = .@"error"; | |
| 479 | }; | |
| 480 | const unused_value = struct { | |
| 481 | const msg = "expression result unused"; | |
| 482 | const opt = "unused-value"; | |
| 483 | const kind = .warning; | |
| 484 | const all = true; | |
| 485 | }; | |
| 486 | const continue_not_in_loop = struct { | |
| 487 | const msg = "'continue' statement not in a loop"; | |
| 488 | const kind = .@"error"; | |
| 489 | }; | |
| 490 | const break_not_in_loop_or_switch = struct { | |
| 491 | const msg = "'break' statement not in a loop or a switch"; | |
| 492 | const kind = .@"error"; | |
| 493 | }; | |
| 494 | const unreachable_code = struct { | |
| 495 | const msg = "unreachable code"; | |
| 496 | const opt = "unreachable-code"; | |
| 497 | const kind = .warning; | |
| 498 | const all = true; | |
| 499 | }; | |
| 500 | const duplicate_label = struct { | |
| 501 | const msg = "duplicate label '{s}'"; | |
| 502 | const extra = .str; | |
| 503 | const kind = .@"error"; | |
| 504 | }; | |
| 505 | const previous_label = struct { | |
| 506 | const msg = "previous definition of label '{s}' was here"; | |
| 507 | const extra = .str; | |
| 508 | const kind = .note; | |
| 509 | }; | |
| 510 | const undeclared_label = struct { | |
| 511 | const msg = "use of undeclared label '{s}'"; | |
| 512 | const extra = .str; | |
| 513 | const kind = .@"error"; | |
| 514 | }; | |
| 515 | const case_not_in_switch = struct { | |
| 516 | const msg = "'{s}' statement not in a switch statement"; | |
| 517 | const extra = .str; | |
| 518 | const kind = .@"error"; | |
| 519 | }; | |
| 520 | const duplicate_switch_case_signed = struct { | |
| 521 | const msg = "duplicate case value '{d}'"; | |
| 522 | const extra = .signed; | |
| 523 | const kind = .@"error"; | |
| 524 | }; | |
| 525 | const duplicate_switch_case_unsigned = struct { | |
| 526 | const msg = "duplicate case value '{d}'"; | |
| 527 | const extra = .unsigned; | |
| 528 | const kind = .@"error"; | |
| 529 | }; | |
| 530 | const multiple_default = struct { | |
| 531 | const msg = "multiple default cases in the same switch"; | |
| 532 | const kind = .@"error"; | |
| 533 | }; | |
| 534 | const previous_case = struct { | |
| 535 | const msg = "previous case defined here"; | |
| 536 | const kind = .note; | |
| 537 | }; | |
| 538 | const expected_arguments = struct { | |
| 539 | const msg = "expected {d} argument(s) got {d}"; | |
| 540 | const extra = .arguments; | |
| 541 | const kind = .@"error"; | |
| 542 | }; | |
| 543 | const expected_arguments_old = struct { | |
| 544 | const msg = expected_arguments.msg; | |
| 545 | const extra = .arguments; | |
| 546 | const kind = .warning; | |
| 547 | }; | |
| 548 | const expected_at_least_arguments = struct { | |
| 549 | const msg = "expected at least {d} argument(s) got {d}"; | |
| 550 | const extra = .arguments; | |
| 551 | const kind = .warning; | |
| 552 | }; | |
| 553 | const invalid_static_star = struct { | |
| 554 | const msg = "'static' may not be used with an unspecified variable length array size"; | |
| 555 | const kind = .@"error"; | |
| 556 | }; | |
| 557 | const static_non_param = struct { | |
| 558 | const msg = "'static' used outside of function parameters"; | |
| 559 | const kind = .@"error"; | |
| 560 | }; | |
| 561 | const array_qualifiers = struct { | |
| 562 | const msg = "type qualifier in non parameter array type"; | |
| 563 | const kind = .@"error"; | |
| 564 | }; | |
| 565 | const star_non_param = struct { | |
| 566 | const msg = "star modifier used outside of function parameters"; | |
| 567 | const kind = .@"error"; | |
| 568 | }; | |
| 569 | const variable_len_array_file_scope = struct { | |
| 570 | const msg = "variable length arrays not allowed at file scope"; | |
| 571 | const kind = .@"error"; | |
| 572 | }; | |
| 573 | const useless_static = struct { | |
| 574 | const msg = "'static' useless without a constant size"; | |
| 575 | const kind = .warning; | |
| 576 | const w_extra = true; | |
| 577 | }; | |
| 578 | const negative_array_size = struct { | |
| 579 | const msg = "array size must be 0 or greater"; | |
| 580 | const kind = .@"error"; | |
| 581 | }; | |
| 582 | const array_incomplete_elem = struct { | |
| 583 | const msg = "array has incomplete element type '{s}'"; | |
| 584 | const extra = .str; | |
| 585 | const kind = .@"error"; | |
| 586 | }; | |
| 587 | const array_func_elem = struct { | |
| 588 | const msg = "arrays cannot have functions as their element type"; | |
| 589 | const kind = .@"error"; | |
| 590 | }; | |
| 591 | const static_non_outermost_array = struct { | |
| 592 | const msg = "'static' used in non-outermost array type"; | |
| 593 | const kind = .@"error"; | |
| 594 | }; | |
| 595 | const qualifier_non_outermost_array = struct { | |
| 596 | const msg = "type qualifier used in non-outermost array type"; | |
| 597 | const kind = .@"error"; | |
| 598 | }; | |
| 599 | const unterminated_macro_arg_list = struct { | |
| 600 | const msg = "unterminated function macro argument list"; | |
| 601 | const kind = .@"error"; | |
| 602 | }; | |
| 603 | const unknown_warning = struct { | |
| 604 | const msg = "unknown warning '{s}'"; | |
| 605 | const extra = .str; | |
| 606 | const opt = "unknown-warning-option"; | |
| 607 | const kind = .warning; | |
| 608 | }; | |
| 609 | const overflow_signed = struct { | |
| 610 | const msg = "overflow in expression; result is '{d}'"; | |
| 611 | const extra = .signed; | |
| 612 | const opt = "integer-overflow"; | |
| 613 | const kind = .warning; | |
| 614 | }; | |
| 615 | const overflow_unsigned = struct { | |
| 616 | const msg = overflow_signed.msg; | |
| 617 | const extra = .unsigned; | |
| 618 | const opt = "integer-overflow"; | |
| 619 | const kind = .warning; | |
| 620 | }; | |
| 621 | const int_literal_too_big = struct { | |
| 622 | const msg = "integer literal is too large to be represented in any integer type"; | |
| 623 | const kind = .@"error"; | |
| 624 | }; | |
| 625 | const indirection_ptr = struct { | |
| 626 | const msg = "indirection requires pointer operand"; | |
| 627 | const kind = .@"error"; | |
| 628 | }; | |
| 629 | const addr_of_rvalue = struct { | |
| 630 | const msg = "cannot take the address of an rvalue"; | |
| 631 | const kind = .@"error"; | |
| 632 | }; | |
| 633 | const not_assignable = struct { | |
| 634 | const msg = "expression is not assignable"; | |
| 635 | const kind = .@"error"; | |
| 636 | }; | |
| 637 | const ident_or_l_brace = struct { | |
| 638 | const msg = "expected identifier or '{'"; | |
| 639 | const kind = .@"error"; | |
| 640 | }; | |
| 641 | const empty_enum = struct { | |
| 642 | const msg = "empty enum is invalid"; | |
| 643 | const kind = .@"error"; | |
| 644 | }; | |
| 645 | const redefinition = struct { | |
| 646 | const msg = "redefinition of '{s}'"; | |
| 647 | const extra = .str; | |
| 648 | const kind = .@"error"; | |
| 649 | }; | |
| 650 | const previous_definition = struct { | |
| 651 | const msg = "previous definition is here"; | |
| 652 | const kind = .note; | |
| 653 | }; | |
| 654 | const expected_identifier = struct { | |
| 655 | const msg = "expected identifier"; | |
| 656 | const kind = .@"error"; | |
| 657 | }; | |
| 658 | const expected_str_literal = struct { | |
| 659 | const msg = "expected string literal for diagnostic message in static_assert"; | |
| 660 | const kind = .@"error"; | |
| 661 | }; | |
| 662 | const expected_str_literal_in = struct { | |
| 663 | const msg = "expected string literal in '{s}'"; | |
| 664 | const extra = .str; | |
| 665 | const kind = .@"error"; | |
| 666 | }; | |
| 667 | const parameter_missing = struct { | |
| 668 | const msg = "parameter named '{s}' is missing"; | |
| 669 | const extra = .str; | |
| 670 | const kind = .@"error"; | |
| 671 | }; | |
| 672 | const empty_record = struct { | |
| 673 | const msg = "empty {s} is a GNU extension"; | |
| 674 | const extra = .str; | |
| 675 | const opt = "gnu-empty-struct"; | |
| 676 | const kind = .off; | |
| 677 | const pedantic = true; | |
| 678 | }; | |
| 679 | const wrong_tag = struct { | |
| 680 | const msg = "use of '{s}' with tag type that does not match previous definition"; | |
| 681 | const extra = .str; | |
| 682 | const kind = .@"error"; | |
| 683 | }; | |
| 684 | const expected_parens_around_typename = struct { | |
| 685 | const msg = "expected parentheses around type name"; | |
| 686 | const kind = .@"error"; | |
| 687 | }; | |
| 688 | const alignof_expr = struct { | |
| 689 | const msg = "'_Alignof' applied to an expression is a GNU extension"; | |
| 690 | const opt = "gnu-alignof-expression"; | |
| 691 | const kind = .warning; | |
| 692 | const suppress_gnu = true; | |
| 693 | }; | |
| 694 | const invalid_sizeof = struct { | |
| 695 | const msg = "invalid application of 'sizeof' to an incomplete type '{s}'"; | |
| 696 | const extra = .str; | |
| 697 | const kind = .@"error"; | |
| 698 | }; | |
| 699 | const macro_redefined = struct { | |
| 700 | const msg = "'{s}' macro redefined"; | |
| 701 | const extra = .str; | |
| 702 | const opt = "macro-redefined"; | |
| 703 | const kind = .warning; | |
| 704 | }; | |
| 705 | const generic_qual_type = struct { | |
| 706 | const msg = "generic association with qualifiers cannot be matched with"; | |
| 707 | const opt = "generic-qual-type"; | |
| 708 | const kind = .warning; | |
| 709 | }; | |
| 710 | const generic_duplicate = struct { | |
| 711 | const msg = "type '{s}' in generic association compatible with previously specified type"; | |
| 712 | const extra = .str; | |
| 713 | const kind = .@"error"; | |
| 714 | }; | |
| 715 | const generic_duplicate_default = struct { | |
| 716 | const msg = "duplicate default generic association"; | |
| 717 | const kind = .@"error"; | |
| 718 | }; | |
| 719 | const generic_no_match = struct { | |
| 720 | const msg = "controlling expression type '{s}' not compatible with any generic association type"; | |
| 721 | const extra = .str; | |
| 722 | const kind = .@"error"; | |
| 723 | }; | |
| 724 | const escape_sequence_overflow = struct { | |
| 725 | const msg = "escape sequence out of range"; | |
| 726 | const kind = .@"error"; | |
| 727 | }; | |
| 728 | const invalid_universal_character = struct { | |
| 729 | const msg = "invalid universal character"; | |
| 730 | const kind = .@"error"; | |
| 731 | }; | |
| 732 | const multichar_literal = struct { | |
| 733 | const msg = "multi-character character constant"; | |
| 734 | const opt = "multichar"; | |
| 735 | const kind = .warning; | |
| 736 | const all = true; | |
| 737 | }; | |
| 738 | const unicode_multichar_literal = struct { | |
| 739 | const msg = "Unicode character literals may not contain multiple characters"; | |
| 740 | const kind = .@"error"; | |
| 741 | }; | |
| 742 | const wide_multichar_literal = struct { | |
| 743 | const msg = "extraneous characters in character constant ignored"; | |
| 744 | const kind = .warning; | |
| 745 | }; | |
| 746 | const char_lit_too_wide = struct { | |
| 747 | const msg = "character constant too long for its type"; | |
| 748 | const kind = .warning; | |
| 749 | const all = true; | |
| 750 | }; | |
| 751 | const char_too_large = struct { | |
| 752 | const msg = "character too large for enclosing character literal type"; | |
| 753 | const kind = .@"error"; | |
| 754 | }; | |
| 755 | const must_use_struct = struct { | |
| 756 | const msg = "must use 'struct' tag to refer to type '{s}'"; | |
| 757 | const extra = .str; | |
| 758 | const kind = .@"error"; | |
| 759 | }; | |
| 760 | const must_use_union = struct { | |
| 761 | const msg = "must use 'union' tag to refer to type '{s}'"; | |
| 762 | const extra = .str; | |
| 763 | const kind = .@"error"; | |
| 764 | }; | |
| 765 | const must_use_enum = struct { | |
| 766 | const msg = "must use 'enum' tag to refer to type '{s}'"; | |
| 767 | const extra = .str; | |
| 768 | const kind = .@"error"; | |
| 769 | }; | |
| 770 | const redefinition_different_sym = struct { | |
| 771 | const msg = "redefinition of '{s}' as different kind of symbol"; | |
| 772 | const extra = .str; | |
| 773 | const kind = .@"error"; | |
| 774 | }; | |
| 775 | const redefinition_incompatible = struct { | |
| 776 | const msg = "redefinition of '{s}' with a different type"; | |
| 777 | const extra = .str; | |
| 778 | const kind = .@"error"; | |
| 779 | }; | |
| 780 | const redefinition_of_parameter = struct { | |
| 781 | const msg = "redefinition of parameter '{s}'"; | |
| 782 | const extra = .str; | |
| 783 | const kind = .@"error"; | |
| 784 | }; | |
| 785 | const invalid_bin_types = struct { | |
| 786 | const msg = "invalid operands to binary expression ({s})"; | |
| 787 | const extra = .str; | |
| 788 | const kind = .@"error"; | |
| 789 | }; | |
| 790 | const comparison_ptr_int = struct { | |
| 791 | const msg = "comparison between pointer and integer ({s})"; | |
| 792 | const extra = .str; | |
| 793 | const opt = "pointer-integer-compare"; | |
| 794 | const kind = .warning; | |
| 795 | }; | |
| 796 | const comparison_distinct_ptr = struct { | |
| 797 | const msg = "comparison of distinct pointer types ({s})"; | |
| 798 | const extra = .str; | |
| 799 | const opt = "compare-distinct-pointer-types"; | |
| 800 | const kind = .warning; | |
| 801 | }; | |
| 802 | const incompatible_pointers = struct { | |
| 803 | const msg = "incompatible pointer types ({s})"; | |
| 804 | const extra = .str; | |
| 805 | const kind = .@"error"; | |
| 806 | }; | |
| 807 | const invalid_argument_un = struct { | |
| 808 | const msg = "invalid argument type '{s}' to unary expression"; | |
| 809 | const extra = .str; | |
| 810 | const kind = .@"error"; | |
| 811 | }; | |
| 812 | const incompatible_assign = struct { | |
| 813 | const msg = "assignment to {s}"; | |
| 814 | const extra = .str; | |
| 815 | const kind = .@"error"; | |
| 816 | }; | |
| 817 | const implicit_ptr_to_int = struct { | |
| 818 | const msg = "implicit pointer to integer conversion from {s}"; | |
| 819 | const extra = .str; | |
| 820 | const opt = "int-conversion"; | |
| 821 | const kind = .warning; | |
| 822 | }; | |
| 823 | const invalid_cast_to_float = struct { | |
| 824 | const msg = "pointer cannot be cast to type '{s}'"; | |
| 825 | const extra = .str; | |
| 826 | const kind = .@"error"; | |
| 827 | }; | |
| 828 | const invalid_cast_to_pointer = struct { | |
| 829 | const msg = "operand of type '{s}' cannot be cast to a pointer type"; | |
| 830 | const extra = .str; | |
| 831 | const kind = .@"error"; | |
| 832 | }; | |
| 833 | const invalid_cast_type = struct { | |
| 834 | const msg = "cannot cast to non arithmetic or pointer type '{s}'"; | |
| 835 | const extra = .str; | |
| 836 | const kind = .@"error"; | |
| 837 | }; | |
| 838 | const qual_cast = struct { | |
| 839 | const msg = "cast to type '{s}' will not preserve qualifiers"; | |
| 840 | const extra = .str; | |
| 841 | const opt = "cast-qualifiers"; | |
| 842 | const kind = .warning; | |
| 843 | }; | |
| 844 | const invalid_index = struct { | |
| 845 | const msg = "array subscript is not an integer"; | |
| 846 | const kind = .@"error"; | |
| 847 | }; | |
| 848 | const invalid_subscript = struct { | |
| 849 | const msg = "subscripted value is not an array or pointer"; | |
| 850 | const kind = .@"error"; | |
| 851 | }; | |
| 852 | const array_after = struct { | |
| 853 | const msg = "array index {d} is past the end of the array"; | |
| 854 | const extra = .unsigned; | |
| 855 | const opt = "array-bounds"; | |
| 856 | const kind = .warning; | |
| 857 | }; | |
| 858 | const array_before = struct { | |
| 859 | const msg = "array index {d} is before the beginning of the array"; | |
| 860 | const extra = .signed; | |
| 861 | const opt = "array-bounds"; | |
| 862 | const kind = .warning; | |
| 863 | }; | |
| 864 | const statement_int = struct { | |
| 865 | const msg = "statement requires expression with integer type ('{s}' invalid)"; | |
| 866 | const extra = .str; | |
| 867 | const kind = .@"error"; | |
| 868 | }; | |
| 869 | const statement_scalar = struct { | |
| 870 | const msg = "statement requires expression with scalar type ('{s}' invalid)"; | |
| 871 | const extra = .str; | |
| 872 | const kind = .@"error"; | |
| 873 | }; | |
| 874 | const func_should_return = struct { | |
| 875 | const msg = "non-void function '{s}' should return a value"; | |
| 876 | const extra = .str; | |
| 877 | const opt = "return-type"; | |
| 878 | const kind = .@"error"; | |
| 879 | const all = true; | |
| 880 | }; | |
| 881 | const incompatible_return = struct { | |
| 882 | const msg = "returning '{s}' from a function with incompatible result type"; | |
| 883 | const extra = .str; | |
| 884 | const kind = .@"error"; | |
| 885 | }; | |
| 886 | const implicit_int_to_ptr = struct { | |
| 887 | const msg = "implicit integer to pointer conversion from {s}"; | |
| 888 | const extra = .str; | |
| 889 | const opt = "int-conversion"; | |
| 890 | const kind = .warning; | |
| 891 | }; | |
| 892 | const func_does_not_return = struct { | |
| 893 | const msg = "non-void function '{s}' does not return a value"; | |
| 894 | const extra = .str; | |
| 895 | const opt = "return-type"; | |
| 896 | const kind = .warning; | |
| 897 | const all = true; | |
| 898 | }; | |
| 899 | const void_func_returns_value = struct { | |
| 900 | const msg = "void function '{s}' should not return a value"; | |
| 901 | const extra = .str; | |
| 902 | const opt = "return-type"; | |
| 903 | const kind = .@"error"; | |
| 904 | const all = true; | |
| 905 | }; | |
| 906 | const incompatible_param = struct { | |
| 907 | const msg = "passing '{s}' to parameter of incompatible type"; | |
| 908 | const extra = .str; | |
| 909 | const kind = .@"error"; | |
| 910 | }; | |
| 911 | const parameter_here = struct { | |
| 912 | const msg = "passing argument to parameter here"; | |
| 913 | const kind = .note; | |
| 914 | }; | |
| 915 | const atomic_array = struct { | |
| 916 | const msg = "atomic cannot be applied to array type '{s}'"; | |
| 917 | const extra = .str; | |
| 918 | const kind = .@"error"; | |
| 919 | }; | |
| 920 | const atomic_func = struct { | |
| 921 | const msg = "atomic cannot be applied to function type '{s}'"; | |
| 922 | const extra = .str; | |
| 923 | const kind = .@"error"; | |
| 924 | }; | |
| 925 | const atomic_incomplete = struct { | |
| 926 | const msg = "atomic cannot be applied to incomplete type '{s}'"; | |
| 927 | const extra = .str; | |
| 928 | const kind = .@"error"; | |
| 929 | }; | |
| 930 | const addr_of_register = struct { | |
| 931 | const msg = "address of register variable requested"; | |
| 932 | const kind = .@"error"; | |
| 933 | }; | |
| 934 | const variable_incomplete_ty = struct { | |
| 935 | const msg = "variable has incomplete type '{s}'"; | |
| 936 | const extra = .str; | |
| 937 | const kind = .@"error"; | |
| 938 | }; | |
| 939 | const parameter_incomplete_ty = struct { | |
| 940 | const msg = "parameter has incomplete type '{s}'"; | |
| 941 | const extra = .str; | |
| 942 | const kind = .@"error"; | |
| 943 | }; | |
| 944 | const deref_incomplete_ty_ptr = struct { | |
| 945 | const msg = "dereferencing pointer to incomplete type '{s}'"; | |
| 946 | const extra = .str; | |
| 947 | const kind = .@"error"; | |
| 948 | }; | |
| 949 | const alignas_on_func = struct { | |
| 950 | const msg = "'_Alignas' attribute only applies to variables and fields"; | |
| 951 | const kind = .@"error"; | |
| 952 | }; | |
| 953 | const alignas_on_param = struct { | |
| 954 | const msg = "'_Alignas' attribute cannot be applied to a function parameter"; | |
| 955 | const kind = .@"error"; | |
| 956 | }; | |
| 957 | const minimum_alignment = struct { | |
| 958 | const msg = "requested alignment is less than minimum alignment of {d}"; | |
| 959 | const extra = .unsigned; | |
| 960 | const kind = .@"error"; | |
| 961 | }; | |
| 962 | const maximum_alignment = struct { | |
| 963 | const msg = "requested alignment of {d} is too large"; | |
| 964 | const extra = .unsigned; | |
| 965 | const kind = .@"error"; | |
| 966 | }; | |
| 967 | const negative_alignment = struct { | |
| 968 | const msg = "requested negative alignment of {d} is invalid"; | |
| 969 | const extra = .signed; | |
| 970 | const kind = .@"error"; | |
| 971 | }; | |
| 972 | const align_ignored = struct { | |
| 973 | const msg = "'_Alignas' attribute is ignored here"; | |
| 974 | const kind = .warning; | |
| 975 | }; | |
| 976 | const zero_align_ignored = struct { | |
| 977 | const msg = "requested alignment of zero is ignored"; | |
| 978 | const kind = .warning; | |
| 979 | }; | |
| 980 | const non_pow2_align = struct { | |
| 981 | const msg = "requested alignment is not a power of 2"; | |
| 982 | const kind = .@"error"; | |
| 983 | }; | |
| 984 | const pointer_mismatch = struct { | |
| 985 | const msg = "pointer type mismatch ({s})"; | |
| 986 | const extra = .str; | |
| 987 | const opt = "pointer-type-mismatch"; | |
| 988 | const kind = .warning; | |
| 989 | }; | |
| 990 | const static_assert_not_constant = struct { | |
| 991 | const msg = "static_assert expression is not an integral constant expression"; | |
| 992 | const kind = .@"error"; | |
| 993 | }; | |
| 994 | const static_assert_missing_message = struct { | |
| 995 | const msg = "static_assert with no message is a C2X extension"; | |
| 996 | const opt = "c2x-extensions"; | |
| 997 | const kind = .warning; | |
| 998 | const suppress_version = .c2x; | |
| 999 | }; | |
| 1000 | const unbound_vla = struct { | |
| 1001 | const msg = "variable length array must be bound in function definition"; | |
| 1002 | const kind = .@"error"; | |
| 1003 | }; | |
| 1004 | const array_too_large = struct { | |
| 1005 | const msg = "array is too large"; | |
| 1006 | const kind = .@"error"; | |
| 1007 | }; | |
| 1008 | const incompatible_ptr_init = struct { | |
| 1009 | const msg = "incompatible pointer types initializing {s}"; | |
| 1010 | const extra = .str; | |
| 1011 | const opt = "incompatible-pointer-types"; | |
| 1012 | const kind = .warning; | |
| 1013 | }; | |
| 1014 | const incompatible_ptr_assign = struct { | |
| 1015 | const msg = "incompatible pointer types assigning to {s}"; | |
| 1016 | const extra = .str; | |
| 1017 | const opt = "incompatible-pointer-types"; | |
| 1018 | const kind = .warning; | |
| 1019 | }; | |
| 1020 | const vla_init = struct { | |
| 1021 | const msg = "variable-sized object may not be initialized"; | |
| 1022 | const kind = .@"error"; | |
| 1023 | }; | |
| 1024 | const func_init = struct { | |
| 1025 | const msg = "illegal initializer type"; | |
| 1026 | const kind = .@"error"; | |
| 1027 | }; | |
| 1028 | const incompatible_init = struct { | |
| 1029 | const msg = "initializing {s}"; | |
| 1030 | const extra = .str; | |
| 1031 | const kind = .@"error"; | |
| 1032 | }; | |
| 1033 | const empty_scalar_init = struct { | |
| 1034 | const msg = "scalar initializer cannot be empty"; | |
| 1035 | const kind = .@"error"; | |
| 1036 | }; | |
| 1037 | const excess_scalar_init = struct { | |
| 1038 | const msg = "excess elements in scalar initializer"; | |
| 1039 | const opt = "excess-initializers"; | |
| 1040 | const kind = .warning; | |
| 1041 | }; | |
| 1042 | const excess_str_init = struct { | |
| 1043 | const msg = "excess elements in string initializer"; | |
| 1044 | const opt = "excess-initializers"; | |
| 1045 | const kind = .warning; | |
| 1046 | }; | |
| 1047 | const excess_struct_init = struct { | |
| 1048 | const msg = "excess elements in struct initializer"; | |
| 1049 | const opt = "excess-initializers"; | |
| 1050 | const kind = .warning; | |
| 1051 | }; | |
| 1052 | const excess_array_init = struct { | |
| 1053 | const msg = "excess elements in array initializer"; | |
| 1054 | const opt = "excess-initializers"; | |
| 1055 | const kind = .warning; | |
| 1056 | }; | |
| 1057 | const str_init_too_long = struct { | |
| 1058 | const msg = "initializer-string for char array is too long"; | |
| 1059 | const opt = "excess-initializers"; | |
| 1060 | const kind = .warning; | |
| 1061 | }; | |
| 1062 | const arr_init_too_long = struct { | |
| 1063 | const msg = "cannot initialize type ({s})"; | |
| 1064 | const extra = .str; | |
| 1065 | const kind = .@"error"; | |
| 1066 | }; | |
| 1067 | const invalid_typeof = struct { | |
| 1068 | const msg = "'{s} typeof' is invalid"; | |
| 1069 | const extra = .str; | |
| 1070 | const kind = .@"error"; | |
| 1071 | }; | |
| 1072 | const division_by_zero = struct { | |
| 1073 | const msg = "{s} by zero is undefined"; | |
| 1074 | const extra = .str; | |
| 1075 | const opt = "division-by-zero"; | |
| 1076 | const kind = .warning; | |
| 1077 | }; | |
| 1078 | const division_by_zero_macro = struct { | |
| 1079 | const msg = "{s} by zero in preprocessor expression"; | |
| 1080 | const extra = .str; | |
| 1081 | const kind = .@"error"; | |
| 1082 | }; | |
| 1083 | const builtin_choose_cond = struct { | |
| 1084 | const msg = "'__builtin_choose_expr' requires a constant expression"; | |
| 1085 | const kind = .@"error"; | |
| 1086 | }; | |
| 1087 | const alignas_unavailable = struct { | |
| 1088 | const msg = "'_Alignas' attribute requires integer constant expression"; | |
| 1089 | const kind = .@"error"; | |
| 1090 | }; | |
| 1091 | const case_val_unavailable = struct { | |
| 1092 | const msg = "case value must be an integer constant expression"; | |
| 1093 | const kind = .@"error"; | |
| 1094 | }; | |
| 1095 | const enum_val_unavailable = struct { | |
| 1096 | const msg = "enum value must be an integer constant expression"; | |
| 1097 | const kind = .@"error"; | |
| 1098 | }; | |
| 1099 | const incompatible_array_init = struct { | |
| 1100 | const msg = "cannot initialize array of type {s}"; | |
| 1101 | const extra = .str; | |
| 1102 | const kind = .@"error"; | |
| 1103 | }; | |
| 1104 | const array_init_str = struct { | |
| 1105 | const msg = "array initializer must be an initializer list or wide string literal"; | |
| 1106 | const kind = .@"error"; | |
| 1107 | }; | |
| 1108 | const initializer_overrides = struct { | |
| 1109 | const msg = "initializer overrides previous initialization"; | |
| 1110 | const opt = "initializer-overrides"; | |
| 1111 | const kind = .warning; | |
| 1112 | const w_extra = true; | |
| 1113 | }; | |
| 1114 | const previous_initializer = struct { | |
| 1115 | const msg = "previous initialization"; | |
| 1116 | const kind = .note; | |
| 1117 | }; | |
| 1118 | const invalid_array_designator = struct { | |
| 1119 | const msg = "array designator used for non-array type '{s}'"; | |
| 1120 | const extra = .str; | |
| 1121 | const kind = .@"error"; | |
| 1122 | }; | |
| 1123 | const negative_array_designator = struct { | |
| 1124 | const msg = "array designator value {d} is negative"; | |
| 1125 | const extra = .signed; | |
| 1126 | const kind = .@"error"; | |
| 1127 | }; | |
| 1128 | const oob_array_designator = struct { | |
| 1129 | const msg = "array designator index {d} exceeds array bounds"; | |
| 1130 | const extra = .unsigned; | |
| 1131 | const kind = .@"error"; | |
| 1132 | }; | |
| 1133 | const invalid_field_designator = struct { | |
| 1134 | const msg = "field designator used for non-record type '{s}'"; | |
| 1135 | const extra = .str; | |
| 1136 | const kind = .@"error"; | |
| 1137 | }; | |
| 1138 | const no_such_field_designator = struct { | |
| 1139 | const msg = "record type has no field named '{s}'"; | |
| 1140 | const extra = .str; | |
| 1141 | const kind = .@"error"; | |
| 1142 | }; | |
| 1143 | const empty_aggregate_init_braces = struct { | |
| 1144 | const msg = "initializer for aggregate with no elements requires explicit braces"; | |
| 1145 | const kind = .@"error"; | |
| 1146 | }; | |
| 1147 | const ptr_init_discards_quals = struct { | |
| 1148 | const msg = "initializing {s} discards qualifiers"; | |
| 1149 | const extra = .str; | |
| 1150 | const opt = "incompatible-pointer-types-discards-qualifiers"; | |
| 1151 | const kind = .warning; | |
| 1152 | }; | |
| 1153 | const ptr_assign_discards_quals = struct { | |
| 1154 | const msg = "assigning to {s} discards qualifiers"; | |
| 1155 | const extra = .str; | |
| 1156 | const opt = "incompatible-pointer-types-discards-qualifiers"; | |
| 1157 | const kind = .warning; | |
| 1158 | }; | |
| 1159 | const unknown_attribute = struct { | |
| 1160 | const msg = "unknown attribute '{s}' ignored"; | |
| 1161 | const extra = .str; | |
| 1162 | const opt = "unknown-attributes"; | |
| 1163 | const kind = .warning; | |
| 1164 | }; | |
| 1165 | const ignored_attribute = struct { | |
| 1166 | const msg = "{s}"; | |
| 1167 | const extra = .str; | |
| 1168 | const opt = "ignored-attributes"; | |
| 1169 | const kind = .warning; | |
| 1170 | }; | |
| 1171 | const invalid_fallthrough = struct { | |
| 1172 | const msg = "fallthrough annotation does not directly precede switch label"; | |
| 1173 | const kind = .@"error"; | |
| 1174 | }; | |
| 1175 | const cannot_apply_attribute_to_statement = struct { | |
| 1176 | const msg = "attribute cannot be applied to a statement"; | |
| 1177 | const kind = .@"error"; | |
| 1178 | }; | |
| 1179 | const builtin_macro_redefined = struct { | |
| 1180 | const msg = "redefining builtin macro"; | |
| 1181 | const opt = "builtin-macro-redefined"; | |
| 1182 | const kind = .warning; | |
| 1183 | }; | |
| 1184 | const feature_check_requires_identifier = struct { | |
| 1185 | const msg = "builtin feature check macro requires a parenthesized identifier"; | |
| 1186 | const kind = .@"error"; | |
| 1187 | }; | |
| 1188 | const missing_tok_builtin = struct { | |
| 1189 | const msg = "missing '{s}', after builtin feature-check macro"; | |
| 1190 | const extra = .tok_id_expected; | |
| 1191 | const kind = .@"error"; | |
| 1192 | }; | |
| 1193 | const gnu_label_as_value = struct { | |
| 1194 | const msg = "use of GNU address-of-label extension"; | |
| 1195 | const opt = "gnu-label-as-value"; | |
| 1196 | const kind = .off; | |
| 1197 | const pedantic = true; | |
| 1198 | }; | |
| 1199 | const expected_record_ty = struct { | |
| 1200 | const msg = "member reference base type '{s}' is not a structure or union"; | |
| 1201 | const extra = .str; | |
| 1202 | const kind = .@"error"; | |
| 1203 | }; | |
| 1204 | const member_expr_not_ptr = struct { | |
| 1205 | const msg = "member reference type '{s}' is not a pointer; did you mean to use '.'?"; | |
| 1206 | const extra = .str; | |
| 1207 | const kind = .@"error"; | |
| 1208 | }; | |
| 1209 | const member_expr_ptr = struct { | |
| 1210 | const msg = "member reference type '{s}' is a pointer; did you mean to use '->'?"; | |
| 1211 | const extra = .str; | |
| 1212 | const kind = .@"error"; | |
| 1213 | }; | |
| 1214 | const no_such_member = struct { | |
| 1215 | const msg = "no member named {s}"; | |
| 1216 | const extra = .str; | |
| 1217 | const kind = .@"error"; | |
| 1218 | }; | |
| 1219 | const malformed_warning_check = struct { | |
| 1220 | const msg = "{s} expected option name (e.g. \"-Wundef\")"; | |
| 1221 | const extra = .str; | |
| 1222 | const opt = "malformed-warning-check"; | |
| 1223 | const kind = .warning; | |
| 1224 | const all = true; | |
| 1225 | }; | |
| 1226 | const invalid_computed_goto = struct { | |
| 1227 | const msg = "computed goto in function with no address-of-label expressions"; | |
| 1228 | const kind = .@"error"; | |
| 1229 | }; | |
| 1230 | const pragma_warning_message = struct { | |
| 1231 | const msg = "{s}"; | |
| 1232 | const extra = .str; | |
| 1233 | const opt = "#pragma-messages"; | |
| 1234 | const kind = .warning; | |
| 1235 | }; | |
| 1236 | const pragma_error_message = struct { | |
| 1237 | const msg = "{s}"; | |
| 1238 | const extra = .str; | |
| 1239 | const kind = .@"error"; | |
| 1240 | }; | |
| 1241 | const pragma_message = struct { | |
| 1242 | const msg = "#pragma message: {s}"; | |
| 1243 | const extra = .str; | |
| 1244 | const kind = .note; | |
| 1245 | }; | |
| 1246 | const pragma_requires_string_literal = struct { | |
| 1247 | const msg = "pragma {s} requires string literal"; | |
| 1248 | const extra = .str; | |
| 1249 | const kind = .@"error"; | |
| 1250 | }; | |
| 1251 | const poisoned_identifier = struct { | |
| 1252 | const msg = "attempt to use a poisoned identifier"; | |
| 1253 | const kind = .@"error"; | |
| 1254 | }; | |
| 1255 | const pragma_poison_identifier = struct { | |
| 1256 | const msg = "can only poison identifier tokens"; | |
| 1257 | const kind = .@"error"; | |
| 1258 | }; | |
| 1259 | const pragma_poison_macro = struct { | |
| 1260 | const msg = "poisoning existing macro"; | |
| 1261 | const kind = .warning; | |
| 1262 | }; | |
| 1263 | const newline_eof = struct { | |
| 1264 | const msg = "no newline at end of file"; | |
| 1265 | const opt = "newline-eof"; | |
| 1266 | const kind = .off; | |
| 1267 | const pedantic = true; | |
| 1268 | }; | |
| 1269 | const empty_translation_unit = struct { | |
| 1270 | const msg = "ISO C requires a translation unit to contain at least one declaration"; | |
| 1271 | const opt = "empty-translation-unit"; | |
| 1272 | const kind = .off; | |
| 1273 | const pedantic = true; | |
| 1274 | }; | |
| 1275 | const omitting_parameter_name = struct { | |
| 1276 | const msg = "omitting the parameter name in a function definition is a C2x extension"; | |
| 1277 | const opt = "c2x-extensions"; | |
| 1278 | const kind = .warning; | |
| 1279 | const suppress_version = .c2x; | |
| 1280 | }; | |
| 1281 | const non_int_bitfield = struct { | |
| 1282 | const msg = "bit-field has non-integer type '{s}'"; | |
| 1283 | const extra = .str; | |
| 1284 | const kind = .@"error"; | |
| 1285 | }; | |
| 1286 | const negative_bitwidth = struct { | |
| 1287 | const msg = "bit-field has negative width ({d})"; | |
| 1288 | const extra = .signed; | |
| 1289 | const kind = .@"error"; | |
| 1290 | }; | |
| 1291 | const zero_width_named_field = struct { | |
| 1292 | const msg = "named bit-field has zero width"; | |
| 1293 | const kind = .@"error"; | |
| 1294 | }; | |
| 1295 | const bitfield_too_big = struct { | |
| 1296 | const msg = "width of bit-field exceeds width of its type"; | |
| 1297 | const kind = .@"error"; | |
| 1298 | }; | |
| 1299 | const invalid_utf8 = struct { | |
| 1300 | const msg = "source file is not valid UTF-8"; | |
| 1301 | const kind = .@"error"; | |
| 1302 | }; | |
| 1303 | const implicitly_unsigned_literal = struct { | |
| 1304 | const msg = "integer literal is too large to be represented in a signed integer type, interpreting as unsigned"; | |
| 1305 | const opt = "implicitly-unsigned-literal"; | |
| 1306 | const kind = .warning; | |
| 1307 | }; | |
| 1308 | const invalid_preproc_operator = struct { | |
| 1309 | const msg = "token is not a valid binary operator in a preprocessor subexpression"; | |
| 1310 | const kind = .@"error"; | |
| 1311 | }; | |
| 1312 | const invalid_preproc_expr_start = struct { | |
| 1313 | const msg = "invalid token at start of a preprocessor expression"; | |
| 1314 | const kind = .@"error"; | |
| 1315 | }; | |
| 1316 | const c99_compat = struct { | |
| 1317 | const msg = "using this character in an identifier is incompatible with C99"; | |
| 1318 | const opt = "c99-compat"; | |
| 1319 | const kind = .off; | |
| 1320 | }; | |
| 1321 | const unicode_zero_width = struct { | |
| 1322 | const msg = "identifier contains Unicode character <U+{X:0>4}> that is invisible in some environments"; | |
| 1323 | const opt = "unicode-homoglyph"; | |
| 1324 | const extra = .actual_codepoint; | |
| 1325 | const kind = .warning; | |
| 1326 | }; | |
| 1327 | const unicode_homoglyph = struct { | |
| 1328 | const msg = "treating Unicode character <U+{X:0>4}> as identifier character rather than as '{u}' symbol"; | |
| 1329 | const extra = .codepoints; | |
| 1330 | const opt = "unicode-homoglyph"; | |
| 1331 | const kind = .warning; | |
| 1332 | }; | |
| 1333 | const meaningless_asm_qual = struct { | |
| 1334 | const msg = "meaningless '{s}' on assembly outside function"; | |
| 1335 | const extra = .str; | |
| 1336 | const kind = .@"error"; | |
| 1337 | }; | |
| 1338 | const duplicate_asm_qual = struct { | |
| 1339 | const msg = "duplicate asm qualifier '{s}'"; | |
| 1340 | const extra = .str; | |
| 1341 | const kind = .@"error"; | |
| 1342 | }; | |
| 1343 | const invalid_asm_str = struct { | |
| 1344 | const msg = "cannot use {s} string literal in assembly"; | |
| 1345 | const extra = .str; | |
| 1346 | const kind = .@"error"; | |
| 1347 | }; | |
| 1348 | const dollar_in_identifier_extension = struct { | |
| 1349 | const msg = "'$' in identifier"; | |
| 1350 | const opt = "dollar-in-identifier-extension"; | |
| 1351 | const kind = .off; | |
| 1352 | const suppress_language_option = "dollars_in_identifiers"; | |
| 1353 | const pedantic = true; | |
| 1354 | }; | |
| 1355 | const dollars_in_identifiers = struct { | |
| 1356 | const msg = "illegal character '$' in identifier"; | |
| 1357 | const kind = .@"error"; | |
| 1358 | }; | |
| 1359 | const expanded_from_here = struct { | |
| 1360 | const msg = "expanded from here"; | |
| 1361 | const kind = .note; | |
| 1362 | }; | |
| 1363 | const skipping_macro_backtrace = struct { | |
| 1364 | const msg = "(skipping {d} expansions in backtrace; use -fmacro-backtrace-limit=0 to see all)"; | |
| 1365 | const extra = .unsigned; | |
| 1366 | const kind = .note; | |
| 1367 | }; | |
| 1368 | const pragma_operator_string_literal = struct { | |
| 1369 | const msg = "_Pragma requires exactly one string literal token"; | |
| 1370 | const kind = .@"error"; | |
| 1371 | }; | |
| 1372 | const unknown_gcc_pragma = struct { | |
| 1373 | const msg = "pragma GCC expected 'error', 'warning', 'diagnostic', 'poison'"; | |
| 1374 | const opt = "unknown-pragmas"; | |
| 1375 | const kind = .off; | |
| 1376 | const all = true; | |
| 1377 | }; | |
| 1378 | const unknown_gcc_pragma_directive = struct { | |
| 1379 | const msg = "pragma GCC diagnostic expected 'error', 'warning', 'ignored', 'fatal', 'push', or 'pop'"; | |
| 1380 | const opt = "unknown-pragmas"; | |
| 1381 | const kind = .off; | |
| 1382 | const all = true; | |
| 1383 | }; | |
| 1384 | const predefined_top_level = struct { | |
| 1385 | const msg = "predefined identifier is only valid inside function"; | |
| 1386 | const opt = "predefined-identifier-outside-function"; | |
| 1387 | const kind = .warning; | |
| 1388 | }; | |
| 1389 | const incompatible_va_arg = struct { | |
| 1390 | const msg = "first argument to va_arg, is of type '{s}' and not 'va_list'"; | |
| 1391 | const extra = .str; | |
| 1392 | const kind = .@"error"; | |
| 1393 | }; | |
| 1394 | const too_many_scalar_init_braces = struct { | |
| 1395 | const msg = "too many braces around scalar initializer"; | |
| 1396 | const opt = "many-braces-around-scalar-init"; | |
| 1397 | const kind = .warning; | |
| 1398 | }; | |
| 1399 | const uninitialized_in_own_init = struct { | |
| 1400 | const msg = "variable '{s}' is uninitialized when used within its own initialization"; | |
| 1401 | const extra = .str; | |
| 1402 | const opt = "uninitialized"; | |
| 1403 | const kind = .off; | |
| 1404 | const all = true; | |
| 1405 | }; | |
| 1406 | const gnu_statement_expression = struct { | |
| 1407 | const msg = "use of GNU statement expression extension"; | |
| 1408 | const opt = "gnu-statement-expression"; | |
| 1409 | const kind = .off; | |
| 1410 | const suppress_gnu = true; | |
| 1411 | const pedantic = true; | |
| 1412 | }; | |
| 1413 | const stmt_expr_not_allowed_file_scope = struct { | |
| 1414 | const msg = "statement expression not allowed at file scope"; | |
| 1415 | const kind = .@"error"; | |
| 1416 | }; | |
| 1417 | const gnu_imaginary_constant = struct { | |
| 1418 | const msg = "imaginary constants are a GNU extension"; | |
| 1419 | const opt = "gnu-imaginary-constant"; | |
| 1420 | const kind = .off; | |
| 1421 | const suppress_gnu = true; | |
| 1422 | const pedantic = true; | |
| 1423 | }; | |
| 1424 | const plain_complex = struct { | |
| 1425 | const msg = "plain '_Complex' requires a type specifier; assuming '_Complex double'"; | |
| 1426 | const kind = .warning; | |
| 1427 | }; | |
| 1428 | const qual_on_ret_type = struct { | |
| 1429 | const msg = "'{s}' type qualifier on return type has no effect"; | |
| 1430 | const opt = "ignored-qualifiers"; | |
| 1431 | const extra = .str; | |
| 1432 | const kind = .off; | |
| 1433 | const all = true; | |
| 1434 | }; | |
| 1435 | const cli_invalid_standard = struct { | |
| 1436 | const msg = "invalid standard '{s}'"; | |
| 1437 | const extra = .str; | |
| 1438 | const kind = .@"error"; | |
| 1439 | }; | |
| 1440 | const cli_invalid_target = struct { | |
| 1441 | const msg = "invalid target '{s}'"; | |
| 1442 | const extra = .str; | |
| 1443 | const kind = .@"error"; | |
| 1444 | }; | |
| 1445 | const cli_unknown_arg = struct { | |
| 1446 | const msg = "unknown argument '{s}'"; | |
| 1447 | const extra = .str; | |
| 1448 | const kind = .@"error"; | |
| 1449 | }; | |
| 1450 | const cli_error = struct { | |
| 1451 | const msg = "{s}"; | |
| 1452 | const extra = .str; | |
| 1453 | const kind = .@"error"; | |
| 1454 | }; | |
| 1455 | const extra_semi = struct { | |
| 1456 | const msg = "extra ';' outside of a function"; | |
| 1457 | const opt = "extra-semi"; | |
| 1458 | const kind = .off; | |
| 1459 | const pedantic = true; | |
| 1460 | }; | |
| 1461 | const func_field = struct { | |
| 1462 | const msg = "field declared as a function"; | |
| 1463 | const kind = .@"error"; | |
| 1464 | }; | |
| 1465 | const vla_field = struct { | |
| 1466 | const msg = "variable length array fields extension is not supported"; | |
| 1467 | const kind = .@"error"; | |
| 1468 | }; | |
| 1469 | const field_incomplete_ty = struct { | |
| 1470 | const msg = "field has incomplete type '{s}'"; | |
| 1471 | const extra = .str; | |
| 1472 | const kind = .@"error"; | |
| 1473 | }; | |
| 1474 | const flexible_in_union = struct { | |
| 1475 | const msg = "flexible array member in union is not allowed"; | |
| 1476 | const kind = .@"error"; | |
| 1477 | }; | |
| 1478 | const flexible_non_final = struct { | |
| 1479 | const msg = "flexible array member is not at the end of struct"; | |
| 1480 | const kind = .@"error"; | |
| 1481 | }; | |
| 1482 | const flexible_in_empty = struct { | |
| 1483 | const msg = "flexible array member in otherwise empty struct"; | |
| 1484 | const kind = .@"error"; | |
| 1485 | }; | |
| 1486 | const duplicate_member = struct { | |
| 1487 | const msg = "duplicate member '{s}'"; | |
| 1488 | const extra = .str; | |
| 1489 | const kind = .@"error"; | |
| 1490 | }; | |
| 1491 | const binary_integer_literal = struct { | |
| 1492 | const msg = "binary integer literals are a GNU extension"; | |
| 1493 | const kind = .off; | |
| 1494 | const opt = "gnu-binary-literal"; | |
| 1495 | const pedantic = true; | |
| 1496 | }; | |
| 1497 | const gnu_va_macro = struct { | |
| 1498 | const msg = "named variadic macros are a GNU extension"; | |
| 1499 | const opt = "variadic-macros"; | |
| 1500 | const kind = .off; | |
| 1501 | const pedantic = true; | |
| 1502 | }; | |
| 1503 | const builtin_must_be_called = struct { | |
| 1504 | const msg = "builtin function must be directly called"; | |
| 1505 | const kind = .@"error"; | |
| 1506 | }; | |
| 1507 | const va_start_not_in_func = struct { | |
| 1508 | const msg = "'va_start' cannot be used outside a function"; | |
| 1509 | const kind = .@"error"; | |
| 1510 | }; | |
| 1511 | const va_start_fixed_args = struct { | |
| 1512 | const msg = "'va_start' used in a function with fixed args"; | |
| 1513 | const kind = .@"error"; | |
| 1514 | }; | |
| 1515 | const va_start_not_last_param = struct { | |
| 1516 | const msg = "second argument to 'va_start' is not the last named parameter"; | |
| 1517 | const opt = "varargs"; | |
| 1518 | const kind = .warning; | |
| 1519 | }; | |
| 1520 | const attribute_not_enough_args = struct { | |
| 1521 | const msg = "'{s}' attribute takes at least {d} argument(s)"; | |
| 1522 | const kind = .@"error"; | |
| 1523 | const extra = .attr_arg_count; | |
| 1524 | }; | |
| 1525 | const attribute_too_many_args = struct { | |
| 1526 | const msg = "'{s}' attribute takes at most {d} argument(s)"; | |
| 1527 | const kind = .@"error"; | |
| 1528 | const extra = .attr_arg_count; | |
| 1529 | }; | |
| 1530 | const attribute_arg_invalid = struct { | |
| 1531 | const msg = "Attribute argument is invalid, expected {s} but got {s}"; | |
| 1532 | const kind = .@"error"; | |
| 1533 | const extra = .attr_arg_type; | |
| 1534 | }; | |
| 1535 | const unknown_attr_enum = struct { | |
| 1536 | const msg = "Unknown `{s}` argument. Possible values are: {s}"; | |
| 1537 | const kind = .@"error"; | |
| 1538 | const extra = .attr_enum; | |
| 1539 | }; | |
| 1540 | const attribute_requires_identifier = struct { | |
| 1541 | const msg = "'{s}' attribute requires an identifier"; | |
| 1542 | const kind = .@"error"; | |
| 1543 | const extra = .str; | |
| 1544 | }; | |
| 1545 | const declspec_not_enabled = struct { | |
| 1546 | const msg = "'__declspec' attributes are not enabled; use '-fdeclspec' or '-fms-extensions' to enable support for __declspec attributes"; | |
| 1547 | const kind = .@"error"; | |
| 1548 | }; | |
| 1549 | const declspec_attr_not_supported = struct { | |
| 1550 | const msg = "__declspec attribute '{s}' is not supported"; | |
| 1551 | const extra = .str; | |
| 1552 | const opt = "ignored-attributes"; | |
| 1553 | const kind = .warning; | |
| 1554 | }; | |
| 1555 | const deprecated_declarations = struct { | |
| 1556 | const msg = "{s}"; | |
| 1557 | const extra = .str; | |
| 1558 | const opt = "deprecated-declarations"; | |
| 1559 | const kind = .warning; | |
| 1560 | }; | |
| 1561 | const deprecated_note = struct { | |
| 1562 | const msg = "'{s}' has been explicitly marked deprecated here"; | |
| 1563 | const extra = .str; | |
| 1564 | const opt = "deprecated-declarations"; | |
| 1565 | const kind = .note; | |
| 1566 | }; | |
| 1567 | const unavailable = struct { | |
| 1568 | const msg = "{s}"; | |
| 1569 | const extra = .str; | |
| 1570 | const kind = .@"error"; | |
| 1571 | }; | |
| 1572 | const unavailable_note = struct { | |
| 1573 | const msg = "'{s}' has been explicitly marked unavailable here"; | |
| 1574 | const extra = .str; | |
| 1575 | const kind = .note; | |
| 1576 | }; | |
| 1577 | const ignored_record_attr = struct { | |
| 1578 | const msg = "attribute '{s}' is ignored, place it after \"{s}\" to apply attribute to type declaration"; | |
| 1579 | const extra = .ignored_record_attr; | |
| 1580 | const kind = .warning; | |
| 1581 | const opt = "ignored-attributes"; | |
| 1582 | }; | |
| 1583 | const backslash_newline_escape = struct { | |
| 1584 | const msg = "backslash and newline separated by space"; | |
| 1585 | const kind = .warning; | |
| 1586 | const opt = "backslash-newline-escape"; | |
| 1587 | }; | |
| 1588 | const array_size_non_int = struct { | |
| 1589 | const msg = "size of array has non-integer type '{s}'"; | |
| 1590 | const extra = .str; | |
| 1591 | const kind = .@"error"; | |
| 1592 | }; | |
| 1593 | }; | |
| 1594 | ||
| 1595 | list: std.ArrayListUnmanaged(Message) = .{}, | |
| 1596 | arena: std.heap.ArenaAllocator, | |
| 1597 | color: bool = true, | |
| 1598 | fatal_errors: bool = false, | |
| 1599 | options: Options = .{}, | |
| 1600 | errors: u32 = 0, | |
| 1601 | macro_backtrace_limit: u32 = 6, | |
| 1602 | ||
| 1603 | pub fn warningExists(name: []const u8) bool { | |
| 1604 | inline for (std.meta.fields(Options)) |f| { | |
| 1605 | if (mem.eql(u8, f.name, name)) return true; | |
| 1606 | } | |
| 1607 | return false; | |
| 1608 | } | |
| 1609 | ||
| 1610 | pub fn set(diag: *Diagnostics, name: []const u8, to: Kind) !void { | |
| 1611 | inline for (std.meta.fields(Options)) |f| { | |
| 1612 | if (mem.eql(u8, f.name, name)) { | |
| 1613 | @field(diag.options, f.name) = to; | |
| 1614 | return; | |
| 1615 | } | |
| 1616 | } | |
| 1617 | try diag.add(.{ | |
| 1618 | .tag = .unknown_warning, | |
| 1619 | .extra = .{ .str = name }, | |
| 1620 | }, &.{}); | |
| 1621 | } | |
| 1622 | ||
| 1623 | pub fn init(gpa: Allocator) Diagnostics { | |
| 1624 | return .{ | |
| 1625 | .color = std.io.getStdErr().supportsAnsiEscapeCodes() or (is_windows and std.io.getStdErr().isTty()), | |
| 1626 | .arena = std.heap.ArenaAllocator.init(gpa), | |
| 1627 | }; | |
| 1628 | } | |
| 1629 | ||
| 1630 | pub fn deinit(diag: *Diagnostics) void { | |
| 1631 | diag.list.deinit(diag.arena.allocator()); | |
| 1632 | diag.arena.deinit(); | |
| 1633 | } | |
| 1634 | ||
| 1635 | pub fn add(diag: *Diagnostics, msg: Message, expansion_locs: []const Source.Location) Compilation.Error!void { | |
| 1636 | const kind = diag.tagKind(msg.tag); | |
| 1637 | if (kind == .off) return; | |
| 1638 | var copy = msg; | |
| 1639 | copy.kind = kind; | |
| 1640 | ||
| 1641 | if (expansion_locs.len != 0) copy.loc = expansion_locs[expansion_locs.len - 1]; | |
| 1642 | try diag.list.append(diag.arena.allocator(), copy); | |
| 1643 | if (expansion_locs.len != 0) { | |
| 1644 | // Add macro backtrace notes in reverse order omitting from the middle if needed. | |
| 1645 | var i = expansion_locs.len - 1; | |
| 1646 | const half = diag.macro_backtrace_limit / 2; | |
| 1647 | const limit = if (i < diag.macro_backtrace_limit) 0 else i - half; | |
| 1648 | try diag.list.ensureUnusedCapacity( | |
| 1649 | diag.arena.allocator(), | |
| 1650 | if (limit == 0) expansion_locs.len else diag.macro_backtrace_limit + 1, | |
| 1651 | ); | |
| 1652 | while (i > limit) { | |
| 1653 | i -= 1; | |
| 1654 | diag.list.appendAssumeCapacity(.{ | |
| 1655 | .tag = .expanded_from_here, | |
| 1656 | .kind = .note, | |
| 1657 | .loc = expansion_locs[i], | |
| 1658 | }); | |
| 1659 | } | |
| 1660 | if (limit != 0) { | |
| 1661 | diag.list.appendAssumeCapacity(.{ | |
| 1662 | .tag = .skipping_macro_backtrace, | |
| 1663 | .kind = .note, | |
| 1664 | .extra = .{ .unsigned = expansion_locs.len - diag.macro_backtrace_limit }, | |
| 1665 | }); | |
| 1666 | i = half - 1; | |
| 1667 | while (i > 0) { | |
| 1668 | i -= 1; | |
| 1669 | diag.list.appendAssumeCapacity(.{ | |
| 1670 | .tag = .expanded_from_here, | |
| 1671 | .kind = .note, | |
| 1672 | .loc = expansion_locs[i], | |
| 1673 | }); | |
| 1674 | } | |
| 1675 | } | |
| 1676 | ||
| 1677 | diag.list.appendAssumeCapacity(.{ | |
| 1678 | .tag = .expanded_from_here, | |
| 1679 | .kind = .note, | |
| 1680 | .loc = msg.loc, | |
| 1681 | }); | |
| 1682 | } | |
| 1683 | if (kind == .@"fatal error" or (kind == .@"error" and diag.fatal_errors)) | |
| 1684 | return error.FatalError; | |
| 1685 | } | |
| 1686 | ||
| 1687 | pub fn fatal( | |
| 1688 | diag: *Diagnostics, | |
| 1689 | path: []const u8, | |
| 1690 | line: []const u8, | |
| 1691 | line_no: u32, | |
| 1692 | col: u32, | |
| 1693 | comptime fmt: []const u8, | |
| 1694 | args: anytype, | |
| 1695 | ) Compilation.Error { | |
| 1696 | var m = MsgWriter.init(diag.color); | |
| 1697 | defer m.deinit(); | |
| 1698 | ||
| 1699 | m.location(path, line_no, col); | |
| 1700 | m.start(.@"fatal error"); | |
| 1701 | m.print(fmt, args); | |
| 1702 | m.end(line, col, false); | |
| 1703 | return error.FatalError; | |
| 1704 | } | |
| 1705 | ||
| 1706 | pub fn fatalNoSrc(diag: *Diagnostics, comptime fmt: []const u8, args: anytype) error{FatalError} { | |
| 1707 | if (!diag.color) { | |
| 1708 | std.debug.print("fatal error: " ++ fmt ++ "\n", args); | |
| 1709 | } else { | |
| 1710 | const std_err = std.io.getStdErr().writer(); | |
| 1711 | util.setColor(.red, std_err); | |
| 1712 | std_err.writeAll("fatal error: ") catch {}; | |
| 1713 | util.setColor(.white, std_err); | |
| 1714 | std_err.print(fmt ++ "\n", args) catch {}; | |
| 1715 | util.setColor(.reset, std_err); | |
| 1716 | } | |
| 1717 | return error.FatalError; | |
| 1718 | } | |
| 1719 | ||
| 1720 | pub fn render(comp: *Compilation) void { | |
| 1721 | if (comp.diag.list.items.len == 0) return; | |
| 1722 | var m = MsgWriter.init(comp.diag.color); | |
| 1723 | defer m.deinit(); | |
| 1724 | ||
| 1725 | renderExtra(comp, &m); | |
| 1726 | } | |
| 1727 | ||
| 1728 | pub fn renderExtra(comp: *Compilation, m: anytype) void { | |
| 1729 | var errors: u32 = 0; | |
| 1730 | var warnings: u32 = 0; | |
| 1731 | for (comp.diag.list.items) |msg| { | |
| 1732 | switch (msg.kind) { | |
| 1733 | .@"fatal error", .@"error" => errors += 1, | |
| 1734 | .warning => warnings += 1, | |
| 1735 | .note => {}, | |
| 1736 | .off => continue, // happens if an error is added before it is disabled | |
| 1737 | .default => unreachable, | |
| 1738 | } | |
| 1739 | ||
| 1740 | var line: ?[]const u8 = null; | |
| 1741 | var col = switch (msg.tag) { | |
| 1742 | .escape_sequence_overflow, | |
| 1743 | .invalid_universal_character, | |
| 1744 | // use msg.extra.unsigned for index into string literal | |
| 1745 | => @truncate(u32, msg.extra.unsigned), | |
| 1746 | else => 0, | |
| 1747 | }; | |
| 1748 | var width = col; | |
| 1749 | var end_with_splice = false; | |
| 1750 | if (msg.loc.id != .unused) { | |
| 1751 | const source = comp.getSource(msg.loc.id); | |
| 1752 | var line_col = source.lineCol(msg.loc); | |
| 1753 | line = line_col.line; | |
| 1754 | col += line_col.col; | |
| 1755 | width += line_col.width; | |
| 1756 | end_with_splice = line_col.end_with_splice; | |
| 1757 | if (msg.tag == .backslash_newline_escape) { | |
| 1758 | line = line_col.line[0 .. col - 1]; | |
| 1759 | col += 1; | |
| 1760 | width += 1; | |
| 1761 | } | |
| 1762 | m.location(source.path, line_col.line_no, col); | |
| 1763 | } | |
| 1764 | ||
| 1765 | m.start(msg.kind); | |
| 1766 | inline for (std.meta.fields(Tag)) |field| { | |
| 1767 | if (field.value == @enumToInt(msg.tag)) { | |
| 1768 | const info = @field(messages, field.name); | |
| 1769 | if (@hasDecl(info, "extra")) { | |
| 1770 | switch (info.extra) { | |
| 1771 | .str => m.print(info.msg, .{msg.extra.str}), | |
| 1772 | .tok_id => m.print(info.msg, .{ | |
| 1773 | msg.extra.tok_id.expected.symbol(), | |
| 1774 | msg.extra.tok_id.actual.symbol(), | |
| 1775 | }), | |
| 1776 | .tok_id_expected => m.print(info.msg, .{msg.extra.tok_id_expected.symbol()}), | |
| 1777 | .arguments => m.print(info.msg, .{ msg.extra.arguments.expected, msg.extra.arguments.actual }), | |
| 1778 | .codepoints => m.print(info.msg, .{ | |
| 1779 | msg.extra.codepoints.actual, | |
| 1780 | msg.extra.codepoints.resembles, | |
| 1781 | }), | |
| 1782 | .attr_arg_count => m.print(info.msg, .{ | |
| 1783 | @tagName(msg.extra.attr_arg_count.attribute), | |
| 1784 | msg.extra.attr_arg_count.expected, | |
| 1785 | }), | |
| 1786 | .attr_arg_type => m.print(info.msg, .{ | |
| 1787 | msg.extra.attr_arg_type.expected.toString(), | |
| 1788 | msg.extra.attr_arg_type.actual.toString(), | |
| 1789 | }), | |
| 1790 | .actual_codepoint => m.print(info.msg, .{msg.extra.actual_codepoint}), | |
| 1791 | .unsigned => m.print(info.msg, .{msg.extra.unsigned}), | |
| 1792 | .signed => m.print(info.msg, .{msg.extra.signed}), | |
| 1793 | .attr_enum => m.print(info.msg, .{ | |
| 1794 | @tagName(msg.extra.attr_enum.tag), | |
| 1795 | Attribute.Formatting.choices(msg.extra.attr_enum.tag), | |
| 1796 | }), | |
| 1797 | .ignored_record_attr => m.print(info.msg, .{ | |
| 1798 | @tagName(msg.extra.ignored_record_attr.tag), | |
| 1799 | @tagName(msg.extra.ignored_record_attr.specifier), | |
| 1800 | }), | |
| 1801 | else => unreachable, | |
| 1802 | } | |
| 1803 | } else { | |
| 1804 | m.write(info.msg); | |
| 1805 | } | |
| 1806 | ||
| 1807 | if (@hasDecl(info, "opt")) { | |
| 1808 | if (msg.kind == .@"error" and info.kind != .@"error") { | |
| 1809 | m.print(" [-Werror,-W{s}]", .{info.opt}); | |
| 1810 | } else if (msg.kind != .note) { | |
| 1811 | m.print(" [-W{s}]", .{info.opt}); | |
| 1812 | } | |
| 1813 | } | |
| 1814 | } | |
| 1815 | } | |
| 1816 | ||
| 1817 | m.end(line, width, end_with_splice); | |
| 1818 | } | |
| 1819 | const w_s: []const u8 = if (warnings == 1) "" else "s"; | |
| 1820 | const e_s: []const u8 = if (errors == 1) "" else "s"; | |
| 1821 | if (errors != 0 and warnings != 0) { | |
| 1822 | m.print("{d} warning{s} and {d} error{s} generated.\n", .{ warnings, w_s, errors, e_s }); | |
| 1823 | } else if (warnings != 0) { | |
| 1824 | m.print("{d} warning{s} generated.\n", .{ warnings, w_s }); | |
| 1825 | } else if (errors != 0) { | |
| 1826 | m.print("{d} error{s} generated.\n", .{ errors, e_s }); | |
| 1827 | } | |
| 1828 | ||
| 1829 | comp.diag.list.items.len = 0; | |
| 1830 | comp.diag.errors += errors; | |
| 1831 | } | |
| 1832 | ||
| 1833 | fn tagKind(diag: *Diagnostics, tag: Tag) Kind { | |
| 1834 | // XXX: horrible hack, do not do this | |
| 1835 | const comp = @fieldParentPtr(Compilation, "diag", diag); | |
| 1836 | ||
| 1837 | var kind: Kind = undefined; | |
| 1838 | inline for (std.meta.fields(Tag)) |field| { | |
| 1839 | if (field.value == @enumToInt(tag)) { | |
| 1840 | const info = @field(messages, field.name); | |
| 1841 | kind = info.kind; | |
| 1842 | ||
| 1843 | // stage1 doesn't like when I combine these ifs | |
| 1844 | if (@hasDecl(info, "all")) { | |
| 1845 | if (diag.options.all != .default) kind = diag.options.all; | |
| 1846 | } | |
| 1847 | if (@hasDecl(info, "w_extra")) { | |
| 1848 | if (diag.options.extra != .default) kind = diag.options.extra; | |
| 1849 | } | |
| 1850 | if (@hasDecl(info, "pedantic")) { | |
| 1851 | if (diag.options.pedantic != .default) kind = diag.options.pedantic; | |
| 1852 | } | |
| 1853 | if (@hasDecl(info, "opt")) { | |
| 1854 | if (@field(diag.options, info.opt) != .default) kind = @field(diag.options, info.opt); | |
| 1855 | } | |
| 1856 | if (@hasDecl(info, "suppress_version")) if (comp.langopts.standard.atLeast(info.suppress_version)) return .off; | |
| 1857 | if (@hasDecl(info, "suppress_gnu")) if (comp.langopts.standard.isExplicitGNU()) return .off; | |
| 1858 | if (@hasDecl(info, "suppress_language_option")) if (!@field(comp.langopts, info.suppress_language_option)) return .off; | |
| 1859 | if (kind == .@"error" and diag.fatal_errors) kind = .@"fatal error"; | |
| 1860 | return kind; | |
| 1861 | } | |
| 1862 | } | |
| 1863 | unreachable; | |
| 1864 | } | |
| 1865 | ||
| 1866 | const MsgWriter = struct { | |
| 1867 | w: std.io.BufferedWriter(4096, std.fs.File.Writer), | |
| 1868 | color: bool, | |
| 1869 | ||
| 1870 | fn init(color: bool) MsgWriter { | |
| 1871 | std.debug.getStderrMutex().lock(); | |
| 1872 | return .{ | |
| 1873 | .w = std.io.bufferedWriter(std.io.getStdErr().writer()), | |
| 1874 | .color = color, | |
| 1875 | }; | |
| 1876 | } | |
| 1877 | ||
| 1878 | fn deinit(m: *MsgWriter) void { | |
| 1879 | m.w.flush() catch {}; | |
| 1880 | std.debug.getStderrMutex().unlock(); | |
| 1881 | } | |
| 1882 | ||
| 1883 | fn print(m: *MsgWriter, comptime fmt: []const u8, args: anytype) void { | |
| 1884 | m.w.writer().print(fmt, args) catch {}; | |
| 1885 | } | |
| 1886 | ||
| 1887 | fn write(m: *MsgWriter, msg: []const u8) void { | |
| 1888 | m.w.writer().writeAll(msg) catch {}; | |
| 1889 | } | |
| 1890 | ||
| 1891 | fn setColor(m: *MsgWriter, color: util.Color) void { | |
| 1892 | util.setColor(color, m.w.writer()); | |
| 1893 | } | |
| 1894 | ||
| 1895 | fn location(m: *MsgWriter, path: []const u8, line: u32, col: u32) void { | |
| 1896 | const prefix = if (std.fs.path.dirname(path) == null and path[0] != '<') "." ++ std.fs.path.sep_str else ""; | |
| 1897 | if (!m.color) { | |
| 1898 | m.print("{s}{s}:{d}:{d}: ", .{ prefix, path, line, col }); | |
| 1899 | } else { | |
| 1900 | m.setColor(.white); | |
| 1901 | m.print("{s}{s}:{d}:{d}: ", .{ prefix, path, line, col }); | |
| 1902 | } | |
| 1903 | } | |
| 1904 | ||
| 1905 | fn start(m: *MsgWriter, kind: Kind) void { | |
| 1906 | if (!m.color) { | |
| 1907 | m.print("{s}: ", .{@tagName(kind)}); | |
| 1908 | } else { | |
| 1909 | switch (kind) { | |
| 1910 | .@"fatal error", .@"error" => m.setColor(.red), | |
| 1911 | .note => m.setColor(.cyan), | |
| 1912 | .warning => m.setColor(.purple), | |
| 1913 | .off, .default => unreachable, | |
| 1914 | } | |
| 1915 | m.write(switch (kind) { | |
| 1916 | .@"fatal error" => "fatal error: ", | |
| 1917 | .@"error" => "error: ", | |
| 1918 | .note => "note: ", | |
| 1919 | .warning => "warning: ", | |
| 1920 | .off, .default => unreachable, | |
| 1921 | }); | |
| 1922 | m.setColor(.white); | |
| 1923 | } | |
| 1924 | } | |
| 1925 | ||
| 1926 | fn end(m: *MsgWriter, maybe_line: ?[]const u8, col: u32, end_with_splice: bool) void { | |
| 1927 | const line = maybe_line orelse { | |
| 1928 | m.write("\n"); | |
| 1929 | return; | |
| 1930 | }; | |
| 1931 | const trailer = if (end_with_splice) "\\ " else ""; | |
| 1932 | if (!m.color) { | |
| 1933 | m.print("\n{s}{s}\n", .{ line, trailer }); | |
| 1934 | m.print("{s: >[1]}^\n", .{ "", col }); | |
| 1935 | } else { | |
| 1936 | m.setColor(.reset); | |
| 1937 | m.print("\n{s}{s}\n{s: >[3]}", .{ line, trailer, "", col }); | |
| 1938 | m.setColor(.green); | |
| 1939 | m.write("^\n"); | |
| 1940 | m.setColor(.reset); | |
| 1941 | } | |
| 1942 | } | |
| 1943 | }; |
src/aro/InitList.zig created+153| ... | ... | @@ -0,0 +1,153 @@ |
| 1 | //! Sparsely populated list of used indexes. | |
| 2 | //! Used for detecting duplicate initializers. | |
| 3 | const std = @import("std"); | |
| 4 | const Allocator = std.mem.Allocator; | |
| 5 | const testing = std.testing; | |
| 6 | const Tree = @import("Tree.zig"); | |
| 7 | const Token = Tree.Token; | |
| 8 | const TokenIndex = Tree.TokenIndex; | |
| 9 | const NodeIndex = Tree.NodeIndex; | |
| 10 | const Type = @import("Type.zig"); | |
| 11 | const Diagnostics = @import("Diagnostics.zig"); | |
| 12 | const NodeList = std.ArrayList(NodeIndex); | |
| 13 | const Parser = @import("Parser.zig"); | |
| 14 | ||
| 15 | const InitList = @This(); | |
| 16 | ||
| 17 | const Item = struct { | |
| 18 | list: InitList = .{}, | |
| 19 | index: u64, | |
| 20 | ||
| 21 | fn order(_: void, a: Item, b: Item) std.math.Order { | |
| 22 | return std.math.order(a.index, b.index); | |
| 23 | } | |
| 24 | }; | |
| 25 | ||
| 26 | list: std.ArrayListUnmanaged(Item) = .{}, | |
| 27 | node: NodeIndex = .none, | |
| 28 | tok: TokenIndex = 0, | |
| 29 | ||
| 30 | /// Deinitialize freeing all memory. | |
| 31 | pub fn deinit(il: *InitList, gpa: Allocator) void { | |
| 32 | for (il.list.items) |*item| item.list.deinit(gpa); | |
| 33 | il.list.deinit(gpa); | |
| 34 | il.* = undefined; | |
| 35 | } | |
| 36 | ||
| 37 | /// Insert initializer at index, returning previous entry if one exists. | |
| 38 | pub fn put(il: *InitList, gpa: Allocator, index: usize, node: NodeIndex, tok: TokenIndex) !?TokenIndex { | |
| 39 | const items = il.list.items; | |
| 40 | var left: usize = 0; | |
| 41 | var right: usize = items.len; | |
| 42 | ||
| 43 | // Append new value to empty list | |
| 44 | if (left == right) { | |
| 45 | const item = try il.list.addOne(gpa); | |
| 46 | item.* = .{ | |
| 47 | .list = .{ .node = node, .tok = tok }, | |
| 48 | .index = index, | |
| 49 | }; | |
| 50 | return null; | |
| 51 | } | |
| 52 | ||
| 53 | while (left < right) { | |
| 54 | // Avoid overflowing in the midpoint calculation | |
| 55 | const mid = left + (right - left) / 2; | |
| 56 | // Compare the key with the midpoint element | |
| 57 | switch (std.math.order(index, items[mid].index)) { | |
| 58 | .eq => { | |
| 59 | // Replace previous entry. | |
| 60 | const prev = items[mid].list.tok; | |
| 61 | items[mid].list.deinit(gpa); | |
| 62 | items[mid] = .{ | |
| 63 | .list = .{ .node = node, .tok = tok }, | |
| 64 | .index = index, | |
| 65 | }; | |
| 66 | return prev; | |
| 67 | }, | |
| 68 | .gt => left = mid + 1, | |
| 69 | .lt => right = mid, | |
| 70 | } | |
| 71 | } | |
| 72 | ||
| 73 | // Insert a new value into a sorted position. | |
| 74 | try il.list.insert(gpa, left, .{ | |
| 75 | .list = .{ .node = node, .tok = tok }, | |
| 76 | .index = index, | |
| 77 | }); | |
| 78 | return null; | |
| 79 | } | |
| 80 | ||
| 81 | /// Find item at index, create new if one does not exist. | |
| 82 | pub fn find(il: *InitList, gpa: Allocator, index: usize) !*InitList { | |
| 83 | const items = il.list.items; | |
| 84 | var left: usize = 0; | |
| 85 | var right: usize = items.len; | |
| 86 | ||
| 87 | // Append new value to empty list | |
| 88 | if (left == right) { | |
| 89 | const item = try il.list.addOne(gpa); | |
| 90 | item.* = .{ | |
| 91 | .list = .{ .node = .none, .tok = 0 }, | |
| 92 | .index = index, | |
| 93 | }; | |
| 94 | return &item.list; | |
| 95 | } | |
| 96 | ||
| 97 | while (left < right) { | |
| 98 | // Avoid overflowing in the midpoint calculation | |
| 99 | const mid = left + (right - left) / 2; | |
| 100 | // Compare the key with the midpoint element | |
| 101 | switch (std.math.order(index, items[mid].index)) { | |
| 102 | .eq => return &items[mid].list, | |
| 103 | .gt => left = mid + 1, | |
| 104 | .lt => right = mid, | |
| 105 | } | |
| 106 | } | |
| 107 | ||
| 108 | // Insert a new value into a sorted position. | |
| 109 | try il.list.insert(gpa, left, .{ | |
| 110 | .list = .{ .node = .none, .tok = 0 }, | |
| 111 | .index = index, | |
| 112 | }); | |
| 113 | return &il.list.items[left].list; | |
| 114 | } | |
| 115 | ||
| 116 | test "basic usage" { | |
| 117 | const gpa = testing.allocator; | |
| 118 | var il: InitList = .{}; | |
| 119 | defer il.deinit(gpa); | |
| 120 | ||
| 121 | { | |
| 122 | var i: usize = 0; | |
| 123 | while (i < 5) : (i += 1) { | |
| 124 | const prev = try il.put(gpa, i, .none, 0); | |
| 125 | try testing.expect(prev == null); | |
| 126 | } | |
| 127 | } | |
| 128 | ||
| 129 | { | |
| 130 | const failing = testing.failing_allocator; | |
| 131 | var i: usize = 0; | |
| 132 | while (i < 5) : (i += 1) { | |
| 133 | _ = try il.find(failing, i); | |
| 134 | } | |
| 135 | } | |
| 136 | ||
| 137 | { | |
| 138 | var item = try il.find(gpa, 0); | |
| 139 | var i: usize = 1; | |
| 140 | while (i < 5) : (i += 1) { | |
| 141 | item = try item.find(gpa, i); | |
| 142 | } | |
| 143 | } | |
| 144 | ||
| 145 | { | |
| 146 | const failing = testing.failing_allocator; | |
| 147 | var item = try il.find(failing, 0); | |
| 148 | var i: usize = 1; | |
| 149 | while (i < 5) : (i += 1) { | |
| 150 | item = try item.find(failing, i); | |
| 151 | } | |
| 152 | } | |
| 153 | } |
src/aro/LangOpts.zig created+88| ... | ... | @@ -0,0 +1,88 @@ |
| 1 | const std = @import("std"); | |
| 2 | const DiagnosticTag = @import("Diagnostics.zig").Tag; | |
| 3 | ||
| 4 | const LangOpts = @This(); | |
| 5 | ||
| 6 | const Standard = enum { | |
| 7 | /// ISO C 1990 | |
| 8 | c89, | |
| 9 | /// ISO C 1990 with amendment 1 | |
| 10 | iso9899, | |
| 11 | /// ISO C 1990 with GNU extensions | |
| 12 | gnu89, | |
| 13 | /// ISO C 1999 | |
| 14 | c99, | |
| 15 | /// ISO C 1999 with GNU extensions | |
| 16 | gnu99, | |
| 17 | /// ISO C 2011 | |
| 18 | c11, | |
| 19 | /// ISO C 2011 with GNU extensions | |
| 20 | gnu11, | |
| 21 | /// ISO C 2017 | |
| 22 | c17, | |
| 23 | /// Default value if nothing specified; adds the GNU keywords to | |
| 24 | /// C17 but does not suppress warnings about using GNU extensions | |
| 25 | default, | |
| 26 | /// ISO C 2017 with GNU extensions | |
| 27 | gnu17, | |
| 28 | /// Working Draft for ISO C2x | |
| 29 | c2x, | |
| 30 | /// Working Draft for ISO C2x with GNU extensions | |
| 31 | gnu2x, | |
| 32 | ||
| 33 | const NameMap = std.ComptimeStringMap(Standard, .{ | |
| 34 | .{ "c89", .c89 }, .{ "c90", .c89 }, .{ "iso9899:1990", .c89 }, | |
| 35 | .{ "iso9899:199409", .iso9899 }, .{ "gnu89", .gnu89 }, .{ "gnu90", .gnu89 }, | |
| 36 | .{ "c99", .c99 }, .{ "iso9899:1999", .c99 }, .{ "gnu99", .gnu99 }, | |
| 37 | .{ "c11", .c11 }, .{ "iso9899:2011", .c11 }, .{ "gnu11", .gnu11 }, | |
| 38 | .{ "c17", .c17 }, .{ "iso9899:2017", .c17 }, .{ "c18", .c17 }, | |
| 39 | .{ "iso9899:2018", .c17 }, .{ "gnu17", .gnu17 }, .{ "gnu18", .gnu17 }, | |
| 40 | .{ "c2x", .c2x }, .{ "gnu2x", .gnu2x }, | |
| 41 | }); | |
| 42 | ||
| 43 | pub fn atLeast(self: Standard, other: Standard) bool { | |
| 44 | return @enumToInt(self) >= @enumToInt(other); | |
| 45 | } | |
| 46 | ||
| 47 | pub fn isGNU(standard: Standard) bool { | |
| 48 | return switch (standard) { | |
| 49 | .gnu89, .gnu99, .gnu11, .default, .gnu17, .gnu2x => true, | |
| 50 | else => false, | |
| 51 | }; | |
| 52 | } | |
| 53 | ||
| 54 | pub fn isExplicitGNU(standard: Standard) bool { | |
| 55 | return standard.isGNU() and standard != .default; | |
| 56 | } | |
| 57 | ||
| 58 | /// Value reported by __STDC_VERSION__ macro | |
| 59 | pub fn StdCVersionMacro(standard: Standard) ?[]const u8 { | |
| 60 | return switch (standard) { | |
| 61 | .c89, .gnu89 => null, | |
| 62 | .iso9899 => "199409L", | |
| 63 | .c99, .gnu99 => "199901L", | |
| 64 | .c11, .gnu11 => "201112L", | |
| 65 | .default, .c17, .gnu17 => "201710L", | |
| 66 | // todo: update once finalized; this currently matches clang | |
| 67 | .c2x, .gnu2x => "201710L", | |
| 68 | }; | |
| 69 | } | |
| 70 | }; | |
| 71 | ||
| 72 | standard: Standard = .default, | |
| 73 | /// -fshort-enums option, makes enums only take up as much space as they need to hold all the values. | |
| 74 | short_enums: bool = false, | |
| 75 | dollars_in_identifiers: bool = true, | |
| 76 | declspec_attrs: bool = false, | |
| 77 | ||
| 78 | pub fn setStandard(self: *LangOpts, name: []const u8) error{InvalidStandard}!void { | |
| 79 | self.standard = Standard.NameMap.get(name) orelse return error.InvalidStandard; | |
| 80 | } | |
| 81 | ||
| 82 | pub fn enableMSExtensions(self: *LangOpts) void { | |
| 83 | self.declspec_attrs = true; | |
| 84 | } | |
| 85 | ||
| 86 | pub fn disableMSExtensions(self: *LangOpts) void { | |
| 87 | self.declspec_attrs = false; | |
| 88 | } |
src/aro/Parser.zig created+6269| ... | ... | @@ -0,0 +1,6269 @@ |
| 1 | const std = @import("std"); | |
| 2 | const mem = std.mem; | |
| 3 | const Allocator = mem.Allocator; | |
| 4 | const assert = std.debug.assert; | |
| 5 | const Compilation = @import("Compilation.zig"); | |
| 6 | const Source = @import("Source.zig"); | |
| 7 | const Tokenizer = @import("Tokenizer.zig"); | |
| 8 | const Preprocessor = @import("Preprocessor.zig"); | |
| 9 | const Tree = @import("Tree.zig"); | |
| 10 | const Token = Tree.Token; | |
| 11 | const TokenIndex = Tree.TokenIndex; | |
| 12 | const NodeIndex = Tree.NodeIndex; | |
| 13 | const Type = @import("Type.zig"); | |
| 14 | const Diagnostics = @import("Diagnostics.zig"); | |
| 15 | const NodeList = std.ArrayList(NodeIndex); | |
| 16 | const InitList = @import("InitList.zig"); | |
| 17 | const Attribute = @import("Attribute.zig"); | |
| 18 | const CharInfo = @import("CharInfo.zig"); | |
| 19 | const Value = @import("Value.zig"); | |
| 20 | ||
| 21 | const Parser = @This(); | |
| 22 | ||
| 23 | const Scope = union(enum) { | |
| 24 | typedef: Symbol, | |
| 25 | @"struct": Symbol, | |
| 26 | @"union": Symbol, | |
| 27 | @"enum": Symbol, | |
| 28 | decl: Symbol, | |
| 29 | def: Symbol, | |
| 30 | param: Symbol, | |
| 31 | enumeration: Enumeration, | |
| 32 | loop, | |
| 33 | @"switch": *Switch, | |
| 34 | block, | |
| 35 | ||
| 36 | const Symbol = struct { | |
| 37 | name: []const u8, | |
| 38 | ty: Type, | |
| 39 | name_tok: TokenIndex, | |
| 40 | }; | |
| 41 | ||
| 42 | const Enumeration = struct { | |
| 43 | name: []const u8, | |
| 44 | value: Result, | |
| 45 | name_tok: TokenIndex, | |
| 46 | }; | |
| 47 | ||
| 48 | const Switch = struct { | |
| 49 | cases: CaseMap, | |
| 50 | default: ?Case = null, | |
| 51 | ||
| 52 | const ResultContext = struct { | |
| 53 | ty: Type, | |
| 54 | comp: *Compilation, | |
| 55 | ||
| 56 | pub fn eql(ctx: ResultContext, a: Result, b: Result) bool { | |
| 57 | return a.val.compare(.eq, b.val, ctx.ty, ctx.comp); | |
| 58 | } | |
| 59 | pub fn hash(_: ResultContext, a: Result) u64 { | |
| 60 | return a.val.hash(); | |
| 61 | } | |
| 62 | }; | |
| 63 | const CaseMap = std.HashMap(Result, Case, ResultContext, std.hash_map.default_max_load_percentage); | |
| 64 | const Case = struct { | |
| 65 | node: NodeIndex, | |
| 66 | tok: TokenIndex, | |
| 67 | }; | |
| 68 | }; | |
| 69 | }; | |
| 70 | ||
| 71 | const Label = union(enum) { | |
| 72 | unresolved_goto: TokenIndex, | |
| 73 | label: TokenIndex, | |
| 74 | }; | |
| 75 | ||
| 76 | pub const Error = Compilation.Error || error{ParsingFailed}; | |
| 77 | ||
| 78 | /// An attribute that has been parsed but not yet validated in its context | |
| 79 | const TentativeAttribute = struct { | |
| 80 | attr: Attribute, | |
| 81 | tok: TokenIndex, | |
| 82 | }; | |
| 83 | ||
| 84 | // values from preprocessor | |
| 85 | pp: *Preprocessor, | |
| 86 | tok_ids: []const Token.Id, | |
| 87 | tok_i: TokenIndex = 0, | |
| 88 | ||
| 89 | // values of the incomplete Tree | |
| 90 | arena: Allocator, | |
| 91 | nodes: Tree.Node.List = .{}, | |
| 92 | data: NodeList, | |
| 93 | strings: std.ArrayList(u8), | |
| 94 | value_map: Tree.ValueMap, | |
| 95 | ||
| 96 | // buffers used during compilation | |
| 97 | scopes: std.ArrayList(Scope), | |
| 98 | labels: std.ArrayList(Label), | |
| 99 | list_buf: NodeList, | |
| 100 | decl_buf: NodeList, | |
| 101 | param_buf: std.ArrayList(Type.Func.Param), | |
| 102 | enum_buf: std.ArrayList(Type.Enum.Field), | |
| 103 | record_buf: std.ArrayList(Type.Record.Field), | |
| 104 | attr_buf: std.MultiArrayList(TentativeAttribute) = .{}, | |
| 105 | ||
| 106 | // configuration and miscellaneous info | |
| 107 | no_eval: bool = false, | |
| 108 | in_macro: bool = false, | |
| 109 | extension_suppressed: bool = false, | |
| 110 | contains_address_of_label: bool = false, | |
| 111 | label_count: u32 = 0, | |
| 112 | /// location of first computed goto in function currently being parsed | |
| 113 | /// if a computed goto is used, the function must contain an | |
| 114 | /// address-of-label expression (tracked with contains_address_of_label) | |
| 115 | computed_goto_tok: ?TokenIndex = null, | |
| 116 | ||
| 117 | /// Various variables that are different for each function. | |
| 118 | func: struct { | |
| 119 | /// null if not in function, will always be plain func, var_args_func or old_style_func | |
| 120 | ty: ?Type = null, | |
| 121 | name: TokenIndex = 0, | |
| 122 | ident: ?Result = null, | |
| 123 | pretty_ident: ?Result = null, | |
| 124 | } = .{}, | |
| 125 | /// Various variables that are different for each record. | |
| 126 | record: struct { | |
| 127 | // invalid means we're not parsing a record | |
| 128 | kind: Token.Id = .invalid, | |
| 129 | flexible_field: ?TokenIndex = null, | |
| 130 | scopes_top: usize = undefined, | |
| 131 | ||
| 132 | fn addField(r: @This(), p: *Parser, name_tok: TokenIndex) Error!void { | |
| 133 | const name = p.tokSlice(name_tok); | |
| 134 | var i = p.scopes.items.len; | |
| 135 | while (i > r.scopes_top) { | |
| 136 | i -= 1; | |
| 137 | switch (p.scopes.items[i]) { | |
| 138 | .def => |d| if (mem.eql(u8, d.name, name)) { | |
| 139 | try p.errStr(.duplicate_member, name_tok, name); | |
| 140 | try p.errTok(.previous_definition, d.name_tok); | |
| 141 | break; | |
| 142 | }, | |
| 143 | else => {}, | |
| 144 | } | |
| 145 | } | |
| 146 | try p.scopes.append(.{ | |
| 147 | .def = .{ | |
| 148 | .name = name, | |
| 149 | .name_tok = name_tok, | |
| 150 | .ty = undefined, // unused | |
| 151 | }, | |
| 152 | }); | |
| 153 | } | |
| 154 | ||
| 155 | fn addFieldsFromAnonymous(r: @This(), p: *Parser, ty: Type) Error!void { | |
| 156 | for (ty.data.record.fields) |f| { | |
| 157 | if (f.isAnonymousRecord()) { | |
| 158 | try r.addFieldsFromAnonymous(p, f.ty.canonicalize(.standard)); | |
| 159 | } else if (f.name_tok != 0) { | |
| 160 | try r.addField(p, f.name_tok); | |
| 161 | } | |
| 162 | } | |
| 163 | } | |
| 164 | } = .{}, | |
| 165 | ||
| 166 | fn checkIdentifierCodepoint(comp: *Compilation, codepoint: u21, loc: Source.Location) Compilation.Error!bool { | |
| 167 | if (codepoint <= 0x7F) return false; | |
| 168 | var diagnosed = false; | |
| 169 | if (!CharInfo.isC99IdChar(codepoint)) { | |
| 170 | try comp.diag.add(.{ | |
| 171 | .tag = .c99_compat, | |
| 172 | .loc = loc, | |
| 173 | }, &.{}); | |
| 174 | diagnosed = true; | |
| 175 | } | |
| 176 | if (CharInfo.isInvisible(codepoint)) { | |
| 177 | try comp.diag.add(.{ | |
| 178 | .tag = .unicode_zero_width, | |
| 179 | .loc = loc, | |
| 180 | .extra = .{ .actual_codepoint = codepoint }, | |
| 181 | }, &.{}); | |
| 182 | diagnosed = true; | |
| 183 | } | |
| 184 | if (CharInfo.homoglyph(codepoint)) |resembles| { | |
| 185 | try comp.diag.add(.{ | |
| 186 | .tag = .unicode_homoglyph, | |
| 187 | .loc = loc, | |
| 188 | .extra = .{ .codepoints = .{ .actual = codepoint, .resembles = resembles } }, | |
| 189 | }, &.{}); | |
| 190 | diagnosed = true; | |
| 191 | } | |
| 192 | return diagnosed; | |
| 193 | } | |
| 194 | ||
| 195 | fn eatIdentifier(p: *Parser) !?TokenIndex { | |
| 196 | switch (p.tok_ids[p.tok_i]) { | |
| 197 | .identifier => {}, | |
| 198 | .extended_identifier => { | |
| 199 | const slice = p.tokSlice(p.tok_i); | |
| 200 | var it = std.unicode.Utf8View.initUnchecked(slice).iterator(); | |
| 201 | var loc = p.pp.tokens.items(.loc)[p.tok_i]; | |
| 202 | ||
| 203 | if (mem.indexOfScalar(u8, slice, '$')) |i| { | |
| 204 | loc.byte_offset += @intCast(u32, i); | |
| 205 | try p.pp.comp.diag.add(.{ | |
| 206 | .tag = .dollar_in_identifier_extension, | |
| 207 | .loc = loc, | |
| 208 | }, &.{}); | |
| 209 | loc = p.pp.tokens.items(.loc)[p.tok_i]; | |
| 210 | } | |
| 211 | ||
| 212 | while (it.nextCodepoint()) |c| { | |
| 213 | if (try checkIdentifierCodepoint(p.pp.comp, c, loc)) break; | |
| 214 | loc.byte_offset += std.unicode.utf8CodepointSequenceLength(c) catch unreachable; | |
| 215 | } | |
| 216 | }, | |
| 217 | else => return null, | |
| 218 | } | |
| 219 | p.tok_i += 1; | |
| 220 | ||
| 221 | // Handle illegal '$' characters in identifiers | |
| 222 | if (!p.pp.comp.langopts.dollars_in_identifiers) { | |
| 223 | if (p.tok_ids[p.tok_i] == .invalid and p.tokSlice(p.tok_i)[0] == '$') { | |
| 224 | try p.err(.dollars_in_identifiers); | |
| 225 | p.tok_i += 1; | |
| 226 | return error.ParsingFailed; | |
| 227 | } | |
| 228 | } | |
| 229 | ||
| 230 | return p.tok_i - 1; | |
| 231 | } | |
| 232 | ||
| 233 | fn expectIdentifier(p: *Parser) Error!TokenIndex { | |
| 234 | const actual = p.tok_ids[p.tok_i]; | |
| 235 | if (actual != .identifier and actual != .extended_identifier) { | |
| 236 | return p.errExpectedToken(.identifier, actual); | |
| 237 | } | |
| 238 | ||
| 239 | return (try p.eatIdentifier()) orelse unreachable; | |
| 240 | } | |
| 241 | ||
| 242 | fn eatToken(p: *Parser, id: Token.Id) ?TokenIndex { | |
| 243 | assert(id != .identifier and id != .extended_identifier); // use eatIdentifier | |
| 244 | if (p.tok_ids[p.tok_i] == id) { | |
| 245 | defer p.tok_i += 1; | |
| 246 | return p.tok_i; | |
| 247 | } else return null; | |
| 248 | } | |
| 249 | ||
| 250 | fn expectToken(p: *Parser, expected: Token.Id) Error!TokenIndex { | |
| 251 | assert(expected != .identifier and expected != .extended_identifier); // use expectIdentifier | |
| 252 | const actual = p.tok_ids[p.tok_i]; | |
| 253 | if (actual != expected) return p.errExpectedToken(expected, actual); | |
| 254 | defer p.tok_i += 1; | |
| 255 | return p.tok_i; | |
| 256 | } | |
| 257 | ||
| 258 | fn tokSlice(p: *Parser, tok: TokenIndex) []const u8 { | |
| 259 | if (p.tok_ids[tok].lexeme()) |some| return some; | |
| 260 | const loc = p.pp.tokens.items(.loc)[tok]; | |
| 261 | var tmp_tokenizer = Tokenizer{ | |
| 262 | .buf = p.pp.comp.getSource(loc.id).buf, | |
| 263 | .comp = p.pp.comp, | |
| 264 | .index = loc.byte_offset, | |
| 265 | .source = .generated, | |
| 266 | }; | |
| 267 | const res = tmp_tokenizer.next(); | |
| 268 | return tmp_tokenizer.buf[res.start..res.end]; | |
| 269 | } | |
| 270 | ||
| 271 | fn expectClosing(p: *Parser, opening: TokenIndex, id: Token.Id) Error!void { | |
| 272 | _ = p.expectToken(id) catch |e| { | |
| 273 | if (e == error.ParsingFailed) { | |
| 274 | try p.errTok(switch (id) { | |
| 275 | .r_paren => .to_match_paren, | |
| 276 | .r_brace => .to_match_brace, | |
| 277 | .r_bracket => .to_match_brace, | |
| 278 | else => unreachable, | |
| 279 | }, opening); | |
| 280 | } | |
| 281 | return e; | |
| 282 | }; | |
| 283 | } | |
| 284 | ||
| 285 | fn errOverflow(p: *Parser, op_tok: TokenIndex, res: Result) !void { | |
| 286 | if (res.ty.isUnsignedInt(p.pp.comp)) { | |
| 287 | try p.errExtra(.overflow_unsigned, op_tok, .{ .unsigned = res.val.data.int }); | |
| 288 | } else { | |
| 289 | try p.errExtra(.overflow_signed, op_tok, .{ .signed = res.val.signExtend(res.ty, p.pp.comp) }); | |
| 290 | } | |
| 291 | } | |
| 292 | ||
| 293 | fn errExpectedToken(p: *Parser, expected: Token.Id, actual: Token.Id) Error { | |
| 294 | switch (actual) { | |
| 295 | .invalid => try p.errExtra(.expected_invalid, p.tok_i, .{ .tok_id_expected = expected }), | |
| 296 | .eof => try p.errExtra(.expected_eof, p.tok_i, .{ .tok_id_expected = expected }), | |
| 297 | else => try p.errExtra(.expected_token, p.tok_i, .{ .tok_id = .{ | |
| 298 | .expected = expected, | |
| 299 | .actual = actual, | |
| 300 | } }), | |
| 301 | } | |
| 302 | return error.ParsingFailed; | |
| 303 | } | |
| 304 | ||
| 305 | pub fn errStr(p: *Parser, tag: Diagnostics.Tag, tok_i: TokenIndex, str: []const u8) Compilation.Error!void { | |
| 306 | @setCold(true); | |
| 307 | return p.errExtra(tag, tok_i, .{ .str = str }); | |
| 308 | } | |
| 309 | ||
| 310 | pub fn errExtra(p: *Parser, tag: Diagnostics.Tag, tok_i: TokenIndex, extra: Diagnostics.Message.Extra) Compilation.Error!void { | |
| 311 | @setCold(true); | |
| 312 | const tok = p.pp.tokens.get(tok_i); | |
| 313 | var loc = tok.loc; | |
| 314 | if (tok_i != 0 and tok.id == .eof) { | |
| 315 | // if the token is EOF, point at the end of the previous token instead | |
| 316 | const prev = p.pp.tokens.get(tok_i - 1); | |
| 317 | loc = prev.loc; | |
| 318 | loc.byte_offset += @intCast(u32, p.tokSlice(tok_i - 1).len); | |
| 319 | } | |
| 320 | try p.pp.comp.diag.add(.{ | |
| 321 | .tag = tag, | |
| 322 | .loc = loc, | |
| 323 | .extra = extra, | |
| 324 | }, tok.expansionSlice()); | |
| 325 | } | |
| 326 | ||
| 327 | pub fn errTok(p: *Parser, tag: Diagnostics.Tag, tok_i: TokenIndex) Compilation.Error!void { | |
| 328 | @setCold(true); | |
| 329 | return p.errExtra(tag, tok_i, .{ .none = {} }); | |
| 330 | } | |
| 331 | ||
| 332 | pub fn err(p: *Parser, tag: Diagnostics.Tag) Compilation.Error!void { | |
| 333 | @setCold(true); | |
| 334 | return p.errTok(tag, p.tok_i); | |
| 335 | } | |
| 336 | ||
| 337 | pub fn todo(p: *Parser, msg: []const u8) Error { | |
| 338 | try p.errStr(.todo, p.tok_i, msg); | |
| 339 | return error.ParsingFailed; | |
| 340 | } | |
| 341 | ||
| 342 | pub fn ignoredAttrStr(p: *Parser, attr: Attribute.Tag, context: Attribute.ParseContext) ![]const u8 { | |
| 343 | const strings_top = p.strings.items.len; | |
| 344 | defer p.strings.items.len = strings_top; | |
| 345 | ||
| 346 | try p.strings.writer().print("Attribute '{s}' ignored in {s} context", .{ @tagName(attr), @tagName(context) }); | |
| 347 | return try p.pp.comp.diag.arena.allocator().dupe(u8, p.strings.items[strings_top..]); | |
| 348 | } | |
| 349 | ||
| 350 | pub fn typeStr(p: *Parser, ty: Type) ![]const u8 { | |
| 351 | if (Type.Builder.fromType(ty).str()) |str| return str; | |
| 352 | const strings_top = p.strings.items.len; | |
| 353 | defer p.strings.items.len = strings_top; | |
| 354 | ||
| 355 | try ty.print(p.strings.writer()); | |
| 356 | return try p.pp.comp.diag.arena.allocator().dupe(u8, p.strings.items[strings_top..]); | |
| 357 | } | |
| 358 | ||
| 359 | pub fn typePairStr(p: *Parser, a: Type, b: Type) ![]const u8 { | |
| 360 | return p.typePairStrExtra(a, " and ", b); | |
| 361 | } | |
| 362 | ||
| 363 | pub fn typePairStrExtra(p: *Parser, a: Type, msg: []const u8, b: Type) ![]const u8 { | |
| 364 | const strings_top = p.strings.items.len; | |
| 365 | defer p.strings.items.len = strings_top; | |
| 366 | ||
| 367 | try p.strings.append('\''); | |
| 368 | try a.print(p.strings.writer()); | |
| 369 | try p.strings.append('\''); | |
| 370 | try p.strings.appendSlice(msg); | |
| 371 | try p.strings.append('\''); | |
| 372 | try b.print(p.strings.writer()); | |
| 373 | try p.strings.append('\''); | |
| 374 | return try p.pp.comp.diag.arena.allocator().dupe(u8, p.strings.items[strings_top..]); | |
| 375 | } | |
| 376 | ||
| 377 | fn checkDeprecatedUnavailable(p: *Parser, ty: Type, usage_tok: TokenIndex, decl_tok: TokenIndex) !void { | |
| 378 | if (ty.getAttribute(.unavailable)) |unavailable| { | |
| 379 | try p.errDeprecated(.unavailable, usage_tok, unavailable.msg); | |
| 380 | try p.errStr(.unavailable_note, unavailable.__name_tok, p.tokSlice(decl_tok)); | |
| 381 | return error.ParsingFailed; | |
| 382 | } else if (ty.getAttribute(.deprecated)) |deprecated| { | |
| 383 | try p.errDeprecated(.deprecated_declarations, usage_tok, deprecated.msg); | |
| 384 | try p.errStr(.deprecated_note, deprecated.__name_tok, p.tokSlice(decl_tok)); | |
| 385 | } | |
| 386 | } | |
| 387 | ||
| 388 | fn errDeprecated(p: *Parser, tag: Diagnostics.Tag, tok_i: TokenIndex, msg: ?[]const u8) Compilation.Error!void { | |
| 389 | const strings_top = p.strings.items.len; | |
| 390 | defer p.strings.items.len = strings_top; | |
| 391 | ||
| 392 | const w = p.strings.writer(); | |
| 393 | try w.print("'{s}' is ", .{p.tokSlice(tok_i)}); | |
| 394 | const reason: []const u8 = switch (tag) { | |
| 395 | .unavailable => "unavailable", | |
| 396 | .deprecated_declarations => "deprecated", | |
| 397 | else => unreachable, | |
| 398 | }; | |
| 399 | try w.writeAll(reason); | |
| 400 | if (msg) |m| { | |
| 401 | try w.print(": {s}", .{m}); | |
| 402 | } | |
| 403 | const str = try p.pp.comp.diag.arena.allocator().dupe(u8, p.strings.items[strings_top..]); | |
| 404 | return p.errStr(tag, tok_i, str); | |
| 405 | } | |
| 406 | ||
| 407 | fn addNode(p: *Parser, node: Tree.Node) Allocator.Error!NodeIndex { | |
| 408 | if (p.in_macro) return .none; | |
| 409 | const res = p.nodes.len; | |
| 410 | try p.nodes.append(p.pp.comp.gpa, node); | |
| 411 | return @intToEnum(NodeIndex, res); | |
| 412 | } | |
| 413 | ||
| 414 | fn addList(p: *Parser, nodes: []const NodeIndex) Allocator.Error!Tree.Node.Range { | |
| 415 | if (p.in_macro) return Tree.Node.Range{ .start = 0, .end = 0 }; | |
| 416 | const start = @intCast(u32, p.data.items.len); | |
| 417 | try p.data.appendSlice(nodes); | |
| 418 | const end = @intCast(u32, p.data.items.len); | |
| 419 | return Tree.Node.Range{ .start = start, .end = end }; | |
| 420 | } | |
| 421 | ||
| 422 | fn findTypedef(p: *Parser, name_tok: TokenIndex, no_type_yet: bool) !?Scope.Symbol { | |
| 423 | const name = p.tokSlice(name_tok); | |
| 424 | var i = p.scopes.items.len; | |
| 425 | while (i > 0) { | |
| 426 | i -= 1; | |
| 427 | switch (p.scopes.items[i]) { | |
| 428 | .typedef => |t| if (mem.eql(u8, t.name, name)) return t, | |
| 429 | .@"struct" => |s| if (mem.eql(u8, s.name, name)) { | |
| 430 | if (no_type_yet) return null; | |
| 431 | try p.errStr(.must_use_struct, name_tok, name); | |
| 432 | return s; | |
| 433 | }, | |
| 434 | .@"union" => |u| if (mem.eql(u8, u.name, name)) { | |
| 435 | if (no_type_yet) return null; | |
| 436 | try p.errStr(.must_use_union, name_tok, name); | |
| 437 | return u; | |
| 438 | }, | |
| 439 | .@"enum" => |e| if (mem.eql(u8, e.name, name)) { | |
| 440 | if (no_type_yet) return null; | |
| 441 | try p.errStr(.must_use_enum, name_tok, name); | |
| 442 | return e; | |
| 443 | }, | |
| 444 | .def, .decl => |d| if (mem.eql(u8, d.name, name)) return null, | |
| 445 | else => {}, | |
| 446 | } | |
| 447 | } | |
| 448 | return null; | |
| 449 | } | |
| 450 | ||
| 451 | fn findSymbol(p: *Parser, name_tok: TokenIndex, ref_kind: enum { reference, definition }) ?Scope { | |
| 452 | const name = p.tokSlice(name_tok); | |
| 453 | var i = p.scopes.items.len; | |
| 454 | while (i > 0) { | |
| 455 | i -= 1; | |
| 456 | const sym = p.scopes.items[i]; | |
| 457 | switch (sym) { | |
| 458 | .def, .decl, .param => |s| if (mem.eql(u8, s.name, name)) return sym, | |
| 459 | .enumeration => |e| if (mem.eql(u8, e.name, name)) return sym, | |
| 460 | .block => if (ref_kind == .definition) return null, | |
| 461 | else => {}, | |
| 462 | } | |
| 463 | } | |
| 464 | return null; | |
| 465 | } | |
| 466 | ||
| 467 | fn findTag(p: *Parser, kind: Token.Id, name_tok: TokenIndex, ref_kind: enum { reference, definition }) !?Scope.Symbol { | |
| 468 | const name = p.tokSlice(name_tok); | |
| 469 | var i = p.scopes.items.len; | |
| 470 | var saw_block = false; | |
| 471 | while (i > 0) { | |
| 472 | i -= 1; | |
| 473 | const sym = p.scopes.items[i]; | |
| 474 | switch (sym) { | |
| 475 | .@"enum" => |e| if (mem.eql(u8, e.name, name)) { | |
| 476 | if (kind == .keyword_enum) return e; | |
| 477 | if (saw_block) return null; | |
| 478 | try p.errStr(.wrong_tag, name_tok, name); | |
| 479 | try p.errTok(.previous_definition, e.name_tok); | |
| 480 | return null; | |
| 481 | }, | |
| 482 | .@"struct" => |s| if (mem.eql(u8, s.name, name)) { | |
| 483 | if (kind == .keyword_struct) return s; | |
| 484 | if (saw_block) return null; | |
| 485 | try p.errStr(.wrong_tag, name_tok, name); | |
| 486 | try p.errTok(.previous_definition, s.name_tok); | |
| 487 | return null; | |
| 488 | }, | |
| 489 | .@"union" => |u| if (mem.eql(u8, u.name, name)) { | |
| 490 | if (kind == .keyword_union) return u; | |
| 491 | if (saw_block) return null; | |
| 492 | try p.errStr(.wrong_tag, name_tok, name); | |
| 493 | try p.errTok(.previous_definition, u.name_tok); | |
| 494 | return null; | |
| 495 | }, | |
| 496 | .block => if (ref_kind == .reference) { | |
| 497 | saw_block = true; | |
| 498 | } else return null, | |
| 499 | else => {}, | |
| 500 | } | |
| 501 | } | |
| 502 | return null; | |
| 503 | } | |
| 504 | ||
| 505 | fn inLoop(p: *Parser) bool { | |
| 506 | var i = p.scopes.items.len; | |
| 507 | while (i > 0) { | |
| 508 | i -= 1; | |
| 509 | switch (p.scopes.items[i]) { | |
| 510 | .loop => return true, | |
| 511 | else => {}, | |
| 512 | } | |
| 513 | } | |
| 514 | return false; | |
| 515 | } | |
| 516 | ||
| 517 | fn inLoopOrSwitch(p: *Parser) bool { | |
| 518 | var i = p.scopes.items.len; | |
| 519 | while (i > 0) { | |
| 520 | i -= 1; | |
| 521 | switch (p.scopes.items[i]) { | |
| 522 | .loop, .@"switch" => return true, | |
| 523 | else => {}, | |
| 524 | } | |
| 525 | } | |
| 526 | return false; | |
| 527 | } | |
| 528 | ||
| 529 | fn findLabel(p: *Parser, name: []const u8) ?TokenIndex { | |
| 530 | for (p.labels.items) |item| { | |
| 531 | switch (item) { | |
| 532 | .label => |l| if (mem.eql(u8, p.tokSlice(l), name)) return l, | |
| 533 | .unresolved_goto => {}, | |
| 534 | } | |
| 535 | } | |
| 536 | return null; | |
| 537 | } | |
| 538 | ||
| 539 | fn findSwitch(p: *Parser) ?*Scope.Switch { | |
| 540 | var i = p.scopes.items.len; | |
| 541 | while (i > 0) { | |
| 542 | i -= 1; | |
| 543 | switch (p.scopes.items[i]) { | |
| 544 | .@"switch" => |s| return s, | |
| 545 | else => {}, | |
| 546 | } | |
| 547 | } | |
| 548 | return null; | |
| 549 | } | |
| 550 | ||
| 551 | fn nodeIs(p: *Parser, node: NodeIndex, tag: Tree.Tag) bool { | |
| 552 | return p.getNode(node, tag) != null; | |
| 553 | } | |
| 554 | ||
| 555 | fn getNode(p: *Parser, node: NodeIndex, tag: Tree.Tag) ?NodeIndex { | |
| 556 | var cur = node; | |
| 557 | const tags = p.nodes.items(.tag); | |
| 558 | const data = p.nodes.items(.data); | |
| 559 | while (true) { | |
| 560 | const cur_tag = tags[@enumToInt(cur)]; | |
| 561 | if (cur_tag == .paren_expr) { | |
| 562 | cur = data[@enumToInt(cur)].un; | |
| 563 | } else if (cur_tag == tag) { | |
| 564 | return cur; | |
| 565 | } else { | |
| 566 | return null; | |
| 567 | } | |
| 568 | } | |
| 569 | } | |
| 570 | ||
| 571 | fn pragma(p: *Parser) Compilation.Error!bool { | |
| 572 | var found_pragma = false; | |
| 573 | while (p.eatToken(.keyword_pragma)) |_| { | |
| 574 | found_pragma = true; | |
| 575 | ||
| 576 | const name_tok = p.tok_i; | |
| 577 | const name = p.tokSlice(name_tok); | |
| 578 | ||
| 579 | const end_idx = mem.indexOfScalarPos(Token.Id, p.tok_ids, p.tok_i, .nl).?; | |
| 580 | const pragma_len = @intCast(TokenIndex, end_idx) - p.tok_i; | |
| 581 | defer p.tok_i += pragma_len + 1; // skip past .nl as well | |
| 582 | if (p.pp.comp.getPragma(name)) |prag| { | |
| 583 | try prag.parserCB(p, p.tok_i); | |
| 584 | } | |
| 585 | } | |
| 586 | return found_pragma; | |
| 587 | } | |
| 588 | ||
| 589 | /// root : (decl | assembly ';' | staticAssert)* | |
| 590 | pub fn parse(pp: *Preprocessor) Compilation.Error!Tree { | |
| 591 | pp.comp.pragmaEvent(.before_parse); | |
| 592 | ||
| 593 | var arena = std.heap.ArenaAllocator.init(pp.comp.gpa); | |
| 594 | errdefer arena.deinit(); | |
| 595 | var p = Parser{ | |
| 596 | .pp = pp, | |
| 597 | .arena = arena.allocator(), | |
| 598 | .tok_ids = pp.tokens.items(.id), | |
| 599 | .strings = std.ArrayList(u8).init(pp.comp.gpa), | |
| 600 | .value_map = Tree.ValueMap.init(pp.comp.gpa), | |
| 601 | .data = NodeList.init(pp.comp.gpa), | |
| 602 | .labels = std.ArrayList(Label).init(pp.comp.gpa), | |
| 603 | .scopes = std.ArrayList(Scope).init(pp.comp.gpa), | |
| 604 | .list_buf = NodeList.init(pp.comp.gpa), | |
| 605 | .decl_buf = NodeList.init(pp.comp.gpa), | |
| 606 | .param_buf = std.ArrayList(Type.Func.Param).init(pp.comp.gpa), | |
| 607 | .enum_buf = std.ArrayList(Type.Enum.Field).init(pp.comp.gpa), | |
| 608 | .record_buf = std.ArrayList(Type.Record.Field).init(pp.comp.gpa), | |
| 609 | }; | |
| 610 | errdefer { | |
| 611 | p.nodes.deinit(pp.comp.gpa); | |
| 612 | p.strings.deinit(); | |
| 613 | p.value_map.deinit(); | |
| 614 | } | |
| 615 | defer { | |
| 616 | p.data.deinit(); | |
| 617 | p.labels.deinit(); | |
| 618 | p.scopes.deinit(); | |
| 619 | p.list_buf.deinit(); | |
| 620 | p.decl_buf.deinit(); | |
| 621 | p.param_buf.deinit(); | |
| 622 | p.enum_buf.deinit(); | |
| 623 | p.record_buf.deinit(); | |
| 624 | p.attr_buf.deinit(pp.comp.gpa); | |
| 625 | } | |
| 626 | ||
| 627 | // NodeIndex 0 must be invalid | |
| 628 | _ = try p.addNode(.{ .tag = .invalid, .ty = undefined, .data = undefined }); | |
| 629 | ||
| 630 | { | |
| 631 | const ty = &pp.comp.types.va_list; | |
| 632 | const sym = Scope.Symbol{ .name = "__builtin_va_list", .ty = ty.*, .name_tok = 0 }; | |
| 633 | try p.scopes.append(.{ .typedef = sym }); | |
| 634 | ||
| 635 | if (ty.isArray()) ty.decayArray(); | |
| 636 | } | |
| 637 | ||
| 638 | while (p.eatToken(.eof) == null) { | |
| 639 | if (try p.pragma()) continue; | |
| 640 | if (try p.parseOrNextDecl(staticAssert)) continue; | |
| 641 | if (try p.parseOrNextDecl(decl)) continue; | |
| 642 | if (p.eatToken(.keyword_extension)) |_| { | |
| 643 | const saved_extension = p.extension_suppressed; | |
| 644 | defer p.extension_suppressed = saved_extension; | |
| 645 | p.extension_suppressed = true; | |
| 646 | ||
| 647 | if (try p.parseOrNextDecl(decl)) continue; | |
| 648 | switch (p.tok_ids[p.tok_i]) { | |
| 649 | .semicolon => p.tok_i += 1, | |
| 650 | .keyword_static_assert, | |
| 651 | .keyword_pragma, | |
| 652 | .keyword_extension, | |
| 653 | .keyword_asm, | |
| 654 | .keyword_asm1, | |
| 655 | .keyword_asm2, | |
| 656 | => {}, | |
| 657 | else => try p.err(.expected_external_decl), | |
| 658 | } | |
| 659 | continue; | |
| 660 | } | |
| 661 | if (p.assembly(.global) catch |er| switch (er) { | |
| 662 | error.ParsingFailed => { | |
| 663 | p.nextExternDecl(); | |
| 664 | continue; | |
| 665 | }, | |
| 666 | else => |e| return e, | |
| 667 | }) |_| continue; | |
| 668 | if (p.eatToken(.semicolon)) |tok| { | |
| 669 | try p.errTok(.extra_semi, tok); | |
| 670 | continue; | |
| 671 | } | |
| 672 | try p.err(.expected_external_decl); | |
| 673 | p.tok_i += 1; | |
| 674 | } | |
| 675 | const root_decls = p.decl_buf.toOwnedSlice(); | |
| 676 | if (root_decls.len == 0) { | |
| 677 | try p.errTok(.empty_translation_unit, p.tok_i - 1); | |
| 678 | } | |
| 679 | pp.comp.pragmaEvent(.after_parse); | |
| 680 | return Tree{ | |
| 681 | .comp = pp.comp, | |
| 682 | .tokens = pp.tokens.slice(), | |
| 683 | .arena = arena, | |
| 684 | .generated = pp.comp.generated_buf.items, | |
| 685 | .nodes = p.nodes.toOwnedSlice(), | |
| 686 | .data = p.data.toOwnedSlice(), | |
| 687 | .root_decls = root_decls, | |
| 688 | .strings = p.strings.toOwnedSlice(), | |
| 689 | .value_map = p.value_map, | |
| 690 | }; | |
| 691 | } | |
| 692 | ||
| 693 | fn skipToPragmaSentinel(p: *Parser) void { | |
| 694 | while (true) : (p.tok_i += 1) { | |
| 695 | if (p.tok_ids[p.tok_i] == .nl) return; | |
| 696 | if (p.tok_ids[p.tok_i] == .eof) { | |
| 697 | p.tok_i -= 1; | |
| 698 | return; | |
| 699 | } | |
| 700 | } | |
| 701 | } | |
| 702 | ||
| 703 | fn parseOrNextDecl(p: *Parser, comptime func: fn (*Parser) Error!bool) Compilation.Error!bool { | |
| 704 | return func(p) catch |er| switch (er) { | |
| 705 | error.ParsingFailed => { | |
| 706 | p.nextExternDecl(); | |
| 707 | return true; | |
| 708 | }, | |
| 709 | else => |e| return e, | |
| 710 | }; | |
| 711 | } | |
| 712 | ||
| 713 | fn nextExternDecl(p: *Parser) void { | |
| 714 | var parens: u32 = 0; | |
| 715 | while (true) : (p.tok_i += 1) { | |
| 716 | switch (p.tok_ids[p.tok_i]) { | |
| 717 | .l_paren, .l_brace, .l_bracket => parens += 1, | |
| 718 | .r_paren, .r_brace, .r_bracket => if (parens != 0) { | |
| 719 | parens -= 1; | |
| 720 | }, | |
| 721 | .keyword_typedef, | |
| 722 | .keyword_extern, | |
| 723 | .keyword_static, | |
| 724 | .keyword_auto, | |
| 725 | .keyword_register, | |
| 726 | .keyword_thread_local, | |
| 727 | .keyword_inline, | |
| 728 | .keyword_inline1, | |
| 729 | .keyword_inline2, | |
| 730 | .keyword_noreturn, | |
| 731 | .keyword_void, | |
| 732 | .keyword_bool, | |
| 733 | .keyword_char, | |
| 734 | .keyword_short, | |
| 735 | .keyword_int, | |
| 736 | .keyword_long, | |
| 737 | .keyword_signed, | |
| 738 | .keyword_unsigned, | |
| 739 | .keyword_float, | |
| 740 | .keyword_double, | |
| 741 | .keyword_complex, | |
| 742 | .keyword_atomic, | |
| 743 | .keyword_enum, | |
| 744 | .keyword_struct, | |
| 745 | .keyword_union, | |
| 746 | .keyword_alignas, | |
| 747 | .identifier, | |
| 748 | .extended_identifier, | |
| 749 | .keyword_typeof, | |
| 750 | .keyword_typeof1, | |
| 751 | .keyword_typeof2, | |
| 752 | .keyword_extension, | |
| 753 | => if (parens == 0) return, | |
| 754 | .keyword_pragma => p.skipToPragmaSentinel(), | |
| 755 | .eof => return, | |
| 756 | .semicolon => if (parens == 0) { | |
| 757 | p.tok_i += 1; | |
| 758 | return; | |
| 759 | }, | |
| 760 | else => {}, | |
| 761 | } | |
| 762 | } | |
| 763 | } | |
| 764 | ||
| 765 | fn skipTo(p: *Parser, id: Token.Id) void { | |
| 766 | var parens: u32 = 0; | |
| 767 | while (true) : (p.tok_i += 1) { | |
| 768 | if (p.tok_ids[p.tok_i] == id and parens == 0) { | |
| 769 | p.tok_i += 1; | |
| 770 | return; | |
| 771 | } | |
| 772 | switch (p.tok_ids[p.tok_i]) { | |
| 773 | .l_paren, .l_brace, .l_bracket => parens += 1, | |
| 774 | .r_paren, .r_brace, .r_bracket => if (parens != 0) { | |
| 775 | parens -= 1; | |
| 776 | }, | |
| 777 | .keyword_pragma => p.skipToPragmaSentinel(), | |
| 778 | .eof => return, | |
| 779 | else => {}, | |
| 780 | } | |
| 781 | } | |
| 782 | } | |
| 783 | ||
| 784 | pub fn withAttributes(p: *Parser, ty: Type, attr_buf_start: usize) !Type { | |
| 785 | const attrs = p.attr_buf.items(.attr)[attr_buf_start..]; | |
| 786 | return ty.withAttributes(p.arena, attrs); | |
| 787 | } | |
| 788 | ||
| 789 | // ====== declarations ====== | |
| 790 | ||
| 791 | /// decl | |
| 792 | /// : declSpec (initDeclarator ( ',' initDeclarator)*)? ';' | |
| 793 | /// | declSpec declarator decl* compoundStmt | |
| 794 | fn decl(p: *Parser) Error!bool { | |
| 795 | _ = try p.pragma(); | |
| 796 | const first_tok = p.tok_i; | |
| 797 | const attr_buf_top = p.attr_buf.len; | |
| 798 | defer p.attr_buf.len = attr_buf_top; | |
| 799 | ||
| 800 | try p.attributeSpecifier(); | |
| 801 | ||
| 802 | var decl_spec = if (try p.declSpec(false)) |some| some else blk: { | |
| 803 | if (p.func.ty != null) { | |
| 804 | p.tok_i = first_tok; | |
| 805 | return false; | |
| 806 | } | |
| 807 | switch (p.tok_ids[first_tok]) { | |
| 808 | .asterisk, .l_paren, .identifier, .extended_identifier => {}, | |
| 809 | else => if (p.tok_i != first_tok) { | |
| 810 | try p.err(.expected_ident_or_l_paren); | |
| 811 | return error.ParsingFailed; | |
| 812 | } else return false, | |
| 813 | } | |
| 814 | var spec: Type.Builder = .{}; | |
| 815 | break :blk DeclSpec{ .ty = try spec.finish(p, p.attr_buf.len) }; | |
| 816 | }; | |
| 817 | if (decl_spec.@"noreturn") |tok| { | |
| 818 | const attr = Attribute{ .tag = .noreturn, .args = .{ .noreturn = {} } }; | |
| 819 | try p.attr_buf.append(p.pp.comp.gpa, .{ .attr = attr, .tok = tok }); | |
| 820 | } | |
| 821 | try decl_spec.warnIgnoredAttrs(p, attr_buf_top); | |
| 822 | var init_d = (try p.initDeclarator(&decl_spec)) orelse { | |
| 823 | _ = try p.expectToken(.semicolon); | |
| 824 | if (decl_spec.ty.is(.@"enum") or | |
| 825 | (decl_spec.ty.isRecord() and !decl_spec.ty.isAnonymousRecord() and | |
| 826 | !decl_spec.ty.isTypeof())) // we follow GCC and clang's behavior here | |
| 827 | return true; | |
| 828 | ||
| 829 | try p.errTok(.missing_declaration, first_tok); | |
| 830 | return true; | |
| 831 | }; | |
| 832 | ||
| 833 | init_d.d.ty = try p.withAttributes(init_d.d.ty, attr_buf_top); | |
| 834 | try p.validateAlignas(init_d.d.ty, null); | |
| 835 | ||
| 836 | // Check for function definition. | |
| 837 | if (init_d.d.func_declarator != null and init_d.initializer == .none and init_d.d.ty.isFunc()) fn_def: { | |
| 838 | switch (p.tok_ids[p.tok_i]) { | |
| 839 | .comma, .semicolon => break :fn_def, | |
| 840 | .l_brace => {}, | |
| 841 | else => if (init_d.d.old_style_func == null) { | |
| 842 | try p.err(.expected_fn_body); | |
| 843 | return true; | |
| 844 | }, | |
| 845 | } | |
| 846 | if (p.func.ty != null) try p.err(.func_not_in_root); | |
| 847 | ||
| 848 | if (p.findSymbol(init_d.d.name, .definition)) |sym| { | |
| 849 | if (sym == .def) { | |
| 850 | try p.errStr(.redefinition, init_d.d.name, p.tokSlice(init_d.d.name)); | |
| 851 | try p.errTok(.previous_definition, sym.def.name_tok); | |
| 852 | } | |
| 853 | } | |
| 854 | try p.scopes.append(.{ .def = .{ | |
| 855 | .name = p.tokSlice(init_d.d.name), | |
| 856 | .ty = init_d.d.ty, | |
| 857 | .name_tok = init_d.d.name, | |
| 858 | } }); | |
| 859 | ||
| 860 | const func = p.func; | |
| 861 | p.func = .{ | |
| 862 | .ty = init_d.d.ty, | |
| 863 | .name = init_d.d.name, | |
| 864 | }; | |
| 865 | defer p.func = func; | |
| 866 | ||
| 867 | const scopes_top = p.scopes.items.len; | |
| 868 | defer p.scopes.items.len = scopes_top; | |
| 869 | ||
| 870 | // findSymbol stops the search at .block | |
| 871 | try p.scopes.append(.block); | |
| 872 | ||
| 873 | // Collect old style parameter declarations. | |
| 874 | if (init_d.d.old_style_func != null) { | |
| 875 | const attrs = init_d.d.ty.getAttributes(); | |
| 876 | var base_ty = if (init_d.d.ty.specifier == .attributed) init_d.d.ty.elemType() else init_d.d.ty; | |
| 877 | base_ty.specifier = .func; | |
| 878 | init_d.d.ty = try base_ty.withAttributes(p.arena, attrs); | |
| 879 | ||
| 880 | const param_buf_top = p.param_buf.items.len; | |
| 881 | defer p.param_buf.items.len = param_buf_top; | |
| 882 | ||
| 883 | param_loop: while (true) { | |
| 884 | const param_decl_spec = (try p.declSpec(true)) orelse break; | |
| 885 | if (p.eatToken(.semicolon)) |semi| { | |
| 886 | try p.errTok(.missing_declaration, semi); | |
| 887 | continue :param_loop; | |
| 888 | } | |
| 889 | ||
| 890 | while (true) { | |
| 891 | var d = (try p.declarator(param_decl_spec.ty, .normal)) orelse { | |
| 892 | try p.errTok(.missing_declaration, first_tok); | |
| 893 | _ = try p.expectToken(.semicolon); | |
| 894 | continue :param_loop; | |
| 895 | }; | |
| 896 | if (d.ty.hasIncompleteSize() and !d.ty.is(.void)) try p.errStr(.parameter_incomplete_ty, d.name, try p.typeStr(d.ty)); | |
| 897 | if (d.ty.isFunc()) { | |
| 898 | // Params declared as functions are converted to function pointers. | |
| 899 | const elem_ty = try p.arena.create(Type); | |
| 900 | elem_ty.* = d.ty; | |
| 901 | d.ty = Type{ | |
| 902 | .specifier = .pointer, | |
| 903 | .data = .{ .sub_type = elem_ty }, | |
| 904 | }; | |
| 905 | } else if (d.ty.isArray()) { | |
| 906 | // params declared as arrays are converted to pointers | |
| 907 | d.ty.decayArray(); | |
| 908 | } else if (d.ty.is(.void)) { | |
| 909 | try p.errTok(.invalid_void_param, d.name); | |
| 910 | } | |
| 911 | ||
| 912 | // find and correct parameter types | |
| 913 | // TODO check for missing declarations and redefinitions | |
| 914 | const name_str = p.tokSlice(d.name); | |
| 915 | for (init_d.d.ty.params()) |*param| { | |
| 916 | if (mem.eql(u8, param.name, name_str)) { | |
| 917 | param.ty = d.ty; | |
| 918 | break; | |
| 919 | } | |
| 920 | } else { | |
| 921 | try p.errStr(.parameter_missing, d.name, name_str); | |
| 922 | } | |
| 923 | ||
| 924 | try p.scopes.append(.{ .param = .{ | |
| 925 | .name = name_str, | |
| 926 | .name_tok = d.name, | |
| 927 | .ty = d.ty, | |
| 928 | } }); | |
| 929 | if (p.eatToken(.comma) == null) break; | |
| 930 | } | |
| 931 | _ = try p.expectToken(.semicolon); | |
| 932 | } | |
| 933 | } else { | |
| 934 | for (init_d.d.ty.params()) |param| { | |
| 935 | if (param.ty.hasUnboundVLA()) try p.errTok(.unbound_vla, param.name_tok); | |
| 936 | if (param.ty.hasIncompleteSize() and !param.ty.is(.void)) try p.errStr(.parameter_incomplete_ty, param.name_tok, try p.typeStr(param.ty)); | |
| 937 | ||
| 938 | if (param.name.len == 0) { | |
| 939 | try p.errTok(.omitting_parameter_name, param.name_tok); | |
| 940 | continue; | |
| 941 | } | |
| 942 | ||
| 943 | try p.scopes.append(.{ | |
| 944 | .param = .{ | |
| 945 | .name = param.name, | |
| 946 | .ty = param.ty, | |
| 947 | .name_tok = param.name_tok, | |
| 948 | }, | |
| 949 | }); | |
| 950 | } | |
| 951 | } | |
| 952 | ||
| 953 | const body = (try p.compoundStmt(true, null)) orelse { | |
| 954 | assert(init_d.d.old_style_func != null); | |
| 955 | try p.err(.expected_fn_body); | |
| 956 | return true; | |
| 957 | }; | |
| 958 | const node = try p.addNode(.{ | |
| 959 | .ty = init_d.d.ty, | |
| 960 | .tag = try decl_spec.validateFnDef(p), | |
| 961 | .data = .{ .decl = .{ .name = init_d.d.name, .node = body } }, | |
| 962 | }); | |
| 963 | try p.decl_buf.append(node); | |
| 964 | ||
| 965 | // check gotos | |
| 966 | if (func.ty == null) { | |
| 967 | for (p.labels.items) |item| { | |
| 968 | if (item == .unresolved_goto) | |
| 969 | try p.errStr(.undeclared_label, item.unresolved_goto, p.tokSlice(item.unresolved_goto)); | |
| 970 | } | |
| 971 | if (p.computed_goto_tok) |goto_tok| { | |
| 972 | if (!p.contains_address_of_label) try p.errTok(.invalid_computed_goto, goto_tok); | |
| 973 | } | |
| 974 | p.labels.items.len = 0; | |
| 975 | p.label_count = 0; | |
| 976 | p.contains_address_of_label = false; | |
| 977 | p.computed_goto_tok = null; | |
| 978 | } | |
| 979 | return true; | |
| 980 | } | |
| 981 | ||
| 982 | // Declare all variable/typedef declarators. | |
| 983 | while (true) { | |
| 984 | if (init_d.d.old_style_func) |tok_i| try p.errTok(.invalid_old_style_params, tok_i); | |
| 985 | const tag = try decl_spec.validate(p, &init_d.d.ty, init_d.initializer != .none); | |
| 986 | // const attrs = p.attr_buf.items(.attr)[attr_buf_top..]; | |
| 987 | // init_d.d.ty = try init_d.d.ty.withAttributes(p.arena, attrs); | |
| 988 | ||
| 989 | const node = try p.addNode(.{ .ty = init_d.d.ty, .tag = tag, .data = .{ | |
| 990 | .decl = .{ .name = init_d.d.name, .node = init_d.initializer }, | |
| 991 | } }); | |
| 992 | try p.decl_buf.append(node); | |
| 993 | ||
| 994 | const sym = Scope.Symbol{ | |
| 995 | .name = p.tokSlice(init_d.d.name), | |
| 996 | .ty = init_d.d.ty, | |
| 997 | .name_tok = init_d.d.name, | |
| 998 | }; | |
| 999 | if (decl_spec.storage_class == .typedef) { | |
| 1000 | try p.scopes.append(.{ .typedef = sym }); | |
| 1001 | } else if (init_d.initializer != .none) { | |
| 1002 | try p.scopes.append(.{ .def = sym }); | |
| 1003 | } else { | |
| 1004 | try p.scopes.append(.{ .decl = sym }); | |
| 1005 | } | |
| 1006 | ||
| 1007 | if (p.eatToken(.comma) == null) break; | |
| 1008 | ||
| 1009 | init_d = (try p.initDeclarator(&decl_spec)) orelse { | |
| 1010 | try p.err(.expected_ident_or_l_paren); | |
| 1011 | continue; | |
| 1012 | }; | |
| 1013 | } | |
| 1014 | ||
| 1015 | _ = try p.expectToken(.semicolon); | |
| 1016 | return true; | |
| 1017 | } | |
| 1018 | ||
| 1019 | /// staticAssert : keyword_static_assert '(' constExpr ',' STRING_LITERAL ')' ';' | |
| 1020 | fn staticAssert(p: *Parser) Error!bool { | |
| 1021 | const static_assert = p.eatToken(.keyword_static_assert) orelse return false; | |
| 1022 | const l_paren = try p.expectToken(.l_paren); | |
| 1023 | const res_token = p.tok_i; | |
| 1024 | const res = try p.constExpr(); | |
| 1025 | const str = if (p.eatToken(.comma) != null) | |
| 1026 | switch (p.tok_ids[p.tok_i]) { | |
| 1027 | .string_literal, | |
| 1028 | .string_literal_utf_16, | |
| 1029 | .string_literal_utf_8, | |
| 1030 | .string_literal_utf_32, | |
| 1031 | .string_literal_wide, | |
| 1032 | => try p.stringLiteral(), | |
| 1033 | else => { | |
| 1034 | try p.err(.expected_str_literal); | |
| 1035 | return error.ParsingFailed; | |
| 1036 | }, | |
| 1037 | } | |
| 1038 | else | |
| 1039 | Result{}; | |
| 1040 | try p.expectClosing(l_paren, .r_paren); | |
| 1041 | _ = try p.expectToken(.semicolon); | |
| 1042 | if (str.node == .none) try p.errTok(.static_assert_missing_message, static_assert); | |
| 1043 | ||
| 1044 | if (res.val.tag == .unavailable) { | |
| 1045 | // an unavailable sizeof expression is already a compile error, so we don't emit | |
| 1046 | // another error for an invalid _Static_assert condition. This matches the behavior | |
| 1047 | // of gcc/clang | |
| 1048 | if (!p.nodeIs(res.node, .sizeof_expr)) try p.errTok(.static_assert_not_constant, res_token); | |
| 1049 | } else if (!res.val.getBool()) { | |
| 1050 | if (str.node != .none) { | |
| 1051 | var buf = std.ArrayList(u8).init(p.pp.comp.gpa); | |
| 1052 | defer buf.deinit(); | |
| 1053 | ||
| 1054 | const data = str.val.data.bytes; | |
| 1055 | try buf.ensureUnusedCapacity(data.len); | |
| 1056 | try Tree.dumpStr( | |
| 1057 | data, | |
| 1058 | p.nodes.items(.tag)[@enumToInt(str.node)], | |
| 1059 | buf.writer(), | |
| 1060 | ); | |
| 1061 | try p.errStr( | |
| 1062 | .static_assert_failure_message, | |
| 1063 | static_assert, | |
| 1064 | try p.pp.comp.diag.arena.allocator().dupe(u8, buf.items), | |
| 1065 | ); | |
| 1066 | } else try p.errTok(.static_assert_failure, static_assert); | |
| 1067 | } | |
| 1068 | const node = try p.addNode(.{ | |
| 1069 | .tag = .static_assert, | |
| 1070 | .data = .{ .bin = .{ | |
| 1071 | .lhs = res.node, | |
| 1072 | .rhs = str.node, | |
| 1073 | } }, | |
| 1074 | }); | |
| 1075 | try p.decl_buf.append(node); | |
| 1076 | return true; | |
| 1077 | } | |
| 1078 | ||
| 1079 | pub const DeclSpec = struct { | |
| 1080 | storage_class: union(enum) { | |
| 1081 | auto: TokenIndex, | |
| 1082 | @"extern": TokenIndex, | |
| 1083 | register: TokenIndex, | |
| 1084 | static: TokenIndex, | |
| 1085 | typedef: TokenIndex, | |
| 1086 | none, | |
| 1087 | } = .none, | |
| 1088 | thread_local: ?TokenIndex = null, | |
| 1089 | @"inline": ?TokenIndex = null, | |
| 1090 | @"noreturn": ?TokenIndex = null, | |
| 1091 | ty: Type, | |
| 1092 | ||
| 1093 | fn validateParam(d: DeclSpec, p: *Parser, ty: *Type) Error!void { | |
| 1094 | switch (d.storage_class) { | |
| 1095 | .none => {}, | |
| 1096 | .register => ty.qual.register = true, | |
| 1097 | .auto, .@"extern", .static, .typedef => |tok_i| try p.errTok(.invalid_storage_on_param, tok_i), | |
| 1098 | } | |
| 1099 | if (d.thread_local) |tok_i| try p.errTok(.threadlocal_non_var, tok_i); | |
| 1100 | if (d.@"inline") |tok_i| try p.errStr(.func_spec_non_func, tok_i, "inline"); | |
| 1101 | if (d.@"noreturn") |tok_i| try p.errStr(.func_spec_non_func, tok_i, "_Noreturn"); | |
| 1102 | } | |
| 1103 | ||
| 1104 | fn validateFnDef(d: DeclSpec, p: *Parser) Error!Tree.Tag { | |
| 1105 | switch (d.storage_class) { | |
| 1106 | .none, .@"extern", .static => {}, | |
| 1107 | .auto, .register, .typedef => |tok_i| try p.errTok(.illegal_storage_on_func, tok_i), | |
| 1108 | } | |
| 1109 | if (d.thread_local) |tok_i| try p.errTok(.threadlocal_non_var, tok_i); | |
| 1110 | ||
| 1111 | const is_static = d.storage_class == .static; | |
| 1112 | const is_inline = d.@"inline" != null; | |
| 1113 | if (is_static) { | |
| 1114 | if (is_inline) return .inline_static_fn_def; | |
| 1115 | return .static_fn_def; | |
| 1116 | } else { | |
| 1117 | if (is_inline) return .inline_fn_def; | |
| 1118 | return .fn_def; | |
| 1119 | } | |
| 1120 | } | |
| 1121 | ||
| 1122 | fn validate(d: DeclSpec, p: *Parser, ty: *Type, has_init: bool) Error!Tree.Tag { | |
| 1123 | const is_static = d.storage_class == .static; | |
| 1124 | if (ty.isFunc() and d.storage_class != .typedef) { | |
| 1125 | switch (d.storage_class) { | |
| 1126 | .none, .@"extern" => {}, | |
| 1127 | .static => |tok_i| if (p.func.ty != null) try p.errTok(.static_func_not_global, tok_i), | |
| 1128 | .typedef => unreachable, | |
| 1129 | .auto, .register => |tok_i| try p.errTok(.illegal_storage_on_func, tok_i), | |
| 1130 | } | |
| 1131 | if (d.thread_local) |tok_i| try p.errTok(.threadlocal_non_var, tok_i); | |
| 1132 | ||
| 1133 | const is_inline = d.@"inline" != null; | |
| 1134 | if (is_static) { | |
| 1135 | if (is_inline) return .inline_static_fn_proto; | |
| 1136 | return .static_fn_proto; | |
| 1137 | } else { | |
| 1138 | if (is_inline) return .inline_fn_proto; | |
| 1139 | return .fn_proto; | |
| 1140 | } | |
| 1141 | } else { | |
| 1142 | if (d.@"inline") |tok_i| try p.errStr(.func_spec_non_func, tok_i, "inline"); | |
| 1143 | // TODO move to attribute validation | |
| 1144 | if (d.@"noreturn") |tok_i| try p.errStr(.func_spec_non_func, tok_i, "_Noreturn"); | |
| 1145 | switch (d.storage_class) { | |
| 1146 | .auto, .register => if (p.func.ty == null) try p.err(.illegal_storage_on_global), | |
| 1147 | .typedef => return .typedef, | |
| 1148 | else => {}, | |
| 1149 | } | |
| 1150 | ty.qual.register = d.storage_class == .register; | |
| 1151 | ||
| 1152 | const is_extern = d.storage_class == .@"extern" and !has_init; | |
| 1153 | if (d.thread_local != null) { | |
| 1154 | if (is_static) return .threadlocal_static_var; | |
| 1155 | if (is_extern) return .threadlocal_extern_var; | |
| 1156 | return .threadlocal_var; | |
| 1157 | } else { | |
| 1158 | if (is_static) return .static_var; | |
| 1159 | if (is_extern) return .extern_var; | |
| 1160 | return .@"var"; | |
| 1161 | } | |
| 1162 | } | |
| 1163 | } | |
| 1164 | ||
| 1165 | fn warnIgnoredAttrs(d: DeclSpec, p: *Parser, attr_buf_start: usize) !void { | |
| 1166 | if (!d.ty.isEnumOrRecord()) return; | |
| 1167 | ||
| 1168 | var i = attr_buf_start; | |
| 1169 | while (i < p.attr_buf.len) : (i += 1) { | |
| 1170 | const ignored_attr = p.attr_buf.get(i); | |
| 1171 | try p.errExtra(.ignored_record_attr, ignored_attr.tok, .{ | |
| 1172 | .ignored_record_attr = .{ .tag = ignored_attr.attr.tag, .specifier = switch (d.ty.specifier) { | |
| 1173 | .@"enum" => .@"enum", | |
| 1174 | .@"struct" => .@"struct", | |
| 1175 | .@"union" => .@"union", | |
| 1176 | else => continue, | |
| 1177 | } }, | |
| 1178 | }); | |
| 1179 | } | |
| 1180 | } | |
| 1181 | }; | |
| 1182 | ||
| 1183 | /// typeof | |
| 1184 | /// : keyword_typeof '(' typeName ')' | |
| 1185 | /// | keyword_typeof '(' expr ')' | |
| 1186 | fn typeof(p: *Parser) Error!?Type { | |
| 1187 | switch (p.tok_ids[p.tok_i]) { | |
| 1188 | .keyword_typeof, .keyword_typeof1, .keyword_typeof2 => p.tok_i += 1, | |
| 1189 | else => return null, | |
| 1190 | } | |
| 1191 | const l_paren = try p.expectToken(.l_paren); | |
| 1192 | if (try p.typeName()) |ty| { | |
| 1193 | try p.expectClosing(l_paren, .r_paren); | |
| 1194 | const typeof_ty = try p.arena.create(Type); | |
| 1195 | typeof_ty.* = .{ | |
| 1196 | .data = ty.data, | |
| 1197 | .qual = ty.qual.inheritFromTypeof(), | |
| 1198 | .specifier = ty.specifier, | |
| 1199 | }; | |
| 1200 | ||
| 1201 | return Type{ | |
| 1202 | .data = .{ .sub_type = typeof_ty }, | |
| 1203 | .specifier = .typeof_type, | |
| 1204 | }; | |
| 1205 | } | |
| 1206 | const typeof_expr = try p.parseNoEval(expr); | |
| 1207 | try typeof_expr.expect(p); | |
| 1208 | try p.expectClosing(l_paren, .r_paren); | |
| 1209 | ||
| 1210 | const inner = try p.arena.create(Type.Expr); | |
| 1211 | inner.* = .{ | |
| 1212 | .node = typeof_expr.node, | |
| 1213 | .ty = .{ | |
| 1214 | .data = typeof_expr.ty.data, | |
| 1215 | .qual = typeof_expr.ty.qual.inheritFromTypeof(), | |
| 1216 | .specifier = typeof_expr.ty.specifier, | |
| 1217 | }, | |
| 1218 | }; | |
| 1219 | ||
| 1220 | return Type{ | |
| 1221 | .data = .{ .expr = inner }, | |
| 1222 | .specifier = .typeof_expr, | |
| 1223 | }; | |
| 1224 | } | |
| 1225 | ||
| 1226 | /// declSpec: (storageClassSpec | typeSpec | typeQual | funcSpec | alignSpec)+ | |
| 1227 | /// storageClassSpec: | |
| 1228 | /// : keyword_typedef | |
| 1229 | /// | keyword_extern | |
| 1230 | /// | keyword_static | |
| 1231 | /// | keyword_threadlocal | |
| 1232 | /// | keyword_auto | |
| 1233 | /// | keyword_register | |
| 1234 | /// funcSpec : keyword_inline | keyword_noreturn | |
| 1235 | fn declSpec(p: *Parser, is_param: bool) Error!?DeclSpec { | |
| 1236 | var d: DeclSpec = .{ .ty = .{ .specifier = undefined } }; | |
| 1237 | var spec: Type.Builder = .{}; | |
| 1238 | const attr_buf_top = p.attr_buf.len; | |
| 1239 | defer p.attr_buf.len = attr_buf_top; | |
| 1240 | ||
| 1241 | const start = p.tok_i; | |
| 1242 | while (true) { | |
| 1243 | if (try p.typeSpec(&spec)) continue; | |
| 1244 | const id = p.tok_ids[p.tok_i]; | |
| 1245 | switch (id) { | |
| 1246 | .keyword_typedef, | |
| 1247 | .keyword_extern, | |
| 1248 | .keyword_static, | |
| 1249 | .keyword_auto, | |
| 1250 | .keyword_register, | |
| 1251 | => { | |
| 1252 | if (d.storage_class != .none) { | |
| 1253 | try p.errStr(.multiple_storage_class, p.tok_i, @tagName(d.storage_class)); | |
| 1254 | return error.ParsingFailed; | |
| 1255 | } | |
| 1256 | if (d.thread_local != null) { | |
| 1257 | switch (id) { | |
| 1258 | .keyword_typedef, | |
| 1259 | .keyword_auto, | |
| 1260 | .keyword_register, | |
| 1261 | => try p.errStr(.cannot_combine_spec, p.tok_i, id.lexeme().?), | |
| 1262 | else => {}, | |
| 1263 | } | |
| 1264 | } | |
| 1265 | switch (id) { | |
| 1266 | .keyword_typedef => d.storage_class = .{ .typedef = p.tok_i }, | |
| 1267 | .keyword_extern => d.storage_class = .{ .@"extern" = p.tok_i }, | |
| 1268 | .keyword_static => d.storage_class = .{ .static = p.tok_i }, | |
| 1269 | .keyword_auto => d.storage_class = .{ .auto = p.tok_i }, | |
| 1270 | .keyword_register => d.storage_class = .{ .register = p.tok_i }, | |
| 1271 | else => unreachable, | |
| 1272 | } | |
| 1273 | }, | |
| 1274 | .keyword_thread_local => { | |
| 1275 | if (d.thread_local != null) { | |
| 1276 | try p.errStr(.duplicate_decl_spec, p.tok_i, "_Thread_local"); | |
| 1277 | } | |
| 1278 | switch (d.storage_class) { | |
| 1279 | .@"extern", .none, .static => {}, | |
| 1280 | else => try p.errStr(.cannot_combine_spec, p.tok_i, @tagName(d.storage_class)), | |
| 1281 | } | |
| 1282 | d.thread_local = p.tok_i; | |
| 1283 | }, | |
| 1284 | .keyword_inline, .keyword_inline1, .keyword_inline2 => { | |
| 1285 | if (d.@"inline" != null) { | |
| 1286 | try p.errStr(.duplicate_decl_spec, p.tok_i, "inline"); | |
| 1287 | } | |
| 1288 | d.@"inline" = p.tok_i; | |
| 1289 | }, | |
| 1290 | .keyword_noreturn => { | |
| 1291 | if (d.@"noreturn" != null) { | |
| 1292 | try p.errStr(.duplicate_decl_spec, p.tok_i, "_Noreturn"); | |
| 1293 | } | |
| 1294 | d.@"noreturn" = p.tok_i; | |
| 1295 | }, | |
| 1296 | else => break, | |
| 1297 | } | |
| 1298 | p.tok_i += 1; | |
| 1299 | } | |
| 1300 | ||
| 1301 | if (p.tok_i == start) return null; | |
| 1302 | ||
| 1303 | d.ty = try spec.finish(p, attr_buf_top); | |
| 1304 | if (is_param) try p.validateAlignas(d.ty, .alignas_on_param); | |
| 1305 | return d; | |
| 1306 | } | |
| 1307 | ||
| 1308 | fn validateAlignas(p: *Parser, ty: Type, tag: ?Diagnostics.Tag) !void { | |
| 1309 | const base = ty.canonicalize(.standard); | |
| 1310 | const default_align = base.alignof(p.pp.comp); | |
| 1311 | for (ty.getAttributes()) |attr| { | |
| 1312 | if (attr.tag != .aligned) continue; | |
| 1313 | if (attr.args.aligned.alignment) |alignment| { | |
| 1314 | if (!alignment.alignas) continue; | |
| 1315 | ||
| 1316 | const align_tok = attr.args.aligned.__name_tok; | |
| 1317 | if (tag) |t| try p.errTok(t, align_tok); | |
| 1318 | if (ty.isFunc()) { | |
| 1319 | try p.errTok(.alignas_on_func, align_tok); | |
| 1320 | } else if (alignment.requested < default_align) { | |
| 1321 | try p.errExtra(.minimum_alignment, align_tok, .{ .unsigned = default_align }); | |
| 1322 | } | |
| 1323 | } | |
| 1324 | } | |
| 1325 | } | |
| 1326 | ||
| 1327 | const InitDeclarator = struct { d: Declarator, initializer: NodeIndex = .none }; | |
| 1328 | ||
| 1329 | /// attribute | |
| 1330 | /// : attrIdentifier | |
| 1331 | /// | attrIdentifier '(' identifier ')' | |
| 1332 | /// | attrIdentifier '(' identifier (',' expr)+ ')' | |
| 1333 | /// | attrIdentifier '(' (expr (',' expr)*)? ')' | |
| 1334 | fn attribute(p: *Parser, kind: Attribute.Kind, namespace: ?[]const u8) Error!?TentativeAttribute { | |
| 1335 | const name_tok = p.tok_i; | |
| 1336 | switch (p.tok_ids[p.tok_i]) { | |
| 1337 | .keyword_const, .keyword_const1, .keyword_const2 => p.tok_i += 1, | |
| 1338 | else => _ = try p.expectIdentifier(), | |
| 1339 | } | |
| 1340 | const name = p.tokSlice(name_tok); | |
| 1341 | ||
| 1342 | const attr = Attribute.fromString(kind, namespace, name) orelse { | |
| 1343 | const tag: Diagnostics.Tag = if (kind == .declspec) .declspec_attr_not_supported else .unknown_attribute; | |
| 1344 | try p.errStr(tag, name_tok, name); | |
| 1345 | if (p.eatToken(.l_paren)) |_| p.skipTo(.r_paren); | |
| 1346 | return null; | |
| 1347 | }; | |
| 1348 | ||
| 1349 | const required_count = Attribute.requiredArgCount(attr); | |
| 1350 | var arguments = Attribute.initArguments(attr, name_tok); | |
| 1351 | var arg_idx: u32 = 0; | |
| 1352 | ||
| 1353 | switch (p.tok_ids[p.tok_i]) { | |
| 1354 | .comma, .r_paren => {}, // will be consumed in attributeList | |
| 1355 | .l_paren => blk: { | |
| 1356 | p.tok_i += 1; | |
| 1357 | if (p.eatToken(.r_paren)) |_| break :blk; | |
| 1358 | ||
| 1359 | if (Attribute.wantsIdentEnum(attr)) { | |
| 1360 | if (try p.eatIdentifier()) |ident| { | |
| 1361 | if (Attribute.diagnoseIdent(attr, &arguments, p.tokSlice(ident))) |msg| { | |
| 1362 | try p.errExtra(msg.tag, ident, msg.extra); | |
| 1363 | p.skipTo(.r_paren); | |
| 1364 | return error.ParsingFailed; | |
| 1365 | } | |
| 1366 | } else { | |
| 1367 | try p.errExtra(.attribute_requires_identifier, name_tok, .{ .str = name }); | |
| 1368 | return error.ParsingFailed; | |
| 1369 | } | |
| 1370 | } else { | |
| 1371 | const arg_start = p.tok_i; | |
| 1372 | var first_expr = try p.assignExpr(); | |
| 1373 | try first_expr.expect(p); | |
| 1374 | if (p.diagnose(attr, &arguments, arg_idx, first_expr)) |msg| { | |
| 1375 | try p.errExtra(msg.tag, arg_start, msg.extra); | |
| 1376 | p.skipTo(.r_paren); | |
| 1377 | return error.ParsingFailed; | |
| 1378 | } | |
| 1379 | } | |
| 1380 | arg_idx += 1; | |
| 1381 | while (p.eatToken(.r_paren) == null) : (arg_idx += 1) { | |
| 1382 | _ = try p.expectToken(.comma); | |
| 1383 | ||
| 1384 | const arg_start = p.tok_i; | |
| 1385 | var arg_expr = try p.assignExpr(); | |
| 1386 | try arg_expr.expect(p); | |
| 1387 | if (p.diagnose(attr, &arguments, arg_idx, arg_expr)) |msg| { | |
| 1388 | try p.errExtra(msg.tag, arg_start, msg.extra); | |
| 1389 | p.skipTo(.r_paren); | |
| 1390 | return error.ParsingFailed; | |
| 1391 | } | |
| 1392 | } | |
| 1393 | }, | |
| 1394 | else => {}, | |
| 1395 | } | |
| 1396 | if (arg_idx < required_count) { | |
| 1397 | try p.errExtra(.attribute_not_enough_args, name_tok, .{ .attr_arg_count = .{ .attribute = attr, .expected = required_count } }); | |
| 1398 | return error.ParsingFailed; | |
| 1399 | } | |
| 1400 | return TentativeAttribute{ .attr = .{ .tag = attr, .args = arguments }, .tok = name_tok }; | |
| 1401 | } | |
| 1402 | ||
| 1403 | fn diagnose(p: *Parser, attr: Attribute.Tag, arguments: *Attribute.Arguments, arg_idx: u32, res: Result) ?Diagnostics.Message { | |
| 1404 | if (Attribute.wantsAlignment(attr, arg_idx)) { | |
| 1405 | return Attribute.diagnoseAlignment(attr, arguments, arg_idx, res.val, res.ty, p.pp.comp); | |
| 1406 | } | |
| 1407 | const node = p.nodes.get(@enumToInt(res.node)); | |
| 1408 | return Attribute.diagnose(attr, arguments, arg_idx, res.val, node); | |
| 1409 | } | |
| 1410 | ||
| 1411 | /// attributeList : (attribute (',' attribute)*)? | |
| 1412 | fn gnuAttributeList(p: *Parser) Error!void { | |
| 1413 | if (p.tok_ids[p.tok_i] == .r_paren) return; | |
| 1414 | ||
| 1415 | if (try p.attribute(.gnu, null)) |attr| try p.attr_buf.append(p.pp.comp.gpa, attr); | |
| 1416 | while (p.tok_ids[p.tok_i] != .r_paren) { | |
| 1417 | _ = try p.expectToken(.comma); | |
| 1418 | if (try p.attribute(.gnu, null)) |attr| try p.attr_buf.append(p.pp.comp.gpa, attr); | |
| 1419 | } | |
| 1420 | } | |
| 1421 | ||
| 1422 | fn c2xAttributeList(p: *Parser) Error!void { | |
| 1423 | while (p.tok_ids[p.tok_i] != .r_bracket) { | |
| 1424 | var namespace_tok = try p.expectIdentifier(); | |
| 1425 | var namespace: ?[]const u8 = null; | |
| 1426 | if (p.eatToken(.colon_colon)) |_| { | |
| 1427 | namespace = p.tokSlice(namespace_tok); | |
| 1428 | } else { | |
| 1429 | p.tok_i -= 1; | |
| 1430 | } | |
| 1431 | if (try p.attribute(.c2x, namespace)) |attr| try p.attr_buf.append(p.pp.comp.gpa, attr); | |
| 1432 | _ = p.eatToken(.comma); | |
| 1433 | } | |
| 1434 | } | |
| 1435 | ||
| 1436 | fn msvcAttributeList(p: *Parser) Error!void { | |
| 1437 | while (p.tok_ids[p.tok_i] != .r_paren) { | |
| 1438 | if (try p.attribute(.declspec, null)) |attr| try p.attr_buf.append(p.pp.comp.gpa, attr); | |
| 1439 | _ = p.eatToken(.comma); | |
| 1440 | } | |
| 1441 | } | |
| 1442 | ||
| 1443 | fn c2xAttribute(p: *Parser) !bool { | |
| 1444 | if (!p.pp.comp.langopts.standard.atLeast(.c2x)) return false; | |
| 1445 | const bracket1 = p.eatToken(.l_bracket) orelse return false; | |
| 1446 | const bracket2 = p.eatToken(.l_bracket) orelse { | |
| 1447 | p.tok_i -= 1; | |
| 1448 | return false; | |
| 1449 | }; | |
| 1450 | ||
| 1451 | try p.c2xAttributeList(); | |
| 1452 | ||
| 1453 | _ = try p.expectClosing(bracket2, .r_bracket); | |
| 1454 | _ = try p.expectClosing(bracket1, .r_bracket); | |
| 1455 | ||
| 1456 | return true; | |
| 1457 | } | |
| 1458 | ||
| 1459 | fn msvcAttribute(p: *Parser) !bool { | |
| 1460 | const declspec_tok = p.eatToken(.keyword_declspec) orelse return false; | |
| 1461 | if (!p.pp.comp.langopts.declspec_attrs) { | |
| 1462 | try p.errTok(.declspec_not_enabled, declspec_tok); | |
| 1463 | return error.ParsingFailed; | |
| 1464 | } | |
| 1465 | const l_paren = try p.expectToken(.l_paren); | |
| 1466 | try p.msvcAttributeList(); | |
| 1467 | _ = try p.expectClosing(l_paren, .r_paren); | |
| 1468 | ||
| 1469 | return false; | |
| 1470 | } | |
| 1471 | ||
| 1472 | fn gnuAttribute(p: *Parser) !bool { | |
| 1473 | switch (p.tok_ids[p.tok_i]) { | |
| 1474 | .keyword_attribute1, .keyword_attribute2 => p.tok_i += 1, | |
| 1475 | else => return false, | |
| 1476 | } | |
| 1477 | const paren1 = try p.expectToken(.l_paren); | |
| 1478 | const paren2 = try p.expectToken(.l_paren); | |
| 1479 | ||
| 1480 | try p.gnuAttributeList(); | |
| 1481 | ||
| 1482 | _ = try p.expectClosing(paren2, .r_paren); | |
| 1483 | _ = try p.expectClosing(paren1, .r_paren); | |
| 1484 | return true; | |
| 1485 | } | |
| 1486 | ||
| 1487 | /// alignAs : keyword_alignas '(' (typeName | constExpr ) ')' | |
| 1488 | fn alignAs(p: *Parser) !bool { | |
| 1489 | const align_tok = p.eatToken(.keyword_alignas) orelse return false; | |
| 1490 | const l_paren = try p.expectToken(.l_paren); | |
| 1491 | if (try p.typeName()) |inner_ty| { | |
| 1492 | const alignment = Attribute.Alignment{ .requested = inner_ty.alignof(p.pp.comp), .alignas = true }; | |
| 1493 | const attr = Attribute{ .tag = .aligned, .args = .{ .aligned = .{ .alignment = alignment, .__name_tok = align_tok } } }; | |
| 1494 | try p.attr_buf.append(p.pp.comp.gpa, .{ .attr = attr, .tok = align_tok }); | |
| 1495 | } else { | |
| 1496 | const arg_start = p.tok_i; | |
| 1497 | const res = try p.constExpr(); | |
| 1498 | if (!res.val.isZero()) { | |
| 1499 | var args = Attribute.initArguments(.aligned, align_tok); | |
| 1500 | if (p.diagnose(.aligned, &args, 0, res)) |msg| { | |
| 1501 | try p.errExtra(msg.tag, arg_start, msg.extra); | |
| 1502 | p.skipTo(.r_paren); | |
| 1503 | return error.ParsingFailed; | |
| 1504 | } | |
| 1505 | args.aligned.alignment.?.node = res.node; | |
| 1506 | args.aligned.alignment.?.alignas = true; | |
| 1507 | try p.attr_buf.append(p.pp.comp.gpa, .{ .attr = .{ .tag = .aligned, .args = args }, .tok = align_tok }); | |
| 1508 | } | |
| 1509 | } | |
| 1510 | try p.expectClosing(l_paren, .r_paren); | |
| 1511 | return true; | |
| 1512 | } | |
| 1513 | ||
| 1514 | /// attributeSpecifier : (keyword_attribute '( '(' attributeList ')' ')')* | |
| 1515 | fn attributeSpecifier(p: *Parser) Error!void { | |
| 1516 | while (true) { | |
| 1517 | if (try p.alignAs()) continue; | |
| 1518 | if (try p.gnuAttribute()) continue; | |
| 1519 | if (try p.c2xAttribute()) continue; | |
| 1520 | if (try p.msvcAttribute()) continue; | |
| 1521 | break; | |
| 1522 | } | |
| 1523 | } | |
| 1524 | ||
| 1525 | /// initDeclarator : declarator assembly? attributeSpecifier? ('=' initializer)? | |
| 1526 | fn initDeclarator(p: *Parser, decl_spec: *DeclSpec) Error!?InitDeclarator { | |
| 1527 | var init_d = InitDeclarator{ | |
| 1528 | .d = (try p.declarator(decl_spec.ty, .normal)) orelse return null, | |
| 1529 | }; | |
| 1530 | _ = try p.assembly(.decl_label); | |
| 1531 | try p.attributeSpecifier(); // if (init_d.d.ty.isFunc()) .function else .variable | |
| 1532 | if (p.eatToken(.equal)) |eq| init: { | |
| 1533 | if (decl_spec.storage_class == .typedef or init_d.d.func_declarator != null) { | |
| 1534 | try p.errTok(.illegal_initializer, eq); | |
| 1535 | } else if (init_d.d.ty.is(.variable_len_array)) { | |
| 1536 | try p.errTok(.vla_init, eq); | |
| 1537 | } else if (decl_spec.storage_class == .@"extern") { | |
| 1538 | try p.err(.extern_initializer); | |
| 1539 | decl_spec.storage_class = .none; | |
| 1540 | } | |
| 1541 | ||
| 1542 | if (init_d.d.ty.hasIncompleteSize() and !init_d.d.ty.is(.incomplete_array)) { | |
| 1543 | try p.errStr(.variable_incomplete_ty, init_d.d.name, try p.typeStr(init_d.d.ty)); | |
| 1544 | return error.ParsingFailed; | |
| 1545 | } | |
| 1546 | ||
| 1547 | const scopes_len = p.scopes.items.len; | |
| 1548 | defer p.scopes.items.len = scopes_len; | |
| 1549 | try p.scopes.append(.{ .decl = .{ | |
| 1550 | .name = p.tokSlice(init_d.d.name), | |
| 1551 | .ty = init_d.d.ty, | |
| 1552 | .name_tok = init_d.d.name, | |
| 1553 | } }); | |
| 1554 | var init_list_expr = try p.initializer(init_d.d.ty); | |
| 1555 | init_d.initializer = init_list_expr.node; | |
| 1556 | if (!init_list_expr.ty.isArray()) break :init; | |
| 1557 | if (init_d.d.ty.specifier == .incomplete_array) { | |
| 1558 | // Modifying .data is exceptionally allowed for .incomplete_array. | |
| 1559 | init_d.d.ty.data.array.len = init_list_expr.ty.arrayLen() orelse break :init; | |
| 1560 | init_d.d.ty.specifier = .array; | |
| 1561 | } else if (init_d.d.ty.is(.incomplete_array)) { | |
| 1562 | const attrs = init_d.d.ty.getAttributes(); | |
| 1563 | ||
| 1564 | const arr_ty = try p.arena.create(Type.Array); | |
| 1565 | arr_ty.* = .{ .elem = init_d.d.ty.elemType(), .len = init_list_expr.ty.arrayLen().? }; | |
| 1566 | const ty = Type{ | |
| 1567 | .specifier = .array, | |
| 1568 | .data = .{ .array = arr_ty }, | |
| 1569 | }; | |
| 1570 | init_d.d.ty = try ty.withAttributes(p.arena, attrs); | |
| 1571 | } | |
| 1572 | } | |
| 1573 | const name = init_d.d.name; | |
| 1574 | if (decl_spec.storage_class != .typedef and init_d.d.ty.hasIncompleteSize()) incomplete: { | |
| 1575 | const specifier = init_d.d.ty.canonicalize(.standard).specifier; | |
| 1576 | if (decl_spec.storage_class == .@"extern") switch (specifier) { | |
| 1577 | .@"struct", .@"union", .@"enum" => break :incomplete, | |
| 1578 | .incomplete_array => { | |
| 1579 | init_d.d.ty.decayArray(); | |
| 1580 | break :incomplete; | |
| 1581 | }, | |
| 1582 | else => {}, | |
| 1583 | }; | |
| 1584 | // if there was an initializer expression it must have contained an error | |
| 1585 | if (init_d.initializer != .none) break :incomplete; | |
| 1586 | try p.errStr(.variable_incomplete_ty, name, try p.typeStr(init_d.d.ty)); | |
| 1587 | return init_d; | |
| 1588 | } | |
| 1589 | if (p.findSymbol(name, .definition)) |scope| switch (scope) { | |
| 1590 | .enumeration => { | |
| 1591 | try p.errStr(.redefinition_different_sym, name, p.tokSlice(name)); | |
| 1592 | try p.errTok(.previous_definition, scope.enumeration.name_tok); | |
| 1593 | }, | |
| 1594 | .decl => |s| if (!s.ty.eql(init_d.d.ty, p.pp.comp, true)) { | |
| 1595 | try p.errStr(.redefinition_incompatible, name, p.tokSlice(name)); | |
| 1596 | try p.errTok(.previous_definition, s.name_tok); | |
| 1597 | }, | |
| 1598 | .def => |s| if (!s.ty.eql(init_d.d.ty, p.pp.comp, true)) { | |
| 1599 | try p.errStr(.redefinition_incompatible, name, p.tokSlice(name)); | |
| 1600 | try p.errTok(.previous_definition, s.name_tok); | |
| 1601 | } else if (init_d.initializer != .none) { | |
| 1602 | try p.errStr(.redefinition, name, p.tokSlice(name)); | |
| 1603 | try p.errTok(.previous_definition, s.name_tok); | |
| 1604 | }, | |
| 1605 | .param => |s| { | |
| 1606 | try p.errStr(.redefinition, name, p.tokSlice(name)); | |
| 1607 | try p.errTok(.previous_definition, s.name_tok); | |
| 1608 | }, | |
| 1609 | else => unreachable, | |
| 1610 | }; | |
| 1611 | return init_d; | |
| 1612 | } | |
| 1613 | ||
| 1614 | /// typeSpec | |
| 1615 | /// : keyword_void | |
| 1616 | /// | keyword_char | |
| 1617 | /// | keyword_short | |
| 1618 | /// | keyword_int | |
| 1619 | /// | keyword_long | |
| 1620 | /// | keyword_float | |
| 1621 | /// | keyword_double | |
| 1622 | /// | keyword_signed | |
| 1623 | /// | keyword_unsigned | |
| 1624 | /// | keyword_bool | |
| 1625 | /// | keyword_complex | |
| 1626 | /// | atomicTypeSpec | |
| 1627 | /// | recordSpec | |
| 1628 | /// | enumSpec | |
| 1629 | /// | typedef // IDENTIFIER | |
| 1630 | /// | typeof | |
| 1631 | /// atomicTypeSpec : keyword_atomic '(' typeName ')' | |
| 1632 | /// alignSpec | |
| 1633 | /// : keyword_alignas '(' typeName ')' | |
| 1634 | /// | keyword_alignas '(' constExpr ')' | |
| 1635 | fn typeSpec(p: *Parser, ty: *Type.Builder) Error!bool { | |
| 1636 | const start = p.tok_i; | |
| 1637 | while (true) { | |
| 1638 | try p.attributeSpecifier(); // .typedef | |
| 1639 | ||
| 1640 | if (try p.typeof()) |inner_ty| { | |
| 1641 | try ty.combineFromTypeof(p, inner_ty, start); | |
| 1642 | continue; | |
| 1643 | } | |
| 1644 | if (try p.typeQual(&ty.qual)) continue; | |
| 1645 | switch (p.tok_ids[p.tok_i]) { | |
| 1646 | .keyword_void => try ty.combine(p, .void, p.tok_i), | |
| 1647 | .keyword_bool => try ty.combine(p, .bool, p.tok_i), | |
| 1648 | .keyword_char => try ty.combine(p, .char, p.tok_i), | |
| 1649 | .keyword_short => try ty.combine(p, .short, p.tok_i), | |
| 1650 | .keyword_int => try ty.combine(p, .int, p.tok_i), | |
| 1651 | .keyword_long => try ty.combine(p, .long, p.tok_i), | |
| 1652 | .keyword_signed => try ty.combine(p, .signed, p.tok_i), | |
| 1653 | .keyword_unsigned => try ty.combine(p, .unsigned, p.tok_i), | |
| 1654 | .keyword_float => try ty.combine(p, .float, p.tok_i), | |
| 1655 | .keyword_double => try ty.combine(p, .double, p.tok_i), | |
| 1656 | .keyword_complex => try ty.combine(p, .complex, p.tok_i), | |
| 1657 | .keyword_atomic => { | |
| 1658 | const atomic_tok = p.tok_i; | |
| 1659 | p.tok_i += 1; | |
| 1660 | const l_paren = p.eatToken(.l_paren) orelse { | |
| 1661 | // _Atomic qualifier not _Atomic(typeName) | |
| 1662 | p.tok_i = atomic_tok; | |
| 1663 | break; | |
| 1664 | }; | |
| 1665 | const inner_ty = (try p.typeName()) orelse { | |
| 1666 | try p.err(.expected_type); | |
| 1667 | return error.ParsingFailed; | |
| 1668 | }; | |
| 1669 | try p.expectClosing(l_paren, .r_paren); | |
| 1670 | ||
| 1671 | const new_spec = Type.Builder.fromType(inner_ty); | |
| 1672 | try ty.combine(p, new_spec, atomic_tok); | |
| 1673 | ||
| 1674 | if (ty.qual.atomic != null) | |
| 1675 | try p.errStr(.duplicate_decl_spec, atomic_tok, "atomic") | |
| 1676 | else | |
| 1677 | ty.qual.atomic = atomic_tok; | |
| 1678 | continue; | |
| 1679 | }, | |
| 1680 | .keyword_struct => { | |
| 1681 | const tag_tok = p.tok_i; | |
| 1682 | try ty.combine(p, .{ .@"struct" = try p.recordSpec() }, tag_tok); | |
| 1683 | continue; | |
| 1684 | }, | |
| 1685 | .keyword_union => { | |
| 1686 | const tag_tok = p.tok_i; | |
| 1687 | try ty.combine(p, .{ .@"union" = try p.recordSpec() }, tag_tok); | |
| 1688 | continue; | |
| 1689 | }, | |
| 1690 | .keyword_enum => { | |
| 1691 | const tag_tok = p.tok_i; | |
| 1692 | try ty.combine(p, .{ .@"enum" = try p.enumSpec() }, tag_tok); | |
| 1693 | continue; | |
| 1694 | }, | |
| 1695 | .identifier, .extended_identifier => { | |
| 1696 | const typedef = (try p.findTypedef(p.tok_i, ty.specifier != .none)) orelse break; | |
| 1697 | if (!ty.combineTypedef(p, typedef.ty, typedef.name_tok)) break; | |
| 1698 | }, | |
| 1699 | else => break, | |
| 1700 | } | |
| 1701 | // consume single token specifiers here | |
| 1702 | p.tok_i += 1; | |
| 1703 | } | |
| 1704 | return p.tok_i != start; | |
| 1705 | } | |
| 1706 | ||
| 1707 | fn getAnonymousName(p: *Parser, kind_tok: TokenIndex) ![]const u8 { | |
| 1708 | const loc = p.pp.tokens.items(.loc)[kind_tok]; | |
| 1709 | const source = p.pp.comp.getSource(loc.id); | |
| 1710 | const line_col = source.lineCol(loc); | |
| 1711 | ||
| 1712 | const kind_str = switch (p.tok_ids[kind_tok]) { | |
| 1713 | .keyword_struct, .keyword_union, .keyword_enum => p.tokSlice(kind_tok), | |
| 1714 | else => "record field", | |
| 1715 | }; | |
| 1716 | ||
| 1717 | return std.fmt.allocPrint( | |
| 1718 | p.arena, | |
| 1719 | "(anonymous {s} at {s}:{d}:{d})", | |
| 1720 | .{ kind_str, source.path, line_col.line_no, line_col.col }, | |
| 1721 | ); | |
| 1722 | } | |
| 1723 | ||
| 1724 | /// recordSpec | |
| 1725 | /// : (keyword_struct | keyword_union) IDENTIFIER? { recordDecl* } | |
| 1726 | /// | (keyword_struct | keyword_union) IDENTIFIER | |
| 1727 | fn recordSpec(p: *Parser) Error!*Type.Record { | |
| 1728 | const kind_tok = p.tok_i; | |
| 1729 | const is_struct = p.tok_ids[kind_tok] == .keyword_struct; | |
| 1730 | p.tok_i += 1; | |
| 1731 | const attr_buf_top = p.attr_buf.len; | |
| 1732 | defer p.attr_buf.len = attr_buf_top; | |
| 1733 | try p.attributeSpecifier(); // .record | |
| 1734 | ||
| 1735 | const maybe_ident = try p.eatIdentifier(); | |
| 1736 | const l_brace = p.eatToken(.l_brace) orelse { | |
| 1737 | const ident = maybe_ident orelse { | |
| 1738 | try p.err(.ident_or_l_brace); | |
| 1739 | return error.ParsingFailed; | |
| 1740 | }; | |
| 1741 | // check if this is a reference to a previous type | |
| 1742 | if (try p.findTag(p.tok_ids[kind_tok], ident, .reference)) |prev| { | |
| 1743 | return prev.ty.data.record; | |
| 1744 | } else { | |
| 1745 | // this is a forward declaration, create a new record Type. | |
| 1746 | const record_ty = try Type.Record.create(p.arena, p.tokSlice(ident)); | |
| 1747 | const ty = Type{ | |
| 1748 | .specifier = if (is_struct) .@"struct" else .@"union", | |
| 1749 | .data = .{ .record = record_ty }, | |
| 1750 | }; | |
| 1751 | const sym = Scope.Symbol{ .name = record_ty.name, .ty = ty, .name_tok = ident }; | |
| 1752 | try p.scopes.append(if (is_struct) .{ .@"struct" = sym } else .{ .@"union" = sym }); | |
| 1753 | return record_ty; | |
| 1754 | } | |
| 1755 | }; | |
| 1756 | ||
| 1757 | // Get forward declared type or create a new one | |
| 1758 | var defined = false; | |
| 1759 | const record_ty: *Type.Record = if (maybe_ident) |ident| record_ty: { | |
| 1760 | if (try p.findTag(p.tok_ids[kind_tok], ident, .definition)) |prev| { | |
| 1761 | if (!prev.ty.data.record.isIncomplete()) { | |
| 1762 | // if the record isn't incomplete, this is a redefinition | |
| 1763 | try p.errStr(.redefinition, ident, p.tokSlice(ident)); | |
| 1764 | try p.errTok(.previous_definition, prev.name_tok); | |
| 1765 | } else { | |
| 1766 | defined = true; | |
| 1767 | break :record_ty prev.ty.data.record; | |
| 1768 | } | |
| 1769 | } | |
| 1770 | break :record_ty try Type.Record.create(p.arena, p.tokSlice(ident)); | |
| 1771 | } else try Type.Record.create(p.arena, try p.getAnonymousName(kind_tok)); | |
| 1772 | const ty = Type{ | |
| 1773 | .specifier = if (is_struct) .@"struct" else .@"union", | |
| 1774 | .data = .{ .record = record_ty }, | |
| 1775 | }; | |
| 1776 | ||
| 1777 | // declare a symbol for the type | |
| 1778 | if (maybe_ident != null and !defined) { | |
| 1779 | const sym = Scope.Symbol{ .name = record_ty.name, .ty = ty, .name_tok = maybe_ident.? }; | |
| 1780 | try p.scopes.append(if (is_struct) .{ .@"struct" = sym } else .{ .@"union" = sym }); | |
| 1781 | } | |
| 1782 | ||
| 1783 | // reserve space for this record | |
| 1784 | try p.decl_buf.append(.none); | |
| 1785 | const decl_buf_top = p.decl_buf.items.len; | |
| 1786 | const record_buf_top = p.record_buf.items.len; | |
| 1787 | const scopes_top = p.scopes.items.len; | |
| 1788 | errdefer p.decl_buf.items.len = decl_buf_top - 1; | |
| 1789 | defer { | |
| 1790 | p.decl_buf.items.len = decl_buf_top; | |
| 1791 | p.record_buf.items.len = record_buf_top; | |
| 1792 | p.scopes.items.len = scopes_top; | |
| 1793 | } | |
| 1794 | ||
| 1795 | const old_record = p.record; | |
| 1796 | defer p.record = old_record; | |
| 1797 | p.record = .{ | |
| 1798 | .kind = p.tok_ids[kind_tok], | |
| 1799 | .scopes_top = scopes_top, | |
| 1800 | }; | |
| 1801 | ||
| 1802 | try p.recordDecls(); | |
| 1803 | ||
| 1804 | if (p.record.flexible_field) |some| { | |
| 1805 | if (p.record_buf.items[record_buf_top..].len == 1 and is_struct) { | |
| 1806 | try p.errTok(.flexible_in_empty, some); | |
| 1807 | } | |
| 1808 | } | |
| 1809 | ||
| 1810 | record_ty.fields = try p.arena.dupe(Type.Record.Field, p.record_buf.items[record_buf_top..]); | |
| 1811 | // TODO actually calculate | |
| 1812 | record_ty.size = 1; | |
| 1813 | record_ty.alignment = 1; | |
| 1814 | ||
| 1815 | if (p.record_buf.items.len == record_buf_top) try p.errStr(.empty_record, kind_tok, p.tokSlice(kind_tok)); | |
| 1816 | try p.expectClosing(l_brace, .r_brace); | |
| 1817 | try p.attributeSpecifier(); // .record | |
| 1818 | ||
| 1819 | // finish by creating a node | |
| 1820 | var node: Tree.Node = .{ | |
| 1821 | .tag = if (is_struct) .struct_decl_two else .union_decl_two, | |
| 1822 | .ty = ty, | |
| 1823 | .data = .{ .bin = .{ .lhs = .none, .rhs = .none } }, | |
| 1824 | }; | |
| 1825 | const record_decls = p.decl_buf.items[decl_buf_top..]; | |
| 1826 | switch (record_decls.len) { | |
| 1827 | 0 => {}, | |
| 1828 | 1 => node.data = .{ .bin = .{ .lhs = record_decls[0], .rhs = .none } }, | |
| 1829 | 2 => node.data = .{ .bin = .{ .lhs = record_decls[0], .rhs = record_decls[1] } }, | |
| 1830 | else => { | |
| 1831 | node.tag = if (is_struct) .struct_decl else .union_decl; | |
| 1832 | node.data = .{ .range = try p.addList(record_decls) }; | |
| 1833 | }, | |
| 1834 | } | |
| 1835 | p.decl_buf.items[decl_buf_top - 1] = try p.addNode(node); | |
| 1836 | return record_ty; | |
| 1837 | } | |
| 1838 | ||
| 1839 | /// recordDecl | |
| 1840 | /// : specQual (recordDeclarator (',' recordDeclarator)*)? ; | |
| 1841 | /// | staticAssert | |
| 1842 | fn recordDecls(p: *Parser) Error!void { | |
| 1843 | while (true) { | |
| 1844 | if (try p.pragma()) continue; | |
| 1845 | if (try p.parseOrNextDecl(staticAssert)) continue; | |
| 1846 | if (p.eatToken(.keyword_extension)) |_| { | |
| 1847 | const saved_extension = p.extension_suppressed; | |
| 1848 | defer p.extension_suppressed = saved_extension; | |
| 1849 | p.extension_suppressed = true; | |
| 1850 | ||
| 1851 | if (try p.parseOrNextDecl(recordDeclarator)) continue; | |
| 1852 | try p.err(.expected_type); | |
| 1853 | p.nextExternDecl(); | |
| 1854 | continue; | |
| 1855 | } | |
| 1856 | if (try p.parseOrNextDecl(recordDeclarator)) continue; | |
| 1857 | break; | |
| 1858 | } | |
| 1859 | } | |
| 1860 | ||
| 1861 | /// recordDeclarator : keyword_extension? declarator (':' constExpr)? | |
| 1862 | fn recordDeclarator(p: *Parser) Error!bool { | |
| 1863 | const attr_buf_top = p.attr_buf.len; | |
| 1864 | defer p.attr_buf.len = attr_buf_top; | |
| 1865 | const base_ty = (try p.specQual()) orelse return false; | |
| 1866 | ||
| 1867 | while (true) { | |
| 1868 | const this_decl_top = p.attr_buf.len; | |
| 1869 | defer p.attr_buf.len = this_decl_top; | |
| 1870 | ||
| 1871 | try p.attributeSpecifier(); // .record | |
| 1872 | ||
| 1873 | // 0 means unnamed | |
| 1874 | var name_tok: TokenIndex = 0; | |
| 1875 | var ty = base_ty; | |
| 1876 | var bits_node: NodeIndex = .none; | |
| 1877 | var bits: u32 = 0; | |
| 1878 | const first_tok = p.tok_i; | |
| 1879 | if (try p.declarator(ty, .record)) |d| { | |
| 1880 | name_tok = d.name; | |
| 1881 | ty = d.ty; | |
| 1882 | } | |
| 1883 | try p.attributeSpecifier(); // .record | |
| 1884 | ty = try p.withAttributes(ty, attr_buf_top); | |
| 1885 | ||
| 1886 | if (p.eatToken(.colon)) |_| bits: { | |
| 1887 | const res = try p.constExpr(); | |
| 1888 | if (!ty.isInt()) { | |
| 1889 | try p.errStr(.non_int_bitfield, first_tok, try p.typeStr(ty)); | |
| 1890 | break :bits; | |
| 1891 | } | |
| 1892 | ||
| 1893 | if (res.val.tag == .unavailable) { | |
| 1894 | try p.errTok(.expected_integer_constant_expr, first_tok); | |
| 1895 | break :bits; | |
| 1896 | } else if (res.val.compare(.lt, Value.int(0), res.ty, p.pp.comp)) { | |
| 1897 | try p.errExtra(.negative_bitwidth, first_tok, .{ | |
| 1898 | .signed = res.val.signExtend(res.ty, p.pp.comp), | |
| 1899 | }); | |
| 1900 | break :bits; | |
| 1901 | } | |
| 1902 | ||
| 1903 | // incomplete size error is reported later | |
| 1904 | const bit_size = ty.bitSizeof(p.pp.comp) orelse break :bits; | |
| 1905 | if (res.val.compare(.gt, Value.int(bit_size), res.ty, p.pp.comp)) { | |
| 1906 | try p.errTok(.bitfield_too_big, name_tok); | |
| 1907 | break :bits; | |
| 1908 | } else if (res.val.isZero() and name_tok != 0) { | |
| 1909 | try p.errTok(.zero_width_named_field, name_tok); | |
| 1910 | break :bits; | |
| 1911 | } | |
| 1912 | ||
| 1913 | bits = res.val.getInt(u32); | |
| 1914 | bits_node = res.node; | |
| 1915 | } | |
| 1916 | ||
| 1917 | if (name_tok == 0 and bits_node == .none) unnamed: { | |
| 1918 | if (ty.is(.@"enum")) break :unnamed; | |
| 1919 | if (ty.isAnonymousRecord()) { | |
| 1920 | // An anonymous record appears as indirect fields on the parent | |
| 1921 | try p.record_buf.append(.{ | |
| 1922 | .name = try p.getAnonymousName(first_tok), | |
| 1923 | .ty = ty, | |
| 1924 | .bit_width = 0, | |
| 1925 | }); | |
| 1926 | const node = try p.addNode(.{ | |
| 1927 | .tag = .indirect_record_field_decl, | |
| 1928 | .ty = ty, | |
| 1929 | .data = undefined, | |
| 1930 | }); | |
| 1931 | try p.decl_buf.append(node); | |
| 1932 | try p.record.addFieldsFromAnonymous(p, ty); | |
| 1933 | break; // must be followed by a semicolon | |
| 1934 | } | |
| 1935 | try p.err(.missing_declaration); | |
| 1936 | } else { | |
| 1937 | try p.record_buf.append(.{ | |
| 1938 | .name = if (name_tok != 0) p.tokSlice(name_tok) else try p.getAnonymousName(first_tok), | |
| 1939 | .ty = ty, | |
| 1940 | .name_tok = name_tok, | |
| 1941 | .bit_width = bits, | |
| 1942 | }); | |
| 1943 | if (name_tok != 0) try p.record.addField(p, name_tok); | |
| 1944 | const node = try p.addNode(.{ | |
| 1945 | .tag = .record_field_decl, | |
| 1946 | .ty = ty, | |
| 1947 | .data = .{ .decl = .{ .name = name_tok, .node = bits_node } }, | |
| 1948 | }); | |
| 1949 | try p.decl_buf.append(node); | |
| 1950 | } | |
| 1951 | ||
| 1952 | if (ty.isFunc()) { | |
| 1953 | try p.errTok(.func_field, first_tok); | |
| 1954 | } else if (ty.is(.variable_len_array)) { | |
| 1955 | try p.errTok(.vla_field, first_tok); | |
| 1956 | } else if (ty.is(.incomplete_array)) { | |
| 1957 | if (p.record.kind == .keyword_union) { | |
| 1958 | try p.errTok(.flexible_in_union, first_tok); | |
| 1959 | } | |
| 1960 | if (p.record.flexible_field) |some| { | |
| 1961 | try p.errTok(.flexible_non_final, some); | |
| 1962 | } | |
| 1963 | p.record.flexible_field = first_tok; | |
| 1964 | } else if (ty.hasIncompleteSize()) { | |
| 1965 | try p.errStr(.field_incomplete_ty, first_tok, try p.typeStr(ty)); | |
| 1966 | } else if (p.record.flexible_field) |some| { | |
| 1967 | if (some != first_tok) try p.errTok(.flexible_non_final, some); | |
| 1968 | } | |
| 1969 | if (p.eatToken(.comma) == null) break; | |
| 1970 | } | |
| 1971 | _ = try p.expectToken(.semicolon); | |
| 1972 | return true; | |
| 1973 | } | |
| 1974 | ||
| 1975 | fn checkAlignasUsage(p: *Parser, tag: Diagnostics.Tag, attr_buf_start: usize) !void { | |
| 1976 | var i = attr_buf_start; | |
| 1977 | while (i < p.attr_buf.len) : (i += 1) { | |
| 1978 | const tentative_attr = p.attr_buf.get(i); | |
| 1979 | if (tentative_attr.attr.tag != .aligned) continue; | |
| 1980 | if (tentative_attr.attr.args.aligned.alignment) |alignment| { | |
| 1981 | if (alignment.alignas) try p.errTok(tag, tentative_attr.tok); | |
| 1982 | } | |
| 1983 | } | |
| 1984 | } | |
| 1985 | ||
| 1986 | /// specQual : (typeSpec | typeQual | alignSpec)+ | |
| 1987 | fn specQual(p: *Parser) Error!?Type { | |
| 1988 | var spec: Type.Builder = .{}; | |
| 1989 | const attr_buf_top = p.attr_buf.len; | |
| 1990 | defer p.attr_buf.len = attr_buf_top; | |
| 1991 | if (try p.typeSpec(&spec)) { | |
| 1992 | const ty = try spec.finish(p, attr_buf_top); | |
| 1993 | try p.validateAlignas(ty, .align_ignored); | |
| 1994 | return ty; | |
| 1995 | } | |
| 1996 | return null; | |
| 1997 | } | |
| 1998 | ||
| 1999 | /// enumSpec | |
| 2000 | /// : keyword_enum IDENTIFIER? { enumerator (',' enumerator)? ',') } | |
| 2001 | /// | keyword_enum IDENTIFIER | |
| 2002 | fn enumSpec(p: *Parser) Error!*Type.Enum { | |
| 2003 | const enum_tok = p.tok_i; | |
| 2004 | p.tok_i += 1; | |
| 2005 | const attr_buf_top = p.attr_buf.len; | |
| 2006 | defer p.attr_buf.len = attr_buf_top; | |
| 2007 | try p.attributeSpecifier(); // record | |
| 2008 | ||
| 2009 | const maybe_ident = try p.eatIdentifier(); | |
| 2010 | const l_brace = p.eatToken(.l_brace) orelse { | |
| 2011 | const ident = maybe_ident orelse { | |
| 2012 | try p.err(.ident_or_l_brace); | |
| 2013 | return error.ParsingFailed; | |
| 2014 | }; | |
| 2015 | // check if this is a reference to a previous type | |
| 2016 | if (try p.findTag(.keyword_enum, ident, .reference)) |prev| { | |
| 2017 | return prev.ty.data.@"enum"; | |
| 2018 | } else { | |
| 2019 | // this is a forward declaration, create a new enum Type. | |
| 2020 | const enum_ty = try Type.Enum.create(p.arena, p.tokSlice(ident)); | |
| 2021 | const ty = Type{ .specifier = .@"enum", .data = .{ .@"enum" = enum_ty } }; | |
| 2022 | const sym = Scope.Symbol{ .name = enum_ty.name, .ty = ty, .name_tok = ident }; | |
| 2023 | try p.scopes.append(.{ .@"enum" = sym }); | |
| 2024 | return enum_ty; | |
| 2025 | } | |
| 2026 | }; | |
| 2027 | ||
| 2028 | // Get forward declared type or create a new one | |
| 2029 | var defined = false; | |
| 2030 | const enum_ty: *Type.Enum = if (maybe_ident) |ident| enum_ty: { | |
| 2031 | if (try p.findTag(.keyword_enum, ident, .definition)) |prev| { | |
| 2032 | if (!prev.ty.data.@"enum".isIncomplete()) { | |
| 2033 | // if the enum isn't incomplete, this is a redefinition | |
| 2034 | try p.errStr(.redefinition, ident, p.tokSlice(ident)); | |
| 2035 | try p.errTok(.previous_definition, prev.name_tok); | |
| 2036 | } else { | |
| 2037 | defined = true; | |
| 2038 | break :enum_ty prev.ty.data.@"enum"; | |
| 2039 | } | |
| 2040 | } | |
| 2041 | break :enum_ty try Type.Enum.create(p.arena, p.tokSlice(ident)); | |
| 2042 | } else try Type.Enum.create(p.arena, try p.getAnonymousName(enum_tok)); | |
| 2043 | const ty = Type{ | |
| 2044 | .specifier = .@"enum", | |
| 2045 | .data = .{ .@"enum" = enum_ty }, | |
| 2046 | }; | |
| 2047 | ||
| 2048 | // declare a symbol for the type | |
| 2049 | if (maybe_ident != null and !defined) { | |
| 2050 | try p.scopes.append(.{ .@"enum" = .{ | |
| 2051 | .name = enum_ty.name, | |
| 2052 | .ty = ty, | |
| 2053 | .name_tok = maybe_ident.?, | |
| 2054 | } }); | |
| 2055 | } | |
| 2056 | ||
| 2057 | // reserve space for this enum | |
| 2058 | try p.decl_buf.append(.none); | |
| 2059 | const decl_buf_top = p.decl_buf.items.len; | |
| 2060 | const list_buf_top = p.list_buf.items.len; | |
| 2061 | const enum_buf_top = p.enum_buf.items.len; | |
| 2062 | errdefer p.decl_buf.items.len = decl_buf_top - 1; | |
| 2063 | defer { | |
| 2064 | p.decl_buf.items.len = decl_buf_top; | |
| 2065 | p.list_buf.items.len = list_buf_top; | |
| 2066 | p.enum_buf.items.len = enum_buf_top; | |
| 2067 | } | |
| 2068 | ||
| 2069 | var e = Enumerator.init(p); | |
| 2070 | while (try p.enumerator(&e)) |field_and_node| { | |
| 2071 | try p.enum_buf.append(field_and_node.field); | |
| 2072 | try p.list_buf.append(field_and_node.node); | |
| 2073 | if (p.eatToken(.comma) == null) break; | |
| 2074 | } | |
| 2075 | enum_ty.fields = try p.arena.dupe(Type.Enum.Field, p.enum_buf.items[enum_buf_top..]); | |
| 2076 | enum_ty.tag_ty = e.res.ty; | |
| 2077 | ||
| 2078 | if (p.enum_buf.items.len == enum_buf_top) try p.err(.empty_enum); | |
| 2079 | try p.expectClosing(l_brace, .r_brace); | |
| 2080 | try p.attributeSpecifier(); // record | |
| 2081 | ||
| 2082 | // finish by creating a node | |
| 2083 | var node: Tree.Node = .{ .tag = .enum_decl_two, .ty = ty, .data = .{ | |
| 2084 | .bin = .{ .lhs = .none, .rhs = .none }, | |
| 2085 | } }; | |
| 2086 | const field_nodes = p.list_buf.items[list_buf_top..]; | |
| 2087 | switch (field_nodes.len) { | |
| 2088 | 0 => {}, | |
| 2089 | 1 => node.data = .{ .bin = .{ .lhs = field_nodes[0], .rhs = .none } }, | |
| 2090 | 2 => node.data = .{ .bin = .{ .lhs = field_nodes[0], .rhs = field_nodes[1] } }, | |
| 2091 | else => { | |
| 2092 | node.tag = .enum_decl; | |
| 2093 | node.data = .{ .range = try p.addList(field_nodes) }; | |
| 2094 | }, | |
| 2095 | } | |
| 2096 | p.decl_buf.items[decl_buf_top - 1] = try p.addNode(node); | |
| 2097 | return enum_ty; | |
| 2098 | } | |
| 2099 | ||
| 2100 | const Enumerator = struct { | |
| 2101 | res: Result, | |
| 2102 | ||
| 2103 | fn init(p: *Parser) Enumerator { | |
| 2104 | return .{ .res = .{ | |
| 2105 | .ty = .{ .specifier = if (p.pp.comp.langopts.short_enums) .schar else .int }, | |
| 2106 | .val = Value.int(0), | |
| 2107 | } }; | |
| 2108 | } | |
| 2109 | ||
| 2110 | /// Increment enumerator value adjusting type if needed. | |
| 2111 | fn incr(e: *Enumerator, p: *Parser) !void { | |
| 2112 | e.res.node = .none; | |
| 2113 | _ = p; | |
| 2114 | _ = e.res.val.add(e.res.val, Value.int(1), e.res.ty, p.pp.comp); | |
| 2115 | // TODO adjust type if value does not fit current | |
| 2116 | } | |
| 2117 | ||
| 2118 | /// Set enumerator value to specified value, adjusting type if needed. | |
| 2119 | fn set(e: *Enumerator, p: *Parser, res: Result) !void { | |
| 2120 | _ = p; | |
| 2121 | e.res = res; | |
| 2122 | // TODO adjust res type to try to fit with the previous type | |
| 2123 | } | |
| 2124 | }; | |
| 2125 | ||
| 2126 | const EnumFieldAndNode = struct { field: Type.Enum.Field, node: NodeIndex }; | |
| 2127 | ||
| 2128 | /// enumerator : IDENTIFIER ('=' constExpr) | |
| 2129 | fn enumerator(p: *Parser, e: *Enumerator) Error!?EnumFieldAndNode { | |
| 2130 | _ = try p.pragma(); | |
| 2131 | const name_tok = (try p.eatIdentifier()) orelse { | |
| 2132 | if (p.tok_ids[p.tok_i] == .r_brace) return null; | |
| 2133 | try p.err(.expected_identifier); | |
| 2134 | p.skipTo(.r_brace); | |
| 2135 | return error.ParsingFailed; | |
| 2136 | }; | |
| 2137 | const name = p.tokSlice(name_tok); | |
| 2138 | const attr_buf_top = p.attr_buf.len; | |
| 2139 | defer p.attr_buf.len = attr_buf_top; | |
| 2140 | try p.attributeSpecifier(); | |
| 2141 | ||
| 2142 | if (p.eatToken(.equal)) |_| { | |
| 2143 | const specified = try p.constExpr(); | |
| 2144 | if (specified.val.tag == .unavailable) { | |
| 2145 | try p.errTok(.enum_val_unavailable, name_tok + 2); | |
| 2146 | try e.incr(p); | |
| 2147 | } else { | |
| 2148 | try e.set(p, specified); | |
| 2149 | } | |
| 2150 | } else { | |
| 2151 | try e.incr(p); | |
| 2152 | } | |
| 2153 | ||
| 2154 | if (p.findSymbol(name_tok, .definition)) |scope| switch (scope) { | |
| 2155 | .enumeration => |sym| { | |
| 2156 | try p.errStr(.redefinition, name_tok, name); | |
| 2157 | try p.errTok(.previous_definition, sym.name_tok); | |
| 2158 | }, | |
| 2159 | .decl, .def, .param => |sym| { | |
| 2160 | try p.errStr(.redefinition_different_sym, name_tok, name); | |
| 2161 | try p.errTok(.previous_definition, sym.name_tok); | |
| 2162 | }, | |
| 2163 | else => unreachable, | |
| 2164 | }; | |
| 2165 | ||
| 2166 | var res = e.res; | |
| 2167 | res.ty = try p.withAttributes(res.ty, attr_buf_top); | |
| 2168 | ||
| 2169 | try p.scopes.append(.{ .enumeration = .{ | |
| 2170 | .name = name, | |
| 2171 | .value = res, | |
| 2172 | .name_tok = name_tok, | |
| 2173 | } }); | |
| 2174 | const node = try p.addNode(.{ | |
| 2175 | .tag = .enum_field_decl, | |
| 2176 | .ty = res.ty, | |
| 2177 | .data = .{ .decl = .{ | |
| 2178 | .name = name_tok, | |
| 2179 | .node = res.node, | |
| 2180 | } }, | |
| 2181 | }); | |
| 2182 | return EnumFieldAndNode{ .field = .{ | |
| 2183 | .name = name, | |
| 2184 | .ty = res.ty, | |
| 2185 | .name_tok = name_tok, | |
| 2186 | .node = res.node, | |
| 2187 | }, .node = node }; | |
| 2188 | } | |
| 2189 | ||
| 2190 | /// typeQual : keyword_const | keyword_restrict | keyword_volatile | keyword_atomic | |
| 2191 | fn typeQual(p: *Parser, b: *Type.Qualifiers.Builder) Error!bool { | |
| 2192 | var any = false; | |
| 2193 | while (true) { | |
| 2194 | switch (p.tok_ids[p.tok_i]) { | |
| 2195 | .keyword_restrict, .keyword_restrict1, .keyword_restrict2 => { | |
| 2196 | if (b.restrict != null) | |
| 2197 | try p.errStr(.duplicate_decl_spec, p.tok_i, "restrict") | |
| 2198 | else | |
| 2199 | b.restrict = p.tok_i; | |
| 2200 | }, | |
| 2201 | .keyword_const, .keyword_const1, .keyword_const2 => { | |
| 2202 | if (b.@"const" != null) | |
| 2203 | try p.errStr(.duplicate_decl_spec, p.tok_i, "const") | |
| 2204 | else | |
| 2205 | b.@"const" = p.tok_i; | |
| 2206 | }, | |
| 2207 | .keyword_volatile, .keyword_volatile1, .keyword_volatile2 => { | |
| 2208 | if (b.@"volatile" != null) | |
| 2209 | try p.errStr(.duplicate_decl_spec, p.tok_i, "volatile") | |
| 2210 | else | |
| 2211 | b.@"volatile" = p.tok_i; | |
| 2212 | }, | |
| 2213 | .keyword_atomic => { | |
| 2214 | // _Atomic(typeName) instead of just _Atomic | |
| 2215 | if (p.tok_ids[p.tok_i + 1] == .l_paren) break; | |
| 2216 | if (b.atomic != null) | |
| 2217 | try p.errStr(.duplicate_decl_spec, p.tok_i, "atomic") | |
| 2218 | else | |
| 2219 | b.atomic = p.tok_i; | |
| 2220 | }, | |
| 2221 | else => break, | |
| 2222 | } | |
| 2223 | p.tok_i += 1; | |
| 2224 | any = true; | |
| 2225 | } | |
| 2226 | return any; | |
| 2227 | } | |
| 2228 | ||
| 2229 | const Declarator = struct { | |
| 2230 | name: TokenIndex, | |
| 2231 | ty: Type, | |
| 2232 | func_declarator: ?TokenIndex = null, | |
| 2233 | old_style_func: ?TokenIndex = null, | |
| 2234 | }; | |
| 2235 | const DeclaratorKind = enum { normal, abstract, param, record }; | |
| 2236 | ||
| 2237 | /// declarator : pointer? (IDENTIFIER | '(' declarator ')') directDeclarator* | |
| 2238 | /// abstractDeclarator | |
| 2239 | /// : pointer? ('(' abstractDeclarator ')')? directAbstractDeclarator* | |
| 2240 | fn declarator( | |
| 2241 | p: *Parser, | |
| 2242 | base_type: Type, | |
| 2243 | kind: DeclaratorKind, | |
| 2244 | ) Error!?Declarator { | |
| 2245 | const start = p.tok_i; | |
| 2246 | var d = Declarator{ .name = 0, .ty = try p.pointer(base_type) }; | |
| 2247 | ||
| 2248 | const attr_buf_top = p.attr_buf.len; | |
| 2249 | defer p.attr_buf.len = attr_buf_top; | |
| 2250 | ||
| 2251 | const maybe_ident = p.tok_i; | |
| 2252 | if (kind != .abstract and (try p.eatIdentifier()) != null) { | |
| 2253 | d.name = maybe_ident; | |
| 2254 | const combine_tok = p.tok_i; | |
| 2255 | d.ty = try p.directDeclarator(d.ty, &d, kind); | |
| 2256 | try d.ty.validateCombinedType(p, combine_tok); | |
| 2257 | d.ty = try p.withAttributes(d.ty, attr_buf_top); | |
| 2258 | return d; | |
| 2259 | } else if (p.eatToken(.l_paren)) |l_paren| blk: { | |
| 2260 | var res = (try p.declarator(.{ .specifier = .void }, kind)) orelse { | |
| 2261 | p.tok_i = l_paren; | |
| 2262 | break :blk; | |
| 2263 | }; | |
| 2264 | try p.expectClosing(l_paren, .r_paren); | |
| 2265 | const suffix_start = p.tok_i; | |
| 2266 | const outer = try p.directDeclarator(d.ty, &d, kind); | |
| 2267 | try res.ty.combine(outer, p, res.func_declarator orelse suffix_start); | |
| 2268 | try res.ty.validateCombinedType(p, suffix_start); | |
| 2269 | res.old_style_func = d.old_style_func; | |
| 2270 | return res; | |
| 2271 | } | |
| 2272 | ||
| 2273 | const expected_ident = p.tok_i; | |
| 2274 | ||
| 2275 | d.ty = try p.directDeclarator(d.ty, &d, kind); | |
| 2276 | ||
| 2277 | if (kind == .normal and !d.ty.isEnumOrRecord()) { | |
| 2278 | try p.errTok(.expected_ident_or_l_paren, expected_ident); | |
| 2279 | return error.ParsingFailed; | |
| 2280 | } | |
| 2281 | try d.ty.validateCombinedType(p, expected_ident); | |
| 2282 | d.ty = try p.withAttributes(d.ty, attr_buf_top); | |
| 2283 | if (start == p.tok_i) return null; | |
| 2284 | return d; | |
| 2285 | } | |
| 2286 | ||
| 2287 | /// directDeclarator | |
| 2288 | /// : '[' typeQual* assignExpr? ']' directDeclarator? | |
| 2289 | /// | '[' keyword_static typeQual* assignExpr ']' directDeclarator? | |
| 2290 | /// | '[' typeQual+ keyword_static assignExpr ']' directDeclarator? | |
| 2291 | /// | '[' typeQual* '*' ']' directDeclarator? | |
| 2292 | /// | '(' paramDecls ')' directDeclarator? | |
| 2293 | /// | '(' (IDENTIFIER (',' IDENTIFIER))? ')' directDeclarator? | |
| 2294 | /// directAbstractDeclarator | |
| 2295 | /// : '[' typeQual* assignExpr? ']' | |
| 2296 | /// | '[' keyword_static typeQual* assignExpr ']' | |
| 2297 | /// | '[' typeQual+ keyword_static assignExpr ']' | |
| 2298 | /// | '[' '*' ']' | |
| 2299 | /// | '(' paramDecls? ')' | |
| 2300 | fn directDeclarator(p: *Parser, base_type: Type, d: *Declarator, kind: DeclaratorKind) Error!Type { | |
| 2301 | try p.attributeSpecifier(); | |
| 2302 | if (p.eatToken(.l_bracket)) |l_bracket| { | |
| 2303 | var res_ty = Type{ | |
| 2304 | // so that we can get any restrict type that might be present | |
| 2305 | .specifier = .pointer, | |
| 2306 | }; | |
| 2307 | var quals = Type.Qualifiers.Builder{}; | |
| 2308 | ||
| 2309 | var got_quals = try p.typeQual(&quals); | |
| 2310 | var static = p.eatToken(.keyword_static); | |
| 2311 | if (static != null and !got_quals) got_quals = try p.typeQual(&quals); | |
| 2312 | var star = p.eatToken(.asterisk); | |
| 2313 | const size_tok = p.tok_i; | |
| 2314 | const size = if (star) |_| Result{} else try p.assignExpr(); | |
| 2315 | try p.expectClosing(l_bracket, .r_bracket); | |
| 2316 | ||
| 2317 | if (star != null and static != null) { | |
| 2318 | try p.errTok(.invalid_static_star, static.?); | |
| 2319 | static = null; | |
| 2320 | } | |
| 2321 | if (kind != .param) { | |
| 2322 | if (static != null) | |
| 2323 | try p.errTok(.static_non_param, l_bracket) | |
| 2324 | else if (got_quals) | |
| 2325 | try p.errTok(.array_qualifiers, l_bracket); | |
| 2326 | if (star) |some| try p.errTok(.star_non_param, some); | |
| 2327 | static = null; | |
| 2328 | quals = .{}; | |
| 2329 | star = null; | |
| 2330 | } else { | |
| 2331 | try quals.finish(p, &res_ty); | |
| 2332 | } | |
| 2333 | if (static) |_| try size.expect(p); | |
| 2334 | ||
| 2335 | const outer = try p.directDeclarator(base_type, d, kind); | |
| 2336 | var max_bits = p.pp.comp.target.cpu.arch.ptrBitWidth(); | |
| 2337 | if (max_bits > 61) max_bits = 61; | |
| 2338 | const max_bytes = (@as(u64, 1) << @truncate(u6, max_bits)) - 1; | |
| 2339 | // `outer` is validated later so it may be invalid here | |
| 2340 | const outer_size = if (outer.hasIncompleteSize()) 1 else outer.sizeof(p.pp.comp); | |
| 2341 | const max_elems = max_bytes / std.math.max(1, outer_size orelse 1); | |
| 2342 | ||
| 2343 | if (size.val.tag == .unavailable) { | |
| 2344 | if (size.node != .none) { | |
| 2345 | if (p.func.ty == null and kind != .param and p.record.kind == .invalid) { | |
| 2346 | try p.errTok(.variable_len_array_file_scope, l_bracket); | |
| 2347 | } | |
| 2348 | const expr_ty = try p.arena.create(Type.Expr); | |
| 2349 | expr_ty.node = size.node; | |
| 2350 | res_ty.data = .{ .expr = expr_ty }; | |
| 2351 | res_ty.specifier = .variable_len_array; | |
| 2352 | ||
| 2353 | if (static) |some| try p.errTok(.useless_static, some); | |
| 2354 | } else if (star) |_| { | |
| 2355 | const elem_ty = try p.arena.create(Type); | |
| 2356 | res_ty.data = .{ .sub_type = elem_ty }; | |
| 2357 | res_ty.specifier = .unspecified_variable_len_array; | |
| 2358 | } else { | |
| 2359 | const arr_ty = try p.arena.create(Type.Array); | |
| 2360 | arr_ty.len = 0; | |
| 2361 | res_ty.data = .{ .array = arr_ty }; | |
| 2362 | res_ty.specifier = .incomplete_array; | |
| 2363 | } | |
| 2364 | } else if (!size.ty.isInt() and !size.ty.isFloat()) { | |
| 2365 | try p.errStr(.array_size_non_int, size_tok, try p.typeStr(size.ty)); | |
| 2366 | return error.ParsingFailed; | |
| 2367 | } else { | |
| 2368 | var size_val = size.val; | |
| 2369 | const size_t = p.pp.comp.types.size; | |
| 2370 | if (size_val.tag == .float) { | |
| 2371 | size_val.floatToInt(size.ty, size_t, p.pp.comp); | |
| 2372 | } | |
| 2373 | if (size_val.compare(.lt, Value.int(0), size_t, p.pp.comp)) { | |
| 2374 | try p.errTok(.negative_array_size, l_bracket); | |
| 2375 | } | |
| 2376 | const arr_ty = try p.arena.create(Type.Array); | |
| 2377 | if (size_val.compare(.gt, Value.int(max_elems), size_t, p.pp.comp)) { | |
| 2378 | try p.errTok(.array_too_large, l_bracket); | |
| 2379 | arr_ty.len = max_elems; | |
| 2380 | } else { | |
| 2381 | arr_ty.len = size_val.getInt(u64); | |
| 2382 | } | |
| 2383 | res_ty.data = .{ .array = arr_ty }; | |
| 2384 | res_ty.specifier = .array; | |
| 2385 | } | |
| 2386 | ||
| 2387 | try res_ty.combine(outer, p, l_bracket); | |
| 2388 | return res_ty; | |
| 2389 | } else if (p.eatToken(.l_paren)) |l_paren| { | |
| 2390 | d.func_declarator = l_paren; | |
| 2391 | ||
| 2392 | const func_ty = try p.arena.create(Type.Func); | |
| 2393 | func_ty.params = &.{}; | |
| 2394 | var specifier: Type.Specifier = .func; | |
| 2395 | ||
| 2396 | if (p.eatToken(.ellipsis)) |_| { | |
| 2397 | try p.err(.param_before_var_args); | |
| 2398 | try p.expectClosing(l_paren, .r_paren); | |
| 2399 | var res_ty = Type{ .specifier = .func, .data = .{ .func = func_ty } }; | |
| 2400 | ||
| 2401 | const outer = try p.directDeclarator(base_type, d, kind); | |
| 2402 | try res_ty.combine(outer, p, l_paren); | |
| 2403 | return res_ty; | |
| 2404 | } | |
| 2405 | ||
| 2406 | if (try p.paramDecls()) |params| { | |
| 2407 | func_ty.params = params; | |
| 2408 | if (p.eatToken(.ellipsis)) |_| specifier = .var_args_func; | |
| 2409 | } else if (p.tok_ids[p.tok_i] == .r_paren) { | |
| 2410 | specifier = .old_style_func; | |
| 2411 | } else if (p.tok_ids[p.tok_i] == .identifier or p.tok_ids[p.tok_i] == .extended_identifier) { | |
| 2412 | d.old_style_func = p.tok_i; | |
| 2413 | const param_buf_top = p.param_buf.items.len; | |
| 2414 | const scopes_top = p.scopes.items.len; | |
| 2415 | defer { | |
| 2416 | p.param_buf.items.len = param_buf_top; | |
| 2417 | p.scopes.items.len = scopes_top; | |
| 2418 | } | |
| 2419 | ||
| 2420 | // findSymbol stops the search at .block | |
| 2421 | try p.scopes.append(.block); | |
| 2422 | ||
| 2423 | specifier = .old_style_func; | |
| 2424 | while (true) { | |
| 2425 | const name_tok = try p.expectIdentifier(); | |
| 2426 | if (p.findSymbol(name_tok, .definition)) |scope| { | |
| 2427 | try p.errStr(.redefinition_of_parameter, name_tok, p.tokSlice(name_tok)); | |
| 2428 | try p.errTok(.previous_definition, scope.param.name_tok); | |
| 2429 | } | |
| 2430 | try p.scopes.append(.{ .param = .{ | |
| 2431 | .name = p.tokSlice(name_tok), | |
| 2432 | .ty = undefined, | |
| 2433 | .name_tok = name_tok, | |
| 2434 | } }); | |
| 2435 | try p.param_buf.append(.{ | |
| 2436 | .name = p.tokSlice(name_tok), | |
| 2437 | .name_tok = name_tok, | |
| 2438 | .ty = .{ .specifier = .int }, | |
| 2439 | }); | |
| 2440 | if (p.eatToken(.comma) == null) break; | |
| 2441 | } | |
| 2442 | func_ty.params = try p.arena.dupe(Type.Func.Param, p.param_buf.items[param_buf_top..]); | |
| 2443 | } else { | |
| 2444 | try p.err(.expected_param_decl); | |
| 2445 | } | |
| 2446 | ||
| 2447 | try p.expectClosing(l_paren, .r_paren); | |
| 2448 | var res_ty = Type{ | |
| 2449 | .specifier = specifier, | |
| 2450 | .data = .{ .func = func_ty }, | |
| 2451 | }; | |
| 2452 | ||
| 2453 | const outer = try p.directDeclarator(base_type, d, kind); | |
| 2454 | try res_ty.combine(outer, p, l_paren); | |
| 2455 | return res_ty; | |
| 2456 | } else return base_type; | |
| 2457 | } | |
| 2458 | ||
| 2459 | /// pointer : '*' typeQual* pointer? | |
| 2460 | fn pointer(p: *Parser, base_ty: Type) Error!Type { | |
| 2461 | var ty = base_ty; | |
| 2462 | while (p.eatToken(.asterisk)) |_| { | |
| 2463 | const elem_ty = try p.arena.create(Type); | |
| 2464 | elem_ty.* = ty; | |
| 2465 | ty = Type{ | |
| 2466 | .specifier = .pointer, | |
| 2467 | .data = .{ .sub_type = elem_ty }, | |
| 2468 | }; | |
| 2469 | var quals = Type.Qualifiers.Builder{}; | |
| 2470 | _ = try p.typeQual(&quals); | |
| 2471 | try quals.finish(p, &ty); | |
| 2472 | } | |
| 2473 | return ty; | |
| 2474 | } | |
| 2475 | ||
| 2476 | /// paramDecls : paramDecl (',' paramDecl)* (',' '...') | |
| 2477 | /// paramDecl : declSpec (declarator | abstractDeclarator) | |
| 2478 | fn paramDecls(p: *Parser) Error!?[]Type.Func.Param { | |
| 2479 | // TODO warn about visibility of types declared here | |
| 2480 | const param_buf_top = p.param_buf.items.len; | |
| 2481 | const scopes_top = p.scopes.items.len; | |
| 2482 | defer { | |
| 2483 | p.param_buf.items.len = param_buf_top; | |
| 2484 | p.scopes.items.len = scopes_top; | |
| 2485 | } | |
| 2486 | ||
| 2487 | // findSymbol stops the search at .block | |
| 2488 | try p.scopes.append(.block); | |
| 2489 | ||
| 2490 | while (true) { | |
| 2491 | const param_decl_spec = if (try p.declSpec(true)) |some| | |
| 2492 | some | |
| 2493 | else if (p.param_buf.items.len == param_buf_top) | |
| 2494 | return null | |
| 2495 | else blk: { | |
| 2496 | var spec: Type.Builder = .{}; | |
| 2497 | break :blk DeclSpec{ .ty = try spec.finish(p, p.attr_buf.len) }; | |
| 2498 | }; | |
| 2499 | ||
| 2500 | var name_tok: TokenIndex = 0; | |
| 2501 | const first_tok = p.tok_i; | |
| 2502 | var param_ty = param_decl_spec.ty; | |
| 2503 | if (try p.declarator(param_decl_spec.ty, .param)) |some| { | |
| 2504 | if (some.old_style_func) |tok_i| try p.errTok(.invalid_old_style_params, tok_i); | |
| 2505 | ||
| 2506 | const attr_buf_top = p.attr_buf.len; | |
| 2507 | defer p.attr_buf.len = attr_buf_top; | |
| 2508 | try p.attributeSpecifier(); | |
| 2509 | ||
| 2510 | name_tok = some.name; | |
| 2511 | param_ty = try p.withAttributes(some.ty, attr_buf_top); | |
| 2512 | if (some.name != 0) { | |
| 2513 | if (p.findSymbol(name_tok, .definition)) |scope| { | |
| 2514 | if (scope == .enumeration) { | |
| 2515 | try p.errStr(.redefinition_of_parameter, name_tok, p.tokSlice(name_tok)); | |
| 2516 | try p.errTok(.previous_definition, scope.enumeration.name_tok); | |
| 2517 | } else { | |
| 2518 | try p.errStr(.redefinition_of_parameter, name_tok, p.tokSlice(name_tok)); | |
| 2519 | try p.errTok(.previous_definition, scope.param.name_tok); | |
| 2520 | } | |
| 2521 | } | |
| 2522 | try p.scopes.append(.{ .param = .{ | |
| 2523 | .name = p.tokSlice(name_tok), | |
| 2524 | .ty = param_ty, | |
| 2525 | .name_tok = name_tok, | |
| 2526 | } }); | |
| 2527 | } | |
| 2528 | } | |
| 2529 | ||
| 2530 | if (param_ty.isFunc()) { | |
| 2531 | // params declared as functions are converted to function pointers | |
| 2532 | const elem_ty = try p.arena.create(Type); | |
| 2533 | elem_ty.* = param_ty; | |
| 2534 | param_ty = Type{ | |
| 2535 | .specifier = .pointer, | |
| 2536 | .data = .{ .sub_type = elem_ty }, | |
| 2537 | }; | |
| 2538 | } else if (param_ty.isArray()) { | |
| 2539 | // params declared as arrays are converted to pointers | |
| 2540 | param_ty.decayArray(); | |
| 2541 | } else if (param_ty.is(.void)) { | |
| 2542 | // validate void parameters | |
| 2543 | if (p.param_buf.items.len == param_buf_top) { | |
| 2544 | if (p.tok_ids[p.tok_i] != .r_paren) { | |
| 2545 | try p.err(.void_only_param); | |
| 2546 | if (param_ty.anyQual()) try p.err(.void_param_qualified); | |
| 2547 | return error.ParsingFailed; | |
| 2548 | } | |
| 2549 | return &[0]Type.Func.Param{}; | |
| 2550 | } | |
| 2551 | try p.err(.void_must_be_first_param); | |
| 2552 | return error.ParsingFailed; | |
| 2553 | } | |
| 2554 | ||
| 2555 | try param_decl_spec.validateParam(p, &param_ty); | |
| 2556 | try p.param_buf.append(.{ | |
| 2557 | .name = if (name_tok == 0) "" else p.tokSlice(name_tok), | |
| 2558 | .name_tok = if (name_tok == 0) first_tok else name_tok, | |
| 2559 | .ty = param_ty, | |
| 2560 | }); | |
| 2561 | ||
| 2562 | if (p.eatToken(.comma) == null) break; | |
| 2563 | if (p.tok_ids[p.tok_i] == .ellipsis) break; | |
| 2564 | } | |
| 2565 | return try p.arena.dupe(Type.Func.Param, p.param_buf.items[param_buf_top..]); | |
| 2566 | } | |
| 2567 | ||
| 2568 | /// typeName : specQual abstractDeclarator | |
| 2569 | fn typeName(p: *Parser) Error!?Type { | |
| 2570 | var ty = (try p.specQual()) orelse return null; | |
| 2571 | if (try p.declarator(ty, .abstract)) |some| { | |
| 2572 | if (some.old_style_func) |tok_i| try p.errTok(.invalid_old_style_params, tok_i); | |
| 2573 | return some.ty; | |
| 2574 | } else return ty; | |
| 2575 | } | |
| 2576 | ||
| 2577 | /// initializer | |
| 2578 | /// : assignExpr | |
| 2579 | /// | '{' initializerItems '}' | |
| 2580 | fn initializer(p: *Parser, init_ty: Type) Error!Result { | |
| 2581 | // fast path for non-braced initializers | |
| 2582 | if (p.tok_ids[p.tok_i] != .l_brace) { | |
| 2583 | const tok = p.tok_i; | |
| 2584 | var res = try p.assignExpr(); | |
| 2585 | try res.expect(p); | |
| 2586 | if (try p.coerceArrayInit(&res, tok, init_ty)) return res; | |
| 2587 | try p.coerceInit(&res, tok, init_ty); | |
| 2588 | return res; | |
| 2589 | } | |
| 2590 | ||
| 2591 | var il: InitList = .{}; | |
| 2592 | defer il.deinit(p.pp.comp.gpa); | |
| 2593 | ||
| 2594 | _ = try p.initializerItem(&il, init_ty); | |
| 2595 | ||
| 2596 | const res = try p.convertInitList(il, init_ty); | |
| 2597 | var res_ty = p.nodes.items(.ty)[@enumToInt(res)]; | |
| 2598 | res_ty.qual = init_ty.qual; | |
| 2599 | return Result{ .ty = res_ty, .node = res }; | |
| 2600 | } | |
| 2601 | ||
| 2602 | /// initializerItems : designation? initializer (',' designation? initializer)* ','? | |
| 2603 | /// designation : designator+ '=' | |
| 2604 | /// designator | |
| 2605 | /// : '[' constExpr ']' | |
| 2606 | /// | '.' identifier | |
| 2607 | fn initializerItem(p: *Parser, il: *InitList, init_ty: Type) Error!bool { | |
| 2608 | const l_brace = p.eatToken(.l_brace) orelse { | |
| 2609 | const tok = p.tok_i; | |
| 2610 | var res = try p.assignExpr(); | |
| 2611 | if (res.empty(p)) return false; | |
| 2612 | ||
| 2613 | const arr = try p.coerceArrayInit(&res, tok, init_ty); | |
| 2614 | if (!arr) try p.coerceInit(&res, tok, init_ty); | |
| 2615 | if (il.tok != 0) { | |
| 2616 | try p.errTok(.initializer_overrides, tok); | |
| 2617 | try p.errTok(.previous_initializer, il.tok); | |
| 2618 | } | |
| 2619 | il.node = res.node; | |
| 2620 | il.tok = tok; | |
| 2621 | return true; | |
| 2622 | }; | |
| 2623 | ||
| 2624 | const is_scalar = init_ty.isInt() or init_ty.isFloat() or init_ty.isPtr(); | |
| 2625 | if (p.eatToken(.r_brace)) |_| { | |
| 2626 | if (is_scalar) try p.errTok(.empty_scalar_init, l_brace); | |
| 2627 | if (il.tok != 0) { | |
| 2628 | try p.errTok(.initializer_overrides, l_brace); | |
| 2629 | try p.errTok(.previous_initializer, il.tok); | |
| 2630 | } | |
| 2631 | il.node = .none; | |
| 2632 | il.tok = l_brace; | |
| 2633 | return true; | |
| 2634 | } | |
| 2635 | ||
| 2636 | var count: u64 = 0; | |
| 2637 | var warned_excess = false; | |
| 2638 | var is_str_init = false; | |
| 2639 | var index_hint: ?usize = null; | |
| 2640 | while (true) : (count += 1) { | |
| 2641 | errdefer p.skipTo(.r_brace); | |
| 2642 | ||
| 2643 | const first_tok = p.tok_i; | |
| 2644 | var cur_ty = init_ty; | |
| 2645 | var cur_il = il; | |
| 2646 | var designation = false; | |
| 2647 | var cur_index_hint: ?usize = null; | |
| 2648 | while (true) { | |
| 2649 | if (p.eatToken(.l_bracket)) |l_bracket| { | |
| 2650 | if (!cur_ty.isArray()) { | |
| 2651 | try p.errStr(.invalid_array_designator, l_bracket, try p.typeStr(cur_ty)); | |
| 2652 | return error.ParsingFailed; | |
| 2653 | } | |
| 2654 | const expr_tok = p.tok_i; | |
| 2655 | const index_res = try p.constExpr(); | |
| 2656 | try p.expectClosing(l_bracket, .r_bracket); | |
| 2657 | ||
| 2658 | if (index_res.val.tag == .unavailable) { | |
| 2659 | try p.errTok(.expected_integer_constant_expr, expr_tok); | |
| 2660 | return error.ParsingFailed; | |
| 2661 | } else if (index_res.val.compare(.lt, index_res.val.zero(), index_res.ty, p.pp.comp)) { | |
| 2662 | try p.errExtra(.negative_array_designator, l_bracket + 1, .{ | |
| 2663 | .signed = index_res.val.signExtend(index_res.ty, p.pp.comp), | |
| 2664 | }); | |
| 2665 | return error.ParsingFailed; | |
| 2666 | } | |
| 2667 | ||
| 2668 | const max_len = cur_ty.arrayLen() orelse std.math.maxInt(usize); | |
| 2669 | if (index_res.val.data.int >= max_len) { | |
| 2670 | try p.errExtra(.oob_array_designator, l_bracket + 1, .{ .unsigned = index_res.val.data.int }); | |
| 2671 | return error.ParsingFailed; | |
| 2672 | } | |
| 2673 | const checked = index_res.val.getInt(u64); | |
| 2674 | cur_index_hint = cur_index_hint orelse checked; | |
| 2675 | ||
| 2676 | cur_il = try cur_il.find(p.pp.comp.gpa, checked); | |
| 2677 | cur_ty = cur_ty.elemType(); | |
| 2678 | designation = true; | |
| 2679 | } else if (p.eatToken(.period)) |period| { | |
| 2680 | const field_name = p.tokSlice(try p.expectIdentifier()); | |
| 2681 | cur_ty = cur_ty.canonicalize(.standard); | |
| 2682 | if (!cur_ty.isRecord()) { | |
| 2683 | try p.errStr(.invalid_field_designator, period, try p.typeStr(cur_ty)); | |
| 2684 | return error.ParsingFailed; | |
| 2685 | } else if (!cur_ty.hasField(field_name)) { | |
| 2686 | try p.errStr(.no_such_field_designator, period, field_name); | |
| 2687 | return error.ParsingFailed; | |
| 2688 | } | |
| 2689 | ||
| 2690 | // TODO check if union already has field set | |
| 2691 | outer: while (true) { | |
| 2692 | for (cur_ty.data.record.fields) |f, i| { | |
| 2693 | if (f.isAnonymousRecord()) { | |
| 2694 | // Recurse into anonymous field if it has a field by the name. | |
| 2695 | if (!f.ty.hasField(field_name)) continue; | |
| 2696 | cur_ty = f.ty.canonicalize(.standard); | |
| 2697 | cur_il = try il.find(p.pp.comp.gpa, i); | |
| 2698 | cur_index_hint = cur_index_hint orelse i; | |
| 2699 | continue :outer; | |
| 2700 | } | |
| 2701 | if (std.mem.eql(u8, field_name, f.name)) { | |
| 2702 | cur_il = try cur_il.find(p.pp.comp.gpa, i); | |
| 2703 | cur_ty = f.ty; | |
| 2704 | cur_index_hint = cur_index_hint orelse i; | |
| 2705 | break :outer; | |
| 2706 | } | |
| 2707 | } | |
| 2708 | unreachable; // we already checked that the starting type has this field | |
| 2709 | } | |
| 2710 | designation = true; | |
| 2711 | } else break; | |
| 2712 | } | |
| 2713 | if (designation) index_hint = null; | |
| 2714 | defer index_hint = cur_index_hint orelse null; | |
| 2715 | ||
| 2716 | if (designation) _ = try p.expectToken(.equal); | |
| 2717 | ||
| 2718 | var saw = false; | |
| 2719 | if (is_str_init and p.isStringInit(init_ty)) { | |
| 2720 | // discard further strings | |
| 2721 | var tmp_il = InitList{}; | |
| 2722 | defer tmp_il.deinit(p.pp.comp.gpa); | |
| 2723 | saw = try p.initializerItem(&tmp_il, .{ .specifier = .void }); | |
| 2724 | } else if (count == 0 and p.isStringInit(init_ty)) { | |
| 2725 | is_str_init = true; | |
| 2726 | saw = try p.initializerItem(il, init_ty); | |
| 2727 | } else if (is_scalar and count != 0) { | |
| 2728 | // discard further scalars | |
| 2729 | var tmp_il = InitList{}; | |
| 2730 | defer tmp_il.deinit(p.pp.comp.gpa); | |
| 2731 | saw = try p.initializerItem(&tmp_il, .{ .specifier = .void }); | |
| 2732 | } else if (p.tok_ids[p.tok_i] == .l_brace) { | |
| 2733 | if (designation) { | |
| 2734 | // designation overrides previous value, let existing mechanism handle it | |
| 2735 | saw = try p.initializerItem(cur_il, cur_ty); | |
| 2736 | } else if (try p.findAggregateInitializer(&cur_il, &cur_ty, &index_hint)) { | |
| 2737 | saw = try p.initializerItem(cur_il, cur_ty); | |
| 2738 | } else { | |
| 2739 | // discard further values | |
| 2740 | var tmp_il = InitList{}; | |
| 2741 | defer tmp_il.deinit(p.pp.comp.gpa); | |
| 2742 | saw = try p.initializerItem(&tmp_il, .{ .specifier = .void }); | |
| 2743 | if (!warned_excess) try p.errTok(if (init_ty.isArray()) .excess_array_init else .excess_struct_init, first_tok); | |
| 2744 | warned_excess = true; | |
| 2745 | } | |
| 2746 | } else if (index_hint != null and try p.findScalarInitializerAt(&cur_il, &cur_ty, &index_hint.?)) { | |
| 2747 | saw = try p.initializerItem(cur_il, cur_ty); | |
| 2748 | } else if (try p.findScalarInitializer(&cur_il, &cur_ty)) { | |
| 2749 | saw = try p.initializerItem(cur_il, cur_ty); | |
| 2750 | } else if (designation) { | |
| 2751 | // designation overrides previous value, let existing mechanism handle it | |
| 2752 | saw = try p.initializerItem(cur_il, cur_ty); | |
| 2753 | } else { | |
| 2754 | // discard further values | |
| 2755 | var tmp_il = InitList{}; | |
| 2756 | defer tmp_il.deinit(p.pp.comp.gpa); | |
| 2757 | saw = try p.initializerItem(&tmp_il, .{ .specifier = .void }); | |
| 2758 | if (!warned_excess and saw) try p.errTok(if (init_ty.isArray()) .excess_array_init else .excess_struct_init, first_tok); | |
| 2759 | warned_excess = true; | |
| 2760 | } | |
| 2761 | ||
| 2762 | if (!saw) { | |
| 2763 | if (designation) { | |
| 2764 | try p.err(.expected_expr); | |
| 2765 | return error.ParsingFailed; | |
| 2766 | } | |
| 2767 | break; | |
| 2768 | } else if (count == 1) { | |
| 2769 | if (is_str_init) try p.errTok(.excess_str_init, first_tok); | |
| 2770 | if (is_scalar) try p.errTok(.excess_scalar_init, first_tok); | |
| 2771 | } | |
| 2772 | ||
| 2773 | if (p.eatToken(.comma) == null) break; | |
| 2774 | } | |
| 2775 | try p.expectClosing(l_brace, .r_brace); | |
| 2776 | ||
| 2777 | if (is_scalar or is_str_init) return true; | |
| 2778 | if (il.tok != 0) { | |
| 2779 | try p.errTok(.initializer_overrides, l_brace); | |
| 2780 | try p.errTok(.previous_initializer, il.tok); | |
| 2781 | } | |
| 2782 | il.node = .none; | |
| 2783 | il.tok = l_brace; | |
| 2784 | return true; | |
| 2785 | } | |
| 2786 | ||
| 2787 | /// Returns true if the value is unused. | |
| 2788 | fn findScalarInitializerAt(p: *Parser, il: **InitList, ty: *Type, start_index: *usize) Error!bool { | |
| 2789 | if (ty.isArray()) { | |
| 2790 | start_index.* += 1; | |
| 2791 | ||
| 2792 | const arr_ty = ty.*; | |
| 2793 | const elem_count = arr_ty.arrayLen() orelse std.math.maxInt(usize); | |
| 2794 | if (elem_count == 0) { | |
| 2795 | if (p.tok_ids[p.tok_i] != .l_brace) { | |
| 2796 | try p.err(.empty_aggregate_init_braces); | |
| 2797 | return error.ParsingFailed; | |
| 2798 | } | |
| 2799 | return false; | |
| 2800 | } | |
| 2801 | const elem_ty = arr_ty.elemType(); | |
| 2802 | const arr_il = il.*; | |
| 2803 | if (start_index.* < elem_count) { | |
| 2804 | ty.* = elem_ty; | |
| 2805 | il.* = try arr_il.find(p.pp.comp.gpa, start_index.*); | |
| 2806 | _ = try p.findScalarInitializer(il, ty); | |
| 2807 | return true; | |
| 2808 | } | |
| 2809 | return false; | |
| 2810 | } else if (ty.get(.@"struct")) |struct_ty| { | |
| 2811 | start_index.* += 1; | |
| 2812 | ||
| 2813 | const field_count = struct_ty.data.record.fields.len; | |
| 2814 | if (field_count == 0) { | |
| 2815 | if (p.tok_ids[p.tok_i] != .l_brace) { | |
| 2816 | try p.err(.empty_aggregate_init_braces); | |
| 2817 | return error.ParsingFailed; | |
| 2818 | } | |
| 2819 | return false; | |
| 2820 | } | |
| 2821 | const struct_il = il.*; | |
| 2822 | if (start_index.* < field_count) { | |
| 2823 | const field = struct_ty.data.record.fields[start_index.*]; | |
| 2824 | ty.* = field.ty; | |
| 2825 | il.* = try struct_il.find(p.pp.comp.gpa, start_index.*); | |
| 2826 | _ = try p.findScalarInitializer(il, ty); | |
| 2827 | return true; | |
| 2828 | } | |
| 2829 | return false; | |
| 2830 | } else if (ty.get(.@"union")) |_| { | |
| 2831 | return false; | |
| 2832 | } | |
| 2833 | return il.*.node == .none; | |
| 2834 | } | |
| 2835 | ||
| 2836 | /// Returns true if the value is unused. | |
| 2837 | fn findScalarInitializer(p: *Parser, il: **InitList, ty: *Type) Error!bool { | |
| 2838 | if (ty.isArray()) { | |
| 2839 | var index = il.*.list.items.len; | |
| 2840 | if (index != 0) index = il.*.list.items[index - 1].index; | |
| 2841 | ||
| 2842 | const arr_ty = ty.*; | |
| 2843 | const elem_count = arr_ty.arrayLen() orelse std.math.maxInt(usize); | |
| 2844 | if (elem_count == 0) { | |
| 2845 | if (p.tok_ids[p.tok_i] != .l_brace) { | |
| 2846 | try p.err(.empty_aggregate_init_braces); | |
| 2847 | return error.ParsingFailed; | |
| 2848 | } | |
| 2849 | return false; | |
| 2850 | } | |
| 2851 | const elem_ty = arr_ty.elemType(); | |
| 2852 | const arr_il = il.*; | |
| 2853 | while (index < elem_count) : (index += 1) { | |
| 2854 | ty.* = elem_ty; | |
| 2855 | il.* = try arr_il.find(p.pp.comp.gpa, index); | |
| 2856 | if (try p.findScalarInitializer(il, ty)) return true; | |
| 2857 | } | |
| 2858 | return false; | |
| 2859 | } else if (ty.get(.@"struct")) |struct_ty| { | |
| 2860 | var index = il.*.list.items.len; | |
| 2861 | if (index != 0) index = il.*.list.items[index - 1].index + 1; | |
| 2862 | ||
| 2863 | const field_count = struct_ty.data.record.fields.len; | |
| 2864 | if (field_count == 0) { | |
| 2865 | if (p.tok_ids[p.tok_i] != .l_brace) { | |
| 2866 | try p.err(.empty_aggregate_init_braces); | |
| 2867 | return error.ParsingFailed; | |
| 2868 | } | |
| 2869 | return false; | |
| 2870 | } | |
| 2871 | const struct_il = il.*; | |
| 2872 | while (index < field_count) : (index += 1) { | |
| 2873 | const field = struct_ty.data.record.fields[index]; | |
| 2874 | ty.* = field.ty; | |
| 2875 | il.* = try struct_il.find(p.pp.comp.gpa, index); | |
| 2876 | if (try p.findScalarInitializer(il, ty)) return true; | |
| 2877 | } | |
| 2878 | return false; | |
| 2879 | } else if (ty.get(.@"union")) |union_ty| { | |
| 2880 | if (union_ty.data.record.fields.len == 0) { | |
| 2881 | if (p.tok_ids[p.tok_i] != .l_brace) { | |
| 2882 | try p.err(.empty_aggregate_init_braces); | |
| 2883 | return error.ParsingFailed; | |
| 2884 | } | |
| 2885 | return false; | |
| 2886 | } | |
| 2887 | ty.* = union_ty.data.record.fields[0].ty; | |
| 2888 | il.* = try il.*.find(p.pp.comp.gpa, 0); | |
| 2889 | if (try p.findScalarInitializer(il, ty)) return true; | |
| 2890 | return false; | |
| 2891 | } | |
| 2892 | return il.*.node == .none; | |
| 2893 | } | |
| 2894 | ||
| 2895 | fn findAggregateInitializer(p: *Parser, il: **InitList, ty: *Type, start_index: *?usize) Error!bool { | |
| 2896 | if (ty.isArray()) { | |
| 2897 | var index = il.*.list.items.len; | |
| 2898 | if (index != 0) index = il.*.list.items[index - 1].index + 1; | |
| 2899 | if (start_index.*) |*some| { | |
| 2900 | some.* += 1; | |
| 2901 | index = some.*; | |
| 2902 | } | |
| 2903 | ||
| 2904 | const arr_ty = ty.*; | |
| 2905 | const elem_count = arr_ty.arrayLen() orelse std.math.maxInt(usize); | |
| 2906 | const elem_ty = arr_ty.elemType(); | |
| 2907 | if (index < elem_count) { | |
| 2908 | ty.* = elem_ty; | |
| 2909 | il.* = try il.*.find(p.pp.comp.gpa, index); | |
| 2910 | return true; | |
| 2911 | } | |
| 2912 | return false; | |
| 2913 | } else if (ty.get(.@"struct")) |struct_ty| { | |
| 2914 | var index = il.*.list.items.len; | |
| 2915 | if (index != 0) index = il.*.list.items[index - 1].index + 1; | |
| 2916 | if (start_index.*) |*some| { | |
| 2917 | some.* += 1; | |
| 2918 | index = some.*; | |
| 2919 | } | |
| 2920 | ||
| 2921 | const field_count = struct_ty.data.record.fields.len; | |
| 2922 | if (index < field_count) { | |
| 2923 | ty.* = struct_ty.data.record.fields[index].ty; | |
| 2924 | il.* = try il.*.find(p.pp.comp.gpa, index); | |
| 2925 | return true; | |
| 2926 | } | |
| 2927 | return false; | |
| 2928 | } else if (ty.get(.@"union")) |union_ty| { | |
| 2929 | if (start_index.*) |_| return false; // overrides | |
| 2930 | ||
| 2931 | ty.* = union_ty.data.record.fields[0].ty; | |
| 2932 | il.* = try il.*.find(p.pp.comp.gpa, 0); | |
| 2933 | return true; | |
| 2934 | } else { | |
| 2935 | try p.err(.too_many_scalar_init_braces); | |
| 2936 | return il.*.node == .none; | |
| 2937 | } | |
| 2938 | } | |
| 2939 | ||
| 2940 | fn coerceArrayInit(p: *Parser, item: *Result, tok: TokenIndex, target: Type) !bool { | |
| 2941 | if (!target.isArray()) return false; | |
| 2942 | ||
| 2943 | const is_str_lit = p.nodeIs(item.node, .string_literal_expr); | |
| 2944 | if (!is_str_lit and !p.nodeIs(item.node, .compound_literal_expr)) { | |
| 2945 | try p.errTok(.array_init_str, tok); | |
| 2946 | return true; // do not do further coercion | |
| 2947 | } | |
| 2948 | ||
| 2949 | const target_spec = target.elemType().canonicalize(.standard).specifier; | |
| 2950 | const item_spec = item.ty.elemType().canonicalize(.standard).specifier; | |
| 2951 | ||
| 2952 | const compatible = target.elemType().eql(item.ty.elemType(), p.pp.comp, false) or | |
| 2953 | (is_str_lit and item_spec == .char and (target_spec == .uchar or target_spec == .schar)); | |
| 2954 | if (!compatible) { | |
| 2955 | const e_msg = " with array of type "; | |
| 2956 | try p.errStr(.incompatible_array_init, tok, try p.typePairStrExtra(target, e_msg, item.ty)); | |
| 2957 | return true; // do not do further coercion | |
| 2958 | } | |
| 2959 | ||
| 2960 | if (target.get(.array)) |arr_ty| { | |
| 2961 | assert(item.ty.specifier == .array); | |
| 2962 | var len = item.ty.arrayLen().?; | |
| 2963 | const array_len = arr_ty.arrayLen().?; | |
| 2964 | if (is_str_lit) { | |
| 2965 | // the null byte of a string can be dropped | |
| 2966 | if (len - 1 > array_len) | |
| 2967 | try p.errTok(.str_init_too_long, tok); | |
| 2968 | } else if (len > array_len) { | |
| 2969 | try p.errStr( | |
| 2970 | .arr_init_too_long, | |
| 2971 | tok, | |
| 2972 | try p.typePairStrExtra(target, " with array of type ", item.ty), | |
| 2973 | ); | |
| 2974 | } | |
| 2975 | } | |
| 2976 | return true; | |
| 2977 | } | |
| 2978 | ||
| 2979 | fn coerceInit(p: *Parser, item: *Result, tok: TokenIndex, target: Type) !void { | |
| 2980 | if (target.is(.void)) return; // Do not do type coercion on excess items | |
| 2981 | ||
| 2982 | // item does not need to be qualified | |
| 2983 | var unqual_ty = target.canonicalize(.standard); | |
| 2984 | unqual_ty.qual = .{}; | |
| 2985 | const e_msg = " from incompatible type "; | |
| 2986 | try item.lvalConversion(p); | |
| 2987 | if (unqual_ty.is(.bool)) { | |
| 2988 | // this is ridiculous but it's what clang does | |
| 2989 | if (item.ty.isInt() or item.ty.isFloat() or item.ty.isPtr()) { | |
| 2990 | try item.boolCast(p, unqual_ty); | |
| 2991 | } else { | |
| 2992 | try p.errStr(.incompatible_init, tok, try p.typePairStrExtra(target, e_msg, item.ty)); | |
| 2993 | } | |
| 2994 | } else if (unqual_ty.isInt()) { | |
| 2995 | if (item.ty.isInt() or item.ty.isFloat()) { | |
| 2996 | try item.intCast(p, unqual_ty); | |
| 2997 | } else if (item.ty.isPtr()) { | |
| 2998 | try p.errStr(.implicit_ptr_to_int, tok, try p.typePairStrExtra(item.ty, " to ", target)); | |
| 2999 | try item.intCast(p, unqual_ty); | |
| 3000 | } else { | |
| 3001 | try p.errStr(.incompatible_init, tok, try p.typePairStrExtra(target, e_msg, item.ty)); | |
| 3002 | } | |
| 3003 | } else if (unqual_ty.isFloat()) { | |
| 3004 | if (item.ty.isInt() or item.ty.isFloat()) { | |
| 3005 | try item.floatCast(p, unqual_ty); | |
| 3006 | } else { | |
| 3007 | try p.errStr(.incompatible_init, tok, try p.typePairStrExtra(target, e_msg, item.ty)); | |
| 3008 | } | |
| 3009 | } else if (unqual_ty.isPtr()) { | |
| 3010 | if (item.val.isZero()) { | |
| 3011 | try item.nullCast(p, target); | |
| 3012 | } else if (item.ty.isInt()) { | |
| 3013 | try p.errStr(.implicit_int_to_ptr, tok, try p.typePairStrExtra(item.ty, " to ", target)); | |
| 3014 | try item.ptrCast(p, unqual_ty); | |
| 3015 | } else if (item.ty.isPtr()) { | |
| 3016 | if (!item.ty.isVoidStar() and !unqual_ty.isVoidStar() and !unqual_ty.eql(item.ty, p.pp.comp, false)) { | |
| 3017 | try p.errStr(.incompatible_ptr_init, tok, try p.typePairStrExtra(target, e_msg, item.ty)); | |
| 3018 | try item.ptrCast(p, unqual_ty); | |
| 3019 | } else if (!unqual_ty.eql(item.ty, p.pp.comp, true)) { | |
| 3020 | if (!unqual_ty.elemType().qual.hasQuals(item.ty.elemType().qual)) { | |
| 3021 | try p.errStr(.ptr_init_discards_quals, tok, try p.typePairStrExtra(target, e_msg, item.ty)); | |
| 3022 | } | |
| 3023 | try item.ptrCast(p, unqual_ty); | |
| 3024 | } | |
| 3025 | } else { | |
| 3026 | try p.errStr(.incompatible_init, tok, try p.typePairStrExtra(target, e_msg, item.ty)); | |
| 3027 | } | |
| 3028 | } else if (unqual_ty.isRecord()) { | |
| 3029 | if (!unqual_ty.eql(item.ty, p.pp.comp, false)) | |
| 3030 | try p.errStr(.incompatible_init, tok, try p.typePairStrExtra(target, e_msg, item.ty)); | |
| 3031 | } else if (unqual_ty.isArray() or unqual_ty.isFunc()) { | |
| 3032 | // we have already issued an error for this | |
| 3033 | } else { | |
| 3034 | try p.errStr(.incompatible_init, tok, try p.typePairStrExtra(target, e_msg, item.ty)); | |
| 3035 | } | |
| 3036 | } | |
| 3037 | ||
| 3038 | fn isStringInit(p: *Parser, ty: Type) bool { | |
| 3039 | if (!ty.isArray() or !ty.elemType().isInt()) return false; | |
| 3040 | var i = p.tok_i; | |
| 3041 | while (true) : (i += 1) { | |
| 3042 | switch (p.tok_ids[i]) { | |
| 3043 | .l_paren => {}, | |
| 3044 | .string_literal, | |
| 3045 | .string_literal_utf_16, | |
| 3046 | .string_literal_utf_8, | |
| 3047 | .string_literal_utf_32, | |
| 3048 | .string_literal_wide, | |
| 3049 | => return true, | |
| 3050 | else => return false, | |
| 3051 | } | |
| 3052 | } | |
| 3053 | } | |
| 3054 | ||
| 3055 | /// Convert InitList into an AST | |
| 3056 | fn convertInitList(p: *Parser, il: InitList, init_ty: Type) Error!NodeIndex { | |
| 3057 | if (init_ty.isInt() or init_ty.isFloat() or init_ty.isPtr()) { | |
| 3058 | if (il.node == .none) { | |
| 3059 | return p.addNode(.{ .tag = .default_init_expr, .ty = init_ty, .data = undefined }); | |
| 3060 | } | |
| 3061 | return il.node; | |
| 3062 | } else if (init_ty.is(.variable_len_array)) { | |
| 3063 | return error.ParsingFailed; // vla invalid, reported earlier | |
| 3064 | } else if (init_ty.isArray()) { | |
| 3065 | if (il.node != .none) { | |
| 3066 | return il.node; | |
| 3067 | } | |
| 3068 | const list_buf_top = p.list_buf.items.len; | |
| 3069 | defer p.list_buf.items.len = list_buf_top; | |
| 3070 | ||
| 3071 | const elem_ty = init_ty.elemType(); | |
| 3072 | ||
| 3073 | const max_items = init_ty.arrayLen() orelse std.math.maxInt(usize); | |
| 3074 | var start: u64 = 0; | |
| 3075 | for (il.list.items) |*init| { | |
| 3076 | if (init.index > start) { | |
| 3077 | const elem = try p.addNode(.{ | |
| 3078 | .tag = .array_filler_expr, | |
| 3079 | .ty = elem_ty, | |
| 3080 | .data = .{ .int = init.index - start }, | |
| 3081 | }); | |
| 3082 | try p.list_buf.append(elem); | |
| 3083 | } | |
| 3084 | start = init.index + 1; | |
| 3085 | ||
| 3086 | const elem = try p.convertInitList(init.list, elem_ty); | |
| 3087 | try p.list_buf.append(elem); | |
| 3088 | } | |
| 3089 | ||
| 3090 | var arr_init_node: Tree.Node = .{ | |
| 3091 | .tag = .array_init_expr_two, | |
| 3092 | .ty = init_ty, | |
| 3093 | .data = .{ .bin = .{ .lhs = .none, .rhs = .none } }, | |
| 3094 | }; | |
| 3095 | ||
| 3096 | if (init_ty.specifier == .incomplete_array) { | |
| 3097 | arr_init_node.ty.specifier = .array; | |
| 3098 | arr_init_node.ty.data.array.len = start; | |
| 3099 | } else if (init_ty.is(.incomplete_array)) { | |
| 3100 | const arr_ty = try p.arena.create(Type.Array); | |
| 3101 | arr_ty.* = .{ .elem = init_ty.elemType(), .len = start }; | |
| 3102 | arr_init_node.ty = .{ | |
| 3103 | .specifier = .array, | |
| 3104 | .data = .{ .array = arr_ty }, | |
| 3105 | }; | |
| 3106 | const attrs = init_ty.getAttributes(); | |
| 3107 | arr_init_node.ty = try arr_init_node.ty.withAttributes(p.arena, attrs); | |
| 3108 | } else if (start < max_items) { | |
| 3109 | const elem = try p.addNode(.{ | |
| 3110 | .tag = .array_filler_expr, | |
| 3111 | .ty = elem_ty, | |
| 3112 | .data = .{ .int = max_items - start }, | |
| 3113 | }); | |
| 3114 | try p.list_buf.append(elem); | |
| 3115 | } | |
| 3116 | ||
| 3117 | const items = p.list_buf.items[list_buf_top..]; | |
| 3118 | switch (items.len) { | |
| 3119 | 0 => {}, | |
| 3120 | 1 => arr_init_node.data.bin.lhs = items[0], | |
| 3121 | 2 => arr_init_node.data.bin = .{ .lhs = items[0], .rhs = items[1] }, | |
| 3122 | else => { | |
| 3123 | arr_init_node.tag = .array_init_expr; | |
| 3124 | arr_init_node.data = .{ .range = try p.addList(items) }; | |
| 3125 | }, | |
| 3126 | } | |
| 3127 | return try p.addNode(arr_init_node); | |
| 3128 | } else if (init_ty.get(.@"struct")) |struct_ty| { | |
| 3129 | assert(!struct_ty.hasIncompleteSize()); | |
| 3130 | ||
| 3131 | const list_buf_top = p.list_buf.items.len; | |
| 3132 | defer p.list_buf.items.len = list_buf_top; | |
| 3133 | ||
| 3134 | var init_index: usize = 0; | |
| 3135 | for (struct_ty.data.record.fields) |f, i| { | |
| 3136 | if (init_index < il.list.items.len and il.list.items[init_index].index == i) { | |
| 3137 | const item = try p.convertInitList(il.list.items[init_index].list, f.ty); | |
| 3138 | try p.list_buf.append(item); | |
| 3139 | init_index += 1; | |
| 3140 | } else { | |
| 3141 | const item = try p.addNode(.{ .tag = .default_init_expr, .ty = f.ty, .data = undefined }); | |
| 3142 | try p.list_buf.append(item); | |
| 3143 | } | |
| 3144 | } | |
| 3145 | ||
| 3146 | var struct_init_node: Tree.Node = .{ | |
| 3147 | .tag = .struct_init_expr_two, | |
| 3148 | .ty = init_ty, | |
| 3149 | .data = .{ .bin = .{ .lhs = .none, .rhs = .none } }, | |
| 3150 | }; | |
| 3151 | const items = p.list_buf.items[list_buf_top..]; | |
| 3152 | switch (items.len) { | |
| 3153 | 0 => {}, | |
| 3154 | 1 => struct_init_node.data.bin.lhs = items[0], | |
| 3155 | 2 => struct_init_node.data.bin = .{ .lhs = items[0], .rhs = items[1] }, | |
| 3156 | else => { | |
| 3157 | struct_init_node.tag = .struct_init_expr; | |
| 3158 | struct_init_node.data = .{ .range = try p.addList(items) }; | |
| 3159 | }, | |
| 3160 | } | |
| 3161 | return try p.addNode(struct_init_node); | |
| 3162 | } else if (init_ty.get(.@"union")) |union_ty| { | |
| 3163 | var union_init_node: Tree.Node = .{ | |
| 3164 | .tag = .union_init_expr, | |
| 3165 | .ty = init_ty, | |
| 3166 | .data = .{ .union_init = .{ .field_index = 0, .node = .none } }, | |
| 3167 | }; | |
| 3168 | if (union_ty.data.record.fields.len == 0) { | |
| 3169 | // do nothing for empty unions | |
| 3170 | } else if (il.list.items.len == 0) { | |
| 3171 | union_init_node.data.union_init.node = try p.addNode(.{ | |
| 3172 | .tag = .default_init_expr, | |
| 3173 | .ty = init_ty, | |
| 3174 | .data = undefined, | |
| 3175 | }); | |
| 3176 | } else { | |
| 3177 | const init = il.list.items[0]; | |
| 3178 | const field_ty = union_ty.data.record.fields[init.index].ty; | |
| 3179 | union_init_node.data.union_init = .{ | |
| 3180 | .field_index = @truncate(u32, init.index), | |
| 3181 | .node = try p.convertInitList(init.list, field_ty), | |
| 3182 | }; | |
| 3183 | } | |
| 3184 | return try p.addNode(union_init_node); | |
| 3185 | } else { | |
| 3186 | return error.ParsingFailed; // initializer target is invalid, reported earlier | |
| 3187 | } | |
| 3188 | } | |
| 3189 | ||
| 3190 | /// assembly : keyword_asm asmQual* '(' asmStr ')' | |
| 3191 | fn assembly(p: *Parser, kind: enum { global, decl_label, stmt }) Error!?NodeIndex { | |
| 3192 | const asm_tok = p.tok_i; | |
| 3193 | switch (p.tok_ids[p.tok_i]) { | |
| 3194 | .keyword_asm, .keyword_asm1, .keyword_asm2 => p.tok_i += 1, | |
| 3195 | else => return null, | |
| 3196 | } | |
| 3197 | ||
| 3198 | var @"volatile" = false; | |
| 3199 | var @"inline" = false; | |
| 3200 | var goto = false; | |
| 3201 | while (true) : (p.tok_i += 1) switch (p.tok_ids[p.tok_i]) { | |
| 3202 | .keyword_volatile, .keyword_volatile1, .keyword_volatile2 => { | |
| 3203 | if (kind != .stmt) try p.errStr(.meaningless_asm_qual, p.tok_i, "volatile"); | |
| 3204 | if (@"volatile") try p.errStr(.duplicate_asm_qual, p.tok_i, "volatile"); | |
| 3205 | @"volatile" = true; | |
| 3206 | }, | |
| 3207 | .keyword_inline, .keyword_inline1, .keyword_inline2 => { | |
| 3208 | if (kind != .stmt) try p.errStr(.meaningless_asm_qual, p.tok_i, "inline"); | |
| 3209 | if (@"inline") try p.errStr(.duplicate_asm_qual, p.tok_i, "inline"); | |
| 3210 | @"inline" = true; | |
| 3211 | }, | |
| 3212 | .keyword_goto => { | |
| 3213 | if (kind != .stmt) try p.errStr(.meaningless_asm_qual, p.tok_i, "goto"); | |
| 3214 | if (goto) try p.errStr(.duplicate_asm_qual, p.tok_i, "goto"); | |
| 3215 | goto = true; | |
| 3216 | }, | |
| 3217 | else => break, | |
| 3218 | }; | |
| 3219 | ||
| 3220 | const l_paren = try p.expectToken(.l_paren); | |
| 3221 | switch (kind) { | |
| 3222 | .decl_label => { | |
| 3223 | const str = (try p.asmStr()).val.data.bytes; | |
| 3224 | const attr = Attribute{ .tag = .asm_label, .args = .{ .asm_label = .{ .name = str[0 .. str.len - 1] } } }; | |
| 3225 | try p.attr_buf.append(p.pp.comp.gpa, .{ .attr = attr, .tok = asm_tok }); | |
| 3226 | }, | |
| 3227 | .global => _ = try p.asmStr(), | |
| 3228 | .stmt => return p.todo("assembly statements"), | |
| 3229 | } | |
| 3230 | try p.expectClosing(l_paren, .r_paren); | |
| 3231 | ||
| 3232 | if (kind != .decl_label) _ = try p.expectToken(.semicolon); | |
| 3233 | return .none; | |
| 3234 | } | |
| 3235 | ||
| 3236 | /// Same as stringLiteral but errors on unicode and wide string literals | |
| 3237 | fn asmStr(p: *Parser) Error!Result { | |
| 3238 | var i = p.tok_i; | |
| 3239 | while (true) : (i += 1) switch (p.tok_ids[i]) { | |
| 3240 | .string_literal => {}, | |
| 3241 | .string_literal_utf_16, .string_literal_utf_8, .string_literal_utf_32 => { | |
| 3242 | try p.errStr(.invalid_asm_str, p.tok_i, "unicode"); | |
| 3243 | return error.ParsingFailed; | |
| 3244 | }, | |
| 3245 | .string_literal_wide => { | |
| 3246 | try p.errStr(.invalid_asm_str, p.tok_i, "wide"); | |
| 3247 | return error.ParsingFailed; | |
| 3248 | }, | |
| 3249 | else => break, | |
| 3250 | }; | |
| 3251 | return try p.stringLiteral(); | |
| 3252 | } | |
| 3253 | ||
| 3254 | // ====== statements ====== | |
| 3255 | ||
| 3256 | /// stmt | |
| 3257 | /// : labeledStmt | |
| 3258 | /// | compoundStmt | |
| 3259 | /// | keyword_if '(' expr ')' stmt (keyword_else stmt)? | |
| 3260 | /// | keyword_switch '(' expr ')' stmt | |
| 3261 | /// | keyword_while '(' expr ')' stmt | |
| 3262 | /// | keyword_do stmt while '(' expr ')' ';' | |
| 3263 | /// | keyword_for '(' (decl | expr? ';') expr? ';' expr? ')' stmt | |
| 3264 | /// | keyword_goto (IDENTIFIER | ('*' expr)) ';' | |
| 3265 | /// | keyword_continue ';' | |
| 3266 | /// | keyword_break ';' | |
| 3267 | /// | keyword_return expr? ';' | |
| 3268 | /// | assembly ';' | |
| 3269 | /// | expr? ';' | |
| 3270 | fn stmt(p: *Parser) Error!NodeIndex { | |
| 3271 | if (try p.labeledStmt()) |some| return some; | |
| 3272 | if (try p.compoundStmt(false, null)) |some| return some; | |
| 3273 | if (p.eatToken(.keyword_if)) |_| { | |
| 3274 | const start_scopes_len = p.scopes.items.len; | |
| 3275 | defer p.scopes.items.len = start_scopes_len; | |
| 3276 | ||
| 3277 | const l_paren = try p.expectToken(.l_paren); | |
| 3278 | var cond = try p.expr(); | |
| 3279 | try cond.expect(p); | |
| 3280 | try cond.lvalConversion(p); | |
| 3281 | if (cond.ty.isInt()) | |
| 3282 | try cond.intCast(p, cond.ty.integerPromotion(p.pp.comp)) | |
| 3283 | else if (!cond.ty.isFloat() and !cond.ty.isPtr()) | |
| 3284 | try p.errStr(.statement_scalar, l_paren + 1, try p.typeStr(cond.ty)); | |
| 3285 | try cond.saveValue(p); | |
| 3286 | try p.expectClosing(l_paren, .r_paren); | |
| 3287 | ||
| 3288 | const then = try p.stmt(); | |
| 3289 | const @"else" = if (p.eatToken(.keyword_else)) |_| try p.stmt() else .none; | |
| 3290 | ||
| 3291 | if (then != .none and @"else" != .none) | |
| 3292 | return try p.addNode(.{ | |
| 3293 | .tag = .if_then_else_stmt, | |
| 3294 | .data = .{ .if3 = .{ .cond = cond.node, .body = (try p.addList(&.{ then, @"else" })).start } }, | |
| 3295 | }) | |
| 3296 | else if (then == .none and @"else" != .none) | |
| 3297 | return try p.addNode(.{ | |
| 3298 | .tag = .if_else_stmt, | |
| 3299 | .data = .{ .bin = .{ .lhs = cond.node, .rhs = @"else" } }, | |
| 3300 | }) | |
| 3301 | else | |
| 3302 | return try p.addNode(.{ | |
| 3303 | .tag = .if_then_stmt, | |
| 3304 | .data = .{ .bin = .{ .lhs = cond.node, .rhs = then } }, | |
| 3305 | }); | |
| 3306 | } | |
| 3307 | if (p.eatToken(.keyword_switch)) |_| { | |
| 3308 | const start_scopes_len = p.scopes.items.len; | |
| 3309 | defer p.scopes.items.len = start_scopes_len; | |
| 3310 | ||
| 3311 | const l_paren = try p.expectToken(.l_paren); | |
| 3312 | var cond = try p.expr(); | |
| 3313 | try cond.expect(p); | |
| 3314 | try cond.lvalConversion(p); | |
| 3315 | if (cond.ty.isInt()) | |
| 3316 | try cond.intCast(p, cond.ty.integerPromotion(p.pp.comp)) | |
| 3317 | else | |
| 3318 | try p.errStr(.statement_int, l_paren + 1, try p.typeStr(cond.ty)); | |
| 3319 | try cond.saveValue(p); | |
| 3320 | try p.expectClosing(l_paren, .r_paren); | |
| 3321 | ||
| 3322 | var switch_scope = Scope.Switch{ | |
| 3323 | .cases = Scope.Switch.CaseMap.initContext( | |
| 3324 | p.pp.comp.gpa, | |
| 3325 | .{ .ty = cond.ty, .comp = p.pp.comp }, | |
| 3326 | ), | |
| 3327 | }; | |
| 3328 | defer switch_scope.cases.deinit(); | |
| 3329 | try p.scopes.append(.{ .@"switch" = &switch_scope }); | |
| 3330 | const body = try p.stmt(); | |
| 3331 | ||
| 3332 | return try p.addNode(.{ | |
| 3333 | .tag = .switch_stmt, | |
| 3334 | .data = .{ .bin = .{ .lhs = cond.node, .rhs = body } }, | |
| 3335 | }); | |
| 3336 | } | |
| 3337 | if (p.eatToken(.keyword_while)) |_| { | |
| 3338 | const start_scopes_len = p.scopes.items.len; | |
| 3339 | defer p.scopes.items.len = start_scopes_len; | |
| 3340 | ||
| 3341 | const l_paren = try p.expectToken(.l_paren); | |
| 3342 | var cond = try p.expr(); | |
| 3343 | try cond.expect(p); | |
| 3344 | try cond.lvalConversion(p); | |
| 3345 | if (cond.ty.isInt()) | |
| 3346 | try cond.intCast(p, cond.ty.integerPromotion(p.pp.comp)) | |
| 3347 | else if (!cond.ty.isFloat() and !cond.ty.isPtr()) | |
| 3348 | try p.errStr(.statement_scalar, l_paren + 1, try p.typeStr(cond.ty)); | |
| 3349 | try cond.saveValue(p); | |
| 3350 | try p.expectClosing(l_paren, .r_paren); | |
| 3351 | ||
| 3352 | try p.scopes.append(.loop); | |
| 3353 | const body = try p.stmt(); | |
| 3354 | ||
| 3355 | return try p.addNode(.{ | |
| 3356 | .tag = .while_stmt, | |
| 3357 | .data = .{ .bin = .{ .rhs = cond.node, .lhs = body } }, | |
| 3358 | }); | |
| 3359 | } | |
| 3360 | if (p.eatToken(.keyword_do)) |_| { | |
| 3361 | const start_scopes_len = p.scopes.items.len; | |
| 3362 | defer p.scopes.items.len = start_scopes_len; | |
| 3363 | ||
| 3364 | try p.scopes.append(.loop); | |
| 3365 | const body = try p.stmt(); | |
| 3366 | p.scopes.items.len = start_scopes_len; | |
| 3367 | ||
| 3368 | _ = try p.expectToken(.keyword_while); | |
| 3369 | const l_paren = try p.expectToken(.l_paren); | |
| 3370 | var cond = try p.expr(); | |
| 3371 | try cond.expect(p); | |
| 3372 | try cond.lvalConversion(p); | |
| 3373 | if (cond.ty.isInt()) | |
| 3374 | try cond.intCast(p, cond.ty.integerPromotion(p.pp.comp)) | |
| 3375 | else if (!cond.ty.isFloat() and !cond.ty.isPtr()) | |
| 3376 | try p.errStr(.statement_scalar, l_paren + 1, try p.typeStr(cond.ty)); | |
| 3377 | try cond.saveValue(p); | |
| 3378 | try p.expectClosing(l_paren, .r_paren); | |
| 3379 | ||
| 3380 | _ = try p.expectToken(.semicolon); | |
| 3381 | return try p.addNode(.{ | |
| 3382 | .tag = .do_while_stmt, | |
| 3383 | .data = .{ .bin = .{ .rhs = cond.node, .lhs = body } }, | |
| 3384 | }); | |
| 3385 | } | |
| 3386 | if (p.eatToken(.keyword_for)) |_| { | |
| 3387 | const start_scopes_len = p.scopes.items.len; | |
| 3388 | defer p.scopes.items.len = start_scopes_len; | |
| 3389 | const decl_buf_top = p.decl_buf.items.len; | |
| 3390 | defer p.decl_buf.items.len = decl_buf_top; | |
| 3391 | ||
| 3392 | const l_paren = try p.expectToken(.l_paren); | |
| 3393 | const got_decl = try p.decl(); | |
| 3394 | ||
| 3395 | // for (init | |
| 3396 | const init_start = p.tok_i; | |
| 3397 | var err_start = p.pp.comp.diag.list.items.len; | |
| 3398 | var init = if (!got_decl) try p.expr() else Result{}; | |
| 3399 | try init.saveValue(p); | |
| 3400 | try init.maybeWarnUnused(p, init_start, err_start); | |
| 3401 | if (!got_decl) _ = try p.expectToken(.semicolon); | |
| 3402 | ||
| 3403 | // for (init; cond | |
| 3404 | var cond = try p.expr(); | |
| 3405 | if (cond.node != .none) { | |
| 3406 | try cond.lvalConversion(p); | |
| 3407 | if (cond.ty.isInt()) | |
| 3408 | try cond.intCast(p, cond.ty.integerPromotion(p.pp.comp)) | |
| 3409 | else if (!cond.ty.isFloat() and !cond.ty.isPtr()) | |
| 3410 | try p.errStr(.statement_scalar, l_paren + 1, try p.typeStr(cond.ty)); | |
| 3411 | } | |
| 3412 | try cond.saveValue(p); | |
| 3413 | _ = try p.expectToken(.semicolon); | |
| 3414 | ||
| 3415 | // for (init; cond; incr | |
| 3416 | const incr_start = p.tok_i; | |
| 3417 | err_start = p.pp.comp.diag.list.items.len; | |
| 3418 | var incr = try p.expr(); | |
| 3419 | try incr.maybeWarnUnused(p, incr_start, err_start); | |
| 3420 | try incr.saveValue(p); | |
| 3421 | try p.expectClosing(l_paren, .r_paren); | |
| 3422 | ||
| 3423 | try p.scopes.append(.loop); | |
| 3424 | const body = try p.stmt(); | |
| 3425 | ||
| 3426 | if (got_decl) { | |
| 3427 | const start = (try p.addList(p.decl_buf.items[decl_buf_top..])).start; | |
| 3428 | const end = (try p.addList(&.{ cond.node, incr.node, body })).end; | |
| 3429 | ||
| 3430 | return try p.addNode(.{ | |
| 3431 | .tag = .for_decl_stmt, | |
| 3432 | .data = .{ .range = .{ .start = start, .end = end } }, | |
| 3433 | }); | |
| 3434 | } else if (init.node == .none and cond.node == .none and incr.node == .none) { | |
| 3435 | return try p.addNode(.{ | |
| 3436 | .tag = .forever_stmt, | |
| 3437 | .data = .{ .un = body }, | |
| 3438 | }); | |
| 3439 | } else return try p.addNode(.{ .tag = .for_stmt, .data = .{ .if3 = .{ | |
| 3440 | .cond = body, | |
| 3441 | .body = (try p.addList(&.{ init.node, cond.node, incr.node })).start, | |
| 3442 | } } }); | |
| 3443 | } | |
| 3444 | if (p.eatToken(.keyword_goto)) |goto_tok| { | |
| 3445 | if (p.eatToken(.asterisk)) |_| { | |
| 3446 | const expr_tok = p.tok_i; | |
| 3447 | var e = try p.expr(); | |
| 3448 | try e.expect(p); | |
| 3449 | try e.lvalConversion(p); | |
| 3450 | p.computed_goto_tok = p.computed_goto_tok orelse goto_tok; | |
| 3451 | if (!e.ty.isPtr()) { | |
| 3452 | if (!e.ty.isInt()) { | |
| 3453 | try p.errStr(.incompatible_param, expr_tok, try p.typeStr(e.ty)); | |
| 3454 | return error.ParsingFailed; | |
| 3455 | } | |
| 3456 | const elem_ty = try p.arena.create(Type); | |
| 3457 | elem_ty.* = .{ .specifier = .void, .qual = .{ .@"const" = true } }; | |
| 3458 | const result_ty = Type{ | |
| 3459 | .specifier = .pointer, | |
| 3460 | .data = .{ .sub_type = elem_ty }, | |
| 3461 | }; | |
| 3462 | if (e.val.isZero()) { | |
| 3463 | try e.nullCast(p, result_ty); | |
| 3464 | } else { | |
| 3465 | try p.errStr(.implicit_int_to_ptr, expr_tok, try p.typePairStrExtra(e.ty, " to ", result_ty)); | |
| 3466 | try e.ptrCast(p, result_ty); | |
| 3467 | } | |
| 3468 | } | |
| 3469 | ||
| 3470 | try e.un(p, .computed_goto_stmt); | |
| 3471 | _ = try p.expectToken(.semicolon); | |
| 3472 | return e.node; | |
| 3473 | } | |
| 3474 | const name_tok = try p.expectIdentifier(); | |
| 3475 | const str = p.tokSlice(name_tok); | |
| 3476 | if (p.findLabel(str) == null) { | |
| 3477 | try p.labels.append(.{ .unresolved_goto = name_tok }); | |
| 3478 | } | |
| 3479 | _ = try p.expectToken(.semicolon); | |
| 3480 | return try p.addNode(.{ | |
| 3481 | .tag = .goto_stmt, | |
| 3482 | .data = .{ .decl_ref = name_tok }, | |
| 3483 | }); | |
| 3484 | } | |
| 3485 | if (p.eatToken(.keyword_continue)) |cont| { | |
| 3486 | if (!p.inLoop()) try p.errTok(.continue_not_in_loop, cont); | |
| 3487 | _ = try p.expectToken(.semicolon); | |
| 3488 | return try p.addNode(.{ .tag = .continue_stmt, .data = undefined }); | |
| 3489 | } | |
| 3490 | if (p.eatToken(.keyword_break)) |br| { | |
| 3491 | if (!p.inLoopOrSwitch()) try p.errTok(.break_not_in_loop_or_switch, br); | |
| 3492 | _ = try p.expectToken(.semicolon); | |
| 3493 | return try p.addNode(.{ .tag = .break_stmt, .data = undefined }); | |
| 3494 | } | |
| 3495 | if (try p.returnStmt()) |some| return some; | |
| 3496 | if (try p.assembly(.stmt)) |some| return some; | |
| 3497 | ||
| 3498 | const expr_start = p.tok_i; | |
| 3499 | const err_start = p.pp.comp.diag.list.items.len; | |
| 3500 | ||
| 3501 | const e = try p.expr(); | |
| 3502 | if (e.node != .none) { | |
| 3503 | _ = try p.expectToken(.semicolon); | |
| 3504 | try e.maybeWarnUnused(p, expr_start, err_start); | |
| 3505 | return e.node; | |
| 3506 | } | |
| 3507 | ||
| 3508 | const attr_buf_top = p.attr_buf.len; | |
| 3509 | defer p.attr_buf.len = attr_buf_top; | |
| 3510 | try p.attributeSpecifier(); // statement | |
| 3511 | ||
| 3512 | if (p.eatToken(.semicolon)) |_| { | |
| 3513 | var null_node: Tree.Node = .{ .tag = .null_stmt, .data = undefined }; | |
| 3514 | null_node.ty = try p.withAttributes(null_node.ty, attr_buf_top); | |
| 3515 | if (null_node.ty.getAttribute(.fallthrough) != null) { | |
| 3516 | if (p.tok_ids[p.tok_i] != .keyword_case and p.tok_ids[p.tok_i] != .keyword_default) { | |
| 3517 | // TODO: this condition is not completely correct; the last statement of a compound | |
| 3518 | // statement is also valid if it precedes a switch label (so intervening '}' are ok, | |
| 3519 | // but only if they close a compound statement) | |
| 3520 | try p.errTok(.invalid_fallthrough, expr_start); | |
| 3521 | } | |
| 3522 | } | |
| 3523 | return p.addNode(null_node); | |
| 3524 | } | |
| 3525 | ||
| 3526 | try p.err(.expected_stmt); | |
| 3527 | return error.ParsingFailed; | |
| 3528 | } | |
| 3529 | ||
| 3530 | /// labeledStmt | |
| 3531 | /// : IDENTIFIER ':' stmt | |
| 3532 | /// | keyword_case constExpr ':' stmt | |
| 3533 | /// | keyword_default ':' stmt | |
| 3534 | fn labeledStmt(p: *Parser) Error!?NodeIndex { | |
| 3535 | if ((p.tok_ids[p.tok_i] == .identifier or p.tok_ids[p.tok_i] == .extended_identifier) and p.tok_ids[p.tok_i + 1] == .colon) { | |
| 3536 | const name_tok = p.expectIdentifier() catch unreachable; | |
| 3537 | const str = p.tokSlice(name_tok); | |
| 3538 | if (p.findLabel(str)) |some| { | |
| 3539 | try p.errStr(.duplicate_label, name_tok, str); | |
| 3540 | try p.errStr(.previous_label, some, str); | |
| 3541 | } else { | |
| 3542 | p.label_count += 1; | |
| 3543 | try p.labels.append(.{ .label = name_tok }); | |
| 3544 | var i: usize = 0; | |
| 3545 | while (i < p.labels.items.len) { | |
| 3546 | if (p.labels.items[i] == .unresolved_goto and | |
| 3547 | mem.eql(u8, p.tokSlice(p.labels.items[i].unresolved_goto), str)) | |
| 3548 | { | |
| 3549 | _ = p.labels.swapRemove(i); | |
| 3550 | } else i += 1; | |
| 3551 | } | |
| 3552 | } | |
| 3553 | ||
| 3554 | p.tok_i += 1; | |
| 3555 | const attr_buf_top = p.attr_buf.len; | |
| 3556 | defer p.attr_buf.len = attr_buf_top; | |
| 3557 | try p.attributeSpecifier(); // label | |
| 3558 | ||
| 3559 | return try p.addNode(.{ | |
| 3560 | .tag = .labeled_stmt, | |
| 3561 | .data = .{ .decl = .{ .name = name_tok, .node = try p.stmt() } }, | |
| 3562 | }); | |
| 3563 | } else if (p.eatToken(.keyword_case)) |case| { | |
| 3564 | const val = try p.constExpr(); | |
| 3565 | _ = try p.expectToken(.colon); | |
| 3566 | const s = try p.stmt(); | |
| 3567 | const node = try p.addNode(.{ | |
| 3568 | .tag = .case_stmt, | |
| 3569 | .data = .{ .bin = .{ .lhs = val.node, .rhs = s } }, | |
| 3570 | }); | |
| 3571 | if (p.findSwitch()) |some| { | |
| 3572 | if (val.val.tag == .unavailable) { | |
| 3573 | try p.errTok(.case_val_unavailable, case + 1); | |
| 3574 | return node; | |
| 3575 | } | |
| 3576 | // TODO cast to target type | |
| 3577 | const gop = try some.cases.getOrPut(val); | |
| 3578 | if (gop.found_existing) { | |
| 3579 | if (some.cases.ctx.ty.isUnsignedInt(p.pp.comp)) { | |
| 3580 | try p.errExtra(.duplicate_switch_case_unsigned, case, .{ | |
| 3581 | .unsigned = val.val.data.int, | |
| 3582 | }); | |
| 3583 | } else { | |
| 3584 | try p.errExtra(.duplicate_switch_case_signed, case, .{ | |
| 3585 | .signed = val.val.signExtend(val.ty, p.pp.comp), | |
| 3586 | }); | |
| 3587 | } | |
| 3588 | try p.errTok(.previous_case, gop.value_ptr.tok); | |
| 3589 | } else { | |
| 3590 | gop.value_ptr.* = .{ | |
| 3591 | .tok = case, | |
| 3592 | .node = node, | |
| 3593 | }; | |
| 3594 | } | |
| 3595 | } else { | |
| 3596 | try p.errStr(.case_not_in_switch, case, "case"); | |
| 3597 | } | |
| 3598 | return node; | |
| 3599 | } else if (p.eatToken(.keyword_default)) |default| { | |
| 3600 | _ = try p.expectToken(.colon); | |
| 3601 | const s = try p.stmt(); | |
| 3602 | const node = try p.addNode(.{ | |
| 3603 | .tag = .default_stmt, | |
| 3604 | .data = .{ .un = s }, | |
| 3605 | }); | |
| 3606 | if (p.findSwitch()) |some| { | |
| 3607 | if (some.default) |previous| { | |
| 3608 | try p.errTok(.multiple_default, default); | |
| 3609 | try p.errTok(.previous_case, previous.tok); | |
| 3610 | } else { | |
| 3611 | some.default = .{ | |
| 3612 | .tok = default, | |
| 3613 | .node = node, | |
| 3614 | }; | |
| 3615 | } | |
| 3616 | } else { | |
| 3617 | try p.errStr(.case_not_in_switch, default, "default"); | |
| 3618 | } | |
| 3619 | return node; | |
| 3620 | } else return null; | |
| 3621 | } | |
| 3622 | ||
| 3623 | const StmtExprState = struct { | |
| 3624 | last_expr_tok: TokenIndex = 0, | |
| 3625 | last_expr_res: Result = .{ .ty = .{ .specifier = .void } }, | |
| 3626 | }; | |
| 3627 | ||
| 3628 | /// compoundStmt : '{' ( decl | keyword_extension decl | staticAssert | stmt)* '}' | |
| 3629 | fn compoundStmt(p: *Parser, is_fn_body: bool, stmt_expr_state: ?*StmtExprState) Error!?NodeIndex { | |
| 3630 | const l_brace = p.eatToken(.l_brace) orelse return null; | |
| 3631 | ||
| 3632 | const decl_buf_top = p.decl_buf.items.len; | |
| 3633 | defer p.decl_buf.items.len = decl_buf_top; | |
| 3634 | ||
| 3635 | const scopes_top = p.scopes.items.len; | |
| 3636 | defer p.scopes.items.len = scopes_top; | |
| 3637 | // the parameters of a function are in the same scope as the body | |
| 3638 | if (!is_fn_body) try p.scopes.append(.block); | |
| 3639 | ||
| 3640 | var noreturn_index: ?TokenIndex = null; | |
| 3641 | var noreturn_label_count: u32 = 0; | |
| 3642 | ||
| 3643 | while (p.eatToken(.r_brace) == null) : (_ = try p.pragma()) { | |
| 3644 | if (stmt_expr_state) |state| state.* = .{}; | |
| 3645 | if (try p.parseOrNextStmt(staticAssert, l_brace)) continue; | |
| 3646 | if (try p.parseOrNextStmt(decl, l_brace)) continue; | |
| 3647 | if (p.eatToken(.keyword_extension)) |ext| { | |
| 3648 | const saved_extension = p.extension_suppressed; | |
| 3649 | defer p.extension_suppressed = saved_extension; | |
| 3650 | p.extension_suppressed = true; | |
| 3651 | ||
| 3652 | if (try p.parseOrNextStmt(decl, l_brace)) continue; | |
| 3653 | p.tok_i = ext; | |
| 3654 | } | |
| 3655 | const stmt_tok = p.tok_i; | |
| 3656 | const s = p.stmt() catch |er| switch (er) { | |
| 3657 | error.ParsingFailed => { | |
| 3658 | try p.nextStmt(l_brace); | |
| 3659 | continue; | |
| 3660 | }, | |
| 3661 | else => |e| return e, | |
| 3662 | }; | |
| 3663 | if (s == .none) continue; | |
| 3664 | if (stmt_expr_state) |state| { | |
| 3665 | state.* = .{ | |
| 3666 | .last_expr_tok = stmt_tok, | |
| 3667 | .last_expr_res = .{ | |
| 3668 | .node = s, | |
| 3669 | .ty = p.nodes.items(.ty)[@enumToInt(s)], | |
| 3670 | }, | |
| 3671 | }; | |
| 3672 | } | |
| 3673 | try p.decl_buf.append(s); | |
| 3674 | ||
| 3675 | if (noreturn_index == null and p.nodeIsNoreturn(s)) { | |
| 3676 | noreturn_index = p.tok_i; | |
| 3677 | noreturn_label_count = p.label_count; | |
| 3678 | } | |
| 3679 | switch (p.nodes.items(.tag)[@enumToInt(s)]) { | |
| 3680 | .case_stmt, .default_stmt, .labeled_stmt => noreturn_index = null, | |
| 3681 | else => {}, | |
| 3682 | } | |
| 3683 | } | |
| 3684 | ||
| 3685 | if (noreturn_index) |some| { | |
| 3686 | // if new labels were defined we cannot be certain that the code is unreachable | |
| 3687 | if (some != p.tok_i - 1 and noreturn_label_count == p.label_count) try p.errTok(.unreachable_code, some); | |
| 3688 | } | |
| 3689 | if (is_fn_body and (p.decl_buf.items.len == decl_buf_top or !p.nodeIsNoreturn(p.decl_buf.items[p.decl_buf.items.len - 1]))) { | |
| 3690 | if (!p.func.ty.?.returnType().is(.void)) try p.errStr(.func_does_not_return, p.tok_i - 1, p.tokSlice(p.func.name)); | |
| 3691 | try p.decl_buf.append(try p.addNode(.{ .tag = .implicit_return, .ty = p.func.ty.?.returnType(), .data = undefined })); | |
| 3692 | } | |
| 3693 | if (is_fn_body) { | |
| 3694 | if (p.func.ident) |some| try p.decl_buf.insert(decl_buf_top, some.node); | |
| 3695 | if (p.func.pretty_ident) |some| try p.decl_buf.insert(decl_buf_top, some.node); | |
| 3696 | } | |
| 3697 | ||
| 3698 | var node: Tree.Node = .{ | |
| 3699 | .tag = .compound_stmt_two, | |
| 3700 | .data = .{ .bin = .{ .lhs = .none, .rhs = .none } }, | |
| 3701 | }; | |
| 3702 | const statements = p.decl_buf.items[decl_buf_top..]; | |
| 3703 | switch (statements.len) { | |
| 3704 | 0 => {}, | |
| 3705 | 1 => node.data = .{ .bin = .{ .lhs = statements[0], .rhs = .none } }, | |
| 3706 | 2 => node.data = .{ .bin = .{ .lhs = statements[0], .rhs = statements[1] } }, | |
| 3707 | else => { | |
| 3708 | node.tag = .compound_stmt; | |
| 3709 | node.data = .{ .range = try p.addList(statements) }; | |
| 3710 | }, | |
| 3711 | } | |
| 3712 | return try p.addNode(node); | |
| 3713 | } | |
| 3714 | ||
| 3715 | fn nodeIsNoreturn(p: *Parser, node: NodeIndex) bool { | |
| 3716 | switch (p.nodes.items(.tag)[@enumToInt(node)]) { | |
| 3717 | .break_stmt, .continue_stmt, .return_stmt => return true, | |
| 3718 | .if_then_else_stmt => { | |
| 3719 | const data = p.data.items[p.nodes.items(.data)[@enumToInt(node)].if3.body..]; | |
| 3720 | return p.nodeIsNoreturn(data[0]) and p.nodeIsNoreturn(data[1]); | |
| 3721 | }, | |
| 3722 | .compound_stmt_two => { | |
| 3723 | const data = p.nodes.items(.data)[@enumToInt(node)]; | |
| 3724 | if (data.bin.rhs != .none) return p.nodeIsNoreturn(data.bin.rhs); | |
| 3725 | if (data.bin.lhs != .none) return p.nodeIsNoreturn(data.bin.lhs); | |
| 3726 | return false; | |
| 3727 | }, | |
| 3728 | .compound_stmt => { | |
| 3729 | const data = p.nodes.items(.data)[@enumToInt(node)]; | |
| 3730 | return p.nodeIsNoreturn(p.data.items[data.range.end - 1]); | |
| 3731 | }, | |
| 3732 | .labeled_stmt => { | |
| 3733 | const data = p.nodes.items(.data)[@enumToInt(node)]; | |
| 3734 | return p.nodeIsNoreturn(data.decl.node); | |
| 3735 | }, | |
| 3736 | else => return false, | |
| 3737 | } | |
| 3738 | } | |
| 3739 | ||
| 3740 | fn parseOrNextStmt(p: *Parser, comptime func: fn (*Parser) Error!bool, l_brace: TokenIndex) !bool { | |
| 3741 | return func(p) catch |er| switch (er) { | |
| 3742 | error.ParsingFailed => { | |
| 3743 | try p.nextStmt(l_brace); | |
| 3744 | return true; | |
| 3745 | }, | |
| 3746 | else => |e| return e, | |
| 3747 | }; | |
| 3748 | } | |
| 3749 | ||
| 3750 | fn nextStmt(p: *Parser, l_brace: TokenIndex) !void { | |
| 3751 | var parens: u32 = 0; | |
| 3752 | while (p.tok_i < p.tok_ids.len) : (p.tok_i += 1) { | |
| 3753 | switch (p.tok_ids[p.tok_i]) { | |
| 3754 | .l_paren, .l_brace, .l_bracket => parens += 1, | |
| 3755 | .r_paren, .r_bracket => if (parens != 0) { | |
| 3756 | parens -= 1; | |
| 3757 | }, | |
| 3758 | .r_brace => if (parens == 0) | |
| 3759 | return | |
| 3760 | else { | |
| 3761 | parens -= 1; | |
| 3762 | }, | |
| 3763 | .semicolon, | |
| 3764 | .keyword_for, | |
| 3765 | .keyword_while, | |
| 3766 | .keyword_do, | |
| 3767 | .keyword_if, | |
| 3768 | .keyword_goto, | |
| 3769 | .keyword_switch, | |
| 3770 | .keyword_case, | |
| 3771 | .keyword_default, | |
| 3772 | .keyword_continue, | |
| 3773 | .keyword_break, | |
| 3774 | .keyword_return, | |
| 3775 | .keyword_typedef, | |
| 3776 | .keyword_extern, | |
| 3777 | .keyword_static, | |
| 3778 | .keyword_auto, | |
| 3779 | .keyword_register, | |
| 3780 | .keyword_thread_local, | |
| 3781 | .keyword_inline, | |
| 3782 | .keyword_inline1, | |
| 3783 | .keyword_inline2, | |
| 3784 | .keyword_noreturn, | |
| 3785 | .keyword_void, | |
| 3786 | .keyword_bool, | |
| 3787 | .keyword_char, | |
| 3788 | .keyword_short, | |
| 3789 | .keyword_int, | |
| 3790 | .keyword_long, | |
| 3791 | .keyword_signed, | |
| 3792 | .keyword_unsigned, | |
| 3793 | .keyword_float, | |
| 3794 | .keyword_double, | |
| 3795 | .keyword_complex, | |
| 3796 | .keyword_atomic, | |
| 3797 | .keyword_enum, | |
| 3798 | .keyword_struct, | |
| 3799 | .keyword_union, | |
| 3800 | .keyword_alignas, | |
| 3801 | .keyword_typeof, | |
| 3802 | .keyword_typeof1, | |
| 3803 | .keyword_typeof2, | |
| 3804 | .keyword_extension, | |
| 3805 | => if (parens == 0) return, | |
| 3806 | .keyword_pragma => p.skipToPragmaSentinel(), | |
| 3807 | else => {}, | |
| 3808 | } | |
| 3809 | } | |
| 3810 | p.tok_i -= 1; // So we can consume EOF | |
| 3811 | try p.expectClosing(l_brace, .r_brace); | |
| 3812 | unreachable; | |
| 3813 | } | |
| 3814 | ||
| 3815 | fn returnStmt(p: *Parser) Error!?NodeIndex { | |
| 3816 | const ret_tok = p.eatToken(.keyword_return) orelse return null; | |
| 3817 | ||
| 3818 | const e_tok = p.tok_i; | |
| 3819 | var e = try p.expr(); | |
| 3820 | _ = try p.expectToken(.semicolon); | |
| 3821 | const ret_ty = p.func.ty.?.returnType(); | |
| 3822 | ||
| 3823 | if (e.node == .none) { | |
| 3824 | if (!ret_ty.is(.void)) try p.errStr(.func_should_return, ret_tok, p.tokSlice(p.func.name)); | |
| 3825 | return try p.addNode(.{ .tag = .return_stmt, .data = .{ .un = e.node } }); | |
| 3826 | } else if (ret_ty.is(.void)) { | |
| 3827 | try p.errStr(.void_func_returns_value, e_tok, p.tokSlice(p.func.name)); | |
| 3828 | return try p.addNode(.{ .tag = .return_stmt, .data = .{ .un = e.node } }); | |
| 3829 | } | |
| 3830 | ||
| 3831 | try e.lvalConversion(p); | |
| 3832 | // Return type conversion is done as if it was assignment | |
| 3833 | if (ret_ty.is(.bool)) { | |
| 3834 | // this is ridiculous but it's what clang does | |
| 3835 | if (e.ty.isInt() or e.ty.isFloat() or e.ty.isPtr()) { | |
| 3836 | try e.boolCast(p, ret_ty); | |
| 3837 | } else { | |
| 3838 | try p.errStr(.incompatible_return, e_tok, try p.typeStr(e.ty)); | |
| 3839 | } | |
| 3840 | } else if (ret_ty.isInt()) { | |
| 3841 | if (e.ty.isInt() or e.ty.isFloat()) { | |
| 3842 | try e.intCast(p, ret_ty); | |
| 3843 | } else if (e.ty.isPtr()) { | |
| 3844 | try p.errStr(.implicit_ptr_to_int, e_tok, try p.typePairStrExtra(e.ty, " to ", ret_ty)); | |
| 3845 | try e.intCast(p, ret_ty); | |
| 3846 | } else { | |
| 3847 | try p.errStr(.incompatible_return, e_tok, try p.typeStr(e.ty)); | |
| 3848 | } | |
| 3849 | } else if (ret_ty.isFloat()) { | |
| 3850 | if (e.ty.isInt() or e.ty.isFloat()) { | |
| 3851 | try e.floatCast(p, ret_ty); | |
| 3852 | } else { | |
| 3853 | try p.errStr(.incompatible_return, e_tok, try p.typeStr(e.ty)); | |
| 3854 | } | |
| 3855 | } else if (ret_ty.isPtr()) { | |
| 3856 | if (e.val.isZero()) { | |
| 3857 | try e.nullCast(p, ret_ty); | |
| 3858 | } else if (e.ty.isInt()) { | |
| 3859 | try p.errStr(.implicit_int_to_ptr, e_tok, try p.typePairStrExtra(e.ty, " to ", ret_ty)); | |
| 3860 | try e.intCast(p, ret_ty); | |
| 3861 | } else if (!e.ty.isVoidStar() and !ret_ty.isVoidStar() and !ret_ty.eql(e.ty, p.pp.comp, false)) { | |
| 3862 | try p.errStr(.incompatible_return, e_tok, try p.typeStr(e.ty)); | |
| 3863 | } | |
| 3864 | } else if (ret_ty.isRecord()) { | |
| 3865 | if (!ret_ty.eql(e.ty, p.pp.comp, false)) { | |
| 3866 | try p.errStr(.incompatible_return, e_tok, try p.typeStr(e.ty)); | |
| 3867 | } | |
| 3868 | } else if (ret_ty.isFunc()) { | |
| 3869 | // Syntax error reported earlier; just let this return as-is since it is a parse failure anyway | |
| 3870 | } else unreachable; | |
| 3871 | ||
| 3872 | try e.saveValue(p); | |
| 3873 | return try p.addNode(.{ .tag = .return_stmt, .data = .{ .un = e.node } }); | |
| 3874 | } | |
| 3875 | ||
| 3876 | // ====== expressions ====== | |
| 3877 | ||
| 3878 | pub fn macroExpr(p: *Parser) Compilation.Error!bool { | |
| 3879 | const res = p.condExpr() catch |e| switch (e) { | |
| 3880 | error.OutOfMemory => return error.OutOfMemory, | |
| 3881 | error.FatalError => return error.FatalError, | |
| 3882 | error.ParsingFailed => return false, | |
| 3883 | }; | |
| 3884 | if (res.val.tag == .unavailable) { | |
| 3885 | try p.errTok(.expected_expr, p.tok_i); | |
| 3886 | return false; | |
| 3887 | } | |
| 3888 | return res.val.getBool(); | |
| 3889 | } | |
| 3890 | ||
| 3891 | const Result = struct { | |
| 3892 | node: NodeIndex = .none, | |
| 3893 | ty: Type = .{ .specifier = .int }, | |
| 3894 | val: Value = .{}, | |
| 3895 | ||
| 3896 | fn expect(res: Result, p: *Parser) Error!void { | |
| 3897 | if (p.in_macro) { | |
| 3898 | if (res.val.tag == .unavailable) { | |
| 3899 | try p.errTok(.expected_expr, p.tok_i); | |
| 3900 | return error.ParsingFailed; | |
| 3901 | } | |
| 3902 | return; | |
| 3903 | } | |
| 3904 | if (res.node == .none) { | |
| 3905 | try p.errTok(.expected_expr, p.tok_i); | |
| 3906 | return error.ParsingFailed; | |
| 3907 | } | |
| 3908 | } | |
| 3909 | ||
| 3910 | fn empty(res: Result, p: *Parser) bool { | |
| 3911 | if (p.in_macro) return res.val.tag == .unavailable; | |
| 3912 | return res.node == .none; | |
| 3913 | } | |
| 3914 | ||
| 3915 | fn maybeWarnUnused(res: Result, p: *Parser, expr_start: TokenIndex, err_start: usize) Error!void { | |
| 3916 | if (res.ty.is(.void) or res.node == .none) return; | |
| 3917 | // don't warn about unused result if the expression contained errors besides other unused results | |
| 3918 | var i = err_start; | |
| 3919 | while (i < p.pp.comp.diag.list.items.len) : (i += 1) { | |
| 3920 | if (p.pp.comp.diag.list.items[i].tag != .unused_value) return; | |
| 3921 | } | |
| 3922 | var cur_node = res.node; | |
| 3923 | while (true) switch (p.nodes.items(.tag)[@enumToInt(cur_node)]) { | |
| 3924 | .invalid, // So that we don't need to check for node == 0 | |
| 3925 | .assign_expr, | |
| 3926 | .mul_assign_expr, | |
| 3927 | .div_assign_expr, | |
| 3928 | .mod_assign_expr, | |
| 3929 | .add_assign_expr, | |
| 3930 | .sub_assign_expr, | |
| 3931 | .shl_assign_expr, | |
| 3932 | .shr_assign_expr, | |
| 3933 | .bit_and_assign_expr, | |
| 3934 | .bit_xor_assign_expr, | |
| 3935 | .bit_or_assign_expr, | |
| 3936 | .call_expr, | |
| 3937 | .call_expr_one, | |
| 3938 | .pre_inc_expr, | |
| 3939 | .pre_dec_expr, | |
| 3940 | .post_inc_expr, | |
| 3941 | .post_dec_expr, | |
| 3942 | => return, | |
| 3943 | .stmt_expr => { | |
| 3944 | const body = p.nodes.items(.data)[@enumToInt(cur_node)].un; | |
| 3945 | switch (p.nodes.items(.tag)[@enumToInt(body)]) { | |
| 3946 | .compound_stmt_two => { | |
| 3947 | const body_stmt = p.nodes.items(.data)[@enumToInt(body)].bin; | |
| 3948 | cur_node = if (body_stmt.rhs != .none) body_stmt.rhs else body_stmt.lhs; | |
| 3949 | }, | |
| 3950 | .compound_stmt => { | |
| 3951 | const data = p.nodes.items(.data)[@enumToInt(body)]; | |
| 3952 | cur_node = p.data.items[data.range.end - 1]; | |
| 3953 | }, | |
| 3954 | else => unreachable, | |
| 3955 | } | |
| 3956 | }, | |
| 3957 | .comma_expr => cur_node = p.nodes.items(.data)[@enumToInt(cur_node)].bin.rhs, | |
| 3958 | .paren_expr => cur_node = p.nodes.items(.data)[@enumToInt(cur_node)].un, | |
| 3959 | else => break, | |
| 3960 | }; | |
| 3961 | try p.errTok(.unused_value, expr_start); | |
| 3962 | } | |
| 3963 | ||
| 3964 | fn bin(lhs: *Result, p: *Parser, tag: Tree.Tag, rhs: Result) !void { | |
| 3965 | lhs.node = try p.addNode(.{ | |
| 3966 | .tag = tag, | |
| 3967 | .ty = lhs.ty, | |
| 3968 | .data = .{ .bin = .{ .lhs = lhs.node, .rhs = rhs.node } }, | |
| 3969 | }); | |
| 3970 | } | |
| 3971 | ||
| 3972 | fn un(operand: *Result, p: *Parser, tag: Tree.Tag) Error!void { | |
| 3973 | operand.node = try p.addNode(.{ | |
| 3974 | .tag = tag, | |
| 3975 | .ty = operand.ty, | |
| 3976 | .data = .{ .un = operand.node }, | |
| 3977 | }); | |
| 3978 | } | |
| 3979 | ||
| 3980 | fn qualCast(res: *Result, p: *Parser, elem_ty: *Type) Error!void { | |
| 3981 | res.ty = .{ | |
| 3982 | .data = .{ .sub_type = elem_ty }, | |
| 3983 | .specifier = .pointer, | |
| 3984 | }; | |
| 3985 | try res.un(p, .qual_cast); | |
| 3986 | } | |
| 3987 | ||
| 3988 | fn adjustCondExprPtrs(a: *Result, tok: TokenIndex, b: *Result, p: *Parser) !bool { | |
| 3989 | assert(a.ty.isPtr() and b.ty.isPtr()); | |
| 3990 | ||
| 3991 | const a_elem = a.ty.elemType(); | |
| 3992 | const b_elem = b.ty.elemType(); | |
| 3993 | if (a_elem.eql(b_elem, p.pp.comp, true)) return true; | |
| 3994 | ||
| 3995 | var adjusted_elem_ty = try p.arena.create(Type); | |
| 3996 | adjusted_elem_ty.* = a_elem; | |
| 3997 | ||
| 3998 | const has_void_star_branch = a.ty.isVoidStar() or b.ty.isVoidStar(); | |
| 3999 | const only_quals_differ = a_elem.eql(b_elem, p.pp.comp, false); | |
| 4000 | const pointers_compatible = only_quals_differ or has_void_star_branch; | |
| 4001 | ||
| 4002 | if (!pointers_compatible or has_void_star_branch) { | |
| 4003 | if (!pointers_compatible) { | |
| 4004 | try p.errStr(.pointer_mismatch, tok, try p.typePairStrExtra(a.ty, " and ", b.ty)); | |
| 4005 | } | |
| 4006 | adjusted_elem_ty.* = .{ .specifier = .void }; | |
| 4007 | } | |
| 4008 | if (pointers_compatible) { | |
| 4009 | adjusted_elem_ty.qual = a_elem.qual.mergeCV(b_elem.qual); | |
| 4010 | } | |
| 4011 | if (!adjusted_elem_ty.eql(a_elem, p.pp.comp, true)) try a.qualCast(p, adjusted_elem_ty); | |
| 4012 | if (!adjusted_elem_ty.eql(b_elem, p.pp.comp, true)) try b.qualCast(p, adjusted_elem_ty); | |
| 4013 | return true; | |
| 4014 | } | |
| 4015 | ||
| 4016 | /// Adjust types for binary operation, returns true if the result can and should be evaluated. | |
| 4017 | fn adjustTypes(a: *Result, tok: TokenIndex, b: *Result, p: *Parser, kind: enum { | |
| 4018 | integer, | |
| 4019 | arithmetic, | |
| 4020 | boolean_logic, | |
| 4021 | relational, | |
| 4022 | equality, | |
| 4023 | conditional, | |
| 4024 | add, | |
| 4025 | sub, | |
| 4026 | }) !bool { | |
| 4027 | try a.lvalConversion(p); | |
| 4028 | try b.lvalConversion(p); | |
| 4029 | ||
| 4030 | const a_int = a.ty.isInt(); | |
| 4031 | const b_int = b.ty.isInt(); | |
| 4032 | if (a_int and b_int) { | |
| 4033 | try a.usualArithmeticConversion(b, p); | |
| 4034 | return a.shouldEval(b, p); | |
| 4035 | } | |
| 4036 | if (kind == .integer) return a.invalidBinTy(tok, b, p); | |
| 4037 | ||
| 4038 | const a_float = a.ty.isFloat(); | |
| 4039 | const b_float = b.ty.isFloat(); | |
| 4040 | const a_arithmetic = a_int or a_float; | |
| 4041 | const b_arithmetic = b_int or b_float; | |
| 4042 | if (a_arithmetic and b_arithmetic) { | |
| 4043 | // <, <=, >, >= only work on real types | |
| 4044 | if (kind == .relational and (!a.ty.isReal() or !b.ty.isReal())) | |
| 4045 | return a.invalidBinTy(tok, b, p); | |
| 4046 | ||
| 4047 | try a.usualArithmeticConversion(b, p); | |
| 4048 | return a.shouldEval(b, p); | |
| 4049 | } | |
| 4050 | if (kind == .arithmetic) return a.invalidBinTy(tok, b, p); | |
| 4051 | ||
| 4052 | const a_ptr = a.ty.isPtr(); | |
| 4053 | const b_ptr = b.ty.isPtr(); | |
| 4054 | const a_scalar = a_arithmetic or a_ptr; | |
| 4055 | const b_scalar = b_arithmetic or b_ptr; | |
| 4056 | switch (kind) { | |
| 4057 | .boolean_logic => { | |
| 4058 | if (!a_scalar or !b_scalar) return a.invalidBinTy(tok, b, p); | |
| 4059 | ||
| 4060 | // Do integer promotions but nothing else | |
| 4061 | if (a_int) try a.intCast(p, a.ty.integerPromotion(p.pp.comp)); | |
| 4062 | if (b_int) try b.intCast(p, b.ty.integerPromotion(p.pp.comp)); | |
| 4063 | return a.shouldEval(b, p); | |
| 4064 | }, | |
| 4065 | .relational, .equality => { | |
| 4066 | // comparisons between floats and pointes not allowed | |
| 4067 | if (!a_scalar or !b_scalar or (a_float and b_ptr) or (b_float and a_ptr)) | |
| 4068 | return a.invalidBinTy(tok, b, p); | |
| 4069 | ||
| 4070 | if ((a_int or b_int) and !(a.val.isZero() or b.val.isZero())) { | |
| 4071 | try p.errStr(.comparison_ptr_int, tok, try p.typePairStr(a.ty, b.ty)); | |
| 4072 | } else if (a_ptr and b_ptr) { | |
| 4073 | if (!a.ty.isVoidStar() and !b.ty.isVoidStar() and !a.ty.eql(b.ty, p.pp.comp, false)) | |
| 4074 | try p.errStr(.comparison_distinct_ptr, tok, try p.typePairStr(a.ty, b.ty)); | |
| 4075 | } else if (a_ptr) { | |
| 4076 | try b.ptrCast(p, a.ty); | |
| 4077 | } else { | |
| 4078 | assert(b_ptr); | |
| 4079 | try a.ptrCast(p, b.ty); | |
| 4080 | } | |
| 4081 | ||
| 4082 | return a.shouldEval(b, p); | |
| 4083 | }, | |
| 4084 | .conditional => { | |
| 4085 | // doesn't matter what we return here, as the result is ignored | |
| 4086 | if (a.ty.is(.void) or b.ty.is(.void)) { | |
| 4087 | try a.toVoid(p); | |
| 4088 | try b.toVoid(p); | |
| 4089 | return true; | |
| 4090 | } | |
| 4091 | if ((a_ptr and b_int) or (a_int and b_ptr)) { | |
| 4092 | if (a.val.isZero() or b.val.isZero()) { | |
| 4093 | try a.nullCast(p, b.ty); | |
| 4094 | try b.nullCast(p, a.ty); | |
| 4095 | return true; | |
| 4096 | } | |
| 4097 | const int_ty = if (a_int) a else b; | |
| 4098 | const ptr_ty = if (a_ptr) a else b; | |
| 4099 | try p.errStr(.implicit_int_to_ptr, tok, try p.typePairStrExtra(int_ty.ty, " to ", ptr_ty.ty)); | |
| 4100 | try int_ty.ptrCast(p, ptr_ty.ty); | |
| 4101 | ||
| 4102 | return true; | |
| 4103 | } | |
| 4104 | if (a_ptr and b_ptr) return a.adjustCondExprPtrs(tok, b, p); | |
| 4105 | if (a.ty.isRecord() and b.ty.isRecord() and a.ty.eql(b.ty, p.pp.comp, false)) { | |
| 4106 | return true; | |
| 4107 | } | |
| 4108 | return a.invalidBinTy(tok, b, p); | |
| 4109 | }, | |
| 4110 | .add => { | |
| 4111 | // if both aren't arithmetic one should be pointer and the other an integer | |
| 4112 | if (a_ptr == b_ptr or a_int == b_int) return a.invalidBinTy(tok, b, p); | |
| 4113 | ||
| 4114 | // Do integer promotions but nothing else | |
| 4115 | if (a_int) try a.intCast(p, a.ty.integerPromotion(p.pp.comp)); | |
| 4116 | if (b_int) try b.intCast(p, b.ty.integerPromotion(p.pp.comp)); | |
| 4117 | ||
| 4118 | // The result type is the type of the pointer operand | |
| 4119 | if (a_int) a.ty = b.ty else b.ty = a.ty; | |
| 4120 | return a.shouldEval(b, p); | |
| 4121 | }, | |
| 4122 | .sub => { | |
| 4123 | // if both aren't arithmetic then either both should be pointers or just a | |
| 4124 | if (!a_ptr or !(b_ptr or b_int)) return a.invalidBinTy(tok, b, p); | |
| 4125 | ||
| 4126 | if (a_ptr and b_ptr) { | |
| 4127 | if (!a.ty.eql(b.ty, p.pp.comp, false)) try p.errStr(.incompatible_pointers, tok, try p.typePairStr(a.ty, b.ty)); | |
| 4128 | a.ty = p.pp.comp.types.ptrdiff; | |
| 4129 | } | |
| 4130 | ||
| 4131 | // Do integer promotion on b if needed | |
| 4132 | if (b_int) try b.intCast(p, b.ty.integerPromotion(p.pp.comp)); | |
| 4133 | return a.shouldEval(b, p); | |
| 4134 | }, | |
| 4135 | else => return a.invalidBinTy(tok, b, p), | |
| 4136 | } | |
| 4137 | } | |
| 4138 | ||
| 4139 | fn lvalConversion(res: *Result, p: *Parser) Error!void { | |
| 4140 | if (res.ty.isFunc()) { | |
| 4141 | var elem_ty = try p.arena.create(Type); | |
| 4142 | elem_ty.* = res.ty; | |
| 4143 | res.ty.specifier = .pointer; | |
| 4144 | res.ty.data = .{ .sub_type = elem_ty }; | |
| 4145 | try res.un(p, .function_to_pointer); | |
| 4146 | } else if (res.ty.isArray()) { | |
| 4147 | res.val.tag = .unavailable; | |
| 4148 | res.ty.decayArray(); | |
| 4149 | try res.un(p, .array_to_pointer); | |
| 4150 | } else if (!p.in_macro and Tree.isLval(p.nodes.slice(), p.data.items, p.value_map, res.node)) { | |
| 4151 | res.val.tag = .unavailable; | |
| 4152 | res.ty.qual = .{}; | |
| 4153 | try res.un(p, .lval_to_rval); | |
| 4154 | } | |
| 4155 | } | |
| 4156 | ||
| 4157 | fn boolCast(res: *Result, p: *Parser, bool_ty: Type) Error!void { | |
| 4158 | if (res.ty.isPtr()) { | |
| 4159 | res.val.toBool(); | |
| 4160 | res.ty = bool_ty; | |
| 4161 | try res.un(p, .pointer_to_bool); | |
| 4162 | } else if (res.ty.isInt() and !res.ty.is(.bool)) { | |
| 4163 | res.val.toBool(); | |
| 4164 | res.ty = bool_ty; | |
| 4165 | try res.un(p, .int_to_bool); | |
| 4166 | } else if (res.ty.isFloat()) { | |
| 4167 | res.val.floatToInt(res.ty, bool_ty, p.pp.comp); | |
| 4168 | res.ty = bool_ty; | |
| 4169 | try res.un(p, .float_to_bool); | |
| 4170 | } | |
| 4171 | } | |
| 4172 | ||
| 4173 | fn intCast(res: *Result, p: *Parser, int_ty: Type) Error!void { | |
| 4174 | if (res.ty.is(.bool)) { | |
| 4175 | res.ty = int_ty; | |
| 4176 | try res.un(p, .bool_to_int); | |
| 4177 | } else if (res.ty.isPtr()) { | |
| 4178 | res.ty = int_ty; | |
| 4179 | try res.un(p, .pointer_to_int); | |
| 4180 | } else if (res.ty.isFloat()) { | |
| 4181 | res.val.floatToInt(res.ty, int_ty, p.pp.comp); | |
| 4182 | res.ty = int_ty; | |
| 4183 | try res.un(p, .float_to_int); | |
| 4184 | } else if (!res.ty.eql(int_ty, p.pp.comp, true)) { | |
| 4185 | if (int_ty.hasIncompleteSize()) return error.ParsingFailed; // Diagnostic already issued | |
| 4186 | res.val.intCast(res.ty, int_ty, p.pp.comp); | |
| 4187 | res.ty = int_ty; | |
| 4188 | try res.un(p, .int_cast); | |
| 4189 | } | |
| 4190 | } | |
| 4191 | ||
| 4192 | fn floatCast(res: *Result, p: *Parser, float_ty: Type) Error!void { | |
| 4193 | if (res.ty.is(.bool)) { | |
| 4194 | res.val.intToFloat(res.ty, float_ty, p.pp.comp); | |
| 4195 | res.ty = float_ty; | |
| 4196 | try res.un(p, .bool_to_float); | |
| 4197 | } else if (res.ty.isInt()) { | |
| 4198 | res.val.intToFloat(res.ty, float_ty, p.pp.comp); | |
| 4199 | res.ty = float_ty; | |
| 4200 | try res.un(p, .int_to_float); | |
| 4201 | } else if (!res.ty.eql(float_ty, p.pp.comp, true)) { | |
| 4202 | res.val.floatCast(res.ty, float_ty, p.pp.comp); | |
| 4203 | res.ty = float_ty; | |
| 4204 | try res.un(p, .float_cast); | |
| 4205 | } | |
| 4206 | } | |
| 4207 | ||
| 4208 | fn ptrCast(res: *Result, p: *Parser, ptr_ty: Type) Error!void { | |
| 4209 | if (res.ty.is(.bool)) { | |
| 4210 | res.ty = ptr_ty; | |
| 4211 | try res.un(p, .bool_to_pointer); | |
| 4212 | } else if (res.ty.isInt()) { | |
| 4213 | res.val.intCast(res.ty, ptr_ty, p.pp.comp); | |
| 4214 | res.ty = ptr_ty; | |
| 4215 | try res.un(p, .int_to_pointer); | |
| 4216 | } | |
| 4217 | } | |
| 4218 | ||
| 4219 | fn toVoid(res: *Result, p: *Parser) Error!void { | |
| 4220 | if (!res.ty.is(.void)) { | |
| 4221 | res.ty = .{ .specifier = .void }; | |
| 4222 | res.node = try p.addNode(.{ | |
| 4223 | .tag = .to_void, | |
| 4224 | .ty = res.ty, | |
| 4225 | .data = .{ .un = res.node }, | |
| 4226 | }); | |
| 4227 | } | |
| 4228 | } | |
| 4229 | ||
| 4230 | fn nullCast(res: *Result, p: *Parser, ptr_ty: Type) Error!void { | |
| 4231 | if (!res.val.isZero()) return; | |
| 4232 | res.ty = ptr_ty; | |
| 4233 | try res.un(p, .null_to_pointer); | |
| 4234 | } | |
| 4235 | ||
| 4236 | fn usualArithmeticConversion(a: *Result, b: *Result, p: *Parser) Error!void { | |
| 4237 | // if either is a float cast to that type | |
| 4238 | const float_types = [3][2]Type.Specifier{ | |
| 4239 | .{ .complex_long_double, .long_double }, | |
| 4240 | .{ .complex_double, .double }, | |
| 4241 | .{ .complex_float, .float }, | |
| 4242 | }; | |
| 4243 | const a_spec = a.ty.canonicalize(.standard).specifier; | |
| 4244 | const b_spec = b.ty.canonicalize(.standard).specifier; | |
| 4245 | for (float_types) |pair| { | |
| 4246 | if (a_spec == pair[0] or a_spec == pair[1] or | |
| 4247 | b_spec == pair[0] or b_spec == pair[1]) | |
| 4248 | { | |
| 4249 | const both_real = a.ty.isReal() and b.ty.isReal(); | |
| 4250 | const res_spec = pair[@boolToInt(both_real)]; | |
| 4251 | const ty = Type{ .specifier = res_spec }; | |
| 4252 | try a.floatCast(p, ty); | |
| 4253 | try b.floatCast(p, ty); | |
| 4254 | return; | |
| 4255 | } | |
| 4256 | } | |
| 4257 | ||
| 4258 | // Do integer promotion on both operands | |
| 4259 | const a_promoted = a.ty.integerPromotion(p.pp.comp); | |
| 4260 | const b_promoted = b.ty.integerPromotion(p.pp.comp); | |
| 4261 | if (a_promoted.eql(b_promoted, p.pp.comp, true)) { | |
| 4262 | // cast to promoted type | |
| 4263 | try a.intCast(p, a_promoted); | |
| 4264 | try b.intCast(p, a_promoted); | |
| 4265 | return; | |
| 4266 | } | |
| 4267 | ||
| 4268 | const a_unsigned = a_promoted.isUnsignedInt(p.pp.comp); | |
| 4269 | const b_unsigned = b_promoted.isUnsignedInt(p.pp.comp); | |
| 4270 | if (a_unsigned == b_unsigned) { | |
| 4271 | // cast to greater signed or unsigned type | |
| 4272 | const res_spec = std.math.max(@enumToInt(a_promoted.specifier), @enumToInt(b_promoted.specifier)); | |
| 4273 | const res_ty = Type{ .specifier = @intToEnum(Type.Specifier, res_spec) }; | |
| 4274 | try a.intCast(p, res_ty); | |
| 4275 | try b.intCast(p, res_ty); | |
| 4276 | return; | |
| 4277 | } | |
| 4278 | ||
| 4279 | // cast to the unsigned type with greater rank | |
| 4280 | const a_larger = @enumToInt(a_promoted.specifier) > @enumToInt(b_promoted.specifier); | |
| 4281 | const b_larger = @enumToInt(b_promoted.specifier) > @enumToInt(b_promoted.specifier); | |
| 4282 | if (a_unsigned) { | |
| 4283 | const target = if (a_larger) a_promoted else b_promoted; | |
| 4284 | try a.intCast(p, target); | |
| 4285 | try b.intCast(p, target); | |
| 4286 | } else { | |
| 4287 | assert(b_unsigned); | |
| 4288 | const target = if (b_larger) b_promoted else a_promoted; | |
| 4289 | try a.intCast(p, target); | |
| 4290 | try b.intCast(p, target); | |
| 4291 | } | |
| 4292 | } | |
| 4293 | ||
| 4294 | fn invalidBinTy(a: *Result, tok: TokenIndex, b: *Result, p: *Parser) Error!bool { | |
| 4295 | try p.errStr(.invalid_bin_types, tok, try p.typePairStr(a.ty, b.ty)); | |
| 4296 | return false; | |
| 4297 | } | |
| 4298 | ||
| 4299 | fn shouldEval(a: *Result, b: *Result, p: *Parser) Error!bool { | |
| 4300 | if (p.no_eval) return false; | |
| 4301 | if (a.val.tag != .unavailable and b.val.tag != .unavailable) | |
| 4302 | return true; | |
| 4303 | ||
| 4304 | try a.saveValue(p); | |
| 4305 | try b.saveValue(p); | |
| 4306 | return p.no_eval; | |
| 4307 | } | |
| 4308 | ||
| 4309 | /// Saves value and replaces it with `.unavailable`. | |
| 4310 | fn saveValue(res: *Result, p: *Parser) !void { | |
| 4311 | assert(!p.in_macro); | |
| 4312 | if (res.val.tag == .unavailable) return; | |
| 4313 | if (!p.in_macro) try p.value_map.put(res.node, res.val); | |
| 4314 | res.val.tag = .unavailable; | |
| 4315 | } | |
| 4316 | }; | |
| 4317 | ||
| 4318 | /// expr : assignExpr (',' assignExpr)* | |
| 4319 | fn expr(p: *Parser) Error!Result { | |
| 4320 | var expr_start = p.tok_i; | |
| 4321 | var err_start = p.pp.comp.diag.list.items.len; | |
| 4322 | var lhs = try p.assignExpr(); | |
| 4323 | if (p.tok_ids[p.tok_i] == .comma) try lhs.expect(p); | |
| 4324 | while (p.eatToken(.comma)) |_| { | |
| 4325 | try lhs.maybeWarnUnused(p, expr_start, err_start); | |
| 4326 | expr_start = p.tok_i; | |
| 4327 | err_start = p.pp.comp.diag.list.items.len; | |
| 4328 | ||
| 4329 | const rhs = try p.assignExpr(); | |
| 4330 | try rhs.expect(p); | |
| 4331 | lhs.val = rhs.val; | |
| 4332 | lhs.ty = rhs.ty; | |
| 4333 | try lhs.bin(p, .comma_expr, rhs); | |
| 4334 | } | |
| 4335 | return lhs; | |
| 4336 | } | |
| 4337 | ||
| 4338 | fn tokToTag(p: *Parser, tok: TokenIndex) Tree.Tag { | |
| 4339 | return switch (p.tok_ids[tok]) { | |
| 4340 | .equal => .assign_expr, | |
| 4341 | .asterisk_equal => .mul_assign_expr, | |
| 4342 | .slash_equal => .div_assign_expr, | |
| 4343 | .percent_equal => .mod_assign_expr, | |
| 4344 | .plus_equal => .add_assign_expr, | |
| 4345 | .minus_equal => .sub_assign_expr, | |
| 4346 | .angle_bracket_angle_bracket_left_equal => .shl_assign_expr, | |
| 4347 | .angle_bracket_angle_bracket_right_equal => .shr_assign_expr, | |
| 4348 | .ampersand_equal => .bit_and_assign_expr, | |
| 4349 | .caret_equal => .bit_xor_assign_expr, | |
| 4350 | .pipe_equal => .bit_or_assign_expr, | |
| 4351 | .equal_equal => .equal_expr, | |
| 4352 | .bang_equal => .not_equal_expr, | |
| 4353 | .angle_bracket_left => .less_than_expr, | |
| 4354 | .angle_bracket_left_equal => .less_than_equal_expr, | |
| 4355 | .angle_bracket_right => .greater_than_expr, | |
| 4356 | .angle_bracket_right_equal => .greater_than_equal_expr, | |
| 4357 | .angle_bracket_angle_bracket_left => .shl_expr, | |
| 4358 | .angle_bracket_angle_bracket_right => .shr_expr, | |
| 4359 | .plus => .add_expr, | |
| 4360 | .minus => .sub_expr, | |
| 4361 | .asterisk => .mul_expr, | |
| 4362 | .slash => .div_expr, | |
| 4363 | .percent => .mod_expr, | |
| 4364 | else => unreachable, | |
| 4365 | }; | |
| 4366 | } | |
| 4367 | ||
| 4368 | /// assignExpr | |
| 4369 | /// : condExpr | |
| 4370 | /// | unExpr ('=' | '*=' | '/=' | '%=' | '+=' | '-=' | '<<=' | '>>=' | '&=' | '^=' | '|=') assignExpr | |
| 4371 | fn assignExpr(p: *Parser) Error!Result { | |
| 4372 | var lhs = try p.condExpr(); | |
| 4373 | if (lhs.empty(p)) return lhs; | |
| 4374 | ||
| 4375 | const tok = p.tok_i; | |
| 4376 | const eq = p.eatToken(.equal); | |
| 4377 | const mul = eq orelse p.eatToken(.asterisk_equal); | |
| 4378 | const div = mul orelse p.eatToken(.slash_equal); | |
| 4379 | const mod = div orelse p.eatToken(.percent_equal); | |
| 4380 | const add = mod orelse p.eatToken(.plus_equal); | |
| 4381 | const sub = add orelse p.eatToken(.minus_equal); | |
| 4382 | const shl = sub orelse p.eatToken(.angle_bracket_angle_bracket_left_equal); | |
| 4383 | const shr = shl orelse p.eatToken(.angle_bracket_angle_bracket_right_equal); | |
| 4384 | const bit_and = shr orelse p.eatToken(.ampersand_equal); | |
| 4385 | const bit_xor = bit_and orelse p.eatToken(.caret_equal); | |
| 4386 | const bit_or = bit_xor orelse p.eatToken(.pipe_equal); | |
| 4387 | ||
| 4388 | const tag = p.tokToTag(bit_or orelse return lhs); | |
| 4389 | var rhs = try p.assignExpr(); | |
| 4390 | try rhs.expect(p); | |
| 4391 | try rhs.lvalConversion(p); | |
| 4392 | ||
| 4393 | var is_const: bool = undefined; | |
| 4394 | if (!Tree.isLvalExtra(p.nodes.slice(), p.data.items, p.value_map, lhs.node, &is_const) or is_const) { | |
| 4395 | try p.errTok(.not_assignable, tok); | |
| 4396 | return error.ParsingFailed; | |
| 4397 | } | |
| 4398 | ||
| 4399 | // adjustTypes will do do lvalue conversion but we do not want that | |
| 4400 | var lhs_copy = lhs; | |
| 4401 | switch (tag) { | |
| 4402 | .assign_expr => {}, // handle plain assignment separately | |
| 4403 | .mul_assign_expr, | |
| 4404 | .div_assign_expr, | |
| 4405 | .mod_assign_expr, | |
| 4406 | => { | |
| 4407 | if (rhs.val.isZero()) { | |
| 4408 | switch (tag) { | |
| 4409 | .div_assign_expr => try p.errStr(.division_by_zero, div.?, "division"), | |
| 4410 | .mod_assign_expr => try p.errStr(.division_by_zero, mod.?, "remainder"), | |
| 4411 | else => {}, | |
| 4412 | } | |
| 4413 | } | |
| 4414 | _ = try lhs_copy.adjustTypes(tok, &rhs, p, .arithmetic); | |
| 4415 | try lhs.bin(p, tag, rhs); | |
| 4416 | return lhs; | |
| 4417 | }, | |
| 4418 | .sub_assign_expr, | |
| 4419 | .add_assign_expr, | |
| 4420 | => { | |
| 4421 | if (lhs.ty.isPtr() and rhs.ty.isInt()) { | |
| 4422 | try rhs.ptrCast(p, lhs.ty); | |
| 4423 | } else { | |
| 4424 | _ = try lhs_copy.adjustTypes(tok, &rhs, p, .arithmetic); | |
| 4425 | } | |
| 4426 | try lhs.bin(p, tag, rhs); | |
| 4427 | return lhs; | |
| 4428 | }, | |
| 4429 | .shl_assign_expr, | |
| 4430 | .shr_assign_expr, | |
| 4431 | .bit_and_assign_expr, | |
| 4432 | .bit_xor_assign_expr, | |
| 4433 | .bit_or_assign_expr, | |
| 4434 | => { | |
| 4435 | _ = try lhs_copy.adjustTypes(tok, &rhs, p, .integer); | |
| 4436 | try lhs.bin(p, tag, rhs); | |
| 4437 | return lhs; | |
| 4438 | }, | |
| 4439 | else => unreachable, | |
| 4440 | } | |
| 4441 | ||
| 4442 | // rhs does not need to be qualified | |
| 4443 | var unqual_ty = lhs.ty.canonicalize(.standard); | |
| 4444 | unqual_ty.qual = .{}; | |
| 4445 | const e_msg = " from incompatible type "; | |
| 4446 | if (lhs.ty.is(.bool)) { | |
| 4447 | // this is ridiculous but it's what clang does | |
| 4448 | if (rhs.ty.isInt() or rhs.ty.isFloat() or rhs.ty.isPtr()) { | |
| 4449 | try rhs.boolCast(p, unqual_ty); | |
| 4450 | } else { | |
| 4451 | try p.errStr(.incompatible_assign, tok, try p.typePairStrExtra(lhs.ty, e_msg, rhs.ty)); | |
| 4452 | } | |
| 4453 | } else if (unqual_ty.isInt()) { | |
| 4454 | if (rhs.ty.isInt() or rhs.ty.isFloat()) { | |
| 4455 | try rhs.intCast(p, unqual_ty); | |
| 4456 | } else if (rhs.ty.isPtr()) { | |
| 4457 | try p.errStr(.implicit_ptr_to_int, tok, try p.typePairStrExtra(rhs.ty, " to ", lhs.ty)); | |
| 4458 | try rhs.intCast(p, unqual_ty); | |
| 4459 | } else { | |
| 4460 | try p.errStr(.incompatible_assign, tok, try p.typePairStrExtra(lhs.ty, e_msg, rhs.ty)); | |
| 4461 | } | |
| 4462 | } else if (unqual_ty.isFloat()) { | |
| 4463 | if (rhs.ty.isInt() or rhs.ty.isFloat()) { | |
| 4464 | try rhs.floatCast(p, unqual_ty); | |
| 4465 | } else { | |
| 4466 | try p.errStr(.incompatible_assign, tok, try p.typePairStrExtra(lhs.ty, e_msg, rhs.ty)); | |
| 4467 | } | |
| 4468 | } else if (unqual_ty.isPtr()) { | |
| 4469 | if (rhs.val.isZero()) { | |
| 4470 | try rhs.nullCast(p, lhs.ty); | |
| 4471 | } else if (rhs.ty.isInt()) { | |
| 4472 | try p.errStr(.implicit_int_to_ptr, tok, try p.typePairStrExtra(rhs.ty, " to ", lhs.ty)); | |
| 4473 | try rhs.ptrCast(p, unqual_ty); | |
| 4474 | } else if (rhs.ty.isPtr()) { | |
| 4475 | if (!unqual_ty.isVoidStar() and !rhs.ty.isVoidStar() and !unqual_ty.eql(rhs.ty, p.pp.comp, false)) { | |
| 4476 | try p.errStr(.incompatible_ptr_assign, tok, try p.typePairStrExtra(lhs.ty, e_msg, rhs.ty)); | |
| 4477 | try rhs.ptrCast(p, unqual_ty); | |
| 4478 | } else if (!unqual_ty.eql(rhs.ty, p.pp.comp, true)) { | |
| 4479 | if (!unqual_ty.elemType().qual.hasQuals(rhs.ty.elemType().qual)) { | |
| 4480 | try p.errStr(.ptr_assign_discards_quals, tok, try p.typePairStrExtra(lhs.ty, e_msg, rhs.ty)); | |
| 4481 | } | |
| 4482 | try rhs.ptrCast(p, unqual_ty); | |
| 4483 | } | |
| 4484 | } else { | |
| 4485 | try p.errStr(.incompatible_assign, tok, try p.typePairStrExtra(lhs.ty, e_msg, rhs.ty)); | |
| 4486 | } | |
| 4487 | } else if (unqual_ty.isRecord()) { | |
| 4488 | if (!unqual_ty.eql(rhs.ty, p.pp.comp, false)) | |
| 4489 | try p.errStr(.incompatible_assign, tok, try p.typePairStrExtra(lhs.ty, e_msg, rhs.ty)); | |
| 4490 | } else if (unqual_ty.isArray() or unqual_ty.isFunc()) { | |
| 4491 | try p.errTok(.not_assignable, tok); | |
| 4492 | } else { | |
| 4493 | try p.errStr(.incompatible_assign, tok, try p.typePairStrExtra(lhs.ty, e_msg, rhs.ty)); | |
| 4494 | } | |
| 4495 | ||
| 4496 | try lhs.bin(p, tag, rhs); | |
| 4497 | return lhs; | |
| 4498 | } | |
| 4499 | ||
| 4500 | /// constExpr : condExpr | |
| 4501 | fn constExpr(p: *Parser) Error!Result { | |
| 4502 | const start = p.tok_i; | |
| 4503 | const res = try p.condExpr(); | |
| 4504 | try res.expect(p); | |
| 4505 | if (!res.ty.isInt()) { | |
| 4506 | try p.errTok(.expected_integer_constant_expr, start); | |
| 4507 | return error.ParsingFailed; | |
| 4508 | } | |
| 4509 | // saveValue sets val to unavailable | |
| 4510 | var copy = res; | |
| 4511 | try copy.saveValue(p); | |
| 4512 | return res; | |
| 4513 | } | |
| 4514 | ||
| 4515 | /// condExpr : lorExpr ('?' expression? ':' condExpr)? | |
| 4516 | fn condExpr(p: *Parser) Error!Result { | |
| 4517 | var cond = try p.lorExpr(); | |
| 4518 | if (cond.empty(p) or p.eatToken(.question_mark) == null) return cond; | |
| 4519 | const saved_eval = p.no_eval; | |
| 4520 | ||
| 4521 | // Depending on the value of the condition, avoid evaluating unreachable branches. | |
| 4522 | var then_expr = blk: { | |
| 4523 | defer p.no_eval = saved_eval; | |
| 4524 | if (cond.val.tag != .unavailable and !cond.val.getBool()) p.no_eval = true; | |
| 4525 | break :blk try p.expr(); | |
| 4526 | }; | |
| 4527 | try then_expr.expect(p); // TODO binary cond expr | |
| 4528 | const colon = try p.expectToken(.colon); | |
| 4529 | var else_expr = blk: { | |
| 4530 | defer p.no_eval = saved_eval; | |
| 4531 | if (cond.val.tag != .unavailable and cond.val.getBool()) p.no_eval = true; | |
| 4532 | break :blk try p.condExpr(); | |
| 4533 | }; | |
| 4534 | try else_expr.expect(p); | |
| 4535 | ||
| 4536 | _ = try then_expr.adjustTypes(colon, &else_expr, p, .conditional); | |
| 4537 | ||
| 4538 | if (cond.val.tag != .unavailable) { | |
| 4539 | cond.val = if (cond.val.getBool()) then_expr.val else else_expr.val; | |
| 4540 | } else { | |
| 4541 | try then_expr.saveValue(p); | |
| 4542 | try else_expr.saveValue(p); | |
| 4543 | } | |
| 4544 | cond.ty = then_expr.ty; | |
| 4545 | cond.node = try p.addNode(.{ | |
| 4546 | .tag = .cond_expr, | |
| 4547 | .ty = cond.ty, | |
| 4548 | .data = .{ .if3 = .{ .cond = cond.node, .body = (try p.addList(&.{ then_expr.node, else_expr.node })).start } }, | |
| 4549 | }); | |
| 4550 | return cond; | |
| 4551 | } | |
| 4552 | ||
| 4553 | /// lorExpr : landExpr ('||' landExpr)* | |
| 4554 | fn lorExpr(p: *Parser) Error!Result { | |
| 4555 | var lhs = try p.landExpr(); | |
| 4556 | if (lhs.empty(p)) return lhs; | |
| 4557 | const saved_eval = p.no_eval; | |
| 4558 | defer p.no_eval = saved_eval; | |
| 4559 | ||
| 4560 | while (p.eatToken(.pipe_pipe)) |tok| { | |
| 4561 | if (lhs.val.tag != .unavailable and lhs.val.getBool()) p.no_eval = true; | |
| 4562 | var rhs = try p.landExpr(); | |
| 4563 | try rhs.expect(p); | |
| 4564 | ||
| 4565 | if (try lhs.adjustTypes(tok, &rhs, p, .boolean_logic)) { | |
| 4566 | const res = @boolToInt(lhs.val.getBool() or rhs.val.getBool()); | |
| 4567 | lhs.val = Value.int(res); | |
| 4568 | } | |
| 4569 | lhs.ty = .{ .specifier = .int }; | |
| 4570 | try lhs.bin(p, .bool_or_expr, rhs); | |
| 4571 | } | |
| 4572 | return lhs; | |
| 4573 | } | |
| 4574 | ||
| 4575 | /// landExpr : orExpr ('&&' orExpr)* | |
| 4576 | fn landExpr(p: *Parser) Error!Result { | |
| 4577 | var lhs = try p.orExpr(); | |
| 4578 | if (lhs.empty(p)) return lhs; | |
| 4579 | const saved_eval = p.no_eval; | |
| 4580 | defer p.no_eval = saved_eval; | |
| 4581 | ||
| 4582 | while (p.eatToken(.ampersand_ampersand)) |tok| { | |
| 4583 | if (lhs.val.tag != .unavailable and !lhs.val.getBool()) p.no_eval = true; | |
| 4584 | var rhs = try p.orExpr(); | |
| 4585 | try rhs.expect(p); | |
| 4586 | ||
| 4587 | if (try lhs.adjustTypes(tok, &rhs, p, .boolean_logic)) { | |
| 4588 | const res = @boolToInt(lhs.val.getBool() and rhs.val.getBool()); | |
| 4589 | lhs.val = Value.int(res); | |
| 4590 | } | |
| 4591 | lhs.ty = .{ .specifier = .int }; | |
| 4592 | try lhs.bin(p, .bool_and_expr, rhs); | |
| 4593 | } | |
| 4594 | return lhs; | |
| 4595 | } | |
| 4596 | ||
| 4597 | /// orExpr : xorExpr ('|' xorExpr)* | |
| 4598 | fn orExpr(p: *Parser) Error!Result { | |
| 4599 | var lhs = try p.xorExpr(); | |
| 4600 | if (lhs.empty(p)) return lhs; | |
| 4601 | while (p.eatToken(.pipe)) |tok| { | |
| 4602 | var rhs = try p.xorExpr(); | |
| 4603 | try rhs.expect(p); | |
| 4604 | ||
| 4605 | if (try lhs.adjustTypes(tok, &rhs, p, .integer)) { | |
| 4606 | lhs.val = lhs.val.bitOr(rhs.val, lhs.ty, p.pp.comp); | |
| 4607 | } | |
| 4608 | try lhs.bin(p, .bit_or_expr, rhs); | |
| 4609 | } | |
| 4610 | return lhs; | |
| 4611 | } | |
| 4612 | ||
| 4613 | /// xorExpr : andExpr ('^' andExpr)* | |
| 4614 | fn xorExpr(p: *Parser) Error!Result { | |
| 4615 | var lhs = try p.andExpr(); | |
| 4616 | if (lhs.empty(p)) return lhs; | |
| 4617 | while (p.eatToken(.caret)) |tok| { | |
| 4618 | var rhs = try p.andExpr(); | |
| 4619 | try rhs.expect(p); | |
| 4620 | ||
| 4621 | if (try lhs.adjustTypes(tok, &rhs, p, .integer)) { | |
| 4622 | lhs.val = lhs.val.bitXor(rhs.val, lhs.ty, p.pp.comp); | |
| 4623 | } | |
| 4624 | try lhs.bin(p, .bit_xor_expr, rhs); | |
| 4625 | } | |
| 4626 | return lhs; | |
| 4627 | } | |
| 4628 | ||
| 4629 | /// andExpr : eqExpr ('&' eqExpr)* | |
| 4630 | fn andExpr(p: *Parser) Error!Result { | |
| 4631 | var lhs = try p.eqExpr(); | |
| 4632 | if (lhs.empty(p)) return lhs; | |
| 4633 | while (p.eatToken(.ampersand)) |tok| { | |
| 4634 | var rhs = try p.eqExpr(); | |
| 4635 | try rhs.expect(p); | |
| 4636 | ||
| 4637 | if (try lhs.adjustTypes(tok, &rhs, p, .integer)) { | |
| 4638 | lhs.val = lhs.val.bitAnd(rhs.val, lhs.ty, p.pp.comp); | |
| 4639 | } | |
| 4640 | try lhs.bin(p, .bit_and_expr, rhs); | |
| 4641 | } | |
| 4642 | return lhs; | |
| 4643 | } | |
| 4644 | ||
| 4645 | /// eqExpr : compExpr (('==' | '!=') compExpr)* | |
| 4646 | fn eqExpr(p: *Parser) Error!Result { | |
| 4647 | var lhs = try p.compExpr(); | |
| 4648 | if (lhs.empty(p)) return lhs; | |
| 4649 | while (true) { | |
| 4650 | const eq = p.eatToken(.equal_equal); | |
| 4651 | const ne = eq orelse p.eatToken(.bang_equal); | |
| 4652 | const tag = p.tokToTag(ne orelse break); | |
| 4653 | var rhs = try p.compExpr(); | |
| 4654 | try rhs.expect(p); | |
| 4655 | ||
| 4656 | if (try lhs.adjustTypes(ne.?, &rhs, p, .equality)) { | |
| 4657 | const op: std.math.CompareOperator = if (tag == .equal_expr) .eq else .neq; | |
| 4658 | const res = lhs.val.compare(op, rhs.val, lhs.ty, p.pp.comp); | |
| 4659 | lhs.val = Value.int(@boolToInt(res)); | |
| 4660 | } | |
| 4661 | lhs.ty = .{ .specifier = .int }; | |
| 4662 | try lhs.bin(p, tag, rhs); | |
| 4663 | } | |
| 4664 | return lhs; | |
| 4665 | } | |
| 4666 | ||
| 4667 | /// compExpr : shiftExpr (('<' | '<=' | '>' | '>=') shiftExpr)* | |
| 4668 | fn compExpr(p: *Parser) Error!Result { | |
| 4669 | var lhs = try p.shiftExpr(); | |
| 4670 | if (lhs.empty(p)) return lhs; | |
| 4671 | while (true) { | |
| 4672 | const lt = p.eatToken(.angle_bracket_left); | |
| 4673 | const le = lt orelse p.eatToken(.angle_bracket_left_equal); | |
| 4674 | const gt = le orelse p.eatToken(.angle_bracket_right); | |
| 4675 | const ge = gt orelse p.eatToken(.angle_bracket_right_equal); | |
| 4676 | const tag = p.tokToTag(ge orelse break); | |
| 4677 | var rhs = try p.shiftExpr(); | |
| 4678 | try rhs.expect(p); | |
| 4679 | ||
| 4680 | if (try lhs.adjustTypes(ge.?, &rhs, p, .relational)) { | |
| 4681 | const op: std.math.CompareOperator = switch (tag) { | |
| 4682 | .less_than_expr => .lt, | |
| 4683 | .less_than_equal_expr => .lte, | |
| 4684 | .greater_than_expr => .gt, | |
| 4685 | .greater_than_equal_expr => .gte, | |
| 4686 | else => unreachable, | |
| 4687 | }; | |
| 4688 | const res = lhs.val.compare(op, rhs.val, lhs.ty, p.pp.comp); | |
| 4689 | lhs.val = Value.int(@boolToInt(res)); | |
| 4690 | } | |
| 4691 | lhs.ty = .{ .specifier = .int }; | |
| 4692 | try lhs.bin(p, tag, rhs); | |
| 4693 | } | |
| 4694 | return lhs; | |
| 4695 | } | |
| 4696 | ||
| 4697 | /// shiftExpr : addExpr (('<<' | '>>') addExpr)* | |
| 4698 | fn shiftExpr(p: *Parser) Error!Result { | |
| 4699 | var lhs = try p.addExpr(); | |
| 4700 | if (lhs.empty(p)) return lhs; | |
| 4701 | while (true) { | |
| 4702 | const shl = p.eatToken(.angle_bracket_angle_bracket_left); | |
| 4703 | const shr = shl orelse p.eatToken(.angle_bracket_angle_bracket_right); | |
| 4704 | const tag = p.tokToTag(shr orelse break); | |
| 4705 | var rhs = try p.addExpr(); | |
| 4706 | try rhs.expect(p); | |
| 4707 | ||
| 4708 | if (try lhs.adjustTypes(shr.?, &rhs, p, .integer)) { | |
| 4709 | if (shl != null) { | |
| 4710 | lhs.val = lhs.val.shl(rhs.val, lhs.ty, p.pp.comp); | |
| 4711 | } else { | |
| 4712 | lhs.val = lhs.val.shr(rhs.val, lhs.ty, p.pp.comp); | |
| 4713 | } | |
| 4714 | } | |
| 4715 | try lhs.bin(p, tag, rhs); | |
| 4716 | } | |
| 4717 | return lhs; | |
| 4718 | } | |
| 4719 | ||
| 4720 | /// addExpr : mulExpr (('+' | '-') mulExpr)* | |
| 4721 | fn addExpr(p: *Parser) Error!Result { | |
| 4722 | var lhs = try p.mulExpr(); | |
| 4723 | if (lhs.empty(p)) return lhs; | |
| 4724 | while (true) { | |
| 4725 | const plus = p.eatToken(.plus); | |
| 4726 | const minus = plus orelse p.eatToken(.minus); | |
| 4727 | const tag = p.tokToTag(minus orelse break); | |
| 4728 | var rhs = try p.mulExpr(); | |
| 4729 | try rhs.expect(p); | |
| 4730 | ||
| 4731 | if (try lhs.adjustTypes(minus.?, &rhs, p, if (plus != null) .add else .sub)) { | |
| 4732 | if (plus != null) { | |
| 4733 | if (lhs.val.add(lhs.val, rhs.val, lhs.ty, p.pp.comp)) try p.errOverflow(plus.?, lhs); | |
| 4734 | } else { | |
| 4735 | if (lhs.val.sub(lhs.val, rhs.val, lhs.ty, p.pp.comp)) try p.errOverflow(minus.?, lhs); | |
| 4736 | } | |
| 4737 | } | |
| 4738 | try lhs.bin(p, tag, rhs); | |
| 4739 | } | |
| 4740 | return lhs; | |
| 4741 | } | |
| 4742 | ||
| 4743 | /// mulExpr : castExpr (('*' | '/' | '%') castExpr)*´ | |
| 4744 | fn mulExpr(p: *Parser) Error!Result { | |
| 4745 | var lhs = try p.castExpr(); | |
| 4746 | if (lhs.empty(p)) return lhs; | |
| 4747 | while (true) { | |
| 4748 | const mul = p.eatToken(.asterisk); | |
| 4749 | const div = mul orelse p.eatToken(.slash); | |
| 4750 | const percent = div orelse p.eatToken(.percent); | |
| 4751 | const tag = p.tokToTag(percent orelse break); | |
| 4752 | var rhs = try p.castExpr(); | |
| 4753 | try rhs.expect(p); | |
| 4754 | ||
| 4755 | if (rhs.val.isZero() and mul == null and !p.no_eval) { | |
| 4756 | const err_tag: Diagnostics.Tag = if (p.in_macro) .division_by_zero_macro else .division_by_zero; | |
| 4757 | lhs.val.tag = .unavailable; | |
| 4758 | if (div != null) { | |
| 4759 | try p.errStr(err_tag, div.?, "division"); | |
| 4760 | } else { | |
| 4761 | try p.errStr(err_tag, percent.?, "remainder"); | |
| 4762 | } | |
| 4763 | if (p.in_macro) return error.ParsingFailed; | |
| 4764 | } | |
| 4765 | ||
| 4766 | if (try lhs.adjustTypes(percent.?, &rhs, p, if (tag == .mod_expr) .integer else .arithmetic)) { | |
| 4767 | if (mul != null) { | |
| 4768 | if (lhs.val.mul(lhs.val, rhs.val, lhs.ty, p.pp.comp)) try p.errOverflow(mul.?, lhs); | |
| 4769 | } else if (div != null) { | |
| 4770 | lhs.val = Value.div(lhs.val, rhs.val, lhs.ty, p.pp.comp); | |
| 4771 | } else { | |
| 4772 | var res = Value.rem(lhs.val, rhs.val, lhs.ty, p.pp.comp); | |
| 4773 | if (res.tag == .unavailable) { | |
| 4774 | if (p.in_macro) { | |
| 4775 | // match clang behavior by defining invalid remainder to be zero in macros | |
| 4776 | res = Value.int(0); | |
| 4777 | } else { | |
| 4778 | try lhs.saveValue(p); | |
| 4779 | try rhs.saveValue(p); | |
| 4780 | } | |
| 4781 | } | |
| 4782 | lhs.val = res; | |
| 4783 | } | |
| 4784 | } | |
| 4785 | ||
| 4786 | try lhs.bin(p, tag, rhs); | |
| 4787 | } | |
| 4788 | return lhs; | |
| 4789 | } | |
| 4790 | ||
| 4791 | /// This will always be the last message, if present | |
| 4792 | fn removeUnusedWarningForTok(p: *Parser, last_expr_tok: TokenIndex) void { | |
| 4793 | if (last_expr_tok == 0) return; | |
| 4794 | if (p.pp.comp.diag.list.items.len == 0) return; | |
| 4795 | ||
| 4796 | const last_expr_loc = p.pp.tokens.items(.loc)[last_expr_tok]; | |
| 4797 | const last_msg = p.pp.comp.diag.list.items[p.pp.comp.diag.list.items.len - 1]; | |
| 4798 | ||
| 4799 | if (last_msg.tag == .unused_value and last_msg.loc.eql(last_expr_loc)) { | |
| 4800 | p.pp.comp.diag.list.items.len = p.pp.comp.diag.list.items.len - 1; | |
| 4801 | } | |
| 4802 | } | |
| 4803 | ||
| 4804 | /// castExpr | |
| 4805 | /// : '(' compoundStmt ')' | |
| 4806 | /// | '(' typeName ')' castExpr | |
| 4807 | /// | '(' typeName ')' '{' initializerItems '}' | |
| 4808 | /// | __builtin_choose_expr '(' constExpr ',' assignExpr ',' assignExpr ')' | |
| 4809 | /// | __builtin_va_arg '(' assignExpr ',' typeName ')' | |
| 4810 | /// | unExpr | |
| 4811 | fn castExpr(p: *Parser) Error!Result { | |
| 4812 | if (p.eatToken(.l_paren)) |l_paren| cast_expr: { | |
| 4813 | if (p.tok_ids[p.tok_i] == .l_brace) { | |
| 4814 | try p.err(.gnu_statement_expression); | |
| 4815 | if (p.func.ty == null) { | |
| 4816 | try p.err(.stmt_expr_not_allowed_file_scope); | |
| 4817 | return error.ParsingFailed; | |
| 4818 | } | |
| 4819 | var stmt_expr_state: StmtExprState = .{}; | |
| 4820 | const body_node = (try p.compoundStmt(false, &stmt_expr_state)).?; // compoundStmt only returns null if .l_brace isn't the first token | |
| 4821 | p.removeUnusedWarningForTok(stmt_expr_state.last_expr_tok); | |
| 4822 | ||
| 4823 | var res = Result{ | |
| 4824 | .node = body_node, | |
| 4825 | .ty = stmt_expr_state.last_expr_res.ty, | |
| 4826 | .val = stmt_expr_state.last_expr_res.val, | |
| 4827 | }; | |
| 4828 | try p.expectClosing(l_paren, .r_paren); | |
| 4829 | try res.un(p, .stmt_expr); | |
| 4830 | return res; | |
| 4831 | } | |
| 4832 | const ty = (try p.typeName()) orelse { | |
| 4833 | p.tok_i -= 1; | |
| 4834 | break :cast_expr; | |
| 4835 | }; | |
| 4836 | try p.expectClosing(l_paren, .r_paren); | |
| 4837 | ||
| 4838 | if (p.tok_ids[p.tok_i] == .l_brace) { | |
| 4839 | // compound literal | |
| 4840 | if (ty.isFunc()) { | |
| 4841 | try p.err(.func_init); | |
| 4842 | } else if (ty.is(.variable_len_array)) { | |
| 4843 | try p.err(.vla_init); | |
| 4844 | } else if (ty.hasIncompleteSize() and !ty.is(.incomplete_array)) { | |
| 4845 | try p.errStr(.variable_incomplete_ty, p.tok_i, try p.typeStr(ty)); | |
| 4846 | return error.ParsingFailed; | |
| 4847 | } | |
| 4848 | var init_list_expr = try p.initializer(ty); | |
| 4849 | try init_list_expr.un(p, .compound_literal_expr); | |
| 4850 | return init_list_expr; | |
| 4851 | } | |
| 4852 | ||
| 4853 | var operand = try p.castExpr(); | |
| 4854 | try operand.expect(p); | |
| 4855 | if (ty.is(.void)) { | |
| 4856 | // everything can cast to void | |
| 4857 | operand.val.tag = .unavailable; | |
| 4858 | } else if (ty.isInt() or ty.isFloat() or ty.isPtr()) cast: { | |
| 4859 | const old_float = operand.ty.isFloat(); | |
| 4860 | const new_float = ty.isFloat(); | |
| 4861 | ||
| 4862 | if (new_float and operand.ty.isPtr()) { | |
| 4863 | try p.errStr(.invalid_cast_to_float, l_paren, try p.typeStr(operand.ty)); | |
| 4864 | return error.ParsingFailed; | |
| 4865 | } else if (old_float and ty.isPtr()) { | |
| 4866 | try p.errStr(.invalid_cast_to_pointer, l_paren, try p.typeStr(operand.ty)); | |
| 4867 | return error.ParsingFailed; | |
| 4868 | } | |
| 4869 | if (operand.val.tag == .unavailable) break :cast; | |
| 4870 | ||
| 4871 | const old_int = operand.ty.isInt() or operand.ty.isPtr(); | |
| 4872 | const new_int = ty.isInt() or ty.isPtr(); | |
| 4873 | if (ty.is(.bool)) { | |
| 4874 | operand.val.toBool(); | |
| 4875 | } else if (old_float and new_int) { | |
| 4876 | operand.val.floatToInt(operand.ty, ty, p.pp.comp); | |
| 4877 | } else if (new_float and old_int) { | |
| 4878 | operand.val.intToFloat(operand.ty, ty, p.pp.comp); | |
| 4879 | } else if (new_float and old_float) { | |
| 4880 | operand.val.floatCast(operand.ty, ty, p.pp.comp); | |
| 4881 | } | |
| 4882 | } else { | |
| 4883 | try p.errStr(.invalid_cast_type, l_paren, try p.typeStr(operand.ty)); | |
| 4884 | return error.ParsingFailed; | |
| 4885 | } | |
| 4886 | if (ty.anyQual()) try p.errStr(.qual_cast, l_paren, try p.typeStr(ty)); | |
| 4887 | operand.ty = ty; | |
| 4888 | operand.ty.qual = .{}; | |
| 4889 | try operand.un(p, .cast_expr); | |
| 4890 | return operand; | |
| 4891 | } | |
| 4892 | switch (p.tok_ids[p.tok_i]) { | |
| 4893 | .builtin_choose_expr => return p.builtinChooseExpr(), | |
| 4894 | .builtin_va_arg => return p.builtinVaArg(), | |
| 4895 | // TODO: other special-cased builtins | |
| 4896 | else => {}, | |
| 4897 | } | |
| 4898 | return p.unExpr(); | |
| 4899 | } | |
| 4900 | ||
| 4901 | fn builtinChooseExpr(p: *Parser) Error!Result { | |
| 4902 | p.tok_i += 1; | |
| 4903 | const l_paren = try p.expectToken(.l_paren); | |
| 4904 | const cond_tok = p.tok_i; | |
| 4905 | var cond = try p.constExpr(); | |
| 4906 | if (cond.val.tag == .unavailable) { | |
| 4907 | try p.errTok(.builtin_choose_cond, cond_tok); | |
| 4908 | return error.ParsingFailed; | |
| 4909 | } | |
| 4910 | ||
| 4911 | _ = try p.expectToken(.comma); | |
| 4912 | ||
| 4913 | var then_expr = if (cond.val.getBool()) try p.assignExpr() else try p.parseNoEval(assignExpr); | |
| 4914 | try then_expr.expect(p); | |
| 4915 | ||
| 4916 | _ = try p.expectToken(.comma); | |
| 4917 | ||
| 4918 | var else_expr = if (!cond.val.getBool()) try p.assignExpr() else try p.parseNoEval(assignExpr); | |
| 4919 | try else_expr.expect(p); | |
| 4920 | ||
| 4921 | try p.expectClosing(l_paren, .r_paren); | |
| 4922 | ||
| 4923 | if (cond.val.getBool()) { | |
| 4924 | cond.val = then_expr.val; | |
| 4925 | cond.ty = then_expr.ty; | |
| 4926 | } else { | |
| 4927 | cond.val = else_expr.val; | |
| 4928 | cond.ty = else_expr.ty; | |
| 4929 | } | |
| 4930 | cond.node = try p.addNode(.{ | |
| 4931 | .tag = .builtin_choose_expr, | |
| 4932 | .ty = cond.ty, | |
| 4933 | .data = .{ .if3 = .{ .cond = cond.node, .body = (try p.addList(&.{ then_expr.node, else_expr.node })).start } }, | |
| 4934 | }); | |
| 4935 | return cond; | |
| 4936 | } | |
| 4937 | ||
| 4938 | fn builtinVaArg(p: *Parser) Error!Result { | |
| 4939 | const builtin_tok = p.tok_i; | |
| 4940 | p.tok_i += 1; | |
| 4941 | ||
| 4942 | const l_paren = try p.expectToken(.l_paren); | |
| 4943 | const va_list_tok = p.tok_i; | |
| 4944 | var va_list = try p.assignExpr(); | |
| 4945 | try va_list.expect(p); | |
| 4946 | try va_list.lvalConversion(p); | |
| 4947 | ||
| 4948 | _ = try p.expectToken(.comma); | |
| 4949 | ||
| 4950 | const ty = (try p.typeName()) orelse { | |
| 4951 | try p.err(.expected_type); | |
| 4952 | return error.ParsingFailed; | |
| 4953 | }; | |
| 4954 | try p.expectClosing(l_paren, .r_paren); | |
| 4955 | ||
| 4956 | if (!va_list.ty.eql(p.pp.comp.types.va_list, p.pp.comp, true)) { | |
| 4957 | try p.errStr(.incompatible_va_arg, va_list_tok, try p.typeStr(va_list.ty)); | |
| 4958 | return error.ParsingFailed; | |
| 4959 | } | |
| 4960 | ||
| 4961 | return Result{ .ty = ty, .node = try p.addNode(.{ | |
| 4962 | .tag = .builtin_call_expr_one, | |
| 4963 | .ty = ty, | |
| 4964 | .data = .{ .decl = .{ .name = builtin_tok, .node = va_list.node } }, | |
| 4965 | }) }; | |
| 4966 | } | |
| 4967 | ||
| 4968 | /// unExpr | |
| 4969 | /// : primaryExpr suffixExpr* | |
| 4970 | /// | '&&' IDENTIFIER | |
| 4971 | /// | ('&' | '*' | '+' | '-' | '~' | '!' | '++' | '--' | keyword_extension) castExpr | |
| 4972 | /// | keyword_sizeof unExpr | |
| 4973 | /// | keyword_sizeof '(' typeName ')' | |
| 4974 | /// | keyword_alignof '(' typeName ')' | |
| 4975 | fn unExpr(p: *Parser) Error!Result { | |
| 4976 | const tok = p.tok_i; | |
| 4977 | switch (p.tok_ids[tok]) { | |
| 4978 | .ampersand_ampersand => { | |
| 4979 | const address_tok = p.tok_i; | |
| 4980 | p.tok_i += 1; | |
| 4981 | const name_tok = try p.expectIdentifier(); | |
| 4982 | try p.errTok(.gnu_label_as_value, address_tok); | |
| 4983 | p.contains_address_of_label = true; | |
| 4984 | ||
| 4985 | const str = p.tokSlice(name_tok); | |
| 4986 | if (p.findLabel(str) == null) { | |
| 4987 | try p.labels.append(.{ .unresolved_goto = name_tok }); | |
| 4988 | } | |
| 4989 | const elem_ty = try p.arena.create(Type); | |
| 4990 | elem_ty.* = .{ .specifier = .void }; | |
| 4991 | const result_ty = Type{ .specifier = .pointer, .data = .{ .sub_type = elem_ty } }; | |
| 4992 | return Result{ | |
| 4993 | .node = try p.addNode(.{ | |
| 4994 | .tag = .addr_of_label, | |
| 4995 | .data = .{ .decl_ref = name_tok }, | |
| 4996 | .ty = result_ty, | |
| 4997 | }), | |
| 4998 | .ty = result_ty, | |
| 4999 | }; | |
| 5000 | }, | |
| 5001 | .ampersand => { | |
| 5002 | if (p.in_macro) { | |
| 5003 | try p.err(.invalid_preproc_operator); | |
| 5004 | return error.ParsingFailed; | |
| 5005 | } | |
| 5006 | p.tok_i += 1; | |
| 5007 | var operand = try p.castExpr(); | |
| 5008 | try operand.expect(p); | |
| 5009 | ||
| 5010 | const slice = p.nodes.slice(); | |
| 5011 | if (!Tree.isLval(slice, p.data.items, p.value_map, operand.node)) { | |
| 5012 | try p.errTok(.addr_of_rvalue, tok); | |
| 5013 | } | |
| 5014 | if (operand.ty.qual.register) try p.errTok(.addr_of_register, tok); | |
| 5015 | ||
| 5016 | const elem_ty = try p.arena.create(Type); | |
| 5017 | elem_ty.* = operand.ty; | |
| 5018 | operand.ty = Type{ | |
| 5019 | .specifier = .pointer, | |
| 5020 | .data = .{ .sub_type = elem_ty }, | |
| 5021 | }; | |
| 5022 | try operand.saveValue(p); | |
| 5023 | try operand.un(p, .addr_of_expr); | |
| 5024 | return operand; | |
| 5025 | }, | |
| 5026 | .asterisk => { | |
| 5027 | const asterisk_loc = p.tok_i; | |
| 5028 | p.tok_i += 1; | |
| 5029 | var operand = try p.castExpr(); | |
| 5030 | try operand.expect(p); | |
| 5031 | ||
| 5032 | if (operand.ty.isArray() or operand.ty.isPtr()) { | |
| 5033 | operand.ty = operand.ty.elemType(); | |
| 5034 | } else if (!operand.ty.isFunc()) { | |
| 5035 | try p.errTok(.indirection_ptr, tok); | |
| 5036 | } | |
| 5037 | if (operand.ty.hasIncompleteSize() and !operand.ty.is(.void)) { | |
| 5038 | try p.errStr(.deref_incomplete_ty_ptr, asterisk_loc, try p.typeStr(operand.ty)); | |
| 5039 | } | |
| 5040 | operand.ty.qual = .{}; | |
| 5041 | try operand.un(p, .deref_expr); | |
| 5042 | return operand; | |
| 5043 | }, | |
| 5044 | .plus => { | |
| 5045 | p.tok_i += 1; | |
| 5046 | ||
| 5047 | var operand = try p.castExpr(); | |
| 5048 | try operand.expect(p); | |
| 5049 | try operand.lvalConversion(p); | |
| 5050 | if (!operand.ty.isInt() and !operand.ty.isFloat()) | |
| 5051 | try p.errStr(.invalid_argument_un, tok, try p.typeStr(operand.ty)); | |
| 5052 | ||
| 5053 | if (operand.ty.isInt()) try operand.intCast(p, operand.ty.integerPromotion(p.pp.comp)); | |
| 5054 | return operand; | |
| 5055 | }, | |
| 5056 | .minus => { | |
| 5057 | p.tok_i += 1; | |
| 5058 | ||
| 5059 | var operand = try p.castExpr(); | |
| 5060 | try operand.expect(p); | |
| 5061 | try operand.lvalConversion(p); | |
| 5062 | if (!operand.ty.isInt() and !operand.ty.isFloat()) | |
| 5063 | try p.errStr(.invalid_argument_un, tok, try p.typeStr(operand.ty)); | |
| 5064 | ||
| 5065 | if (operand.ty.isInt()) try operand.intCast(p, operand.ty.integerPromotion(p.pp.comp)); | |
| 5066 | if (operand.val.tag != .unavailable) { | |
| 5067 | _ = operand.val.sub(operand.val.zero(), operand.val, operand.ty, p.pp.comp); | |
| 5068 | } | |
| 5069 | try operand.un(p, .negate_expr); | |
| 5070 | return operand; | |
| 5071 | }, | |
| 5072 | .plus_plus => { | |
| 5073 | p.tok_i += 1; | |
| 5074 | ||
| 5075 | var operand = try p.castExpr(); | |
| 5076 | try operand.expect(p); | |
| 5077 | if (!operand.ty.isInt() and !operand.ty.isFloat() and !operand.ty.isReal() and !operand.ty.isPtr()) | |
| 5078 | try p.errStr(.invalid_argument_un, tok, try p.typeStr(operand.ty)); | |
| 5079 | ||
| 5080 | if (!Tree.isLval(p.nodes.slice(), p.data.items, p.value_map, operand.node) or operand.ty.isConst()) { | |
| 5081 | try p.errTok(.not_assignable, tok); | |
| 5082 | return error.ParsingFailed; | |
| 5083 | } | |
| 5084 | if (operand.ty.isInt()) try operand.intCast(p, operand.ty.integerPromotion(p.pp.comp)); | |
| 5085 | ||
| 5086 | if (operand.val.tag != .unavailable) { | |
| 5087 | if (operand.val.add(operand.val, operand.val.one(), operand.ty, p.pp.comp)) | |
| 5088 | try p.errOverflow(tok, operand); | |
| 5089 | } | |
| 5090 | ||
| 5091 | try operand.un(p, .pre_inc_expr); | |
| 5092 | return operand; | |
| 5093 | }, | |
| 5094 | .minus_minus => { | |
| 5095 | p.tok_i += 1; | |
| 5096 | ||
| 5097 | var operand = try p.castExpr(); | |
| 5098 | try operand.expect(p); | |
| 5099 | if (!operand.ty.isInt() and !operand.ty.isFloat() and !operand.ty.isReal() and !operand.ty.isPtr()) | |
| 5100 | try p.errStr(.invalid_argument_un, tok, try p.typeStr(operand.ty)); | |
| 5101 | ||
| 5102 | if (!Tree.isLval(p.nodes.slice(), p.data.items, p.value_map, operand.node) or operand.ty.isConst()) { | |
| 5103 | try p.errTok(.not_assignable, tok); | |
| 5104 | return error.ParsingFailed; | |
| 5105 | } | |
| 5106 | if (operand.ty.isInt()) try operand.intCast(p, operand.ty.integerPromotion(p.pp.comp)); | |
| 5107 | ||
| 5108 | if (operand.val.tag != .unavailable) { | |
| 5109 | if (operand.val.sub(operand.val, operand.val.one(), operand.ty, p.pp.comp)) | |
| 5110 | try p.errOverflow(tok, operand); | |
| 5111 | } | |
| 5112 | ||
| 5113 | try operand.un(p, .pre_dec_expr); | |
| 5114 | return operand; | |
| 5115 | }, | |
| 5116 | .tilde => { | |
| 5117 | p.tok_i += 1; | |
| 5118 | ||
| 5119 | var operand = try p.castExpr(); | |
| 5120 | try operand.expect(p); | |
| 5121 | try operand.lvalConversion(p); | |
| 5122 | if (!operand.ty.isInt()) try p.errStr(.invalid_argument_un, tok, try p.typeStr(operand.ty)); | |
| 5123 | if (operand.ty.isInt()) { | |
| 5124 | try operand.intCast(p, operand.ty.integerPromotion(p.pp.comp)); | |
| 5125 | if (operand.val.tag != .unavailable) { | |
| 5126 | operand.val = operand.val.bitNot(operand.ty, p.pp.comp); | |
| 5127 | } | |
| 5128 | } else { | |
| 5129 | operand.val.tag = .unavailable; | |
| 5130 | } | |
| 5131 | try operand.un(p, .bit_not_expr); | |
| 5132 | return operand; | |
| 5133 | }, | |
| 5134 | .bang => { | |
| 5135 | p.tok_i += 1; | |
| 5136 | ||
| 5137 | var operand = try p.castExpr(); | |
| 5138 | try operand.expect(p); | |
| 5139 | try operand.lvalConversion(p); | |
| 5140 | if (!operand.ty.isInt() and !operand.ty.isFloat() and !operand.ty.isPtr()) | |
| 5141 | try p.errStr(.invalid_argument_un, tok, try p.typeStr(operand.ty)); | |
| 5142 | ||
| 5143 | if (operand.ty.isInt()) try operand.intCast(p, operand.ty.integerPromotion(p.pp.comp)); | |
| 5144 | if (operand.val.tag != .unavailable) { | |
| 5145 | const res = Value.int(@boolToInt(!operand.val.getBool())); | |
| 5146 | operand.val = res; | |
| 5147 | } | |
| 5148 | operand.ty = .{ .specifier = .int }; | |
| 5149 | try operand.un(p, .bool_not_expr); | |
| 5150 | return operand; | |
| 5151 | }, | |
| 5152 | .keyword_sizeof => { | |
| 5153 | p.tok_i += 1; | |
| 5154 | const expected_paren = p.tok_i; | |
| 5155 | var res = Result{}; | |
| 5156 | if (try p.typeName()) |ty| { | |
| 5157 | res.ty = ty; | |
| 5158 | try p.errTok(.expected_parens_around_typename, expected_paren); | |
| 5159 | } else if (p.eatToken(.l_paren)) |l_paren| { | |
| 5160 | if (try p.typeName()) |ty| { | |
| 5161 | res.ty = ty; | |
| 5162 | try p.expectClosing(l_paren, .r_paren); | |
| 5163 | } else { | |
| 5164 | p.tok_i = expected_paren; | |
| 5165 | res = try p.parseNoEval(unExpr); | |
| 5166 | } | |
| 5167 | } else { | |
| 5168 | res = try p.parseNoEval(unExpr); | |
| 5169 | } | |
| 5170 | ||
| 5171 | if (res.ty.sizeof(p.pp.comp)) |size| { | |
| 5172 | res.val = .{ .tag = .int, .data = .{ .int = size } }; | |
| 5173 | } else { | |
| 5174 | res.val.tag = .unavailable; | |
| 5175 | try p.errStr(.invalid_sizeof, expected_paren - 1, try p.typeStr(res.ty)); | |
| 5176 | } | |
| 5177 | res.ty = p.pp.comp.types.size; | |
| 5178 | try res.un(p, .sizeof_expr); | |
| 5179 | return res; | |
| 5180 | }, | |
| 5181 | .keyword_alignof, .keyword_alignof1, .keyword_alignof2 => { | |
| 5182 | p.tok_i += 1; | |
| 5183 | const expected_paren = p.tok_i; | |
| 5184 | var res = Result{}; | |
| 5185 | if (try p.typeName()) |ty| { | |
| 5186 | res.ty = ty; | |
| 5187 | try p.errTok(.expected_parens_around_typename, expected_paren); | |
| 5188 | } else if (p.eatToken(.l_paren)) |l_paren| { | |
| 5189 | if (try p.typeName()) |ty| { | |
| 5190 | res.ty = ty; | |
| 5191 | try p.expectClosing(l_paren, .r_paren); | |
| 5192 | } else { | |
| 5193 | p.tok_i = expected_paren; | |
| 5194 | res = try p.parseNoEval(unExpr); | |
| 5195 | try p.errTok(.alignof_expr, expected_paren); | |
| 5196 | } | |
| 5197 | } else { | |
| 5198 | res = try p.parseNoEval(unExpr); | |
| 5199 | try p.errTok(.alignof_expr, expected_paren); | |
| 5200 | } | |
| 5201 | ||
| 5202 | res.val = Value.int(res.ty.alignof(p.pp.comp)); | |
| 5203 | res.ty = p.pp.comp.types.size; | |
| 5204 | try res.un(p, .alignof_expr); | |
| 5205 | return res; | |
| 5206 | }, | |
| 5207 | .keyword_extension => { | |
| 5208 | p.tok_i += 1; | |
| 5209 | const saved_extension = p.extension_suppressed; | |
| 5210 | defer p.extension_suppressed = saved_extension; | |
| 5211 | p.extension_suppressed = true; | |
| 5212 | ||
| 5213 | var child = try p.castExpr(); | |
| 5214 | try child.expect(p); | |
| 5215 | return child; | |
| 5216 | }, | |
| 5217 | else => { | |
| 5218 | var lhs = try p.primaryExpr(); | |
| 5219 | if (lhs.empty(p)) return lhs; | |
| 5220 | while (true) { | |
| 5221 | const suffix = try p.suffixExpr(lhs); | |
| 5222 | if (suffix.empty(p)) break; | |
| 5223 | lhs = suffix; | |
| 5224 | } | |
| 5225 | return lhs; | |
| 5226 | }, | |
| 5227 | } | |
| 5228 | } | |
| 5229 | ||
| 5230 | /// suffixExpr | |
| 5231 | /// : '[' expr ']' | |
| 5232 | /// | '(' argumentExprList? ')' | |
| 5233 | /// | '.' IDENTIFIER | |
| 5234 | /// | '->' IDENTIFIER | |
| 5235 | /// | '++' | |
| 5236 | /// | '--' | |
| 5237 | /// argumentExprList : assignExpr (',' assignExpr)* | |
| 5238 | fn suffixExpr(p: *Parser, lhs: Result) Error!Result { | |
| 5239 | assert(!lhs.empty(p)); | |
| 5240 | switch (p.tok_ids[p.tok_i]) { | |
| 5241 | .l_paren => return p.callExpr(lhs), | |
| 5242 | .plus_plus => { | |
| 5243 | defer p.tok_i += 1; | |
| 5244 | ||
| 5245 | var operand = lhs; | |
| 5246 | if (!operand.ty.isInt() and !operand.ty.isFloat() and !operand.ty.isReal() and !operand.ty.isPtr()) | |
| 5247 | try p.errStr(.invalid_argument_un, p.tok_i, try p.typeStr(operand.ty)); | |
| 5248 | ||
| 5249 | if (!Tree.isLval(p.nodes.slice(), p.data.items, p.value_map, operand.node) or operand.ty.isConst()) { | |
| 5250 | try p.err(.not_assignable); | |
| 5251 | return error.ParsingFailed; | |
| 5252 | } | |
| 5253 | if (operand.ty.isInt()) try operand.intCast(p, operand.ty.integerPromotion(p.pp.comp)); | |
| 5254 | ||
| 5255 | try operand.un(p, .post_dec_expr); | |
| 5256 | return operand; | |
| 5257 | }, | |
| 5258 | .minus_minus => { | |
| 5259 | defer p.tok_i += 1; | |
| 5260 | ||
| 5261 | var operand = lhs; | |
| 5262 | if (!operand.ty.isInt() and !operand.ty.isFloat() and !operand.ty.isReal() and !operand.ty.isPtr()) | |
| 5263 | try p.errStr(.invalid_argument_un, p.tok_i, try p.typeStr(operand.ty)); | |
| 5264 | ||
| 5265 | if (!Tree.isLval(p.nodes.slice(), p.data.items, p.value_map, operand.node) or operand.ty.isConst()) { | |
| 5266 | try p.err(.not_assignable); | |
| 5267 | return error.ParsingFailed; | |
| 5268 | } | |
| 5269 | if (operand.ty.isInt()) try operand.intCast(p, operand.ty.integerPromotion(p.pp.comp)); | |
| 5270 | ||
| 5271 | try operand.un(p, .post_dec_expr); | |
| 5272 | return operand; | |
| 5273 | }, | |
| 5274 | .l_bracket => { | |
| 5275 | const l_bracket = p.tok_i; | |
| 5276 | p.tok_i += 1; | |
| 5277 | var index = try p.expr(); | |
| 5278 | try index.expect(p); | |
| 5279 | try p.expectClosing(l_bracket, .r_bracket); | |
| 5280 | ||
| 5281 | const l_ty = lhs.ty; | |
| 5282 | const r_ty = index.ty; | |
| 5283 | var ptr = lhs; | |
| 5284 | try ptr.lvalConversion(p); | |
| 5285 | try index.lvalConversion(p); | |
| 5286 | if (ptr.ty.isPtr()) { | |
| 5287 | ptr.ty = ptr.ty.elemType(); | |
| 5288 | if (!index.ty.isInt()) try p.errTok(.invalid_index, l_bracket); | |
| 5289 | try p.checkArrayBounds(index, l_ty, l_bracket); | |
| 5290 | } else if (index.ty.isPtr()) { | |
| 5291 | index.ty = index.ty.elemType(); | |
| 5292 | if (!ptr.ty.isInt()) try p.errTok(.invalid_index, l_bracket); | |
| 5293 | try p.checkArrayBounds(ptr, r_ty, l_bracket); | |
| 5294 | std.mem.swap(Result, &ptr, &index); | |
| 5295 | } else { | |
| 5296 | try p.errTok(.invalid_subscript, l_bracket); | |
| 5297 | } | |
| 5298 | ||
| 5299 | try ptr.saveValue(p); | |
| 5300 | try index.saveValue(p); | |
| 5301 | try ptr.bin(p, .array_access_expr, index); | |
| 5302 | return ptr; | |
| 5303 | }, | |
| 5304 | .period => { | |
| 5305 | p.tok_i += 1; | |
| 5306 | const name = try p.expectIdentifier(); | |
| 5307 | return p.fieldAccess(lhs, name, false); | |
| 5308 | }, | |
| 5309 | .arrow => { | |
| 5310 | p.tok_i += 1; | |
| 5311 | const name = try p.expectIdentifier(); | |
| 5312 | if (lhs.ty.isArray()) { | |
| 5313 | var copy = lhs; | |
| 5314 | copy.ty.decayArray(); | |
| 5315 | try copy.un(p, .array_to_pointer); | |
| 5316 | return p.fieldAccess(copy, name, true); | |
| 5317 | } | |
| 5318 | return p.fieldAccess(lhs, name, true); | |
| 5319 | }, | |
| 5320 | else => return Result{}, | |
| 5321 | } | |
| 5322 | } | |
| 5323 | ||
| 5324 | fn fieldAccess( | |
| 5325 | p: *Parser, | |
| 5326 | lhs: Result, | |
| 5327 | field_name_tok: TokenIndex, | |
| 5328 | is_arrow: bool, | |
| 5329 | ) !Result { | |
| 5330 | const expr_ty = lhs.ty; | |
| 5331 | const is_ptr = expr_ty.isPtr(); | |
| 5332 | const expr_base_ty = if (is_ptr) expr_ty.elemType() else expr_ty; | |
| 5333 | const record_ty = expr_base_ty.canonicalize(.standard); | |
| 5334 | ||
| 5335 | switch (record_ty.specifier) { | |
| 5336 | .@"struct", .@"union" => {}, | |
| 5337 | else => { | |
| 5338 | try p.errStr(.expected_record_ty, field_name_tok, try p.typeStr(expr_ty)); | |
| 5339 | return error.ParsingFailed; | |
| 5340 | }, | |
| 5341 | } | |
| 5342 | if (record_ty.hasIncompleteSize()) { | |
| 5343 | try p.errStr(.deref_incomplete_ty_ptr, field_name_tok - 2, try p.typeStr(expr_base_ty)); | |
| 5344 | return error.ParsingFailed; | |
| 5345 | } | |
| 5346 | if (is_arrow and !is_ptr) try p.errStr(.member_expr_not_ptr, field_name_tok, try p.typeStr(expr_ty)); | |
| 5347 | if (!is_arrow and is_ptr) try p.errStr(.member_expr_ptr, field_name_tok, try p.typeStr(expr_ty)); | |
| 5348 | ||
| 5349 | const field_name = p.tokSlice(field_name_tok); | |
| 5350 | if (!record_ty.hasField(field_name)) { | |
| 5351 | p.strings.items.len = 0; | |
| 5352 | ||
| 5353 | try p.strings.writer().print("'{s}' in '", .{field_name}); | |
| 5354 | try expr_ty.print(p.strings.writer()); | |
| 5355 | try p.strings.append('\''); | |
| 5356 | ||
| 5357 | const duped = try p.pp.comp.diag.arena.allocator().dupe(u8, p.strings.items); | |
| 5358 | try p.errStr(.no_such_member, field_name_tok, duped); | |
| 5359 | return error.ParsingFailed; | |
| 5360 | } | |
| 5361 | return p.fieldAccessExtra(lhs.node, record_ty, field_name, is_arrow); | |
| 5362 | } | |
| 5363 | ||
| 5364 | fn fieldAccessExtra(p: *Parser, lhs: NodeIndex, record_ty: Type, field_name: []const u8, is_arrow: bool) Error!Result { | |
| 5365 | for (record_ty.data.record.fields) |f, i| { | |
| 5366 | if (f.isAnonymousRecord()) { | |
| 5367 | if (!f.ty.hasField(field_name)) continue; | |
| 5368 | const inner = try p.addNode(.{ | |
| 5369 | .tag = if (is_arrow) .member_access_ptr_expr else .member_access_expr, | |
| 5370 | .ty = f.ty, | |
| 5371 | .data = .{ .member = .{ .lhs = lhs, .index = @intCast(u32, i) } }, | |
| 5372 | }); | |
| 5373 | return p.fieldAccessExtra(inner, f.ty, field_name, false); | |
| 5374 | } | |
| 5375 | if (std.mem.eql(u8, field_name, f.name)) return Result{ | |
| 5376 | .ty = f.ty, | |
| 5377 | .node = try p.addNode(.{ | |
| 5378 | .tag = if (is_arrow) .member_access_ptr_expr else .member_access_expr, | |
| 5379 | .ty = f.ty, | |
| 5380 | .data = .{ .member = .{ .lhs = lhs, .index = @intCast(u32, i) } }, | |
| 5381 | }), | |
| 5382 | }; | |
| 5383 | } | |
| 5384 | // We already checked that this container has a field by the name. | |
| 5385 | unreachable; | |
| 5386 | } | |
| 5387 | ||
| 5388 | fn callExpr(p: *Parser, lhs: Result) Error!Result { | |
| 5389 | const l_paren = p.tok_i; | |
| 5390 | p.tok_i += 1; | |
| 5391 | const ty = lhs.ty.isCallable() orelse { | |
| 5392 | try p.errStr(.not_callable, l_paren, try p.typeStr(lhs.ty)); | |
| 5393 | return error.ParsingFailed; | |
| 5394 | }; | |
| 5395 | const params = ty.params(); | |
| 5396 | var func = lhs; | |
| 5397 | try func.lvalConversion(p); | |
| 5398 | ||
| 5399 | const list_buf_top = p.list_buf.items.len; | |
| 5400 | defer p.list_buf.items.len = list_buf_top; | |
| 5401 | try p.list_buf.append(func.node); | |
| 5402 | var arg_count: u32 = 0; | |
| 5403 | ||
| 5404 | const builtin_node = p.getNode(lhs.node, .builtin_call_expr_one); | |
| 5405 | ||
| 5406 | var first_after = l_paren; | |
| 5407 | while (p.eatToken(.r_paren) == null) { | |
| 5408 | const param_tok = p.tok_i; | |
| 5409 | if (arg_count == params.len) first_after = p.tok_i; | |
| 5410 | var arg = try p.assignExpr(); | |
| 5411 | try arg.expect(p); | |
| 5412 | const raw_arg_node = arg.node; | |
| 5413 | try arg.lvalConversion(p); | |
| 5414 | if (arg.ty.hasIncompleteSize() and !arg.ty.is(.void)) return error.ParsingFailed; | |
| 5415 | ||
| 5416 | if (arg_count >= params.len) { | |
| 5417 | if (arg.ty.isInt()) try arg.intCast(p, arg.ty.integerPromotion(p.pp.comp)); | |
| 5418 | if (arg.ty.is(.float)) try arg.floatCast(p, .{ .specifier = .double }); | |
| 5419 | try arg.saveValue(p); | |
| 5420 | try p.list_buf.append(arg.node); | |
| 5421 | arg_count += 1; | |
| 5422 | ||
| 5423 | _ = p.eatToken(.comma) orelse { | |
| 5424 | try p.expectClosing(l_paren, .r_paren); | |
| 5425 | break; | |
| 5426 | }; | |
| 5427 | continue; | |
| 5428 | } | |
| 5429 | ||
| 5430 | const p_ty = params[arg_count].ty; | |
| 5431 | if (p_ty.is(.special_va_start)) va_start: { | |
| 5432 | const builtin_tok = p.nodes.items(.data)[@enumToInt(builtin_node.?)].decl.name; | |
| 5433 | var func_ty = p.func.ty orelse { | |
| 5434 | try p.errTok(.va_start_not_in_func, builtin_tok); | |
| 5435 | break :va_start; | |
| 5436 | }; | |
| 5437 | if (func_ty.specifier != .var_args_func) { | |
| 5438 | try p.errTok(.va_start_fixed_args, builtin_tok); | |
| 5439 | break :va_start; | |
| 5440 | } | |
| 5441 | const func_params = func_ty.params(); | |
| 5442 | const last_param_name = func_params[func_params.len - 1].name; | |
| 5443 | const decl_ref = p.getNode(raw_arg_node, .decl_ref_expr); | |
| 5444 | if (decl_ref == null or | |
| 5445 | !mem.eql(u8, p.tokSlice(p.nodes.items(.data)[@enumToInt(decl_ref.?)].decl_ref), last_param_name)) | |
| 5446 | { | |
| 5447 | try p.errTok(.va_start_not_last_param, param_tok); | |
| 5448 | } | |
| 5449 | } else if (p_ty.is(.bool)) { | |
| 5450 | // this is ridiculous but it's what clang does | |
| 5451 | if (arg.ty.isInt() or arg.ty.isFloat() or arg.ty.isPtr()) { | |
| 5452 | try arg.boolCast(p, p_ty); | |
| 5453 | } else { | |
| 5454 | try p.errStr(.incompatible_param, param_tok, try p.typeStr(arg.ty)); | |
| 5455 | try p.errTok(.parameter_here, params[arg_count].name_tok); | |
| 5456 | } | |
| 5457 | } else if (p_ty.isInt()) { | |
| 5458 | if (arg.ty.isInt() or arg.ty.isFloat()) { | |
| 5459 | try arg.intCast(p, p_ty); | |
| 5460 | } else if (arg.ty.isPtr()) { | |
| 5461 | try p.errStr( | |
| 5462 | .implicit_ptr_to_int, | |
| 5463 | param_tok, | |
| 5464 | try p.typePairStrExtra(arg.ty, " to ", p_ty), | |
| 5465 | ); | |
| 5466 | try p.errTok(.parameter_here, params[arg_count].name_tok); | |
| 5467 | try arg.intCast(p, p_ty); | |
| 5468 | } else { | |
| 5469 | try p.errStr(.incompatible_param, param_tok, try p.typeStr(arg.ty)); | |
| 5470 | try p.errTok(.parameter_here, params[arg_count].name_tok); | |
| 5471 | } | |
| 5472 | } else if (p_ty.isFloat()) { | |
| 5473 | if (arg.ty.isInt() or arg.ty.isFloat()) { | |
| 5474 | try arg.floatCast(p, p_ty); | |
| 5475 | } else { | |
| 5476 | try p.errStr(.incompatible_param, param_tok, try p.typeStr(arg.ty)); | |
| 5477 | try p.errTok(.parameter_here, params[arg_count].name_tok); | |
| 5478 | } | |
| 5479 | } else if (p_ty.isPtr()) { | |
| 5480 | if (arg.val.isZero()) { | |
| 5481 | try arg.nullCast(p, p_ty); | |
| 5482 | } else if (arg.ty.isInt()) { | |
| 5483 | try p.errStr( | |
| 5484 | .implicit_int_to_ptr, | |
| 5485 | param_tok, | |
| 5486 | try p.typePairStrExtra(arg.ty, " to ", p_ty), | |
| 5487 | ); | |
| 5488 | try p.errTok(.parameter_here, params[arg_count].name_tok); | |
| 5489 | try arg.intCast(p, p_ty); | |
| 5490 | } else if (!arg.ty.isVoidStar() and !p_ty.isVoidStar() and !p_ty.eql(arg.ty, p.pp.comp, false)) { | |
| 5491 | try p.errStr(.incompatible_param, param_tok, try p.typeStr(arg.ty)); | |
| 5492 | try p.errTok(.parameter_here, params[arg_count].name_tok); | |
| 5493 | } | |
| 5494 | } else if (p_ty.isRecord()) { | |
| 5495 | if (!p_ty.eql(arg.ty, p.pp.comp, false)) { | |
| 5496 | try p.errStr(.incompatible_param, param_tok, try p.typeStr(arg.ty)); | |
| 5497 | try p.errTok(.parameter_here, params[arg_count].name_tok); | |
| 5498 | } | |
| 5499 | } else { | |
| 5500 | // should be unreachable | |
| 5501 | try p.errStr(.incompatible_param, param_tok, try p.typeStr(arg.ty)); | |
| 5502 | try p.errTok(.parameter_here, params[arg_count].name_tok); | |
| 5503 | } | |
| 5504 | ||
| 5505 | try arg.saveValue(p); | |
| 5506 | try p.list_buf.append(arg.node); | |
| 5507 | arg_count += 1; | |
| 5508 | ||
| 5509 | _ = p.eatToken(.comma) orelse { | |
| 5510 | try p.expectClosing(l_paren, .r_paren); | |
| 5511 | break; | |
| 5512 | }; | |
| 5513 | } | |
| 5514 | ||
| 5515 | const extra = Diagnostics.Message.Extra{ .arguments = .{ | |
| 5516 | .expected = @intCast(u32, params.len), | |
| 5517 | .actual = @intCast(u32, arg_count), | |
| 5518 | } }; | |
| 5519 | if (ty.is(.func) and params.len != arg_count) { | |
| 5520 | try p.errExtra(.expected_arguments, first_after, extra); | |
| 5521 | } | |
| 5522 | if (ty.is(.old_style_func) and params.len != arg_count) { | |
| 5523 | try p.errExtra(.expected_arguments_old, first_after, extra); | |
| 5524 | } | |
| 5525 | if (ty.is(.var_args_func) and arg_count < params.len) { | |
| 5526 | try p.errExtra(.expected_at_least_arguments, first_after, extra); | |
| 5527 | } | |
| 5528 | ||
| 5529 | if (builtin_node) |some| { | |
| 5530 | const index = @enumToInt(some); | |
| 5531 | var call_node = p.nodes.get(index); | |
| 5532 | defer p.nodes.set(index, call_node); | |
| 5533 | const args = p.list_buf.items[list_buf_top..]; | |
| 5534 | switch (arg_count) { | |
| 5535 | 0 => {}, | |
| 5536 | 1 => call_node.data.decl.node = args[1], // args[0] == func.node | |
| 5537 | else => { | |
| 5538 | call_node.tag = .builtin_call_expr; | |
| 5539 | args[0] = @intToEnum(NodeIndex, call_node.data.decl.name); | |
| 5540 | call_node.data = .{ .range = try p.addList(args) }; | |
| 5541 | }, | |
| 5542 | } | |
| 5543 | return Result{ .node = some, .ty = call_node.ty.returnType() }; | |
| 5544 | } | |
| 5545 | ||
| 5546 | var call_node: Tree.Node = .{ | |
| 5547 | .tag = .call_expr_one, | |
| 5548 | .ty = ty.returnType(), | |
| 5549 | .data = .{ .bin = .{ .lhs = func.node, .rhs = .none } }, | |
| 5550 | }; | |
| 5551 | const args = p.list_buf.items[list_buf_top..]; | |
| 5552 | switch (arg_count) { | |
| 5553 | 0 => {}, | |
| 5554 | 1 => call_node.data.bin.rhs = args[1], // args[0] == func.node | |
| 5555 | else => { | |
| 5556 | call_node.tag = .call_expr; | |
| 5557 | call_node.data = .{ .range = try p.addList(args) }; | |
| 5558 | }, | |
| 5559 | } | |
| 5560 | return Result{ .node = try p.addNode(call_node), .ty = call_node.ty }; | |
| 5561 | } | |
| 5562 | ||
| 5563 | fn checkArrayBounds(p: *Parser, index: Result, arr_ty: Type, tok: TokenIndex) !void { | |
| 5564 | if (index.val.tag == .unavailable) return; | |
| 5565 | const len = Value.int(arr_ty.arrayLen() orelse return); | |
| 5566 | ||
| 5567 | if (index.ty.isUnsignedInt(p.pp.comp)) { | |
| 5568 | if (index.val.compare(.gte, len, p.pp.comp.types.size, p.pp.comp)) | |
| 5569 | try p.errExtra(.array_after, tok, .{ .unsigned = index.val.data.int }); | |
| 5570 | } else { | |
| 5571 | if (index.val.compare(.lt, Value.int(0), index.ty, p.pp.comp)) { | |
| 5572 | try p.errExtra(.array_before, tok, .{ | |
| 5573 | .signed = index.val.signExtend(index.ty, p.pp.comp), | |
| 5574 | }); | |
| 5575 | } else if (index.val.compare(.gte, len, p.pp.comp.types.size, p.pp.comp)) { | |
| 5576 | try p.errExtra(.array_after, tok, .{ .unsigned = index.val.data.int }); | |
| 5577 | } | |
| 5578 | } | |
| 5579 | } | |
| 5580 | ||
| 5581 | /// primaryExpr | |
| 5582 | /// : IDENTIFIER | |
| 5583 | /// | INTEGER_LITERAL | |
| 5584 | /// | FLOAT_LITERAL | |
| 5585 | /// | IMAGINARY_LITERAL | |
| 5586 | /// | CHAR_LITERAL | |
| 5587 | /// | STRING_LITERAL | |
| 5588 | /// | '(' expr ')' | |
| 5589 | /// | genericSelection | |
| 5590 | fn primaryExpr(p: *Parser) Error!Result { | |
| 5591 | if (p.eatToken(.l_paren)) |l_paren| { | |
| 5592 | var e = try p.expr(); | |
| 5593 | try e.expect(p); | |
| 5594 | try p.expectClosing(l_paren, .r_paren); | |
| 5595 | try e.un(p, .paren_expr); | |
| 5596 | return e; | |
| 5597 | } | |
| 5598 | switch (p.tok_ids[p.tok_i]) { | |
| 5599 | .identifier, .extended_identifier => { | |
| 5600 | const name_tok = p.expectIdentifier() catch unreachable; | |
| 5601 | const name = p.tokSlice(name_tok); | |
| 5602 | if (p.pp.comp.builtins.get(name)) |some| { | |
| 5603 | for (p.tok_ids[p.tok_i..]) |id| switch (id) { | |
| 5604 | .r_paren => {}, // closing grouped expr | |
| 5605 | .l_paren => break, // beginning of a call | |
| 5606 | else => { | |
| 5607 | try p.errTok(.builtin_must_be_called, name_tok); | |
| 5608 | return error.ParsingFailed; | |
| 5609 | }, | |
| 5610 | }; | |
| 5611 | return Result{ | |
| 5612 | .ty = some, | |
| 5613 | .node = try p.addNode(.{ | |
| 5614 | .tag = .builtin_call_expr_one, | |
| 5615 | .ty = some, | |
| 5616 | .data = .{ .decl = .{ .name = name_tok, .node = .none } }, | |
| 5617 | }), | |
| 5618 | }; | |
| 5619 | } | |
| 5620 | const sym = p.findSymbol(name_tok, .reference) orelse { | |
| 5621 | if (p.tok_ids[p.tok_i] == .l_paren) { | |
| 5622 | // allow implicitly declaring functions before C99 like `puts("foo")` | |
| 5623 | if (mem.startsWith(u8, name, "__builtin_")) | |
| 5624 | try p.errStr(.unknown_builtin, name_tok, name) | |
| 5625 | else | |
| 5626 | try p.errStr(.implicit_func_decl, name_tok, name); | |
| 5627 | ||
| 5628 | const func_ty = try p.arena.create(Type.Func); | |
| 5629 | func_ty.* = .{ .return_type = .{ .specifier = .int }, .params = &.{} }; | |
| 5630 | const ty: Type = .{ .specifier = .old_style_func, .data = .{ .func = func_ty } }; | |
| 5631 | const node = try p.addNode(.{ | |
| 5632 | .ty = ty, | |
| 5633 | .tag = .fn_proto, | |
| 5634 | .data = .{ .decl = .{ .name = name_tok } }, | |
| 5635 | }); | |
| 5636 | ||
| 5637 | try p.decl_buf.append(node); | |
| 5638 | try p.scopes.append(.{ .decl = .{ | |
| 5639 | .name = name, | |
| 5640 | .ty = ty, | |
| 5641 | .name_tok = name_tok, | |
| 5642 | } }); | |
| 5643 | ||
| 5644 | return Result{ | |
| 5645 | .ty = ty, | |
| 5646 | .node = try p.addNode(.{ | |
| 5647 | .tag = .decl_ref_expr, | |
| 5648 | .ty = ty, | |
| 5649 | .data = .{ .decl_ref = name_tok }, | |
| 5650 | }), | |
| 5651 | }; | |
| 5652 | } | |
| 5653 | try p.errStr(.undeclared_identifier, name_tok, p.tokSlice(name_tok)); | |
| 5654 | return error.ParsingFailed; | |
| 5655 | }; | |
| 5656 | switch (sym) { | |
| 5657 | .enumeration => |e| { | |
| 5658 | var res = e.value; | |
| 5659 | try p.checkDeprecatedUnavailable(res.ty, name_tok, e.name_tok); | |
| 5660 | res.node = try p.addNode(.{ | |
| 5661 | .tag = .enumeration_ref, | |
| 5662 | .ty = res.ty, | |
| 5663 | .data = .{ .decl_ref = name_tok }, | |
| 5664 | }); | |
| 5665 | return res; | |
| 5666 | }, | |
| 5667 | .def, .decl, .param => |s| { | |
| 5668 | try p.checkDeprecatedUnavailable(s.ty, name_tok, s.name_tok); | |
| 5669 | return Result{ | |
| 5670 | .ty = s.ty, | |
| 5671 | .node = try p.addNode(.{ | |
| 5672 | .tag = .decl_ref_expr, | |
| 5673 | .ty = s.ty, | |
| 5674 | .data = .{ .decl_ref = name_tok }, | |
| 5675 | }), | |
| 5676 | }; | |
| 5677 | }, | |
| 5678 | else => unreachable, | |
| 5679 | } | |
| 5680 | }, | |
| 5681 | .macro_func, .macro_function => { | |
| 5682 | defer p.tok_i += 1; | |
| 5683 | var ty: Type = undefined; | |
| 5684 | var tok = p.tok_i; | |
| 5685 | if (p.func.ident) |some| { | |
| 5686 | ty = some.ty; | |
| 5687 | tok = p.nodes.items(.data)[@enumToInt(some.node)].decl.name; | |
| 5688 | } else if (p.func.ty) |_| { | |
| 5689 | p.strings.items.len = 0; | |
| 5690 | try p.strings.appendSlice(p.tokSlice(p.func.name)); | |
| 5691 | try p.strings.append(0); | |
| 5692 | const predef = try p.makePredefinedIdentifier(); | |
| 5693 | ty = predef.ty; | |
| 5694 | p.func.ident = predef; | |
| 5695 | } else { | |
| 5696 | p.strings.items.len = 0; | |
| 5697 | try p.strings.append(0); | |
| 5698 | const predef = try p.makePredefinedIdentifier(); | |
| 5699 | ty = predef.ty; | |
| 5700 | p.func.ident = predef; | |
| 5701 | try p.decl_buf.append(predef.node); | |
| 5702 | } | |
| 5703 | if (p.func.ty == null) try p.err(.predefined_top_level); | |
| 5704 | return Result{ | |
| 5705 | .ty = ty, | |
| 5706 | .node = try p.addNode(.{ | |
| 5707 | .tag = .decl_ref_expr, | |
| 5708 | .ty = ty, | |
| 5709 | .data = .{ .decl_ref = tok }, | |
| 5710 | }), | |
| 5711 | }; | |
| 5712 | }, | |
| 5713 | .macro_pretty_func => { | |
| 5714 | defer p.tok_i += 1; | |
| 5715 | var ty: Type = undefined; | |
| 5716 | if (p.func.pretty_ident) |some| { | |
| 5717 | ty = some.ty; | |
| 5718 | } else if (p.func.ty) |func_ty| { | |
| 5719 | p.strings.items.len = 0; | |
| 5720 | try Type.printNamed(func_ty, p.tokSlice(p.func.name), p.strings.writer()); | |
| 5721 | try p.strings.append(0); | |
| 5722 | const predef = try p.makePredefinedIdentifier(); | |
| 5723 | ty = predef.ty; | |
| 5724 | p.func.pretty_ident = predef; | |
| 5725 | } else { | |
| 5726 | p.strings.items.len = 0; | |
| 5727 | try p.strings.appendSlice("top level\x00"); | |
| 5728 | const predef = try p.makePredefinedIdentifier(); | |
| 5729 | ty = predef.ty; | |
| 5730 | p.func.pretty_ident = predef; | |
| 5731 | try p.decl_buf.append(predef.node); | |
| 5732 | } | |
| 5733 | if (p.func.ty == null) try p.err(.predefined_top_level); | |
| 5734 | return Result{ | |
| 5735 | .ty = ty, | |
| 5736 | .node = try p.addNode(.{ | |
| 5737 | .tag = .decl_ref_expr, | |
| 5738 | .ty = ty, | |
| 5739 | .data = .{ .decl_ref = p.tok_i }, | |
| 5740 | }), | |
| 5741 | }; | |
| 5742 | }, | |
| 5743 | .string_literal, | |
| 5744 | .string_literal_utf_16, | |
| 5745 | .string_literal_utf_8, | |
| 5746 | .string_literal_utf_32, | |
| 5747 | .string_literal_wide, | |
| 5748 | => return p.stringLiteral(), | |
| 5749 | .char_literal, | |
| 5750 | .char_literal_utf_16, | |
| 5751 | .char_literal_utf_32, | |
| 5752 | .char_literal_wide, | |
| 5753 | => return p.charLiteral(), | |
| 5754 | .float_literal, .imaginary_literal => |tag| { | |
| 5755 | defer p.tok_i += 1; | |
| 5756 | const ty = Type{ .specifier = .double }; | |
| 5757 | const d_val = try p.parseFloat(p.tok_i, f64); | |
| 5758 | var res = Result{ | |
| 5759 | .ty = ty, | |
| 5760 | .node = try p.addNode(.{ .tag = .double_literal, .ty = ty, .data = undefined }), | |
| 5761 | .val = Value.float(d_val), | |
| 5762 | }; | |
| 5763 | if (!p.in_macro) try p.value_map.put(res.node, res.val); | |
| 5764 | if (tag == .imaginary_literal) { | |
| 5765 | try p.err(.gnu_imaginary_constant); | |
| 5766 | res.ty = .{ .specifier = .complex_double }; | |
| 5767 | res.val.tag = .unavailable; | |
| 5768 | try res.un(p, .imaginary_literal); | |
| 5769 | } | |
| 5770 | return res; | |
| 5771 | }, | |
| 5772 | .float_literal_f, .imaginary_literal_f => |tag| { | |
| 5773 | defer p.tok_i += 1; | |
| 5774 | const ty = Type{ .specifier = .float }; | |
| 5775 | const f_val = try p.parseFloat(p.tok_i, f64); | |
| 5776 | var res = Result{ | |
| 5777 | .ty = ty, | |
| 5778 | .node = try p.addNode(.{ .tag = .float_literal, .ty = ty, .data = undefined }), | |
| 5779 | .val = Value.float(f_val), | |
| 5780 | }; | |
| 5781 | if (!p.in_macro) try p.value_map.put(res.node, res.val); | |
| 5782 | if (tag == .imaginary_literal_f) { | |
| 5783 | try p.err(.gnu_imaginary_constant); | |
| 5784 | res.ty = .{ .specifier = .complex_float }; | |
| 5785 | res.val.tag = .unavailable; | |
| 5786 | try res.un(p, .imaginary_literal); | |
| 5787 | } | |
| 5788 | return res; | |
| 5789 | }, | |
| 5790 | .float_literal_l => return p.todo("long double literals"), | |
| 5791 | .imaginary_literal_l => { | |
| 5792 | try p.err(.gnu_imaginary_constant); | |
| 5793 | return p.todo("long double imaginary literals"); | |
| 5794 | }, | |
| 5795 | .zero => { | |
| 5796 | p.tok_i += 1; | |
| 5797 | var res: Result = .{ .val = Value.int(0) }; | |
| 5798 | res.node = try p.addNode(.{ .tag = .int_literal, .ty = res.ty, .data = undefined }); | |
| 5799 | if (!p.in_macro) try p.value_map.put(res.node, res.val); | |
| 5800 | return res; | |
| 5801 | }, | |
| 5802 | .one => { | |
| 5803 | p.tok_i += 1; | |
| 5804 | var res: Result = .{ .val = Value.int(1) }; | |
| 5805 | res.node = try p.addNode(.{ .tag = .int_literal, .ty = res.ty, .data = undefined }); | |
| 5806 | if (!p.in_macro) try p.value_map.put(res.node, res.val); | |
| 5807 | return res; | |
| 5808 | }, | |
| 5809 | .integer_literal, | |
| 5810 | .integer_literal_u, | |
| 5811 | .integer_literal_l, | |
| 5812 | .integer_literal_lu, | |
| 5813 | .integer_literal_ll, | |
| 5814 | .integer_literal_llu, | |
| 5815 | => return p.integerLiteral(), | |
| 5816 | .keyword_generic => return p.genericSelection(), | |
| 5817 | else => return Result{}, | |
| 5818 | } | |
| 5819 | } | |
| 5820 | ||
| 5821 | fn makePredefinedIdentifier(p: *Parser) !Result { | |
| 5822 | const slice = p.strings.items; | |
| 5823 | const elem_ty = .{ .specifier = .char, .qual = .{ .@"const" = true } }; | |
| 5824 | const arr_ty = try p.arena.create(Type.Array); | |
| 5825 | arr_ty.* = .{ .elem = elem_ty, .len = slice.len }; | |
| 5826 | const ty: Type = .{ .specifier = .array, .data = .{ .array = arr_ty } }; | |
| 5827 | ||
| 5828 | const val = Value.bytes(try p.arena.dupe(u8, slice)); | |
| 5829 | const str_lit = try p.addNode(.{ .tag = .string_literal_expr, .ty = ty, .data = undefined }); | |
| 5830 | if (!p.in_macro) try p.value_map.put(str_lit, val); | |
| 5831 | ||
| 5832 | return Result{ .ty = ty, .node = try p.addNode(.{ | |
| 5833 | .tag = .implicit_static_var, | |
| 5834 | .ty = ty, | |
| 5835 | .data = .{ .decl = .{ .name = p.tok_i, .node = str_lit } }, | |
| 5836 | }) }; | |
| 5837 | } | |
| 5838 | ||
| 5839 | fn stringLiteral(p: *Parser) Error!Result { | |
| 5840 | var start = p.tok_i; | |
| 5841 | // use 1 for wchar_t | |
| 5842 | var width: ?u8 = null; | |
| 5843 | while (true) { | |
| 5844 | switch (p.tok_ids[p.tok_i]) { | |
| 5845 | .string_literal => {}, | |
| 5846 | .string_literal_utf_16 => if (width) |some| { | |
| 5847 | if (some != 16) try p.err(.unsupported_str_cat); | |
| 5848 | } else { | |
| 5849 | width = 16; | |
| 5850 | }, | |
| 5851 | .string_literal_utf_8 => if (width) |some| { | |
| 5852 | if (some != 8) try p.err(.unsupported_str_cat); | |
| 5853 | } else { | |
| 5854 | width = 8; | |
| 5855 | }, | |
| 5856 | .string_literal_utf_32 => if (width) |some| { | |
| 5857 | if (some != 32) try p.err(.unsupported_str_cat); | |
| 5858 | } else { | |
| 5859 | width = 32; | |
| 5860 | }, | |
| 5861 | .string_literal_wide => if (width) |some| { | |
| 5862 | if (some != 1) try p.err(.unsupported_str_cat); | |
| 5863 | } else { | |
| 5864 | width = 1; | |
| 5865 | }, | |
| 5866 | else => break, | |
| 5867 | } | |
| 5868 | p.tok_i += 1; | |
| 5869 | } | |
| 5870 | if (width == null) width = 8; | |
| 5871 | if (width.? != 8) return p.todo("unicode string literals"); | |
| 5872 | p.strings.items.len = 0; | |
| 5873 | while (start < p.tok_i) : (start += 1) { | |
| 5874 | var slice = p.tokSlice(start); | |
| 5875 | slice = slice[0 .. slice.len - 1]; | |
| 5876 | var i = mem.indexOf(u8, slice, "\"").? + 1; | |
| 5877 | try p.strings.ensureUnusedCapacity(slice.len); | |
| 5878 | while (i < slice.len) : (i += 1) { | |
| 5879 | switch (slice[i]) { | |
| 5880 | '\\' => { | |
| 5881 | i += 1; | |
| 5882 | switch (slice[i]) { | |
| 5883 | '\n' => i += 1, | |
| 5884 | '\r' => i += 2, | |
| 5885 | '\'', '\"', '\\', '?' => |c| p.strings.appendAssumeCapacity(c), | |
| 5886 | 'n' => p.strings.appendAssumeCapacity('\n'), | |
| 5887 | 'r' => p.strings.appendAssumeCapacity('\r'), | |
| 5888 | 't' => p.strings.appendAssumeCapacity('\t'), | |
| 5889 | 'a' => p.strings.appendAssumeCapacity(0x07), | |
| 5890 | 'b' => p.strings.appendAssumeCapacity(0x08), | |
| 5891 | 'e' => p.strings.appendAssumeCapacity(0x1B), | |
| 5892 | 'f' => p.strings.appendAssumeCapacity(0x0C), | |
| 5893 | 'v' => p.strings.appendAssumeCapacity(0x0B), | |
| 5894 | 'x' => p.strings.appendAssumeCapacity(try p.parseNumberEscape(start, 16, slice, &i)), | |
| 5895 | '0'...'7' => p.strings.appendAssumeCapacity(try p.parseNumberEscape(start, 8, slice, &i)), | |
| 5896 | 'u' => try p.parseUnicodeEscape(start, 4, slice, &i), | |
| 5897 | 'U' => try p.parseUnicodeEscape(start, 8, slice, &i), | |
| 5898 | else => unreachable, | |
| 5899 | } | |
| 5900 | }, | |
| 5901 | else => |c| p.strings.appendAssumeCapacity(c), | |
| 5902 | } | |
| 5903 | } | |
| 5904 | } | |
| 5905 | try p.strings.append(0); | |
| 5906 | const slice = p.strings.items; | |
| 5907 | ||
| 5908 | const arr_ty = try p.arena.create(Type.Array); | |
| 5909 | arr_ty.* = .{ .elem = .{ .specifier = .char }, .len = slice.len }; | |
| 5910 | var res: Result = .{ | |
| 5911 | .ty = .{ | |
| 5912 | .specifier = .array, | |
| 5913 | .data = .{ .array = arr_ty }, | |
| 5914 | }, | |
| 5915 | .val = Value.bytes(try p.arena.dupe(u8, slice)), | |
| 5916 | }; | |
| 5917 | res.node = try p.addNode(.{ .tag = .string_literal_expr, .ty = res.ty, .data = undefined }); | |
| 5918 | if (!p.in_macro) try p.value_map.put(res.node, res.val); | |
| 5919 | return res; | |
| 5920 | } | |
| 5921 | ||
| 5922 | fn parseNumberEscape(p: *Parser, tok: TokenIndex, base: u8, slice: []const u8, i: *usize) !u8 { | |
| 5923 | if (base == 16) i.* += 1; // skip x | |
| 5924 | var char: u8 = 0; | |
| 5925 | var reported = false; | |
| 5926 | while (i.* < slice.len) : (i.* += 1) { | |
| 5927 | const val = std.fmt.charToDigit(slice[i.*], base) catch break; // validated by Tokenizer | |
| 5928 | if (@mulWithOverflow(u8, char, base, &char) and !reported) { | |
| 5929 | try p.errExtra(.escape_sequence_overflow, tok, .{ .unsigned = i.* }); | |
| 5930 | reported = true; | |
| 5931 | } | |
| 5932 | char += val; | |
| 5933 | } | |
| 5934 | i.* -= 1; | |
| 5935 | return char; | |
| 5936 | } | |
| 5937 | ||
| 5938 | fn parseUnicodeEscape(p: *Parser, tok: TokenIndex, count: u8, slice: []const u8, i: *usize) !void { | |
| 5939 | const c = std.fmt.parseInt(u21, slice[i.* + 1 ..][0..count], 16) catch 0x110000; // count validated by tokenizer | |
| 5940 | i.* += count + 1; | |
| 5941 | if (!std.unicode.utf8ValidCodepoint(c) or (c < 0xa0 and c != '$' and c != '@' and c != '`')) { | |
| 5942 | try p.errExtra(.invalid_universal_character, tok, .{ .unsigned = i.* - count - 2 }); | |
| 5943 | return; | |
| 5944 | } | |
| 5945 | var buf: [4]u8 = undefined; | |
| 5946 | const to_write = std.unicode.utf8Encode(c, &buf) catch unreachable; // validated above | |
| 5947 | p.strings.appendSliceAssumeCapacity(buf[0..to_write]); | |
| 5948 | } | |
| 5949 | ||
| 5950 | fn charLiteral(p: *Parser) Error!Result { | |
| 5951 | defer p.tok_i += 1; | |
| 5952 | const ty: Type = switch (p.tok_ids[p.tok_i]) { | |
| 5953 | .char_literal => .{ .specifier = .int }, | |
| 5954 | .char_literal_wide => p.pp.comp.types.wchar, | |
| 5955 | .char_literal_utf_16 => .{ .specifier = .ushort }, | |
| 5956 | .char_literal_utf_32 => .{ .specifier = .ulong }, | |
| 5957 | else => unreachable, | |
| 5958 | }; | |
| 5959 | const max: u32 = switch (p.tok_ids[p.tok_i]) { | |
| 5960 | .char_literal => std.math.maxInt(u8), | |
| 5961 | .char_literal_wide => std.math.maxInt(u32), // TODO correct | |
| 5962 | .char_literal_utf_16 => std.math.maxInt(u16), | |
| 5963 | .char_literal_utf_32 => std.math.maxInt(u32), | |
| 5964 | else => unreachable, | |
| 5965 | }; | |
| 5966 | var multichar: u8 = switch (p.tok_ids[p.tok_i]) { | |
| 5967 | .char_literal => 0, | |
| 5968 | .char_literal_wide => 4, | |
| 5969 | .char_literal_utf_16 => 2, | |
| 5970 | .char_literal_utf_32 => 2, | |
| 5971 | else => unreachable, | |
| 5972 | }; | |
| 5973 | ||
| 5974 | var val: u32 = 0; | |
| 5975 | var overflow_reported = false; | |
| 5976 | var slice = p.tokSlice(p.tok_i); | |
| 5977 | slice = slice[0 .. slice.len - 1]; | |
| 5978 | var i = mem.indexOf(u8, slice, "\'").? + 1; | |
| 5979 | while (i < slice.len) : (i += 1) { | |
| 5980 | var c: u32 = slice[i]; | |
| 5981 | switch (c) { | |
| 5982 | '\\' => { | |
| 5983 | i += 1; | |
| 5984 | switch (slice[i]) { | |
| 5985 | '\n' => i += 1, | |
| 5986 | '\r' => i += 2, | |
| 5987 | '\'', '\"', '\\', '?' => c = slice[i], | |
| 5988 | 'n' => c = '\n', | |
| 5989 | 'r' => c = '\r', | |
| 5990 | 't' => c = '\t', | |
| 5991 | 'a' => c = 0x07, | |
| 5992 | 'b' => c = 0x08, | |
| 5993 | 'e' => c = 0x1B, | |
| 5994 | 'f' => c = 0x0C, | |
| 5995 | 'v' => c = 0x0B, | |
| 5996 | 'x' => c = try p.parseNumberEscape(p.tok_i, 16, slice, &i), | |
| 5997 | '0'...'7' => c = try p.parseNumberEscape(p.tok_i, 8, slice, &i), | |
| 5998 | 'u', 'U' => return p.todo("unicode escapes in char literals"), | |
| 5999 | else => unreachable, | |
| 6000 | } | |
| 6001 | }, | |
| 6002 | // These are safe since the source is checked to be valid utf8. | |
| 6003 | 0b1100_0000...0b1101_1111 => { | |
| 6004 | c &= 0b00011111; | |
| 6005 | c <<= 6; | |
| 6006 | c |= slice[i + 1] & 0b00111111; | |
| 6007 | i += 1; | |
| 6008 | }, | |
| 6009 | 0b1110_0000...0b1110_1111 => { | |
| 6010 | c &= 0b00001111; | |
| 6011 | c <<= 6; | |
| 6012 | c |= slice[i + 1] & 0b00111111; | |
| 6013 | c <<= 6; | |
| 6014 | c |= slice[i + 2] & 0b00111111; | |
| 6015 | i += 2; | |
| 6016 | }, | |
| 6017 | 0b1111_0000...0b1111_0111 => { | |
| 6018 | c &= 0b00000111; | |
| 6019 | c <<= 6; | |
| 6020 | c |= slice[i + 1] & 0b00111111; | |
| 6021 | c <<= 6; | |
| 6022 | c |= slice[i + 2] & 0b00111111; | |
| 6023 | c <<= 6; | |
| 6024 | c |= slice[i + 3] & 0b00111111; | |
| 6025 | i += 3; | |
| 6026 | }, | |
| 6027 | else => {}, | |
| 6028 | } | |
| 6029 | if (c > max) try p.err(.char_too_large); | |
| 6030 | switch (multichar) { | |
| 6031 | 0, 2, 4 => multichar += 1, | |
| 6032 | 1 => { | |
| 6033 | multichar = 99; | |
| 6034 | try p.err(.multichar_literal); | |
| 6035 | }, | |
| 6036 | 3 => { | |
| 6037 | try p.err(.unicode_multichar_literal); | |
| 6038 | return error.ParsingFailed; | |
| 6039 | }, | |
| 6040 | 5 => { | |
| 6041 | try p.err(.wide_multichar_literal); | |
| 6042 | val = 0; | |
| 6043 | multichar = 6; | |
| 6044 | }, | |
| 6045 | 6 => val = 0, | |
| 6046 | else => {}, | |
| 6047 | } | |
| 6048 | if (@mulWithOverflow(u32, val, max, &val) and !overflow_reported) { | |
| 6049 | try p.errExtra(.char_lit_too_wide, p.tok_i, .{ .unsigned = i }); | |
| 6050 | overflow_reported = true; | |
| 6051 | } | |
| 6052 | val += c; | |
| 6053 | } | |
| 6054 | ||
| 6055 | var res = Result{ | |
| 6056 | .ty = ty, | |
| 6057 | .val = Value.int(val), | |
| 6058 | .node = try p.addNode(.{ .tag = .char_literal, .ty = ty, .data = undefined }), | |
| 6059 | }; | |
| 6060 | if (!p.in_macro) try p.value_map.put(res.node, res.val); | |
| 6061 | return res; | |
| 6062 | } | |
| 6063 | ||
| 6064 | fn parseFloat(p: *Parser, tok: TokenIndex, comptime T: type) Error!T { | |
| 6065 | var bytes = p.tokSlice(tok); | |
| 6066 | switch (p.tok_ids[tok]) { | |
| 6067 | .float_literal => {}, | |
| 6068 | .imaginary_literal, .float_literal_f, .float_literal_l => bytes = bytes[0 .. bytes.len - 1], | |
| 6069 | .imaginary_literal_f, .imaginary_literal_l => bytes = bytes[0 .. bytes.len - 2], | |
| 6070 | else => unreachable, | |
| 6071 | } | |
| 6072 | if (bytes.len > 2 and (bytes[1] == 'x' or bytes[1] == 'X')) { | |
| 6073 | assert(bytes[0] == '0'); // validated by Tokenizer | |
| 6074 | return std.fmt.parseHexFloat(T, bytes) catch |e| switch (e) { | |
| 6075 | error.InvalidCharacter => unreachable, // validated by Tokenizer | |
| 6076 | error.Overflow => p.todo("what to do with hex floats too big"), | |
| 6077 | }; | |
| 6078 | } else { | |
| 6079 | return std.fmt.parseFloat(T, bytes) catch |e| switch (e) { | |
| 6080 | error.InvalidCharacter => unreachable, // validated by Tokenizer | |
| 6081 | }; | |
| 6082 | } | |
| 6083 | } | |
| 6084 | ||
| 6085 | fn integerLiteral(p: *Parser) Error!Result { | |
| 6086 | const id = p.tok_ids[p.tok_i]; | |
| 6087 | var slice = p.tokSlice(p.tok_i); | |
| 6088 | defer p.tok_i += 1; | |
| 6089 | var base: u8 = 10; | |
| 6090 | if (std.ascii.startsWithIgnoreCase(slice, "0x")) { | |
| 6091 | slice = slice[2..]; | |
| 6092 | base = 16; | |
| 6093 | } else if (std.ascii.startsWithIgnoreCase(slice, "0b")) { | |
| 6094 | try p.err(.binary_integer_literal); | |
| 6095 | slice = slice[2..]; | |
| 6096 | base = 2; | |
| 6097 | } else if (slice[0] == '0') { | |
| 6098 | base = 8; | |
| 6099 | } | |
| 6100 | switch (id) { | |
| 6101 | .integer_literal_u, .integer_literal_l => slice = slice[0 .. slice.len - 1], | |
| 6102 | .integer_literal_lu, .integer_literal_ll => slice = slice[0 .. slice.len - 2], | |
| 6103 | .integer_literal_llu => slice = slice[0 .. slice.len - 3], | |
| 6104 | else => {}, | |
| 6105 | } | |
| 6106 | ||
| 6107 | var val: u64 = 0; | |
| 6108 | var overflow = false; | |
| 6109 | for (slice) |c| { | |
| 6110 | const digit: u64 = switch (c) { | |
| 6111 | '0'...'9' => c - '0', | |
| 6112 | 'A'...'Z' => c - 'A' + 10, | |
| 6113 | 'a'...'z' => c - 'a' + 10, | |
| 6114 | else => unreachable, | |
| 6115 | }; | |
| 6116 | ||
| 6117 | if (val != 0 and @mulWithOverflow(u64, val, base, &val)) overflow = true; | |
| 6118 | if (@addWithOverflow(u64, val, digit, &val)) overflow = true; | |
| 6119 | } | |
| 6120 | if (overflow) { | |
| 6121 | try p.err(.int_literal_too_big); | |
| 6122 | var res: Result = .{ .ty = .{ .specifier = .ulong_long }, .val = Value.int(val) }; | |
| 6123 | res.node = try p.addNode(.{ .tag = .int_literal, .ty = res.ty, .data = undefined }); | |
| 6124 | if (!p.in_macro) try p.value_map.put(res.node, res.val); | |
| 6125 | return res; | |
| 6126 | } | |
| 6127 | switch (id) { | |
| 6128 | .integer_literal, .integer_literal_l, .integer_literal_ll => { | |
| 6129 | if (val > std.math.maxInt(i64)) { | |
| 6130 | try p.err(.implicitly_unsigned_literal); | |
| 6131 | } | |
| 6132 | }, | |
| 6133 | else => {}, | |
| 6134 | } | |
| 6135 | ||
| 6136 | if (base == 10) { | |
| 6137 | switch (id) { | |
| 6138 | .integer_literal => return p.castInt(val, &.{ .int, .long, .long_long }), | |
| 6139 | .integer_literal_u => return p.castInt(val, &.{ .uint, .ulong, .ulong_long }), | |
| 6140 | .integer_literal_l => return p.castInt(val, &.{ .long, .long_long }), | |
| 6141 | .integer_literal_lu => return p.castInt(val, &.{ .ulong, .ulong_long }), | |
| 6142 | .integer_literal_ll => return p.castInt(val, &.{.long_long}), | |
| 6143 | .integer_literal_llu => return p.castInt(val, &.{.ulong_long}), | |
| 6144 | else => unreachable, | |
| 6145 | } | |
| 6146 | } else { | |
| 6147 | switch (id) { | |
| 6148 | .integer_literal => return p.castInt(val, &.{ .int, .uint, .long, .ulong, .long_long, .ulong_long }), | |
| 6149 | .integer_literal_u => return p.castInt(val, &.{ .uint, .ulong, .ulong_long }), | |
| 6150 | .integer_literal_l => return p.castInt(val, &.{ .long, .ulong, .long_long, .ulong_long }), | |
| 6151 | .integer_literal_lu => return p.castInt(val, &.{ .ulong, .ulong_long }), | |
| 6152 | .integer_literal_ll => return p.castInt(val, &.{ .long_long, .ulong_long }), | |
| 6153 | .integer_literal_llu => return p.castInt(val, &.{.ulong_long}), | |
| 6154 | else => unreachable, | |
| 6155 | } | |
| 6156 | } | |
| 6157 | } | |
| 6158 | ||
| 6159 | fn castInt(p: *Parser, val: u64, specs: []const Type.Specifier) Error!Result { | |
| 6160 | var res: Result = .{ .val = Value.int(val) }; | |
| 6161 | for (specs) |spec| { | |
| 6162 | const ty = Type{ .specifier = spec }; | |
| 6163 | const unsigned = ty.isUnsignedInt(p.pp.comp); | |
| 6164 | const size = ty.sizeof(p.pp.comp).?; | |
| 6165 | res.ty = ty; | |
| 6166 | ||
| 6167 | if (unsigned) { | |
| 6168 | switch (size) { | |
| 6169 | 2 => if (val <= std.math.maxInt(u16)) break, | |
| 6170 | 4 => if (val <= std.math.maxInt(u32)) break, | |
| 6171 | 8 => if (val <= std.math.maxInt(u64)) break, | |
| 6172 | else => unreachable, | |
| 6173 | } | |
| 6174 | } else { | |
| 6175 | switch (size) { | |
| 6176 | 2 => if (val <= std.math.maxInt(i16)) break, | |
| 6177 | 4 => if (val <= std.math.maxInt(i32)) break, | |
| 6178 | 8 => if (val <= std.math.maxInt(i64)) break, | |
| 6179 | else => unreachable, | |
| 6180 | } | |
| 6181 | } | |
| 6182 | } else { | |
| 6183 | res.ty = .{ .specifier = .ulong_long }; | |
| 6184 | } | |
| 6185 | res.node = try p.addNode(.{ .tag = .int_literal, .ty = res.ty, .data = .{ .int = val } }); | |
| 6186 | if (!p.in_macro) try p.value_map.put(res.node, res.val); | |
| 6187 | return res; | |
| 6188 | } | |
| 6189 | ||
| 6190 | /// Run a parser function but do not evaluate the result | |
| 6191 | fn parseNoEval(p: *Parser, func: fn (*Parser) Error!Result) Error!Result { | |
| 6192 | const no_eval = p.no_eval; | |
| 6193 | defer p.no_eval = no_eval; | |
| 6194 | p.no_eval = true; | |
| 6195 | const parsed = try func(p); | |
| 6196 | try parsed.expect(p); | |
| 6197 | return parsed; | |
| 6198 | } | |
| 6199 | ||
| 6200 | /// genericSelection : keyword_generic '(' assignExpr ',' genericAssoc (',' genericAssoc)* ')' | |
| 6201 | /// genericAssoc | |
| 6202 | /// : typeName ':' assignExpr | |
| 6203 | /// | keyword_default ':' assignExpr | |
| 6204 | fn genericSelection(p: *Parser) Error!Result { | |
| 6205 | p.tok_i += 1; | |
| 6206 | const l_paren = try p.expectToken(.l_paren); | |
| 6207 | const controlling = try p.parseNoEval(assignExpr); | |
| 6208 | _ = try p.expectToken(.comma); | |
| 6209 | ||
| 6210 | const list_buf_top = p.list_buf.items.len; | |
| 6211 | defer p.list_buf.items.len = list_buf_top; | |
| 6212 | try p.list_buf.append(controlling.node); | |
| 6213 | ||
| 6214 | var default_tok: ?TokenIndex = null; | |
| 6215 | // TODO actually choose | |
| 6216 | var chosen: Result = .{}; | |
| 6217 | while (true) { | |
| 6218 | const start = p.tok_i; | |
| 6219 | if (try p.typeName()) |ty| { | |
| 6220 | if (ty.anyQual()) { | |
| 6221 | try p.errTok(.generic_qual_type, start); | |
| 6222 | } | |
| 6223 | _ = try p.expectToken(.colon); | |
| 6224 | chosen = try p.assignExpr(); | |
| 6225 | try chosen.expect(p); | |
| 6226 | try chosen.saveValue(p); | |
| 6227 | try p.list_buf.append(try p.addNode(.{ | |
| 6228 | .tag = .generic_association_expr, | |
| 6229 | .ty = ty, | |
| 6230 | .data = .{ .un = chosen.node }, | |
| 6231 | })); | |
| 6232 | } else if (p.eatToken(.keyword_default)) |tok| { | |
| 6233 | if (default_tok) |prev| { | |
| 6234 | try p.errTok(.generic_duplicate_default, tok); | |
| 6235 | try p.errTok(.previous_case, prev); | |
| 6236 | } | |
| 6237 | default_tok = tok; | |
| 6238 | _ = try p.expectToken(.colon); | |
| 6239 | chosen = try p.assignExpr(); | |
| 6240 | try chosen.expect(p); | |
| 6241 | try chosen.saveValue(p); | |
| 6242 | try p.list_buf.append(try p.addNode(.{ | |
| 6243 | .tag = .generic_default_expr, | |
| 6244 | .data = .{ .un = chosen.node }, | |
| 6245 | })); | |
| 6246 | } else { | |
| 6247 | if (p.list_buf.items.len == list_buf_top + 1) { | |
| 6248 | try p.err(.expected_type); | |
| 6249 | return error.ParsingFailed; | |
| 6250 | } | |
| 6251 | break; | |
| 6252 | } | |
| 6253 | if (p.eatToken(.comma) == null) break; | |
| 6254 | } | |
| 6255 | try p.expectClosing(l_paren, .r_paren); | |
| 6256 | ||
| 6257 | var generic_node: Tree.Node = .{ | |
| 6258 | .tag = .generic_expr_one, | |
| 6259 | .ty = chosen.ty, | |
| 6260 | .data = .{ .bin = .{ .lhs = controlling.node, .rhs = chosen.node } }, | |
| 6261 | }; | |
| 6262 | const associations = p.list_buf.items[list_buf_top..]; | |
| 6263 | if (associations.len > 2) { // associations[0] == controlling.node | |
| 6264 | generic_node.tag = .generic_expr; | |
| 6265 | generic_node.data = .{ .range = try p.addList(associations) }; | |
| 6266 | } | |
| 6267 | chosen.node = try p.addNode(generic_node); | |
| 6268 | return chosen; | |
| 6269 | } |
src/aro/Pragma.zig created+83| ... | ... | @@ -0,0 +1,83 @@ |
| 1 | const std = @import("std"); | |
| 2 | const Compilation = @import("Compilation.zig"); | |
| 3 | const Preprocessor = @import("Preprocessor.zig"); | |
| 4 | const Parser = @import("Parser.zig"); | |
| 5 | const TokenIndex = @import("Tree.zig").TokenIndex; | |
| 6 | ||
| 7 | const Pragma = @This(); | |
| 8 | ||
| 9 | pub const Error = Compilation.Error || error{ UnknownPragma, StopPreprocessing }; | |
| 10 | ||
| 11 | /// Called during Preprocessor.init | |
| 12 | beforePreprocess: ?fn (*Pragma, *Compilation) void = null, | |
| 13 | ||
| 14 | /// Called at the beginning of Parser.parse | |
| 15 | beforeParse: ?fn (*Pragma, *Compilation) void = null, | |
| 16 | ||
| 17 | /// Called at the end of Parser.parse if a Tree was successfully parsed | |
| 18 | afterParse: ?fn (*Pragma, *Compilation) void = null, | |
| 19 | ||
| 20 | /// Called during Compilation.deinit | |
| 21 | deinit: fn (*Pragma, *Compilation) void, | |
| 22 | ||
| 23 | /// Called whenever the preprocessor encounters this pragma. `start_idx` is the index | |
| 24 | /// within `pp.tokens` of the pragma name token. The pragma end is indicated by a | |
| 25 | /// .nl token (which may be generated if the source ends with a pragma with no newline) | |
| 26 | /// As an example, given the following line: | |
| 27 | /// #pragma GCC diagnostic error "-Wnewline-eof" \n | |
| 28 | /// Then pp.tokens.get(start_idx) will return the `GCC` token. | |
| 29 | /// Return error.UnknownPragma to emit an `unknown_pragma` diagnostic | |
| 30 | /// Return error.StopPreprocessing to stop preprocessing the current file (see once.zig) | |
| 31 | preprocessorHandler: ?fn (*Pragma, *Preprocessor, start_idx: TokenIndex) Error!void = null, | |
| 32 | ||
| 33 | /// Called during token pretty-printing (`-E` option). If this returns true, the pragma will | |
| 34 | /// be printed; otherwise it will be omitted. start_idx is the index of the pragma name token | |
| 35 | preserveTokens: ?fn (*Pragma, *Preprocessor, start_idx: TokenIndex) bool = null, | |
| 36 | ||
| 37 | /// Same as preprocessorHandler except called during parsing | |
| 38 | /// The parser's `p.tok_i` field must not be changed | |
| 39 | parserHandler: ?fn (*Pragma, *Parser, start_idx: TokenIndex) Compilation.Error!void = null, | |
| 40 | ||
| 41 | pub fn pasteTokens(pp: *Preprocessor, start_idx: TokenIndex) ![]const u8 { | |
| 42 | if (pp.tokens.get(start_idx).id == .nl) return error.ExpectedStringLiteral; | |
| 43 | ||
| 44 | const char_top = pp.char_buf.items.len; | |
| 45 | defer pp.char_buf.items.len = char_top; | |
| 46 | var i: usize = 0; | |
| 47 | var lparen_count: u32 = 0; | |
| 48 | var rparen_count: u32 = 0; | |
| 49 | while (true) : (i += 1) { | |
| 50 | const tok = pp.tokens.get(start_idx + i); | |
| 51 | if (tok.id == .nl) break; | |
| 52 | switch (tok.id) { | |
| 53 | .l_paren => { | |
| 54 | if (lparen_count != i) return error.ExpectedStringLiteral; | |
| 55 | lparen_count += 1; | |
| 56 | }, | |
| 57 | .r_paren => rparen_count += 1, | |
| 58 | .string_literal => { | |
| 59 | if (rparen_count != 0) return error.ExpectedStringLiteral; | |
| 60 | const str = pp.expandedSlice(tok); | |
| 61 | try pp.char_buf.appendSlice(str[1 .. str.len - 1]); | |
| 62 | }, | |
| 63 | else => return error.ExpectedStringLiteral, | |
| 64 | } | |
| 65 | } | |
| 66 | if (lparen_count != rparen_count) return error.ExpectedStringLiteral; | |
| 67 | return pp.char_buf.items[char_top..]; | |
| 68 | } | |
| 69 | ||
| 70 | pub fn shouldPreserveTokens(self: *Pragma, pp: *Preprocessor, start_idx: TokenIndex) bool { | |
| 71 | if (self.preserveTokens) |func| return func(self, pp, start_idx); | |
| 72 | return false; | |
| 73 | } | |
| 74 | ||
| 75 | pub fn preprocessorCB(self: *Pragma, pp: *Preprocessor, start_idx: TokenIndex) Error!void { | |
| 76 | if (self.preprocessorHandler) |func| return func(self, pp, start_idx); | |
| 77 | } | |
| 78 | ||
| 79 | pub fn parserCB(self: *Pragma, p: *Parser, start_idx: TokenIndex) Compilation.Error!void { | |
| 80 | const tok_index = p.tok_i; | |
| 81 | defer std.debug.assert(tok_index == p.tok_i); | |
| 82 | if (self.parserHandler) |func| return func(self, p, start_idx); | |
| 83 | } |
src/aro/Preprocessor.zig created+1945| ... | ... | @@ -0,0 +1,1945 @@ |
| 1 | const std = @import("std"); | |
| 2 | const mem = std.mem; | |
| 3 | const Allocator = mem.Allocator; | |
| 4 | const assert = std.debug.assert; | |
| 5 | const Compilation = @import("Compilation.zig"); | |
| 6 | const Error = Compilation.Error; | |
| 7 | const Source = @import("Source.zig"); | |
| 8 | const Tokenizer = @import("Tokenizer.zig"); | |
| 9 | const RawToken = Tokenizer.Token; | |
| 10 | const Parser = @import("Parser.zig"); | |
| 11 | const Diagnostics = @import("Diagnostics.zig"); | |
| 12 | const Token = @import("Tree.zig").Token; | |
| 13 | const Attribute = @import("Attribute.zig"); | |
| 14 | const features = @import("features.zig"); | |
| 15 | ||
| 16 | const Preprocessor = @This(); | |
| 17 | const DefineMap = std.StringHashMap(Macro); | |
| 18 | const RawTokenList = std.ArrayList(RawToken); | |
| 19 | const max_include_depth = 200; | |
| 20 | ||
| 21 | /// Errors that can be returned when expanding a macro. | |
| 22 | /// error.UnknownPragma can occur within Preprocessor.pragma() but | |
| 23 | /// it is handled there and doesn't escape that function | |
| 24 | const MacroError = Error || error{StopPreprocessing}; | |
| 25 | ||
| 26 | const Macro = struct { | |
| 27 | /// Parameters of the function type macro | |
| 28 | params: []const []const u8, | |
| 29 | ||
| 30 | /// Token constituting the macro body | |
| 31 | tokens: []const RawToken, | |
| 32 | ||
| 33 | /// If the function type macro has variable number of arguments | |
| 34 | var_args: bool, | |
| 35 | ||
| 36 | /// Is a function type macro | |
| 37 | is_func: bool, | |
| 38 | ||
| 39 | /// Is a predefined macro | |
| 40 | is_builtin: bool = false, | |
| 41 | ||
| 42 | /// Location of macro in the source | |
| 43 | /// `byte_offset` and `line` are used to define the range of tokens included | |
| 44 | /// in the macro. | |
| 45 | loc: Source.Location, | |
| 46 | ||
| 47 | fn eql(a: Macro, b: Macro, pp: *Preprocessor) bool { | |
| 48 | if (a.tokens.len != b.tokens.len) return false; | |
| 49 | if (a.is_builtin != b.is_builtin) return false; | |
| 50 | for (a.tokens) |t, i| if (!tokEql(pp, t, b.tokens[i])) return false; | |
| 51 | ||
| 52 | if (a.is_func and b.is_func) { | |
| 53 | if (a.var_args != b.var_args) return false; | |
| 54 | if (a.params.len != b.params.len) return false; | |
| 55 | for (a.params) |p, i| if (!mem.eql(u8, p, b.params[i])) return false; | |
| 56 | } | |
| 57 | ||
| 58 | return true; | |
| 59 | } | |
| 60 | ||
| 61 | fn tokEql(pp: *Preprocessor, a: RawToken, b: RawToken) bool { | |
| 62 | return mem.eql(u8, pp.tokSlice(a), pp.tokSlice(b)); | |
| 63 | } | |
| 64 | }; | |
| 65 | ||
| 66 | comp: *Compilation, | |
| 67 | arena: std.heap.ArenaAllocator, | |
| 68 | defines: DefineMap, | |
| 69 | tokens: Token.List = .{}, | |
| 70 | token_buf: RawTokenList, | |
| 71 | char_buf: std.ArrayList(u8), | |
| 72 | /// Counter that is incremented each time preprocess() is called | |
| 73 | /// Can be used to distinguish multiple preprocessings of the same file | |
| 74 | preprocess_count: u32 = 0, | |
| 75 | generated_line: u32 = 1, | |
| 76 | add_expansion_nl: u32 = 0, | |
| 77 | include_depth: u8 = 0, | |
| 78 | counter: u32 = 0, | |
| 79 | expansion_source_loc: Source.Location = undefined, | |
| 80 | poisoned_identifiers: std.StringHashMap(void), | |
| 81 | /// Memory is retained to avoid allocation on every single token. | |
| 82 | top_expansion_buf: ExpandBuf, | |
| 83 | ||
| 84 | pub fn init(comp: *Compilation) Preprocessor { | |
| 85 | const pp = Preprocessor{ | |
| 86 | .comp = comp, | |
| 87 | .arena = std.heap.ArenaAllocator.init(comp.gpa), | |
| 88 | .defines = DefineMap.init(comp.gpa), | |
| 89 | .token_buf = RawTokenList.init(comp.gpa), | |
| 90 | .char_buf = std.ArrayList(u8).init(comp.gpa), | |
| 91 | .poisoned_identifiers = std.StringHashMap(void).init(comp.gpa), | |
| 92 | .top_expansion_buf = ExpandBuf.init(comp.gpa), | |
| 93 | }; | |
| 94 | comp.pragmaEvent(.before_preprocess); | |
| 95 | return pp; | |
| 96 | } | |
| 97 | ||
| 98 | const builtin_macros = struct { | |
| 99 | const args = [1][]const u8{"X"}; | |
| 100 | ||
| 101 | const has_attribute = [1]RawToken{.{ | |
| 102 | .id = .macro_param_has_attribute, | |
| 103 | .source = .generated, | |
| 104 | }}; | |
| 105 | const has_warning = [1]RawToken{.{ | |
| 106 | .id = .macro_param_has_warning, | |
| 107 | .source = .generated, | |
| 108 | }}; | |
| 109 | const has_feature = [1]RawToken{.{ | |
| 110 | .id = .macro_param_has_feature, | |
| 111 | .source = .generated, | |
| 112 | }}; | |
| 113 | const has_extension = [1]RawToken{.{ | |
| 114 | .id = .macro_param_has_extension, | |
| 115 | .source = .generated, | |
| 116 | }}; | |
| 117 | const has_builtin = [1]RawToken{.{ | |
| 118 | .id = .macro_param_has_builtin, | |
| 119 | .source = .generated, | |
| 120 | }}; | |
| 121 | ||
| 122 | const is_identifier = [1]RawToken{.{ | |
| 123 | .id = .macro_param_is_identifier, | |
| 124 | .source = .generated, | |
| 125 | }}; | |
| 126 | ||
| 127 | const pragma_operator = [1]RawToken{.{ | |
| 128 | .id = .macro_param_pragma_operator, | |
| 129 | .source = .generated, | |
| 130 | }}; | |
| 131 | ||
| 132 | const file = [1]RawToken{.{ | |
| 133 | .id = .macro_file, | |
| 134 | .source = .generated, | |
| 135 | }}; | |
| 136 | const line = [1]RawToken{.{ | |
| 137 | .id = .macro_line, | |
| 138 | .source = .generated, | |
| 139 | }}; | |
| 140 | const counter = [1]RawToken{.{ | |
| 141 | .id = .macro_counter, | |
| 142 | .source = .generated, | |
| 143 | }}; | |
| 144 | }; | |
| 145 | ||
| 146 | fn addBuiltinMacro(pp: *Preprocessor, name: []const u8, is_func: bool, tokens: []const RawToken) !void { | |
| 147 | try pp.defines.put(name, .{ | |
| 148 | .params = &builtin_macros.args, | |
| 149 | .tokens = tokens, | |
| 150 | .var_args = false, | |
| 151 | .is_func = is_func, | |
| 152 | .loc = .{ .id = .generated }, | |
| 153 | .is_builtin = true, | |
| 154 | }); | |
| 155 | } | |
| 156 | ||
| 157 | pub fn addBuiltinMacros(pp: *Preprocessor) !void { | |
| 158 | try pp.addBuiltinMacro("__has_attribute", true, &builtin_macros.has_attribute); | |
| 159 | try pp.addBuiltinMacro("__has_warning", true, &builtin_macros.has_warning); | |
| 160 | try pp.addBuiltinMacro("__has_feature", true, &builtin_macros.has_feature); | |
| 161 | try pp.addBuiltinMacro("__has_extension", true, &builtin_macros.has_extension); | |
| 162 | try pp.addBuiltinMacro("__has_builtin", true, &builtin_macros.has_builtin); | |
| 163 | try pp.addBuiltinMacro("__is_identifier", true, &builtin_macros.is_identifier); | |
| 164 | try pp.addBuiltinMacro("_Pragma", true, &builtin_macros.pragma_operator); | |
| 165 | ||
| 166 | try pp.addBuiltinMacro("__FILE__", false, &builtin_macros.file); | |
| 167 | try pp.addBuiltinMacro("__LINE__", false, &builtin_macros.line); | |
| 168 | try pp.addBuiltinMacro("__COUNTER__", false, &builtin_macros.counter); | |
| 169 | } | |
| 170 | ||
| 171 | pub fn deinit(pp: *Preprocessor) void { | |
| 172 | pp.defines.deinit(); | |
| 173 | for (pp.tokens.items(.expansion_locs)) |loc| Token.free(loc, pp.comp.gpa); | |
| 174 | pp.tokens.deinit(pp.comp.gpa); | |
| 175 | pp.arena.deinit(); | |
| 176 | pp.token_buf.deinit(); | |
| 177 | pp.char_buf.deinit(); | |
| 178 | pp.poisoned_identifiers.deinit(); | |
| 179 | pp.top_expansion_buf.deinit(); | |
| 180 | } | |
| 181 | ||
| 182 | /// Preprocess a source file, returns eof token. | |
| 183 | pub fn preprocess(pp: *Preprocessor, source: Source) Error!Token { | |
| 184 | return pp.preprocessExtra(source) catch |err| switch (err) { | |
| 185 | // This cannot occur in the main file and is handled in `include`. | |
| 186 | error.StopPreprocessing => unreachable, | |
| 187 | else => |e| return e, | |
| 188 | }; | |
| 189 | } | |
| 190 | ||
| 191 | fn preprocessExtra(pp: *Preprocessor, source: Source) MacroError!Token { | |
| 192 | if (source.invalid_utf8_loc) |loc| { | |
| 193 | try pp.comp.diag.add(.{ | |
| 194 | .tag = .invalid_utf8, | |
| 195 | .loc = loc, | |
| 196 | }, &.{}); | |
| 197 | return error.FatalError; | |
| 198 | } | |
| 199 | ||
| 200 | pp.preprocess_count += 1; | |
| 201 | var tokenizer = Tokenizer{ | |
| 202 | .buf = source.buf, | |
| 203 | .comp = pp.comp, | |
| 204 | .source = source.id, | |
| 205 | }; | |
| 206 | ||
| 207 | // Estimate how many new tokens this source will contain. | |
| 208 | const estimated_token_count = source.buf.len / 8; | |
| 209 | try pp.tokens.ensureTotalCapacity(pp.comp.gpa, pp.tokens.len + estimated_token_count); | |
| 210 | ||
| 211 | var if_level: u8 = 0; | |
| 212 | var if_kind = std.PackedIntArray(u2, 256).init([1]u2{0} ** 256); | |
| 213 | const until_else = 0; | |
| 214 | const until_endif = 1; | |
| 215 | const until_endif_seen_else = 2; | |
| 216 | ||
| 217 | var start_of_line = true; | |
| 218 | while (true) { | |
| 219 | var tok = tokenizer.next(); | |
| 220 | switch (tok.id) { | |
| 221 | .hash => if (start_of_line) { | |
| 222 | const directive = tokenizer.nextNoWS(); | |
| 223 | switch (directive.id) { | |
| 224 | .keyword_error, .keyword_warning => { | |
| 225 | // #error tokens.. | |
| 226 | pp.top_expansion_buf.items.len = 0; | |
| 227 | const char_top = pp.char_buf.items.len; | |
| 228 | defer pp.char_buf.items.len = char_top; | |
| 229 | ||
| 230 | while (true) { | |
| 231 | tok = tokenizer.next(); | |
| 232 | if (tok.id == .nl or tok.id == .eof) break; | |
| 233 | if (tok.id == .whitespace) tok.id = .macro_ws; | |
| 234 | try pp.top_expansion_buf.append(tokFromRaw(tok)); | |
| 235 | } | |
| 236 | try pp.stringify(pp.top_expansion_buf.items); | |
| 237 | const slice = pp.char_buf.items[char_top + 1 .. pp.char_buf.items.len - 2]; | |
| 238 | const duped = try pp.comp.diag.arena.allocator().dupe(u8, slice); | |
| 239 | ||
| 240 | try pp.comp.diag.add(.{ | |
| 241 | .tag = if (directive.id == .keyword_error) .error_directive else .warning_directive, | |
| 242 | .loc = .{ .id = tok.source, .byte_offset = directive.start, .line = directive.line }, | |
| 243 | .extra = .{ .str = duped }, | |
| 244 | }, &.{}); | |
| 245 | }, | |
| 246 | .keyword_if => { | |
| 247 | if (@addWithOverflow(u8, if_level, 1, &if_level)) | |
| 248 | return pp.fatal(directive, "too many #if nestings", .{}); | |
| 249 | ||
| 250 | if (try pp.expr(&tokenizer)) { | |
| 251 | if_kind.set(if_level, until_endif); | |
| 252 | } else { | |
| 253 | if_kind.set(if_level, until_else); | |
| 254 | try pp.skip(&tokenizer, .until_else); | |
| 255 | } | |
| 256 | }, | |
| 257 | .keyword_ifdef => { | |
| 258 | if (@addWithOverflow(u8, if_level, 1, &if_level)) | |
| 259 | return pp.fatal(directive, "too many #if nestings", .{}); | |
| 260 | ||
| 261 | const macro_name = (try pp.expectMacroName(&tokenizer)) orelse continue; | |
| 262 | try pp.expectNl(&tokenizer); | |
| 263 | if (pp.defines.get(macro_name) != null) { | |
| 264 | if_kind.set(if_level, until_endif); | |
| 265 | } else { | |
| 266 | if_kind.set(if_level, until_else); | |
| 267 | try pp.skip(&tokenizer, .until_else); | |
| 268 | } | |
| 269 | }, | |
| 270 | .keyword_ifndef => { | |
| 271 | if (@addWithOverflow(u8, if_level, 1, &if_level)) | |
| 272 | return pp.fatal(directive, "too many #if nestings", .{}); | |
| 273 | ||
| 274 | const macro_name = (try pp.expectMacroName(&tokenizer)) orelse continue; | |
| 275 | try pp.expectNl(&tokenizer); | |
| 276 | if (pp.defines.get(macro_name) == null) { | |
| 277 | if_kind.set(if_level, until_endif); | |
| 278 | } else { | |
| 279 | if_kind.set(if_level, until_else); | |
| 280 | try pp.skip(&tokenizer, .until_else); | |
| 281 | } | |
| 282 | }, | |
| 283 | .keyword_elif => { | |
| 284 | if (if_level == 0) { | |
| 285 | try pp.err(directive, .elif_without_if); | |
| 286 | if_level += 1; | |
| 287 | if_kind.set(if_level, until_else); | |
| 288 | } | |
| 289 | switch (if_kind.get(if_level)) { | |
| 290 | until_else => if (try pp.expr(&tokenizer)) { | |
| 291 | if_kind.set(if_level, until_endif); | |
| 292 | } else { | |
| 293 | try pp.skip(&tokenizer, .until_else); | |
| 294 | }, | |
| 295 | until_endif => try pp.skip(&tokenizer, .until_endif), | |
| 296 | until_endif_seen_else => { | |
| 297 | try pp.err(directive, .elif_after_else); | |
| 298 | skipToNl(&tokenizer); | |
| 299 | }, | |
| 300 | else => unreachable, | |
| 301 | } | |
| 302 | }, | |
| 303 | .keyword_else => { | |
| 304 | try pp.expectNl(&tokenizer); | |
| 305 | if (if_level == 0) { | |
| 306 | try pp.err(directive, .else_without_if); | |
| 307 | continue; | |
| 308 | } | |
| 309 | switch (if_kind.get(if_level)) { | |
| 310 | until_else => if_kind.set(if_level, until_endif_seen_else), | |
| 311 | until_endif => try pp.skip(&tokenizer, .until_endif_seen_else), | |
| 312 | until_endif_seen_else => { | |
| 313 | try pp.err(directive, .else_after_else); | |
| 314 | skipToNl(&tokenizer); | |
| 315 | }, | |
| 316 | else => unreachable, | |
| 317 | } | |
| 318 | }, | |
| 319 | .keyword_endif => { | |
| 320 | try pp.expectNl(&tokenizer); | |
| 321 | if (if_level == 0) { | |
| 322 | try pp.err(directive, .endif_without_if); | |
| 323 | continue; | |
| 324 | } | |
| 325 | if_level -= 1; | |
| 326 | }, | |
| 327 | .keyword_define => try pp.define(&tokenizer), | |
| 328 | .keyword_undef => { | |
| 329 | const macro_name = (try pp.expectMacroName(&tokenizer)) orelse continue; | |
| 330 | ||
| 331 | _ = pp.defines.remove(macro_name); | |
| 332 | try pp.expectNl(&tokenizer); | |
| 333 | }, | |
| 334 | .keyword_include => try pp.include(&tokenizer), | |
| 335 | .keyword_pragma => try pp.pragma(&tokenizer, directive, null, &.{}), | |
| 336 | .keyword_line => { | |
| 337 | // #line number "file" | |
| 338 | const digits = tokenizer.nextNoWS(); | |
| 339 | if (digits.id != .integer_literal) try pp.err(digits, .line_simple_digit); | |
| 340 | if (digits.id == .eof or digits.id == .nl) continue; | |
| 341 | const name = tokenizer.nextNoWS(); | |
| 342 | if (name.id == .eof or name.id == .nl) continue; | |
| 343 | if (name.id != .string_literal) try pp.err(name, .line_invalid_filename); | |
| 344 | try pp.expectNl(&tokenizer); | |
| 345 | }, | |
| 346 | .integer_literal => { | |
| 347 | // # number "file" flags | |
| 348 | const name = tokenizer.nextNoWS(); | |
| 349 | if (name.id == .eof or name.id == .nl) continue; | |
| 350 | if (name.id != .string_literal) try pp.err(name, .line_invalid_filename); | |
| 351 | ||
| 352 | const flag_1 = tokenizer.nextNoWS(); | |
| 353 | if (flag_1.id == .eof or flag_1.id == .nl) continue; | |
| 354 | const flag_2 = tokenizer.nextNoWS(); | |
| 355 | if (flag_2.id == .eof or flag_2.id == .nl) continue; | |
| 356 | const flag_3 = tokenizer.nextNoWS(); | |
| 357 | if (flag_3.id == .eof or flag_3.id == .nl) continue; | |
| 358 | const flag_4 = tokenizer.nextNoWS(); | |
| 359 | if (flag_4.id == .eof or flag_4.id == .nl) continue; | |
| 360 | try pp.expectNl(&tokenizer); | |
| 361 | }, | |
| 362 | .nl => {}, | |
| 363 | .eof => { | |
| 364 | if (if_level != 0) try pp.err(tok, .unterminated_conditional_directive); | |
| 365 | return tokFromRaw(directive); | |
| 366 | }, | |
| 367 | else => { | |
| 368 | try pp.err(tok, .invalid_preprocessing_directive); | |
| 369 | skipToNl(&tokenizer); | |
| 370 | }, | |
| 371 | } | |
| 372 | }, | |
| 373 | .whitespace => if (pp.comp.only_preprocess) try pp.tokens.append(pp.comp.gpa, tokFromRaw(tok)), | |
| 374 | .nl => { | |
| 375 | start_of_line = true; | |
| 376 | if (pp.comp.only_preprocess) try pp.tokens.append(pp.comp.gpa, tokFromRaw(tok)); | |
| 377 | }, | |
| 378 | .eof => { | |
| 379 | if (if_level != 0) try pp.err(tok, .unterminated_conditional_directive); | |
| 380 | // The following check needs to occur here and not at the top of the function | |
| 381 | // because a pragma may change the level during preprocessing | |
| 382 | if (source.buf.len > 0 and source.buf[source.buf.len - 1] != '\n') { | |
| 383 | try pp.err(tok, .newline_eof); | |
| 384 | } | |
| 385 | return tokFromRaw(tok); | |
| 386 | }, | |
| 387 | else => { | |
| 388 | if (tok.id.isMacroIdentifier() and pp.poisoned_identifiers.get(pp.tokSlice(tok)) != null) { | |
| 389 | try pp.err(tok, .poisoned_identifier); | |
| 390 | } | |
| 391 | // Add the token to the buffer doing any necessary expansions. | |
| 392 | start_of_line = false; | |
| 393 | try pp.expandMacro(&tokenizer, tok); | |
| 394 | }, | |
| 395 | } | |
| 396 | } | |
| 397 | } | |
| 398 | ||
| 399 | /// Get raw token source string. | |
| 400 | /// Returned slice is invalidated when comp.generated_buf is updated. | |
| 401 | pub fn tokSlice(pp: *Preprocessor, token: RawToken) []const u8 { | |
| 402 | if (token.id.lexeme()) |some| return some; | |
| 403 | const source = pp.comp.getSource(token.source); | |
| 404 | return source.buf[token.start..token.end]; | |
| 405 | } | |
| 406 | ||
| 407 | /// Convert a token from the Tokenizer into a token used by the parser. | |
| 408 | fn tokFromRaw(raw: RawToken) Token { | |
| 409 | return .{ | |
| 410 | .id = raw.id, | |
| 411 | .loc = .{ | |
| 412 | .id = raw.source, | |
| 413 | .byte_offset = raw.start, | |
| 414 | .line = raw.line, | |
| 415 | }, | |
| 416 | }; | |
| 417 | } | |
| 418 | ||
| 419 | fn err(pp: *Preprocessor, raw: RawToken, tag: Diagnostics.Tag) !void { | |
| 420 | try pp.comp.diag.add(.{ | |
| 421 | .tag = tag, | |
| 422 | .loc = .{ | |
| 423 | .id = raw.source, | |
| 424 | .byte_offset = raw.start, | |
| 425 | .line = raw.line, | |
| 426 | }, | |
| 427 | }, &.{}); | |
| 428 | } | |
| 429 | ||
| 430 | fn fatal(pp: *Preprocessor, raw: RawToken, comptime fmt: []const u8, args: anytype) Compilation.Error { | |
| 431 | const source = pp.comp.getSource(raw.source); | |
| 432 | const line_col = source.lineCol(.{ .id = raw.source, .line = raw.line, .byte_offset = raw.start }); | |
| 433 | return pp.comp.diag.fatal(source.path, line_col.line, raw.line, line_col.col, fmt, args); | |
| 434 | } | |
| 435 | ||
| 436 | /// Consume next token, error if it is not an identifier. | |
| 437 | fn expectMacroName(pp: *Preprocessor, tokenizer: *Tokenizer) Error!?[]const u8 { | |
| 438 | const macro_name = tokenizer.nextNoWS(); | |
| 439 | if (!macro_name.id.isMacroIdentifier()) { | |
| 440 | try pp.err(macro_name, .macro_name_missing); | |
| 441 | skipToNl(tokenizer); | |
| 442 | return null; | |
| 443 | } | |
| 444 | return pp.tokSlice(macro_name); | |
| 445 | } | |
| 446 | ||
| 447 | /// Skip until after a newline, error if extra tokens before it. | |
| 448 | fn expectNl(pp: *Preprocessor, tokenizer: *Tokenizer) Error!void { | |
| 449 | var sent_err = false; | |
| 450 | while (true) { | |
| 451 | const tok = tokenizer.next(); | |
| 452 | if (tok.id == .nl or tok.id == .eof) return; | |
| 453 | if (tok.id == .whitespace) continue; | |
| 454 | if (!sent_err) { | |
| 455 | sent_err = true; | |
| 456 | try pp.err(tok, .extra_tokens_directive_end); | |
| 457 | } | |
| 458 | } | |
| 459 | } | |
| 460 | ||
| 461 | /// Consume all tokens until a newline and parse the result into a boolean. | |
| 462 | fn expr(pp: *Preprocessor, tokenizer: *Tokenizer) MacroError!bool { | |
| 463 | const start = pp.tokens.len; | |
| 464 | defer { | |
| 465 | for (pp.tokens.items(.expansion_locs)[start..]) |loc| Token.free(loc, pp.comp.gpa); | |
| 466 | pp.tokens.len = start; | |
| 467 | } | |
| 468 | ||
| 469 | while (true) { | |
| 470 | var tok = tokenizer.next(); | |
| 471 | switch (tok.id) { | |
| 472 | .nl, .eof => { | |
| 473 | if (pp.tokens.len == start) { | |
| 474 | try pp.err(tok, .expected_value_in_expr); | |
| 475 | try pp.expectNl(tokenizer); | |
| 476 | return false; | |
| 477 | } | |
| 478 | tok.id = .eof; | |
| 479 | try pp.tokens.append(pp.comp.gpa, tokFromRaw(tok)); | |
| 480 | break; | |
| 481 | }, | |
| 482 | .keyword_defined => { | |
| 483 | const first = tokenizer.nextNoWS(); | |
| 484 | const macro_tok = if (first.id == .l_paren) tokenizer.nextNoWS() else first; | |
| 485 | if (!macro_tok.id.isMacroIdentifier()) try pp.err(macro_tok, .macro_name_missing); | |
| 486 | if (first.id == .l_paren) { | |
| 487 | const r_paren = tokenizer.nextNoWS(); | |
| 488 | if (r_paren.id != .r_paren) { | |
| 489 | try pp.err(r_paren, .closing_paren); | |
| 490 | try pp.err(first, .to_match_paren); | |
| 491 | } | |
| 492 | } | |
| 493 | tok.id = if (pp.defines.get(pp.tokSlice(macro_tok)) != null) .one else .zero; | |
| 494 | }, | |
| 495 | .whitespace => continue, | |
| 496 | else => {}, | |
| 497 | } | |
| 498 | try pp.expandMacro(tokenizer, tok); | |
| 499 | } | |
| 500 | ||
| 501 | if (!pp.tokens.items(.id)[start].validPreprocessorExprStart()) { | |
| 502 | const tok = pp.tokens.get(start); | |
| 503 | try pp.comp.diag.add(.{ | |
| 504 | .tag = .invalid_preproc_expr_start, | |
| 505 | .loc = tok.loc, | |
| 506 | }, tok.expansionSlice()); | |
| 507 | return false; | |
| 508 | } | |
| 509 | // validate the tokens in the expression | |
| 510 | for (pp.tokens.items(.id)[start..]) |*id, i| { | |
| 511 | switch (id.*) { | |
| 512 | .string_literal, | |
| 513 | .string_literal_utf_16, | |
| 514 | .string_literal_utf_8, | |
| 515 | .string_literal_utf_32, | |
| 516 | .string_literal_wide, | |
| 517 | => { | |
| 518 | const tok = pp.tokens.get(start + i); | |
| 519 | try pp.comp.diag.add(.{ | |
| 520 | .tag = .string_literal_in_pp_expr, | |
| 521 | .loc = tok.loc, | |
| 522 | }, tok.expansionSlice()); | |
| 523 | return false; | |
| 524 | }, | |
| 525 | .float_literal, | |
| 526 | .float_literal_f, | |
| 527 | .float_literal_l, | |
| 528 | .imaginary_literal, | |
| 529 | .imaginary_literal_f, | |
| 530 | .imaginary_literal_l, | |
| 531 | => { | |
| 532 | const tok = pp.tokens.get(start + i); | |
| 533 | try pp.comp.diag.add(.{ | |
| 534 | .tag = .float_literal_in_pp_expr, | |
| 535 | .loc = tok.loc, | |
| 536 | }, tok.expansionSlice()); | |
| 537 | return false; | |
| 538 | }, | |
| 539 | .plus_plus, | |
| 540 | .minus_minus, | |
| 541 | .plus_equal, | |
| 542 | .minus_equal, | |
| 543 | .asterisk_equal, | |
| 544 | .slash_equal, | |
| 545 | .percent_equal, | |
| 546 | .angle_bracket_angle_bracket_left_equal, | |
| 547 | .angle_bracket_angle_bracket_right_equal, | |
| 548 | .ampersand_equal, | |
| 549 | .caret_equal, | |
| 550 | .pipe_equal, | |
| 551 | .l_bracket, | |
| 552 | .r_bracket, | |
| 553 | .l_brace, | |
| 554 | .r_brace, | |
| 555 | .ellipsis, | |
| 556 | .semicolon, | |
| 557 | .hash, | |
| 558 | .hash_hash, | |
| 559 | .equal, | |
| 560 | .arrow, | |
| 561 | .period, | |
| 562 | => { | |
| 563 | const tok = pp.tokens.get(start + i); | |
| 564 | try pp.comp.diag.add(.{ | |
| 565 | .tag = .invalid_preproc_operator, | |
| 566 | .loc = tok.loc, | |
| 567 | }, tok.expansionSlice()); | |
| 568 | return false; | |
| 569 | }, | |
| 570 | else => if (id.isMacroIdentifier()) { | |
| 571 | id.* = .zero; // undefined macro | |
| 572 | }, | |
| 573 | } | |
| 574 | } | |
| 575 | ||
| 576 | // Actually parse it. | |
| 577 | var parser = Parser{ | |
| 578 | .pp = pp, | |
| 579 | .tok_ids = pp.tokens.items(.id), | |
| 580 | .tok_i = @intCast(u32, start), | |
| 581 | .arena = pp.arena.allocator(), | |
| 582 | .in_macro = true, | |
| 583 | .data = undefined, | |
| 584 | .strings = undefined, | |
| 585 | .value_map = undefined, | |
| 586 | .scopes = undefined, | |
| 587 | .labels = undefined, | |
| 588 | .decl_buf = undefined, | |
| 589 | .list_buf = undefined, | |
| 590 | .param_buf = undefined, | |
| 591 | .enum_buf = undefined, | |
| 592 | .record_buf = undefined, | |
| 593 | .attr_buf = undefined, | |
| 594 | }; | |
| 595 | return parser.macroExpr(); | |
| 596 | } | |
| 597 | ||
| 598 | /// Skip until #else #elif #endif, return last directive token id. | |
| 599 | /// Also skips nested #if ... #endifs. | |
| 600 | fn skip( | |
| 601 | pp: *Preprocessor, | |
| 602 | tokenizer: *Tokenizer, | |
| 603 | cont: enum { until_else, until_endif, until_endif_seen_else }, | |
| 604 | ) Error!void { | |
| 605 | var ifs_seen: u32 = 0; | |
| 606 | var line_start = true; | |
| 607 | while (tokenizer.index < tokenizer.buf.len) { | |
| 608 | if (line_start) { | |
| 609 | const saved_tokenizer = tokenizer.*; | |
| 610 | const hash = tokenizer.nextNoWS(); | |
| 611 | if (hash.id == .nl) continue; | |
| 612 | line_start = false; | |
| 613 | if (hash.id != .hash) continue; | |
| 614 | const directive = tokenizer.nextNoWS(); | |
| 615 | switch (directive.id) { | |
| 616 | .keyword_else => { | |
| 617 | if (ifs_seen != 0) continue; | |
| 618 | if (cont == .until_endif_seen_else) { | |
| 619 | try pp.err(directive, .else_after_else); | |
| 620 | continue; | |
| 621 | } | |
| 622 | tokenizer.* = saved_tokenizer; | |
| 623 | return; | |
| 624 | }, | |
| 625 | .keyword_elif => { | |
| 626 | if (ifs_seen != 0 or cont == .until_endif) continue; | |
| 627 | if (cont == .until_endif_seen_else) { | |
| 628 | try pp.err(directive, .elif_after_else); | |
| 629 | continue; | |
| 630 | } | |
| 631 | tokenizer.* = saved_tokenizer; | |
| 632 | return; | |
| 633 | }, | |
| 634 | .keyword_endif => { | |
| 635 | if (ifs_seen == 0) { | |
| 636 | tokenizer.* = saved_tokenizer; | |
| 637 | return; | |
| 638 | } | |
| 639 | ifs_seen -= 1; | |
| 640 | }, | |
| 641 | .keyword_if, .keyword_ifdef, .keyword_ifndef => ifs_seen += 1, | |
| 642 | else => {}, | |
| 643 | } | |
| 644 | } else if (tokenizer.buf[tokenizer.index] == '\n') { | |
| 645 | line_start = true; | |
| 646 | tokenizer.index += 1; | |
| 647 | tokenizer.line += 1; | |
| 648 | } else { | |
| 649 | line_start = false; | |
| 650 | tokenizer.index += 1; | |
| 651 | } | |
| 652 | } else { | |
| 653 | const eof = tokenizer.next(); | |
| 654 | return pp.err(eof, .unterminated_conditional_directive); | |
| 655 | } | |
| 656 | } | |
| 657 | ||
| 658 | // Skip until newline, ignore other tokens. | |
| 659 | fn skipToNl(tokenizer: *Tokenizer) void { | |
| 660 | while (true) { | |
| 661 | const tok = tokenizer.next(); | |
| 662 | if (tok.id == .nl or tok.id == .eof) return; | |
| 663 | } | |
| 664 | } | |
| 665 | ||
| 666 | const ExpandBuf = std.ArrayList(Token); | |
| 667 | const MacroArguments = std.ArrayList([]const Token); | |
| 668 | fn deinitMacroArguments(allocator: Allocator, args: *const MacroArguments) void { | |
| 669 | for (args.items) |item| { | |
| 670 | for (item) |tok| Token.free(tok.expansion_locs, allocator); | |
| 671 | allocator.free(item); | |
| 672 | } | |
| 673 | args.deinit(); | |
| 674 | } | |
| 675 | ||
| 676 | fn expandObjMacro(pp: *Preprocessor, simple_macro: *const Macro) Error!ExpandBuf { | |
| 677 | var buf = ExpandBuf.init(pp.comp.gpa); | |
| 678 | try buf.ensureTotalCapacity(simple_macro.tokens.len); | |
| 679 | ||
| 680 | // Add all of the simple_macros tokens to the new buffer handling any concats. | |
| 681 | var i: usize = 0; | |
| 682 | while (i < simple_macro.tokens.len) : (i += 1) { | |
| 683 | const raw = simple_macro.tokens[i]; | |
| 684 | const tok = tokFromRaw(raw); | |
| 685 | switch (raw.id) { | |
| 686 | .hash_hash => { | |
| 687 | var rhs = tokFromRaw(simple_macro.tokens[i + 1]); | |
| 688 | i += 1; | |
| 689 | while (rhs.id == .whitespace) { | |
| 690 | rhs = tokFromRaw(simple_macro.tokens[i + 1]); | |
| 691 | i += 1; | |
| 692 | } | |
| 693 | try pp.pasteTokens(&buf, &.{rhs}); | |
| 694 | }, | |
| 695 | .whitespace => if (pp.comp.only_preprocess) buf.appendAssumeCapacity(tok), | |
| 696 | .macro_file => { | |
| 697 | const start = pp.comp.generated_buf.items.len; | |
| 698 | const source = pp.comp.getSource(pp.expansion_source_loc.id); | |
| 699 | try pp.comp.generated_buf.writer().print("\"{s}\"\n", .{source.path}); | |
| 700 | ||
| 701 | buf.appendAssumeCapacity(try pp.makeGeneratedToken(start, .string_literal, tok)); | |
| 702 | }, | |
| 703 | .macro_line => { | |
| 704 | const start = pp.comp.generated_buf.items.len; | |
| 705 | const source = pp.comp.getSource(pp.expansion_source_loc.id); | |
| 706 | try pp.comp.generated_buf.writer().print("{d}\n", .{source.physicalLine(pp.expansion_source_loc)}); | |
| 707 | ||
| 708 | buf.appendAssumeCapacity(try pp.makeGeneratedToken(start, .integer_literal, tok)); | |
| 709 | }, | |
| 710 | .macro_counter => { | |
| 711 | defer pp.counter += 1; | |
| 712 | const start = pp.comp.generated_buf.items.len; | |
| 713 | try pp.comp.generated_buf.writer().print("{d}\n", .{pp.counter}); | |
| 714 | ||
| 715 | buf.appendAssumeCapacity(try pp.makeGeneratedToken(start, .integer_literal, tok)); | |
| 716 | }, | |
| 717 | else => buf.appendAssumeCapacity(tok), | |
| 718 | } | |
| 719 | } | |
| 720 | ||
| 721 | return buf; | |
| 722 | } | |
| 723 | ||
| 724 | /// Join a possibly-parenthesized series of string literal tokens into a single string without | |
| 725 | /// leading or trailing quotes. The returned slice is invalidated if pp.char_buf changes. | |
| 726 | /// Returns error.ExpectedStringLiteral if parentheses are not balanced, a non-string-literal | |
| 727 | /// is encountered, or if no string literals are encountered | |
| 728 | /// TODO: destringize (replace all '\\' with a single `\` and all '\"' with a '"') | |
| 729 | fn pasteStringsUnsafe(pp: *Preprocessor, toks: []const Token) ![]const u8 { | |
| 730 | const char_top = pp.char_buf.items.len; | |
| 731 | defer pp.char_buf.items.len = char_top; | |
| 732 | var unwrapped = toks; | |
| 733 | if (toks.len >= 2 and toks[0].id == .l_paren and toks[toks.len - 1].id == .r_paren) { | |
| 734 | unwrapped = toks[1 .. toks.len - 1]; | |
| 735 | } | |
| 736 | if (unwrapped.len == 0) return error.ExpectedStringLiteral; | |
| 737 | ||
| 738 | for (unwrapped) |tok| { | |
| 739 | if (tok.id == .macro_ws) continue; | |
| 740 | if (tok.id != .string_literal) return error.ExpectedStringLiteral; | |
| 741 | const str = pp.expandedSlice(tok); | |
| 742 | try pp.char_buf.appendSlice(str[1 .. str.len - 1]); | |
| 743 | } | |
| 744 | return pp.char_buf.items[char_top..]; | |
| 745 | } | |
| 746 | ||
| 747 | /// Handle the _Pragma operator (implemented as a builtin macro) | |
| 748 | fn pragmaOperator(pp: *Preprocessor, arg_tok: Token, operator_loc: Source.Location) !void { | |
| 749 | const arg_slice = pp.expandedSlice(arg_tok); | |
| 750 | const content = arg_slice[1 .. arg_slice.len - 1]; | |
| 751 | const directive = "#pragma "; | |
| 752 | ||
| 753 | pp.char_buf.clearRetainingCapacity(); | |
| 754 | const total_len = directive.len + content.len + 1; // destringify can never grow the string, + 1 for newline | |
| 755 | try pp.char_buf.ensureUnusedCapacity(total_len); | |
| 756 | pp.char_buf.appendSliceAssumeCapacity(directive); | |
| 757 | pp.destringify(content); | |
| 758 | pp.char_buf.appendAssumeCapacity('\n'); | |
| 759 | ||
| 760 | const start = pp.comp.generated_buf.items.len; | |
| 761 | try pp.comp.generated_buf.appendSlice(pp.char_buf.items); | |
| 762 | var tmp_tokenizer = Tokenizer{ | |
| 763 | .buf = pp.comp.generated_buf.items, | |
| 764 | .comp = pp.comp, | |
| 765 | .index = @intCast(u32, start), | |
| 766 | .source = .generated, | |
| 767 | .line = pp.generated_line, | |
| 768 | }; | |
| 769 | pp.generated_line += 1; | |
| 770 | const hash_tok = tmp_tokenizer.next(); | |
| 771 | assert(hash_tok.id == .hash); | |
| 772 | const pragma_tok = tmp_tokenizer.next(); | |
| 773 | assert(pragma_tok.id == .keyword_pragma); | |
| 774 | try pp.pragma(&tmp_tokenizer, pragma_tok, operator_loc, arg_tok.expansionSlice()); | |
| 775 | } | |
| 776 | ||
| 777 | /// Inverts the output of the preprocessor stringify (#) operation | |
| 778 | /// (except all whitespace is condensed to a single space) | |
| 779 | /// writes output to pp.char_buf; assumes capacity is sufficient | |
| 780 | /// backslash backslash -> backslash | |
| 781 | /// backslash doublequote -> doublequote | |
| 782 | /// All other characters remain the same | |
| 783 | fn destringify(pp: *Preprocessor, str: []const u8) void { | |
| 784 | var state: enum { start, backslash_seen } = .start; | |
| 785 | for (str) |c| { | |
| 786 | switch (c) { | |
| 787 | '\\' => { | |
| 788 | if (state == .backslash_seen) pp.char_buf.appendAssumeCapacity(c); | |
| 789 | state = if (state == .start) .backslash_seen else .start; | |
| 790 | }, | |
| 791 | else => { | |
| 792 | if (state == .backslash_seen and c != '"') pp.char_buf.appendAssumeCapacity('\\'); | |
| 793 | pp.char_buf.appendAssumeCapacity(c); | |
| 794 | state = .start; | |
| 795 | }, | |
| 796 | } | |
| 797 | } | |
| 798 | } | |
| 799 | ||
| 800 | /// Stringify `tokens` into pp.char_buf. | |
| 801 | /// See https://gcc.gnu.org/onlinedocs/gcc-11.2.0/cpp/Stringizing.html#Stringizing | |
| 802 | fn stringify(pp: *Preprocessor, tokens: []const Token) !void { | |
| 803 | try pp.char_buf.append('"'); | |
| 804 | var ws_state: enum { start, need, not_needed } = .start; | |
| 805 | for (tokens) |tok| { | |
| 806 | if (tok.id == .macro_ws) { | |
| 807 | if (ws_state == .start) continue; | |
| 808 | ws_state = .need; | |
| 809 | continue; | |
| 810 | } | |
| 811 | if (ws_state == .need) try pp.char_buf.append(' '); | |
| 812 | ws_state = .not_needed; | |
| 813 | ||
| 814 | // backslashes not inside strings are not escaped | |
| 815 | const is_str = switch (tok.id) { | |
| 816 | .string_literal, | |
| 817 | .string_literal_utf_16, | |
| 818 | .string_literal_utf_8, | |
| 819 | .string_literal_utf_32, | |
| 820 | .string_literal_wide, | |
| 821 | .char_literal, | |
| 822 | .char_literal_utf_16, | |
| 823 | .char_literal_utf_32, | |
| 824 | .char_literal_wide, | |
| 825 | => true, | |
| 826 | else => false, | |
| 827 | }; | |
| 828 | ||
| 829 | for (pp.expandedSlice(tok)) |c| { | |
| 830 | if (c == '"') | |
| 831 | try pp.char_buf.appendSlice("\\\"") | |
| 832 | else if (c == '\\' and is_str) | |
| 833 | try pp.char_buf.appendSlice("\\\\") | |
| 834 | else | |
| 835 | try pp.char_buf.append(c); | |
| 836 | } | |
| 837 | } | |
| 838 | try pp.char_buf.appendSlice("\"\n"); | |
| 839 | } | |
| 840 | ||
| 841 | fn handleBuiltinMacro(pp: *Preprocessor, builtin: RawToken.Id, param_toks: []const Token, src_loc: Source.Location) Error!bool { | |
| 842 | switch (builtin) { | |
| 843 | .macro_param_has_attribute, | |
| 844 | .macro_param_has_feature, | |
| 845 | .macro_param_has_extension, | |
| 846 | .macro_param_has_builtin, | |
| 847 | => { | |
| 848 | var invalid: ?Token = null; | |
| 849 | var identifier: ?Token = null; | |
| 850 | for (param_toks) |tok| switch (tok.id) { | |
| 851 | .identifier, .extended_identifier, .builtin_choose_expr, .builtin_va_arg => { | |
| 852 | if (identifier) |_| invalid = tok else identifier = tok; | |
| 853 | }, | |
| 854 | .macro_ws => continue, | |
| 855 | else => { | |
| 856 | invalid = tok; | |
| 857 | break; | |
| 858 | }, | |
| 859 | }; | |
| 860 | if (identifier == null and invalid == null) invalid = .{ .id = .eof, .loc = src_loc }; | |
| 861 | if (invalid) |some| { | |
| 862 | try pp.comp.diag.add( | |
| 863 | .{ .tag = .feature_check_requires_identifier, .loc = some.loc }, | |
| 864 | some.expansionSlice(), | |
| 865 | ); | |
| 866 | return false; | |
| 867 | } | |
| 868 | ||
| 869 | const ident_str = pp.expandedSlice(identifier.?); | |
| 870 | return switch (builtin) { | |
| 871 | .macro_param_has_attribute => Attribute.fromString(.gnu, null, ident_str) != null, | |
| 872 | .macro_param_has_feature => features.hasFeature(pp.comp, ident_str), | |
| 873 | .macro_param_has_extension => features.hasExtension(pp.comp, ident_str), | |
| 874 | .macro_param_has_builtin => pp.comp.builtins.hasBuiltin(ident_str), | |
| 875 | else => unreachable, | |
| 876 | }; | |
| 877 | }, | |
| 878 | .macro_param_has_warning => { | |
| 879 | const actual_param = pp.pasteStringsUnsafe(param_toks) catch |err| switch (err) { | |
| 880 | error.ExpectedStringLiteral => { | |
| 881 | try pp.comp.diag.add(.{ | |
| 882 | .tag = .expected_str_literal_in, | |
| 883 | .loc = param_toks[0].loc, | |
| 884 | .extra = .{ .str = "__has_warning" }, | |
| 885 | }, param_toks[0].expansionSlice()); | |
| 886 | return false; | |
| 887 | }, | |
| 888 | else => |e| return e, | |
| 889 | }; | |
| 890 | if (!mem.startsWith(u8, actual_param, "-W")) { | |
| 891 | try pp.comp.diag.add(.{ | |
| 892 | .tag = .malformed_warning_check, | |
| 893 | .loc = param_toks[0].loc, | |
| 894 | .extra = .{ .str = "__has_warning" }, | |
| 895 | }, param_toks[0].expansionSlice()); | |
| 896 | return false; | |
| 897 | } | |
| 898 | const warning_name = actual_param[2..]; | |
| 899 | return Diagnostics.warningExists(warning_name); | |
| 900 | }, | |
| 901 | .macro_param_is_identifier => { | |
| 902 | var invalid: ?Token = null; | |
| 903 | var identifier: ?Token = null; | |
| 904 | for (param_toks) |tok| switch (tok.id) { | |
| 905 | .macro_ws => continue, | |
| 906 | else => { | |
| 907 | if (identifier) |_| invalid = tok else identifier = tok; | |
| 908 | }, | |
| 909 | }; | |
| 910 | if (identifier == null and invalid == null) invalid = .{ .id = .eof, .loc = src_loc }; | |
| 911 | if (invalid) |some| { | |
| 912 | try pp.comp.diag.add(.{ | |
| 913 | .tag = .missing_tok_builtin, | |
| 914 | .loc = some.loc, | |
| 915 | .extra = .{ .tok_id_expected = .r_paren }, | |
| 916 | }, some.expansionSlice()); | |
| 917 | return false; | |
| 918 | } | |
| 919 | ||
| 920 | const id = identifier.?.id; | |
| 921 | return id == .identifier or id == .extended_identifier; | |
| 922 | }, | |
| 923 | else => unreachable, | |
| 924 | } | |
| 925 | } | |
| 926 | ||
| 927 | fn expandFuncMacro( | |
| 928 | pp: *Preprocessor, | |
| 929 | loc: Source.Location, | |
| 930 | func_macro: *const Macro, | |
| 931 | args: *const MacroArguments, | |
| 932 | expanded_args: *const MacroArguments, | |
| 933 | ) MacroError!ExpandBuf { | |
| 934 | var buf = ExpandBuf.init(pp.comp.gpa); | |
| 935 | try buf.ensureTotalCapacity(func_macro.tokens.len); | |
| 936 | errdefer buf.deinit(); | |
| 937 | ||
| 938 | var expanded_variable_arguments = ExpandBuf.init(pp.comp.gpa); | |
| 939 | defer expanded_variable_arguments.deinit(); | |
| 940 | var variable_arguments = ExpandBuf.init(pp.comp.gpa); | |
| 941 | defer variable_arguments.deinit(); | |
| 942 | ||
| 943 | if (func_macro.var_args) { | |
| 944 | var i: usize = func_macro.params.len; | |
| 945 | while (i < expanded_args.items.len) : (i += 1) { | |
| 946 | try variable_arguments.appendSlice(args.items[i]); | |
| 947 | try expanded_variable_arguments.appendSlice(expanded_args.items[i]); | |
| 948 | if (i != expanded_args.items.len - 1) { | |
| 949 | const comma = Token{ .id = .comma, .loc = .{ .id = .generated } }; | |
| 950 | try variable_arguments.append(comma); | |
| 951 | try expanded_variable_arguments.append(comma); | |
| 952 | } | |
| 953 | } | |
| 954 | } | |
| 955 | ||
| 956 | // token concatenation and expansion phase | |
| 957 | var tok_i: usize = 0; | |
| 958 | while (tok_i < func_macro.tokens.len) : (tok_i += 1) { | |
| 959 | const raw = func_macro.tokens[tok_i]; | |
| 960 | switch (raw.id) { | |
| 961 | .hash_hash => while (tok_i + 1 < func_macro.tokens.len) { | |
| 962 | const raw_next = func_macro.tokens[tok_i + 1]; | |
| 963 | tok_i += 1; | |
| 964 | ||
| 965 | const next = switch (raw_next.id) { | |
| 966 | .macro_ws => continue, | |
| 967 | .hash_hash => continue, | |
| 968 | .macro_param, .macro_param_no_expand => args.items[raw_next.end], | |
| 969 | .keyword_va_args => variable_arguments.items, | |
| 970 | else => &[1]Token{tokFromRaw(raw_next)}, | |
| 971 | }; | |
| 972 | ||
| 973 | try pp.pasteTokens(&buf, next); | |
| 974 | if (next.len != 0) break; | |
| 975 | }, | |
| 976 | .macro_param_no_expand => { | |
| 977 | const slice = args.items[raw.end]; | |
| 978 | const raw_loc = Source.Location{ .id = raw.source, .byte_offset = raw.start, .line = raw.line }; | |
| 979 | try bufCopyTokens(&buf, slice, &.{raw_loc}); | |
| 980 | }, | |
| 981 | .macro_param => { | |
| 982 | const arg = expanded_args.items[raw.end]; | |
| 983 | const raw_loc = Source.Location{ .id = raw.source, .byte_offset = raw.start, .line = raw.line }; | |
| 984 | try bufCopyTokens(&buf, arg, &.{raw_loc}); | |
| 985 | }, | |
| 986 | .keyword_va_args => { | |
| 987 | const raw_loc = Source.Location{ .id = raw.source, .byte_offset = raw.start, .line = raw.line }; | |
| 988 | try bufCopyTokens(&buf, expanded_variable_arguments.items, &.{raw_loc}); | |
| 989 | }, | |
| 990 | .stringify_param, .stringify_va_args => { | |
| 991 | const arg = if (raw.id == .stringify_va_args) | |
| 992 | variable_arguments.items | |
| 993 | else | |
| 994 | args.items[raw.end]; | |
| 995 | ||
| 996 | pp.char_buf.clearRetainingCapacity(); | |
| 997 | try pp.stringify(arg); | |
| 998 | ||
| 999 | const start = pp.comp.generated_buf.items.len; | |
| 1000 | try pp.comp.generated_buf.appendSlice(pp.char_buf.items); | |
| 1001 | ||
| 1002 | try buf.append(try pp.makeGeneratedToken(start, .string_literal, tokFromRaw(raw))); | |
| 1003 | }, | |
| 1004 | .macro_param_has_attribute, | |
| 1005 | .macro_param_has_warning, | |
| 1006 | .macro_param_has_feature, | |
| 1007 | .macro_param_has_extension, | |
| 1008 | .macro_param_has_builtin, | |
| 1009 | .macro_param_is_identifier, | |
| 1010 | => { | |
| 1011 | const arg = expanded_args.items[0]; | |
| 1012 | const result = if (arg.len == 0) blk: { | |
| 1013 | const extra = Diagnostics.Message.Extra{ .arguments = .{ .expected = 1, .actual = 0 } }; | |
| 1014 | try pp.comp.diag.add(.{ .tag = .expected_arguments, .loc = loc, .extra = extra }, &.{}); | |
| 1015 | break :blk false; | |
| 1016 | } else try pp.handleBuiltinMacro(raw.id, arg, loc); | |
| 1017 | const start = pp.comp.generated_buf.items.len; | |
| 1018 | try pp.comp.generated_buf.writer().print("{}\n", .{@boolToInt(result)}); | |
| 1019 | try buf.append(try pp.makeGeneratedToken(start, .integer_literal, tokFromRaw(raw))); | |
| 1020 | }, | |
| 1021 | .macro_param_pragma_operator => { | |
| 1022 | const param_toks = expanded_args.items[0]; | |
| 1023 | // Clang and GCC require exactly one token (so, no parentheses or string pasting) | |
| 1024 | // even though their error messages indicate otherwise. Ours is slightly more | |
| 1025 | // descriptive. | |
| 1026 | var invalid: ?Token = null; | |
| 1027 | var string: ?Token = null; | |
| 1028 | for (param_toks) |tok| switch (tok.id) { | |
| 1029 | .string_literal => { | |
| 1030 | if (string) |_| invalid = tok else string = tok; | |
| 1031 | }, | |
| 1032 | .macro_ws => continue, | |
| 1033 | else => { | |
| 1034 | invalid = tok; | |
| 1035 | break; | |
| 1036 | }, | |
| 1037 | }; | |
| 1038 | if (string == null and invalid == null) invalid = .{ .loc = loc, .id = .eof }; | |
| 1039 | if (invalid) |some| try pp.comp.diag.add( | |
| 1040 | .{ .tag = .pragma_operator_string_literal, .loc = some.loc }, | |
| 1041 | some.expansionSlice(), | |
| 1042 | ) else try pp.pragmaOperator(string.?, loc); | |
| 1043 | }, | |
| 1044 | else => try buf.append(tokFromRaw(raw)), | |
| 1045 | } | |
| 1046 | } | |
| 1047 | ||
| 1048 | return buf; | |
| 1049 | } | |
| 1050 | ||
| 1051 | fn shouldExpand(tok: Token, macro: *Macro) bool { | |
| 1052 | // macro.loc.line contains the macros end index | |
| 1053 | if (tok.loc.id == macro.loc.id and | |
| 1054 | tok.loc.byte_offset >= macro.loc.byte_offset and | |
| 1055 | tok.loc.byte_offset <= macro.loc.line) | |
| 1056 | return false; | |
| 1057 | for (tok.expansionSlice()) |loc| { | |
| 1058 | if (loc.id == macro.loc.id and | |
| 1059 | loc.byte_offset >= macro.loc.byte_offset and | |
| 1060 | loc.byte_offset <= macro.loc.line) | |
| 1061 | return false; | |
| 1062 | } | |
| 1063 | ||
| 1064 | return true; | |
| 1065 | } | |
| 1066 | ||
| 1067 | fn bufCopyTokens(buf: *ExpandBuf, tokens: []const Token, src: []const Source.Location) !void { | |
| 1068 | try buf.ensureUnusedCapacity(tokens.len); | |
| 1069 | for (tokens) |tok| { | |
| 1070 | var copy = try tok.dupe(buf.allocator); | |
| 1071 | try copy.addExpansionLocation(buf.allocator, src); | |
| 1072 | buf.appendAssumeCapacity(copy); | |
| 1073 | } | |
| 1074 | } | |
| 1075 | ||
| 1076 | fn nextBufToken( | |
| 1077 | pp: *Preprocessor, | |
| 1078 | tokenizer: *Tokenizer, | |
| 1079 | buf: *ExpandBuf, | |
| 1080 | start_idx: *usize, | |
| 1081 | end_idx: *usize, | |
| 1082 | extend_buf: bool, | |
| 1083 | ) Error!Token { | |
| 1084 | start_idx.* += 1; | |
| 1085 | if (start_idx.* == buf.items.len and start_idx.* >= end_idx.*) { | |
| 1086 | if (extend_buf) { | |
| 1087 | const raw_tok = tokenizer.next(); | |
| 1088 | if (raw_tok.id.isMacroIdentifier() and | |
| 1089 | pp.poisoned_identifiers.get(pp.tokSlice(raw_tok)) != null) | |
| 1090 | try pp.err(raw_tok, .poisoned_identifier); | |
| 1091 | ||
| 1092 | if (raw_tok.id == .nl) pp.add_expansion_nl += 1; | |
| 1093 | ||
| 1094 | const new_tok = tokFromRaw(raw_tok); | |
| 1095 | end_idx.* += 1; | |
| 1096 | try buf.append(new_tok); | |
| 1097 | return new_tok; | |
| 1098 | } else { | |
| 1099 | return Token{ .id = .eof, .loc = .{ .id = .generated } }; | |
| 1100 | } | |
| 1101 | } else { | |
| 1102 | return buf.items[start_idx.*]; | |
| 1103 | } | |
| 1104 | } | |
| 1105 | ||
| 1106 | fn collectMacroFuncArguments( | |
| 1107 | pp: *Preprocessor, | |
| 1108 | tokenizer: *Tokenizer, | |
| 1109 | buf: *ExpandBuf, | |
| 1110 | start_idx: *usize, | |
| 1111 | end_idx: *usize, | |
| 1112 | extend_buf: bool, | |
| 1113 | is_builtin: bool, | |
| 1114 | ) Error!(?MacroArguments) { | |
| 1115 | const name_tok = buf.items[start_idx.*]; | |
| 1116 | const saved_tokenizer = tokenizer.*; | |
| 1117 | const old_end = end_idx.*; | |
| 1118 | ||
| 1119 | while (true) { | |
| 1120 | const tok = try nextBufToken(pp, tokenizer, buf, start_idx, end_idx, extend_buf); | |
| 1121 | switch (tok.id) { | |
| 1122 | .nl, .whitespace, .macro_ws => {}, | |
| 1123 | .l_paren => break, | |
| 1124 | else => { | |
| 1125 | if (is_builtin) { | |
| 1126 | try pp.comp.diag.add(.{ | |
| 1127 | .tag = .missing_tok_builtin, | |
| 1128 | .loc = tok.loc, | |
| 1129 | .extra = .{ .tok_id_expected = .l_paren }, | |
| 1130 | }, tok.expansionSlice()); | |
| 1131 | } | |
| 1132 | // Not a macro function call, go over normal identifier, rewind | |
| 1133 | tokenizer.* = saved_tokenizer; | |
| 1134 | end_idx.* = old_end; | |
| 1135 | return null; | |
| 1136 | }, | |
| 1137 | } | |
| 1138 | } | |
| 1139 | ||
| 1140 | // collect the arguments. | |
| 1141 | var parens: u32 = 0; | |
| 1142 | var args = MacroArguments.init(pp.comp.gpa); | |
| 1143 | errdefer deinitMacroArguments(pp.comp.gpa, &args); | |
| 1144 | var curArgument = std.ArrayList(Token).init(pp.comp.gpa); | |
| 1145 | defer curArgument.deinit(); | |
| 1146 | while (true) { | |
| 1147 | var tok = try nextBufToken(pp, tokenizer, buf, start_idx, end_idx, extend_buf); | |
| 1148 | switch (tok.id) { | |
| 1149 | .comma => { | |
| 1150 | if (parens == 0) { | |
| 1151 | try args.append(curArgument.toOwnedSlice()); | |
| 1152 | } else { | |
| 1153 | try curArgument.append(try tok.dupe(pp.comp.gpa)); | |
| 1154 | } | |
| 1155 | }, | |
| 1156 | .l_paren => { | |
| 1157 | try curArgument.append(try tok.dupe(pp.comp.gpa)); | |
| 1158 | parens += 1; | |
| 1159 | }, | |
| 1160 | .r_paren => { | |
| 1161 | if (parens == 0) { | |
| 1162 | try args.append(curArgument.toOwnedSlice()); | |
| 1163 | break; | |
| 1164 | } else { | |
| 1165 | try curArgument.append(try tok.dupe(pp.comp.gpa)); | |
| 1166 | parens -= 1; | |
| 1167 | } | |
| 1168 | }, | |
| 1169 | .eof => { | |
| 1170 | deinitMacroArguments(pp.comp.gpa, &args); | |
| 1171 | tokenizer.* = saved_tokenizer; | |
| 1172 | end_idx.* = old_end; | |
| 1173 | try pp.comp.diag.add( | |
| 1174 | .{ .tag = .unterminated_macro_arg_list, .loc = name_tok.loc }, | |
| 1175 | name_tok.expansionSlice(), | |
| 1176 | ); | |
| 1177 | return null; | |
| 1178 | }, | |
| 1179 | .nl, .whitespace => { | |
| 1180 | try curArgument.append(.{ .id = .macro_ws, .loc = .{ .id = .generated } }); | |
| 1181 | }, | |
| 1182 | else => { | |
| 1183 | try curArgument.append(try tok.dupe(pp.comp.gpa)); | |
| 1184 | }, | |
| 1185 | } | |
| 1186 | } | |
| 1187 | ||
| 1188 | return args; | |
| 1189 | } | |
| 1190 | ||
| 1191 | fn expandMacroExhaustive( | |
| 1192 | pp: *Preprocessor, | |
| 1193 | tokenizer: *Tokenizer, | |
| 1194 | buf: *ExpandBuf, | |
| 1195 | start_idx: usize, | |
| 1196 | end_idx: usize, | |
| 1197 | extend_buf: bool, | |
| 1198 | ) MacroError!void { | |
| 1199 | var moving_end_idx = end_idx; | |
| 1200 | var advance_index: usize = 0; | |
| 1201 | // rescan loop | |
| 1202 | var do_rescan = true; | |
| 1203 | while (do_rescan) { | |
| 1204 | do_rescan = false; | |
| 1205 | // expansion loop | |
| 1206 | var idx: usize = start_idx + advance_index; | |
| 1207 | while (idx < moving_end_idx) { | |
| 1208 | const macro_tok = buf.items[idx]; | |
| 1209 | const macro_entry = pp.defines.getPtr(pp.expandedSlice(macro_tok)); | |
| 1210 | if (macro_entry == null or !shouldExpand(buf.items[idx], macro_entry.?)) { | |
| 1211 | idx += 1; | |
| 1212 | continue; | |
| 1213 | } | |
| 1214 | if (macro_entry) |macro| macro_handler: { | |
| 1215 | if (macro.is_func) { | |
| 1216 | var macro_scan_idx = idx; | |
| 1217 | // to be saved in case this doesn't turn out to be a call | |
| 1218 | const args = (try pp.collectMacroFuncArguments( | |
| 1219 | tokenizer, | |
| 1220 | buf, | |
| 1221 | &macro_scan_idx, | |
| 1222 | &moving_end_idx, | |
| 1223 | extend_buf, | |
| 1224 | macro.is_builtin, | |
| 1225 | )) orelse { | |
| 1226 | idx += 1; | |
| 1227 | break :macro_handler; | |
| 1228 | }; | |
| 1229 | defer { | |
| 1230 | for (args.items) |item| { | |
| 1231 | pp.comp.gpa.free(item); | |
| 1232 | } | |
| 1233 | args.deinit(); | |
| 1234 | } | |
| 1235 | ||
| 1236 | var args_count = @intCast(u32, args.items.len); | |
| 1237 | // if the macro has zero arguments g() args_count is still 1 | |
| 1238 | if (args_count == 1 and macro.params.len == 0) args_count = 0; | |
| 1239 | ||
| 1240 | // Validate argument count. | |
| 1241 | const extra = Diagnostics.Message.Extra{ | |
| 1242 | .arguments = .{ .expected = @intCast(u32, macro.params.len), .actual = args_count }, | |
| 1243 | }; | |
| 1244 | if (macro.var_args and args_count < macro.params.len) { | |
| 1245 | try pp.comp.diag.add( | |
| 1246 | .{ .tag = .expected_at_least_arguments, .loc = buf.items[idx].loc, .extra = extra }, | |
| 1247 | buf.items[idx].expansionSlice(), | |
| 1248 | ); | |
| 1249 | idx += 1; | |
| 1250 | continue; | |
| 1251 | } | |
| 1252 | if (!macro.var_args and args_count != macro.params.len) { | |
| 1253 | try pp.comp.diag.add( | |
| 1254 | .{ .tag = .expected_arguments, .loc = buf.items[idx].loc, .extra = extra }, | |
| 1255 | buf.items[idx].expansionSlice(), | |
| 1256 | ); | |
| 1257 | idx += 1; | |
| 1258 | continue; | |
| 1259 | } | |
| 1260 | var expanded_args = MacroArguments.init(pp.comp.gpa); | |
| 1261 | defer deinitMacroArguments(pp.comp.gpa, &expanded_args); | |
| 1262 | try expanded_args.ensureTotalCapacity(args.items.len); | |
| 1263 | for (args.items) |arg| { | |
| 1264 | var expand_buf = ExpandBuf.init(pp.comp.gpa); | |
| 1265 | try expand_buf.appendSlice(arg); | |
| 1266 | ||
| 1267 | try pp.expandMacroExhaustive(tokenizer, &expand_buf, 0, expand_buf.items.len, false); | |
| 1268 | ||
| 1269 | expanded_args.appendAssumeCapacity(expand_buf.toOwnedSlice()); | |
| 1270 | } | |
| 1271 | ||
| 1272 | var res = try pp.expandFuncMacro(macro_tok.loc, macro, &args, &expanded_args); | |
| 1273 | defer res.deinit(); | |
| 1274 | ||
| 1275 | const macro_expansion_locs = macro_tok.expansionSlice(); | |
| 1276 | for (res.items) |*tok| { | |
| 1277 | try tok.addExpansionLocation(pp.comp.gpa, &.{macro_tok.loc}); | |
| 1278 | try tok.addExpansionLocation(pp.comp.gpa, macro_expansion_locs); | |
| 1279 | } | |
| 1280 | ||
| 1281 | const count = macro_scan_idx - idx + 1; | |
| 1282 | for (buf.items[idx .. idx + count]) |tok| Token.free(tok.expansion_locs, pp.comp.gpa); | |
| 1283 | try buf.replaceRange(idx, count, res.items); | |
| 1284 | // TODO: moving_end_idx += res.items.len - (macro_scan_idx-idx+1) | |
| 1285 | // doesn't work when the RHS is negative (unsigned!) | |
| 1286 | moving_end_idx = moving_end_idx + res.items.len - count; | |
| 1287 | idx += res.items.len; | |
| 1288 | do_rescan = true; | |
| 1289 | } else { | |
| 1290 | const res = try pp.expandObjMacro(macro); | |
| 1291 | defer res.deinit(); | |
| 1292 | ||
| 1293 | const macro_expansion_locs = macro_tok.expansionSlice(); | |
| 1294 | for (res.items) |*tok| { | |
| 1295 | try tok.addExpansionLocation(pp.comp.gpa, &.{macro_tok.loc}); | |
| 1296 | try tok.addExpansionLocation(pp.comp.gpa, macro_expansion_locs); | |
| 1297 | } | |
| 1298 | ||
| 1299 | Token.free(buf.items[idx].expansion_locs, pp.comp.gpa); | |
| 1300 | try buf.replaceRange(idx, 1, res.items); | |
| 1301 | idx += res.items.len; | |
| 1302 | moving_end_idx = moving_end_idx + res.items.len - 1; | |
| 1303 | do_rescan = true; | |
| 1304 | } | |
| 1305 | } | |
| 1306 | if (idx - start_idx == advance_index + 1 and !do_rescan) { | |
| 1307 | advance_index += 1; | |
| 1308 | } | |
| 1309 | } // end of replacement phase | |
| 1310 | } | |
| 1311 | // end of scanning phase | |
| 1312 | ||
| 1313 | // trim excess buffer | |
| 1314 | for (buf.items[moving_end_idx..]) |item| { | |
| 1315 | Token.free(item.expansion_locs, pp.comp.gpa); | |
| 1316 | } | |
| 1317 | buf.items.len = moving_end_idx; | |
| 1318 | } | |
| 1319 | ||
| 1320 | /// Try to expand a macro after a possible candidate has been read from the `tokenizer` | |
| 1321 | /// into the `raw` token passed as argument | |
| 1322 | fn expandMacro(pp: *Preprocessor, tokenizer: *Tokenizer, raw: RawToken) MacroError!void { | |
| 1323 | var source_tok = tokFromRaw(raw); | |
| 1324 | if (!raw.id.isMacroIdentifier()) { | |
| 1325 | source_tok.id.simplifyMacroKeyword(); | |
| 1326 | return pp.tokens.append(pp.comp.gpa, source_tok); | |
| 1327 | } | |
| 1328 | pp.top_expansion_buf.items.len = 0; | |
| 1329 | try pp.top_expansion_buf.append(source_tok); | |
| 1330 | pp.expansion_source_loc = source_tok.loc; | |
| 1331 | ||
| 1332 | try pp.expandMacroExhaustive(tokenizer, &pp.top_expansion_buf, 0, 1, true); | |
| 1333 | try pp.tokens.ensureUnusedCapacity(pp.comp.gpa, pp.top_expansion_buf.items.len); | |
| 1334 | for (pp.top_expansion_buf.items) |*tok| { | |
| 1335 | if (tok.id == .macro_ws and !pp.comp.only_preprocess) { | |
| 1336 | Token.free(tok.expansion_locs, pp.comp.gpa); | |
| 1337 | continue; | |
| 1338 | } | |
| 1339 | tok.id.simplifyMacroKeyword(); | |
| 1340 | pp.tokens.appendAssumeCapacity(tok.*); | |
| 1341 | } | |
| 1342 | if (pp.comp.only_preprocess) { | |
| 1343 | try pp.tokens.ensureUnusedCapacity(pp.comp.gpa, pp.add_expansion_nl); | |
| 1344 | while (pp.add_expansion_nl > 0) : (pp.add_expansion_nl -= 1) { | |
| 1345 | pp.tokens.appendAssumeCapacity(.{ .id = .nl, .loc = .{ .id = .generated } }); | |
| 1346 | } | |
| 1347 | } | |
| 1348 | } | |
| 1349 | ||
| 1350 | /// Get expanded token source string. | |
| 1351 | pub fn expandedSlice(pp: *Preprocessor, tok: Token) []const u8 { | |
| 1352 | if (tok.id.lexeme()) |some| return some; | |
| 1353 | var tmp_tokenizer = Tokenizer{ | |
| 1354 | .buf = pp.comp.getSource(tok.loc.id).buf, | |
| 1355 | .comp = pp.comp, | |
| 1356 | .index = tok.loc.byte_offset, | |
| 1357 | .source = .generated, | |
| 1358 | }; | |
| 1359 | if (tok.id == .macro_string) { | |
| 1360 | while (true) : (tmp_tokenizer.index += 1) { | |
| 1361 | if (tmp_tokenizer.buf[tmp_tokenizer.index] == '>') break; | |
| 1362 | } | |
| 1363 | return tmp_tokenizer.buf[tok.loc.byte_offset .. tmp_tokenizer.index + 1]; | |
| 1364 | } | |
| 1365 | const res = tmp_tokenizer.next(); | |
| 1366 | return tmp_tokenizer.buf[res.start..res.end]; | |
| 1367 | } | |
| 1368 | ||
| 1369 | /// Concat two tokens and add the result to pp.generated | |
| 1370 | fn pasteTokens(pp: *Preprocessor, lhs_toks: *ExpandBuf, rhs_toks: []const Token) Error!void { | |
| 1371 | const lhs = while (lhs_toks.popOrNull()) |lhs| { | |
| 1372 | if (lhs.id == .macro_ws) | |
| 1373 | Token.free(lhs.expansion_locs, pp.comp.gpa) | |
| 1374 | else | |
| 1375 | break lhs; | |
| 1376 | } else { | |
| 1377 | return bufCopyTokens(lhs_toks, rhs_toks, &.{}); | |
| 1378 | }; | |
| 1379 | ||
| 1380 | var rhs_rest: u32 = 1; | |
| 1381 | const rhs = for (rhs_toks) |rhs| { | |
| 1382 | if (rhs.id != .macro_ws) break rhs; | |
| 1383 | rhs_rest += 1; | |
| 1384 | } else { | |
| 1385 | return lhs_toks.appendAssumeCapacity(lhs); | |
| 1386 | }; | |
| 1387 | defer Token.free(lhs.expansion_locs, pp.comp.gpa); | |
| 1388 | ||
| 1389 | const start = pp.comp.generated_buf.items.len; | |
| 1390 | const end = start + pp.expandedSlice(lhs).len + pp.expandedSlice(rhs).len; | |
| 1391 | try pp.comp.generated_buf.ensureTotalCapacity(end + 1); // +1 for a newline | |
| 1392 | // We cannot use the same slices here since they might be invalidated by `ensureCapacity` | |
| 1393 | pp.comp.generated_buf.appendSliceAssumeCapacity(pp.expandedSlice(lhs)); | |
| 1394 | pp.comp.generated_buf.appendSliceAssumeCapacity(pp.expandedSlice(rhs)); | |
| 1395 | pp.comp.generated_buf.appendAssumeCapacity('\n'); | |
| 1396 | ||
| 1397 | // Try to tokenize the result. | |
| 1398 | var tmp_tokenizer = Tokenizer{ | |
| 1399 | .buf = pp.comp.generated_buf.items, | |
| 1400 | .comp = pp.comp, | |
| 1401 | .index = @intCast(u32, start), | |
| 1402 | .source = .generated, | |
| 1403 | }; | |
| 1404 | const pasted_token = tmp_tokenizer.nextNoWS(); | |
| 1405 | const next = tmp_tokenizer.nextNoWS().id; | |
| 1406 | if (next != .nl and next != .eof) { | |
| 1407 | try pp.comp.diag.add(.{ | |
| 1408 | .tag = .pasting_formed_invalid, | |
| 1409 | .loc = lhs.loc, | |
| 1410 | .extra = .{ .str = try pp.comp.diag.arena.allocator().dupe( | |
| 1411 | u8, | |
| 1412 | pp.comp.generated_buf.items[start..end], | |
| 1413 | ) }, | |
| 1414 | }, lhs.expansionSlice()); | |
| 1415 | } | |
| 1416 | ||
| 1417 | try lhs_toks.append(try pp.makeGeneratedToken(start, pasted_token.id, lhs)); | |
| 1418 | try bufCopyTokens(lhs_toks, rhs_toks[rhs_rest..], &.{}); | |
| 1419 | } | |
| 1420 | ||
| 1421 | fn makeGeneratedToken(pp: *Preprocessor, start: usize, id: Token.Id, source: Token) !Token { | |
| 1422 | var pasted_token = Token{ .id = id, .loc = .{ | |
| 1423 | .id = .generated, | |
| 1424 | .byte_offset = @intCast(u32, start), | |
| 1425 | .line = pp.generated_line, | |
| 1426 | } }; | |
| 1427 | pp.generated_line += 1; | |
| 1428 | try pasted_token.addExpansionLocation(pp.comp.gpa, &.{source.loc}); | |
| 1429 | try pasted_token.addExpansionLocation(pp.comp.gpa, source.expansionSlice()); | |
| 1430 | return pasted_token; | |
| 1431 | } | |
| 1432 | ||
| 1433 | /// Defines a new macro and warns if it is a duplicate | |
| 1434 | fn defineMacro(pp: *Preprocessor, name_tok: RawToken, macro: Macro) Error!void { | |
| 1435 | const name_str = pp.tokSlice(name_tok); | |
| 1436 | const gop = try pp.defines.getOrPut(name_str); | |
| 1437 | if (gop.found_existing and !gop.value_ptr.eql(macro, pp)) { | |
| 1438 | try pp.comp.diag.add(.{ | |
| 1439 | .tag = if (gop.value_ptr.is_builtin) .builtin_macro_redefined else .macro_redefined, | |
| 1440 | .loc = .{ .id = name_tok.source, .byte_offset = name_tok.start, .line = name_tok.line }, | |
| 1441 | .extra = .{ .str = name_str }, | |
| 1442 | }, &.{}); | |
| 1443 | // TODO add a previous definition note | |
| 1444 | } | |
| 1445 | gop.value_ptr.* = macro; | |
| 1446 | } | |
| 1447 | ||
| 1448 | /// Handle a #define directive. | |
| 1449 | fn define(pp: *Preprocessor, tokenizer: *Tokenizer) Error!void { | |
| 1450 | // Get macro name and validate it. | |
| 1451 | const macro_name = tokenizer.nextNoWS(); | |
| 1452 | if (macro_name.id == .keyword_defined) { | |
| 1453 | try pp.err(macro_name, .defined_as_macro_name); | |
| 1454 | return skipToNl(tokenizer); | |
| 1455 | } | |
| 1456 | if (!macro_name.id.isMacroIdentifier()) { | |
| 1457 | try pp.err(macro_name, .macro_name_must_be_identifier); | |
| 1458 | return skipToNl(tokenizer); | |
| 1459 | } | |
| 1460 | ||
| 1461 | // Check for function macros and empty defines. | |
| 1462 | var first = tokenizer.next(); | |
| 1463 | switch (first.id) { | |
| 1464 | .nl, .eof => return pp.defineMacro(macro_name, .{ | |
| 1465 | .params = undefined, | |
| 1466 | .tokens = undefined, | |
| 1467 | .var_args = false, | |
| 1468 | .loc = undefined, | |
| 1469 | .is_func = false, | |
| 1470 | }), | |
| 1471 | .whitespace => first = tokenizer.next(), | |
| 1472 | .l_paren => return pp.defineFn(tokenizer, macro_name, first), | |
| 1473 | else => try pp.err(first, .whitespace_after_macro_name), | |
| 1474 | } | |
| 1475 | if (first.id == .hash_hash) { | |
| 1476 | try pp.err(first, .hash_hash_at_start); | |
| 1477 | return skipToNl(tokenizer); | |
| 1478 | } | |
| 1479 | first.id.simplifyMacroKeyword(); | |
| 1480 | ||
| 1481 | pp.token_buf.items.len = 0; // Safe to use since we can only be in one directive at a time. | |
| 1482 | ||
| 1483 | var need_ws = false; | |
| 1484 | // Collect the token body and validate any ## found. | |
| 1485 | var tok = first; | |
| 1486 | const end_index = while (true) { | |
| 1487 | tok.id.simplifyMacroKeyword(); | |
| 1488 | switch (tok.id) { | |
| 1489 | .hash_hash => { | |
| 1490 | const next = tokenizer.nextNoWS(); | |
| 1491 | switch (next.id) { | |
| 1492 | .nl, .eof => { | |
| 1493 | try pp.err(tok, .hash_hash_at_end); | |
| 1494 | return; | |
| 1495 | }, | |
| 1496 | .hash_hash => { | |
| 1497 | try pp.err(next, .hash_hash_at_end); | |
| 1498 | return; | |
| 1499 | }, | |
| 1500 | else => {}, | |
| 1501 | } | |
| 1502 | try pp.token_buf.append(tok); | |
| 1503 | try pp.token_buf.append(next); | |
| 1504 | }, | |
| 1505 | .nl, .eof => break tok.start, | |
| 1506 | .whitespace => need_ws = true, | |
| 1507 | else => { | |
| 1508 | if (tok.id != .whitespace and need_ws) { | |
| 1509 | need_ws = false; | |
| 1510 | try pp.token_buf.append(.{ .id = .macro_ws, .source = .generated }); | |
| 1511 | } | |
| 1512 | try pp.token_buf.append(tok); | |
| 1513 | }, | |
| 1514 | } | |
| 1515 | tok = tokenizer.next(); | |
| 1516 | } else unreachable; | |
| 1517 | ||
| 1518 | const list = try pp.arena.allocator().dupe(RawToken, pp.token_buf.items); | |
| 1519 | try pp.defineMacro(macro_name, .{ | |
| 1520 | .loc = .{ | |
| 1521 | .id = macro_name.source, | |
| 1522 | .byte_offset = first.start, | |
| 1523 | .line = end_index, | |
| 1524 | }, | |
| 1525 | .tokens = list, | |
| 1526 | .params = undefined, | |
| 1527 | .is_func = false, | |
| 1528 | .var_args = false, | |
| 1529 | }); | |
| 1530 | } | |
| 1531 | ||
| 1532 | /// Handle a function like #define directive. | |
| 1533 | fn defineFn(pp: *Preprocessor, tokenizer: *Tokenizer, macro_name: RawToken, l_paren: RawToken) Error!void { | |
| 1534 | assert(macro_name.id.isMacroIdentifier()); | |
| 1535 | var params = std.ArrayList([]const u8).init(pp.comp.gpa); | |
| 1536 | defer params.deinit(); | |
| 1537 | ||
| 1538 | // Parse the parameter list. | |
| 1539 | var gnu_var_args: []const u8 = ""; | |
| 1540 | var var_args = false; | |
| 1541 | const start_index = while (true) { | |
| 1542 | var tok = tokenizer.nextNoWS(); | |
| 1543 | if (tok.id == .r_paren) break tok.end; | |
| 1544 | if (tok.id == .eof) return pp.err(tok, .unterminated_macro_param_list); | |
| 1545 | if (tok.id == .ellipsis) { | |
| 1546 | var_args = true; | |
| 1547 | const r_paren = tokenizer.nextNoWS(); | |
| 1548 | if (r_paren.id != .r_paren) { | |
| 1549 | try pp.err(r_paren, .missing_paren_param_list); | |
| 1550 | try pp.err(l_paren, .to_match_paren); | |
| 1551 | return skipToNl(tokenizer); | |
| 1552 | } | |
| 1553 | break r_paren.end; | |
| 1554 | } | |
| 1555 | if (!tok.id.isMacroIdentifier()) { | |
| 1556 | try pp.err(tok, .invalid_token_param_list); | |
| 1557 | return skipToNl(tokenizer); | |
| 1558 | } | |
| 1559 | ||
| 1560 | try params.append(pp.tokSlice(tok)); | |
| 1561 | ||
| 1562 | tok = tokenizer.nextNoWS(); | |
| 1563 | if (tok.id == .ellipsis) { | |
| 1564 | try pp.err(tok, .gnu_va_macro); | |
| 1565 | gnu_var_args = params.pop(); | |
| 1566 | const r_paren = tokenizer.nextNoWS(); | |
| 1567 | if (r_paren.id != .r_paren) { | |
| 1568 | try pp.err(r_paren, .missing_paren_param_list); | |
| 1569 | try pp.err(l_paren, .to_match_paren); | |
| 1570 | return skipToNl(tokenizer); | |
| 1571 | } | |
| 1572 | break r_paren.end; | |
| 1573 | } else if (tok.id == .r_paren) { | |
| 1574 | break tok.end; | |
| 1575 | } else if (tok.id != .comma) { | |
| 1576 | try pp.err(tok, .expected_comma_param_list); | |
| 1577 | return skipToNl(tokenizer); | |
| 1578 | } | |
| 1579 | } else unreachable; | |
| 1580 | ||
| 1581 | var need_ws = false; | |
| 1582 | // Collect the body tokens and validate # and ##'s found. | |
| 1583 | pp.token_buf.items.len = 0; // Safe to use since we can only be in one directive at a time. | |
| 1584 | const end_index = tok_loop: while (true) { | |
| 1585 | var tok = tokenizer.next(); | |
| 1586 | switch (tok.id) { | |
| 1587 | .nl, .eof => break tok.start, | |
| 1588 | .whitespace => need_ws = pp.token_buf.items.len != 0, | |
| 1589 | .hash => { | |
| 1590 | if (tok.id != .whitespace and need_ws) { | |
| 1591 | need_ws = false; | |
| 1592 | try pp.token_buf.append(.{ .id = .macro_ws, .source = .generated }); | |
| 1593 | } | |
| 1594 | const param = tokenizer.nextNoWS(); | |
| 1595 | blk: { | |
| 1596 | if (var_args and param.id == .keyword_va_args) { | |
| 1597 | tok.id = .stringify_va_args; | |
| 1598 | try pp.token_buf.append(tok); | |
| 1599 | continue :tok_loop; | |
| 1600 | } | |
| 1601 | if (!param.id.isMacroIdentifier()) break :blk; | |
| 1602 | const s = pp.tokSlice(param); | |
| 1603 | if (mem.eql(u8, s, gnu_var_args)) { | |
| 1604 | tok.id = .stringify_va_args; | |
| 1605 | try pp.token_buf.append(tok); | |
| 1606 | continue :tok_loop; | |
| 1607 | } | |
| 1608 | for (params.items) |p, i| { | |
| 1609 | if (mem.eql(u8, p, s)) { | |
| 1610 | tok.id = .stringify_param; | |
| 1611 | tok.end = @intCast(u32, i); | |
| 1612 | try pp.token_buf.append(tok); | |
| 1613 | continue :tok_loop; | |
| 1614 | } | |
| 1615 | } | |
| 1616 | } | |
| 1617 | try pp.err(param, .hash_not_followed_param); | |
| 1618 | return skipToNl(tokenizer); | |
| 1619 | }, | |
| 1620 | .hash_hash => { | |
| 1621 | need_ws = false; | |
| 1622 | // if ## appears at the beginning, the token buf is still empty | |
| 1623 | // in this case, error out | |
| 1624 | if (pp.token_buf.items.len == 0) { | |
| 1625 | try pp.err(tok, .hash_hash_at_start); | |
| 1626 | return skipToNl(tokenizer); | |
| 1627 | } | |
| 1628 | const saved_tokenizer = tokenizer.*; | |
| 1629 | const next = tokenizer.nextNoWS(); | |
| 1630 | if (next.id == .nl or next.id == .eof) { | |
| 1631 | try pp.err(tok, .hash_hash_at_end); | |
| 1632 | return; | |
| 1633 | } | |
| 1634 | tokenizer.* = saved_tokenizer; | |
| 1635 | // convert the previous token to .macro_param_no_expand if it was .macro_param | |
| 1636 | if (pp.token_buf.items[pp.token_buf.items.len - 1].id == .macro_param) { | |
| 1637 | pp.token_buf.items[pp.token_buf.items.len - 1].id = .macro_param_no_expand; | |
| 1638 | } | |
| 1639 | try pp.token_buf.append(tok); | |
| 1640 | }, | |
| 1641 | else => { | |
| 1642 | if (tok.id != .whitespace and need_ws) { | |
| 1643 | need_ws = false; | |
| 1644 | try pp.token_buf.append(.{ .id = .macro_ws, .source = .generated }); | |
| 1645 | } | |
| 1646 | if (var_args and tok.id == .keyword_va_args) { | |
| 1647 | // do nothing | |
| 1648 | } else if (tok.id.isMacroIdentifier()) { | |
| 1649 | tok.id.simplifyMacroKeyword(); | |
| 1650 | const s = pp.tokSlice(tok); | |
| 1651 | if (mem.eql(u8, gnu_var_args, s)) { | |
| 1652 | tok.id = .keyword_va_args; | |
| 1653 | } else for (params.items) |param, i| { | |
| 1654 | if (mem.eql(u8, param, s)) { | |
| 1655 | // NOTE: it doesn't matter to assign .macro_param_no_expand | |
| 1656 | // here in case a ## was the previous token, because | |
| 1657 | // ## processing will eat this token with the same semantics | |
| 1658 | tok.id = .macro_param; | |
| 1659 | tok.end = @intCast(u32, i); | |
| 1660 | break; | |
| 1661 | } | |
| 1662 | } | |
| 1663 | } | |
| 1664 | try pp.token_buf.append(tok); | |
| 1665 | }, | |
| 1666 | } | |
| 1667 | } else unreachable; | |
| 1668 | ||
| 1669 | const param_list = try pp.arena.allocator().dupe([]const u8, params.items); | |
| 1670 | const token_list = try pp.arena.allocator().dupe(RawToken, pp.token_buf.items); | |
| 1671 | try pp.defineMacro(macro_name, .{ | |
| 1672 | .is_func = true, | |
| 1673 | .params = param_list, | |
| 1674 | .var_args = var_args or gnu_var_args.len != 0, | |
| 1675 | .tokens = token_list, | |
| 1676 | .loc = .{ | |
| 1677 | .id = macro_name.source, | |
| 1678 | .byte_offset = start_index, | |
| 1679 | .line = end_index, | |
| 1680 | }, | |
| 1681 | }); | |
| 1682 | } | |
| 1683 | ||
| 1684 | // Handle a #include directive. | |
| 1685 | fn include(pp: *Preprocessor, tokenizer: *Tokenizer) MacroError!void { | |
| 1686 | const new_source = findIncludeSource(pp, tokenizer) catch |er| switch (er) { | |
| 1687 | error.InvalidInclude => return, | |
| 1688 | else => |e| return e, | |
| 1689 | }; | |
| 1690 | ||
| 1691 | // Prevent stack overflow | |
| 1692 | pp.include_depth += 1; | |
| 1693 | defer pp.include_depth -= 1; | |
| 1694 | if (pp.include_depth > max_include_depth) return; | |
| 1695 | ||
| 1696 | _ = pp.preprocessExtra(new_source) catch |err| switch (err) { | |
| 1697 | error.StopPreprocessing => {}, | |
| 1698 | else => |e| return e, | |
| 1699 | }; | |
| 1700 | } | |
| 1701 | ||
| 1702 | /// tokens that are part of a pragma directive can happen in 3 ways: | |
| 1703 | /// 1. directly in the text via `#pragma ...` | |
| 1704 | /// 2. Via a string literal argument to `_Pragma` | |
| 1705 | /// 3. Via a stringified macro argument which is used as an argument to `_Pragma` | |
| 1706 | /// operator_loc: Location of `_Pragma`; null if this is from #pragma | |
| 1707 | /// arg_locs: expansion locations of the argument to _Pragma. empty if #pragma or a raw string literal was used | |
| 1708 | fn makePragmaToken(pp: *Preprocessor, raw: RawToken, operator_loc: ?Source.Location, arg_locs: []const Source.Location) !Token { | |
| 1709 | var tok = tokFromRaw(raw); | |
| 1710 | if (operator_loc) |loc| { | |
| 1711 | try tok.addExpansionLocation(pp.comp.gpa, &.{loc}); | |
| 1712 | } | |
| 1713 | try tok.addExpansionLocation(pp.comp.gpa, arg_locs); | |
| 1714 | return tok; | |
| 1715 | } | |
| 1716 | ||
| 1717 | /// Handle a pragma directive | |
| 1718 | fn pragma(pp: *Preprocessor, tokenizer: *Tokenizer, pragma_tok: RawToken, operator_loc: ?Source.Location, arg_locs: []const Source.Location) !void { | |
| 1719 | const name_tok = tokenizer.nextNoWS(); | |
| 1720 | if (name_tok.id == .nl or name_tok.id == .eof) return; | |
| 1721 | ||
| 1722 | const name = pp.tokSlice(name_tok); | |
| 1723 | try pp.tokens.append(pp.comp.gpa, try pp.makePragmaToken(pragma_tok, operator_loc, arg_locs)); | |
| 1724 | const pragma_start = @intCast(u32, pp.tokens.len); | |
| 1725 | ||
| 1726 | const pragma_name_tok = try pp.makePragmaToken(name_tok, operator_loc, arg_locs); | |
| 1727 | try pp.tokens.append(pp.comp.gpa, pragma_name_tok); | |
| 1728 | while (true) { | |
| 1729 | const next_tok = tokenizer.next(); | |
| 1730 | if (next_tok.id == .whitespace) continue; | |
| 1731 | if (next_tok.id == .eof) { | |
| 1732 | try pp.tokens.append(pp.comp.gpa, .{ | |
| 1733 | .id = .nl, | |
| 1734 | .loc = .{ .id = .generated }, | |
| 1735 | }); | |
| 1736 | break; | |
| 1737 | } | |
| 1738 | try pp.tokens.append(pp.comp.gpa, try pp.makePragmaToken(next_tok, operator_loc, arg_locs)); | |
| 1739 | if (next_tok.id == .nl) break; | |
| 1740 | } | |
| 1741 | if (pp.comp.getPragma(name)) |prag| unknown: { | |
| 1742 | return prag.preprocessorCB(pp, pragma_start) catch |err| switch (err) { | |
| 1743 | error.UnknownPragma => break :unknown, | |
| 1744 | else => |e| return e, | |
| 1745 | }; | |
| 1746 | } | |
| 1747 | return pp.comp.diag.add(.{ | |
| 1748 | .tag = .unknown_pragma, | |
| 1749 | .loc = pragma_name_tok.loc, | |
| 1750 | }, pragma_name_tok.expansionSlice()); | |
| 1751 | } | |
| 1752 | ||
| 1753 | fn findIncludeSource(pp: *Preprocessor, tokenizer: *Tokenizer) !Source { | |
| 1754 | const start = pp.tokens.len; | |
| 1755 | defer pp.tokens.len = start; | |
| 1756 | ||
| 1757 | var first = tokenizer.nextNoWS(); | |
| 1758 | if (first.id == .angle_bracket_left) to_end: { | |
| 1759 | // The tokenizer does not handle <foo> include strings so do it here. | |
| 1760 | while (tokenizer.index < tokenizer.buf.len) : (tokenizer.index += 1) { | |
| 1761 | switch (tokenizer.buf[tokenizer.index]) { | |
| 1762 | '>' => { | |
| 1763 | tokenizer.index += 1; | |
| 1764 | first.end = tokenizer.index; | |
| 1765 | first.id = .macro_string; | |
| 1766 | break :to_end; | |
| 1767 | }, | |
| 1768 | '\n' => break, | |
| 1769 | else => {}, | |
| 1770 | } | |
| 1771 | } | |
| 1772 | try pp.comp.diag.add(.{ | |
| 1773 | .tag = .header_str_closing, | |
| 1774 | .loc = .{ .id = first.source, .byte_offset = first.start }, | |
| 1775 | }, &.{}); | |
| 1776 | try pp.err(first, .header_str_match); | |
| 1777 | } | |
| 1778 | // Try to expand if the argument is a macro. | |
| 1779 | try pp.expandMacro(tokenizer, first); | |
| 1780 | ||
| 1781 | // Check that we actually got a string. | |
| 1782 | const filename_tok = pp.tokens.get(start); | |
| 1783 | switch (filename_tok.id) { | |
| 1784 | .string_literal, .macro_string => {}, | |
| 1785 | else => { | |
| 1786 | try pp.err(first, .expected_filename); | |
| 1787 | try pp.expectNl(tokenizer); | |
| 1788 | return error.InvalidInclude; | |
| 1789 | }, | |
| 1790 | } | |
| 1791 | // Error on extra tokens. | |
| 1792 | const nl = tokenizer.nextNoWS(); | |
| 1793 | if ((nl.id != .nl and nl.id != .eof) or pp.tokens.len > start + 1) { | |
| 1794 | skipToNl(tokenizer); | |
| 1795 | try pp.err(first, .extra_tokens_directive_end); | |
| 1796 | } | |
| 1797 | ||
| 1798 | // Check for empty filename. | |
| 1799 | const tok_slice = pp.expandedSlice(filename_tok); | |
| 1800 | if (tok_slice.len < 3) { | |
| 1801 | try pp.err(first, .empty_filename); | |
| 1802 | return error.InvalidInclude; | |
| 1803 | } | |
| 1804 | ||
| 1805 | // Find the file. | |
| 1806 | const filename = tok_slice[1 .. tok_slice.len - 1]; | |
| 1807 | return (try pp.comp.findInclude(first, filename, filename_tok.id == .string_literal)) orelse | |
| 1808 | pp.fatal(first, "'{s}' not found", .{filename}); | |
| 1809 | } | |
| 1810 | ||
| 1811 | /// Pretty print tokens and try to preserve whitespace. | |
| 1812 | pub fn prettyPrintTokens(pp: *Preprocessor, w: anytype) !void { | |
| 1813 | var i: u32 = 0; | |
| 1814 | while (true) : (i += 1) { | |
| 1815 | var cur: Token = pp.tokens.get(i); | |
| 1816 | switch (cur.id) { | |
| 1817 | .eof => { | |
| 1818 | if (pp.tokens.len > 1 and pp.tokens.items(.id)[i - 1] != .nl) try w.writeByte('\n'); | |
| 1819 | break; | |
| 1820 | }, | |
| 1821 | .nl => try w.writeAll("\n"), | |
| 1822 | .keyword_pragma => { | |
| 1823 | const pragma_name = pp.expandedSlice(pp.tokens.get(i + 1)); | |
| 1824 | const end_idx = mem.indexOfScalarPos(Token.Id, pp.tokens.items(.id), i, .nl) orelse i + 1; | |
| 1825 | const pragma_len = @intCast(u32, end_idx) - i; | |
| 1826 | ||
| 1827 | if (pp.comp.getPragma(pragma_name)) |prag| { | |
| 1828 | if (!prag.shouldPreserveTokens(pp, i + 1)) { | |
| 1829 | i += pragma_len; | |
| 1830 | cur = pp.tokens.get(i); | |
| 1831 | continue; | |
| 1832 | } | |
| 1833 | } | |
| 1834 | try w.writeAll("#pragma"); | |
| 1835 | i += 1; | |
| 1836 | while (true) : (i += 1) { | |
| 1837 | cur = pp.tokens.get(i); | |
| 1838 | if (cur.id == .nl) { | |
| 1839 | try w.writeByte('\n'); | |
| 1840 | break; | |
| 1841 | } | |
| 1842 | try w.writeByte(' '); | |
| 1843 | const slice = pp.expandedSlice(cur); | |
| 1844 | try w.writeAll(slice); | |
| 1845 | } | |
| 1846 | }, | |
| 1847 | .whitespace => { | |
| 1848 | var slice = pp.expandedSlice(cur); | |
| 1849 | while (mem.indexOfScalar(u8, slice, '\n')) |some| { | |
| 1850 | try w.writeByte('\n'); | |
| 1851 | slice = slice[some + 1 ..]; | |
| 1852 | } | |
| 1853 | for (slice) |_| try w.writeByte(' '); | |
| 1854 | }, | |
| 1855 | else => { | |
| 1856 | const slice = pp.expandedSlice(cur); | |
| 1857 | try w.writeAll(slice); | |
| 1858 | }, | |
| 1859 | } | |
| 1860 | } | |
| 1861 | } | |
| 1862 | ||
| 1863 | test "Preserve pragma tokens sometimes" { | |
| 1864 | const allocator = std.testing.allocator; | |
| 1865 | const Test = struct { | |
| 1866 | fn runPreprocessor(source_text: []const u8) ![]const u8 { | |
| 1867 | var buf = std.ArrayList(u8).init(allocator); | |
| 1868 | defer buf.deinit(); | |
| 1869 | ||
| 1870 | var comp = Compilation.init(allocator); | |
| 1871 | defer comp.deinit(); | |
| 1872 | comp.only_preprocess = true; | |
| 1873 | ||
| 1874 | try comp.addDefaultPragmaHandlers(); | |
| 1875 | ||
| 1876 | var pp = Preprocessor.init(&comp); | |
| 1877 | defer pp.deinit(); | |
| 1878 | ||
| 1879 | const test_runner_macros = try comp.addSourceFromBuffer("<test_runner>", source_text); | |
| 1880 | const eof = try pp.preprocess(test_runner_macros); | |
| 1881 | try pp.tokens.append(pp.comp.gpa, eof); | |
| 1882 | try pp.prettyPrintTokens(buf.writer()); | |
| 1883 | return allocator.dupe(u8, buf.items); | |
| 1884 | } | |
| 1885 | ||
| 1886 | fn check(source_text: []const u8, expected: []const u8) !void { | |
| 1887 | const output = try runPreprocessor(source_text); | |
| 1888 | defer allocator.free(output); | |
| 1889 | ||
| 1890 | try std.testing.expectEqualStrings(expected, output); | |
| 1891 | } | |
| 1892 | }; | |
| 1893 | const preserve_gcc_diagnostic = | |
| 1894 | \\#pragma GCC diagnostic error "-Wnewline-eof" | |
| 1895 | \\#pragma GCC warning error "-Wnewline-eof" | |
| 1896 | \\int x; | |
| 1897 | \\#pragma GCC ignored error "-Wnewline-eof" | |
| 1898 | \\ | |
| 1899 | ; | |
| 1900 | try Test.check(preserve_gcc_diagnostic, preserve_gcc_diagnostic); | |
| 1901 | ||
| 1902 | const omit_once = | |
| 1903 | \\#pragma once | |
| 1904 | \\int x; | |
| 1905 | \\#pragma once | |
| 1906 | \\ | |
| 1907 | ; | |
| 1908 | try Test.check(omit_once, "int x;\n"); | |
| 1909 | ||
| 1910 | const omit_poison = | |
| 1911 | \\#pragma GCC poison foobar | |
| 1912 | \\ | |
| 1913 | ; | |
| 1914 | try Test.check(omit_poison, ""); | |
| 1915 | } | |
| 1916 | ||
| 1917 | test "destringify" { | |
| 1918 | const allocator = std.testing.allocator; | |
| 1919 | const Test = struct { | |
| 1920 | fn testDestringify(pp: *Preprocessor, stringified: []const u8, destringified: []const u8) !void { | |
| 1921 | pp.char_buf.clearRetainingCapacity(); | |
| 1922 | try pp.char_buf.ensureUnusedCapacity(stringified.len); | |
| 1923 | pp.destringify(stringified); | |
| 1924 | try std.testing.expectEqualStrings(destringified, pp.char_buf.items); | |
| 1925 | } | |
| 1926 | }; | |
| 1927 | var comp = Compilation.init(allocator); | |
| 1928 | defer comp.deinit(); | |
| 1929 | var pp = Preprocessor.init(&comp); | |
| 1930 | defer pp.deinit(); | |
| 1931 | ||
| 1932 | try Test.testDestringify(&pp, "hello\tworld\n", "hello\tworld\n"); | |
| 1933 | try Test.testDestringify(&pp, | |
| 1934 | \\ \"FOO BAR BAZ\" | |
| 1935 | , | |
| 1936 | \\ "FOO BAR BAZ" | |
| 1937 | ); | |
| 1938 | try Test.testDestringify(&pp, | |
| 1939 | \\ \\t\\n | |
| 1940 | \\ | |
| 1941 | , | |
| 1942 | \\ \t\n | |
| 1943 | \\ | |
| 1944 | ); | |
| 1945 | } |
src/aro/Source.zig created+131| ... | ... | @@ -0,0 +1,131 @@ |
| 1 | const std = @import("std"); | |
| 2 | const Source = @This(); | |
| 3 | ||
| 4 | pub const Id = enum(u32) { | |
| 5 | unused = 0, | |
| 6 | generated = 1, | |
| 7 | _, | |
| 8 | }; | |
| 9 | ||
| 10 | pub const Location = struct { | |
| 11 | id: Id = .unused, | |
| 12 | byte_offset: u32 = 0, | |
| 13 | line: u32 = 0, | |
| 14 | ||
| 15 | pub fn eql(a: Location, b: Location) bool { | |
| 16 | return a.id == b.id and a.byte_offset == b.byte_offset and a.line == b.line; | |
| 17 | } | |
| 18 | }; | |
| 19 | ||
| 20 | path: []const u8, | |
| 21 | buf: []const u8, | |
| 22 | id: Id, | |
| 23 | invalid_utf8_loc: ?Location = null, | |
| 24 | /// each entry represents a byte position within `buf` where a backslash+newline was deleted | |
| 25 | /// from the original raw buffer. The same position can appear multiple times if multiple | |
| 26 | /// consecutive splices happened. Guaranteed to be non-decreasing | |
| 27 | splice_locs: []const u32, | |
| 28 | ||
| 29 | /// Todo: binary search instead of scanning entire `splice_locs`. | |
| 30 | pub fn numSplicesBefore(source: Source, byte_offset: u32) u32 { | |
| 31 | for (source.splice_locs) |splice_offset, i| { | |
| 32 | if (splice_offset > byte_offset) return @intCast(u32, i); | |
| 33 | } | |
| 34 | return @intCast(u32, source.splice_locs.len); | |
| 35 | } | |
| 36 | ||
| 37 | /// Returns the actual line number (before newline splicing) of a Location | |
| 38 | /// This corresponds to what the user would actually see in their text editor | |
| 39 | pub fn physicalLine(source: Source, loc: Location) u32 { | |
| 40 | return loc.line + source.numSplicesBefore(loc.byte_offset); | |
| 41 | } | |
| 42 | ||
| 43 | const LineCol = struct { line: []const u8, line_no: u32, col: u32, width: u32, end_with_splice: bool }; | |
| 44 | ||
| 45 | pub fn lineCol(source: Source, loc: Location) LineCol { | |
| 46 | var start: usize = 0; | |
| 47 | // find the start of the line which is either a newline or a splice | |
| 48 | if (std.mem.lastIndexOfScalar(u8, source.buf[0..loc.byte_offset], '\n')) |some| start = some + 1; | |
| 49 | const splice_index = for (source.splice_locs) |splice_offset, i| { | |
| 50 | if (splice_offset > start) { | |
| 51 | if (splice_offset < loc.byte_offset) { | |
| 52 | start = splice_offset; | |
| 53 | break @intCast(u32, i) + 1; | |
| 54 | } | |
| 55 | break @intCast(u32, i); | |
| 56 | } | |
| 57 | } else @intCast(u32, source.splice_locs.len); | |
| 58 | var i: usize = start; | |
| 59 | var col: u32 = 1; | |
| 60 | var width: u32 = 0; | |
| 61 | ||
| 62 | while (i < loc.byte_offset) : (col += 1) { // TODO this is still incorrect, but better | |
| 63 | const len = std.unicode.utf8ByteSequenceLength(source.buf[i]) catch unreachable; | |
| 64 | const cp = std.unicode.utf8Decode(source.buf[i..][0..len]) catch unreachable; | |
| 65 | width += codepointWidth(cp); | |
| 66 | i += len; | |
| 67 | } | |
| 68 | ||
| 69 | // find the end of the line which is either a newline, EOF or a splice | |
| 70 | var nl = source.buf.len; | |
| 71 | var end_with_splice = false; | |
| 72 | if (std.mem.indexOfScalar(u8, source.buf[start..], '\n')) |some| nl = some + start; | |
| 73 | if (source.splice_locs.len > splice_index and nl > source.splice_locs[splice_index] and source.splice_locs[splice_index] > start) { | |
| 74 | end_with_splice = true; | |
| 75 | nl = source.splice_locs[splice_index]; | |
| 76 | } | |
| 77 | return .{ | |
| 78 | .line = source.buf[start..nl], | |
| 79 | .line_no = loc.line + splice_index, | |
| 80 | .col = col, | |
| 81 | .width = width, | |
| 82 | .end_with_splice = end_with_splice, | |
| 83 | }; | |
| 84 | } | |
| 85 | ||
| 86 | fn codepointWidth(cp: u32) u32 { | |
| 87 | return switch (cp) { | |
| 88 | 0x1100...0x115F, | |
| 89 | 0x2329, | |
| 90 | 0x232A, | |
| 91 | 0x2E80...0x303F, | |
| 92 | 0x3040...0x3247, | |
| 93 | 0x3250...0x4DBF, | |
| 94 | 0x4E00...0xA4C6, | |
| 95 | 0xA960...0xA97C, | |
| 96 | 0xAC00...0xD7A3, | |
| 97 | 0xF900...0xFAFF, | |
| 98 | 0xFE10...0xFE19, | |
| 99 | 0xFE30...0xFE6B, | |
| 100 | 0xFF01...0xFF60, | |
| 101 | 0xFFE0...0xFFE6, | |
| 102 | 0x1B000...0x1B001, | |
| 103 | 0x1F200...0x1F251, | |
| 104 | 0x20000...0x3FFFD, | |
| 105 | 0x1F300...0x1F5FF, | |
| 106 | 0x1F900...0x1F9FF, | |
| 107 | => 2, | |
| 108 | else => 1, | |
| 109 | }; | |
| 110 | } | |
| 111 | ||
| 112 | /// Returns the first offset, if any, in buf where an invalid utf8 sequence | |
| 113 | /// is found. Code adapted from std.unicode.utf8ValidateSlice | |
| 114 | fn offsetOfInvalidUtf8(buf: []const u8) ?u32 { | |
| 115 | std.debug.assert(buf.len <= std.math.maxInt(u32)); | |
| 116 | var i: u32 = 0; | |
| 117 | while (i < buf.len) { | |
| 118 | if (std.unicode.utf8ByteSequenceLength(buf[i])) |cp_len| { | |
| 119 | if (i + cp_len > buf.len) return i; | |
| 120 | if (std.meta.isError(std.unicode.utf8Decode(buf[i .. i + cp_len]))) return i; | |
| 121 | i += cp_len; | |
| 122 | } else |_| return i; | |
| 123 | } | |
| 124 | return null; | |
| 125 | } | |
| 126 | ||
| 127 | pub fn checkUtf8(source: *Source) void { | |
| 128 | if (offsetOfInvalidUtf8(source.buf)) |offset| { | |
| 129 | source.invalid_utf8_loc = Location{ .id = source.id, .byte_offset = offset }; | |
| 130 | } | |
| 131 | } |
src/aro/Tokenizer.zig created+1995| ... | ... | @@ -0,0 +1,1995 @@ |
| 1 | const std = @import("std"); | |
| 2 | const assert = std.debug.assert; | |
| 3 | const Compilation = @import("Compilation.zig"); | |
| 4 | const Source = @import("Source.zig"); | |
| 5 | const LangOpts = @import("LangOpts.zig"); | |
| 6 | const CharInfo = @import("CharInfo.zig"); | |
| 7 | ||
| 8 | const Tokenizer = @This(); | |
| 9 | ||
| 10 | pub const Token = struct { | |
| 11 | id: Id, | |
| 12 | source: Source.Id, | |
| 13 | start: u32 = 0, | |
| 14 | end: u32 = 0, | |
| 15 | line: u32 = 0, | |
| 16 | ||
| 17 | pub const Id = enum(u8) { | |
| 18 | invalid, | |
| 19 | nl, | |
| 20 | whitespace, | |
| 21 | eof, | |
| 22 | /// identifier containing solely basic character set characters | |
| 23 | identifier, | |
| 24 | /// identifier with at least one extended character | |
| 25 | extended_identifier, | |
| 26 | ||
| 27 | // string literals with prefixes | |
| 28 | string_literal, | |
| 29 | string_literal_utf_16, | |
| 30 | string_literal_utf_8, | |
| 31 | string_literal_utf_32, | |
| 32 | string_literal_wide, | |
| 33 | ||
| 34 | // <foobar> only generated by preprocessor | |
| 35 | macro_string, | |
| 36 | ||
| 37 | // char literals with prefixes | |
| 38 | char_literal, | |
| 39 | char_literal_utf_16, | |
| 40 | char_literal_utf_32, | |
| 41 | char_literal_wide, | |
| 42 | ||
| 43 | // float literals with suffixes | |
| 44 | float_literal, | |
| 45 | float_literal_f, | |
| 46 | float_literal_l, | |
| 47 | ||
| 48 | // imaginary literals | |
| 49 | imaginary_literal, | |
| 50 | imaginary_literal_f, | |
| 51 | imaginary_literal_l, | |
| 52 | ||
| 53 | // integer literals with suffixes | |
| 54 | integer_literal, | |
| 55 | integer_literal_u, | |
| 56 | integer_literal_l, | |
| 57 | integer_literal_lu, | |
| 58 | integer_literal_ll, | |
| 59 | integer_literal_llu, | |
| 60 | ||
| 61 | /// Integer literal tokens generated by preprocessor. | |
| 62 | one, | |
| 63 | zero, | |
| 64 | ||
| 65 | bang, | |
| 66 | bang_equal, | |
| 67 | pipe, | |
| 68 | pipe_pipe, | |
| 69 | pipe_equal, | |
| 70 | equal, | |
| 71 | equal_equal, | |
| 72 | l_paren, | |
| 73 | r_paren, | |
| 74 | l_brace, | |
| 75 | r_brace, | |
| 76 | l_bracket, | |
| 77 | r_bracket, | |
| 78 | period, | |
| 79 | ellipsis, | |
| 80 | caret, | |
| 81 | caret_equal, | |
| 82 | plus, | |
| 83 | plus_plus, | |
| 84 | plus_equal, | |
| 85 | minus, | |
| 86 | minus_minus, | |
| 87 | minus_equal, | |
| 88 | asterisk, | |
| 89 | asterisk_equal, | |
| 90 | percent, | |
| 91 | percent_equal, | |
| 92 | arrow, | |
| 93 | colon, | |
| 94 | colon_colon, | |
| 95 | semicolon, | |
| 96 | slash, | |
| 97 | slash_equal, | |
| 98 | comma, | |
| 99 | ampersand, | |
| 100 | ampersand_ampersand, | |
| 101 | ampersand_equal, | |
| 102 | question_mark, | |
| 103 | angle_bracket_left, | |
| 104 | angle_bracket_left_equal, | |
| 105 | angle_bracket_angle_bracket_left, | |
| 106 | angle_bracket_angle_bracket_left_equal, | |
| 107 | angle_bracket_right, | |
| 108 | angle_bracket_right_equal, | |
| 109 | angle_bracket_angle_bracket_right, | |
| 110 | angle_bracket_angle_bracket_right_equal, | |
| 111 | tilde, | |
| 112 | hash, | |
| 113 | hash_hash, | |
| 114 | ||
| 115 | /// Special token to speed up preprocessing, `loc.end` will be an index to the param list. | |
| 116 | macro_param, | |
| 117 | /// Special token to signal that the argument must be replaced without expansion (e.g. in concatenation) | |
| 118 | macro_param_no_expand, | |
| 119 | /// Special token to speed up preprocessing, `loc.end` will be an index to the param list. | |
| 120 | stringify_param, | |
| 121 | /// Same as stringify_param, but for var args | |
| 122 | stringify_va_args, | |
| 123 | /// Special macro whitespace, always equal to a single space | |
| 124 | macro_ws, | |
| 125 | /// Special token for implementing __has_attribute | |
| 126 | macro_param_has_attribute, | |
| 127 | /// Special token for implementing __has_warning | |
| 128 | macro_param_has_warning, | |
| 129 | /// Special token for implementing __has_feature | |
| 130 | macro_param_has_feature, | |
| 131 | /// Special token for implementing __has_extension | |
| 132 | macro_param_has_extension, | |
| 133 | /// Special token for implementing __has_builtin | |
| 134 | macro_param_has_builtin, | |
| 135 | /// Special token for implementing __is_identifier | |
| 136 | macro_param_is_identifier, | |
| 137 | /// Special token for implementing __FILE__ | |
| 138 | macro_file, | |
| 139 | /// Special token for implementing __LINE__ | |
| 140 | macro_line, | |
| 141 | /// Special token for implementing __COUNTER__ | |
| 142 | macro_counter, | |
| 143 | /// Special token for implementing _Pragma | |
| 144 | macro_param_pragma_operator, | |
| 145 | ||
| 146 | /// Special identifier for implementing __func__ | |
| 147 | macro_func, | |
| 148 | /// Special identifier for implementing __FUNCTION__ | |
| 149 | macro_function, | |
| 150 | /// Special identifier for implementing __PRETTY_FUNCTION__ | |
| 151 | macro_pretty_func, | |
| 152 | ||
| 153 | keyword_auto, | |
| 154 | keyword_break, | |
| 155 | keyword_case, | |
| 156 | keyword_char, | |
| 157 | keyword_const, | |
| 158 | keyword_continue, | |
| 159 | keyword_default, | |
| 160 | keyword_do, | |
| 161 | keyword_double, | |
| 162 | keyword_else, | |
| 163 | keyword_enum, | |
| 164 | keyword_extern, | |
| 165 | keyword_float, | |
| 166 | keyword_for, | |
| 167 | keyword_goto, | |
| 168 | keyword_if, | |
| 169 | keyword_int, | |
| 170 | keyword_long, | |
| 171 | keyword_register, | |
| 172 | keyword_return, | |
| 173 | keyword_short, | |
| 174 | keyword_signed, | |
| 175 | keyword_sizeof, | |
| 176 | keyword_static, | |
| 177 | keyword_struct, | |
| 178 | keyword_switch, | |
| 179 | keyword_typedef, | |
| 180 | keyword_typeof1, | |
| 181 | keyword_typeof2, | |
| 182 | keyword_union, | |
| 183 | keyword_unsigned, | |
| 184 | keyword_void, | |
| 185 | keyword_volatile, | |
| 186 | keyword_while, | |
| 187 | ||
| 188 | // ISO C99 | |
| 189 | keyword_bool, | |
| 190 | keyword_complex, | |
| 191 | keyword_imaginary, | |
| 192 | keyword_inline, | |
| 193 | keyword_restrict, | |
| 194 | ||
| 195 | // ISO C11 | |
| 196 | keyword_alignas, | |
| 197 | keyword_alignof, | |
| 198 | keyword_atomic, | |
| 199 | keyword_generic, | |
| 200 | keyword_noreturn, | |
| 201 | keyword_static_assert, | |
| 202 | keyword_thread_local, | |
| 203 | ||
| 204 | // Preprocessor directives | |
| 205 | keyword_include, | |
| 206 | keyword_define, | |
| 207 | keyword_defined, | |
| 208 | keyword_undef, | |
| 209 | keyword_ifdef, | |
| 210 | keyword_ifndef, | |
| 211 | keyword_elif, | |
| 212 | keyword_endif, | |
| 213 | keyword_error, | |
| 214 | keyword_warning, | |
| 215 | keyword_pragma, | |
| 216 | keyword_line, | |
| 217 | keyword_va_args, | |
| 218 | ||
| 219 | // gcc keywords | |
| 220 | keyword_const1, | |
| 221 | keyword_const2, | |
| 222 | keyword_inline1, | |
| 223 | keyword_inline2, | |
| 224 | keyword_volatile1, | |
| 225 | keyword_volatile2, | |
| 226 | keyword_restrict1, | |
| 227 | keyword_restrict2, | |
| 228 | keyword_alignof1, | |
| 229 | keyword_alignof2, | |
| 230 | keyword_typeof, | |
| 231 | keyword_attribute1, | |
| 232 | keyword_attribute2, | |
| 233 | keyword_extension, | |
| 234 | keyword_asm, | |
| 235 | keyword_asm1, | |
| 236 | keyword_asm2, | |
| 237 | ||
| 238 | // ms keywords | |
| 239 | keyword_declspec, | |
| 240 | ||
| 241 | // builtins that require special parsing | |
| 242 | builtin_choose_expr, | |
| 243 | builtin_va_arg, | |
| 244 | ||
| 245 | /// Return true if token is identifier or keyword. | |
| 246 | pub fn isMacroIdentifier(id: Id) bool { | |
| 247 | switch (id) { | |
| 248 | .keyword_include, | |
| 249 | .keyword_define, | |
| 250 | .keyword_defined, | |
| 251 | .keyword_undef, | |
| 252 | .keyword_ifdef, | |
| 253 | .keyword_ifndef, | |
| 254 | .keyword_elif, | |
| 255 | .keyword_endif, | |
| 256 | .keyword_error, | |
| 257 | .keyword_warning, | |
| 258 | .keyword_pragma, | |
| 259 | .keyword_line, | |
| 260 | .keyword_va_args, | |
| 261 | .macro_func, | |
| 262 | .macro_function, | |
| 263 | .macro_pretty_func, | |
| 264 | .keyword_auto, | |
| 265 | .keyword_break, | |
| 266 | .keyword_case, | |
| 267 | .keyword_char, | |
| 268 | .keyword_const, | |
| 269 | .keyword_continue, | |
| 270 | .keyword_default, | |
| 271 | .keyword_do, | |
| 272 | .keyword_double, | |
| 273 | .keyword_else, | |
| 274 | .keyword_enum, | |
| 275 | .keyword_extern, | |
| 276 | .keyword_float, | |
| 277 | .keyword_for, | |
| 278 | .keyword_goto, | |
| 279 | .keyword_if, | |
| 280 | .keyword_int, | |
| 281 | .keyword_long, | |
| 282 | .keyword_register, | |
| 283 | .keyword_return, | |
| 284 | .keyword_short, | |
| 285 | .keyword_signed, | |
| 286 | .keyword_sizeof, | |
| 287 | .keyword_static, | |
| 288 | .keyword_struct, | |
| 289 | .keyword_switch, | |
| 290 | .keyword_typedef, | |
| 291 | .keyword_union, | |
| 292 | .keyword_unsigned, | |
| 293 | .keyword_void, | |
| 294 | .keyword_volatile, | |
| 295 | .keyword_while, | |
| 296 | .keyword_bool, | |
| 297 | .keyword_complex, | |
| 298 | .keyword_imaginary, | |
| 299 | .keyword_inline, | |
| 300 | .keyword_restrict, | |
| 301 | .keyword_alignas, | |
| 302 | .keyword_alignof, | |
| 303 | .keyword_atomic, | |
| 304 | .keyword_generic, | |
| 305 | .keyword_noreturn, | |
| 306 | .keyword_static_assert, | |
| 307 | .keyword_thread_local, | |
| 308 | .identifier, | |
| 309 | .extended_identifier, | |
| 310 | .keyword_typeof, | |
| 311 | .keyword_typeof1, | |
| 312 | .keyword_typeof2, | |
| 313 | .keyword_const1, | |
| 314 | .keyword_const2, | |
| 315 | .keyword_inline1, | |
| 316 | .keyword_inline2, | |
| 317 | .keyword_volatile1, | |
| 318 | .keyword_volatile2, | |
| 319 | .keyword_restrict1, | |
| 320 | .keyword_restrict2, | |
| 321 | .keyword_alignof1, | |
| 322 | .keyword_alignof2, | |
| 323 | .builtin_choose_expr, | |
| 324 | .builtin_va_arg, | |
| 325 | .keyword_attribute1, | |
| 326 | .keyword_attribute2, | |
| 327 | .keyword_extension, | |
| 328 | .keyword_asm, | |
| 329 | .keyword_asm1, | |
| 330 | .keyword_asm2, | |
| 331 | .keyword_declspec, | |
| 332 | => return true, | |
| 333 | else => return false, | |
| 334 | } | |
| 335 | } | |
| 336 | ||
| 337 | /// Turn macro keywords into identifiers. | |
| 338 | pub fn simplifyMacroKeyword(id: *Id) void { | |
| 339 | switch (id.*) { | |
| 340 | .keyword_include, | |
| 341 | .keyword_define, | |
| 342 | .keyword_defined, | |
| 343 | .keyword_undef, | |
| 344 | .keyword_ifdef, | |
| 345 | .keyword_ifndef, | |
| 346 | .keyword_elif, | |
| 347 | .keyword_endif, | |
| 348 | .keyword_error, | |
| 349 | .keyword_warning, | |
| 350 | .keyword_pragma, | |
| 351 | .keyword_line, | |
| 352 | .keyword_va_args, | |
| 353 | => id.* = .identifier, | |
| 354 | else => {}, | |
| 355 | } | |
| 356 | } | |
| 357 | ||
| 358 | pub fn lexeme(id: Id) ?[]const u8 { | |
| 359 | return switch (id) { | |
| 360 | .invalid, | |
| 361 | .identifier, | |
| 362 | .extended_identifier, | |
| 363 | .string_literal, | |
| 364 | .string_literal_utf_16, | |
| 365 | .string_literal_utf_8, | |
| 366 | .string_literal_utf_32, | |
| 367 | .string_literal_wide, | |
| 368 | .char_literal, | |
| 369 | .char_literal_utf_16, | |
| 370 | .char_literal_utf_32, | |
| 371 | .char_literal_wide, | |
| 372 | .float_literal, | |
| 373 | .float_literal_f, | |
| 374 | .float_literal_l, | |
| 375 | .imaginary_literal, | |
| 376 | .imaginary_literal_f, | |
| 377 | .imaginary_literal_l, | |
| 378 | .integer_literal, | |
| 379 | .integer_literal_u, | |
| 380 | .integer_literal_l, | |
| 381 | .integer_literal_lu, | |
| 382 | .integer_literal_ll, | |
| 383 | .integer_literal_llu, | |
| 384 | .macro_string, | |
| 385 | .whitespace, | |
| 386 | => null, | |
| 387 | ||
| 388 | .zero => "0", | |
| 389 | .one => "1", | |
| 390 | ||
| 391 | .nl, | |
| 392 | .eof, | |
| 393 | .macro_param, | |
| 394 | .macro_param_no_expand, | |
| 395 | .stringify_param, | |
| 396 | .stringify_va_args, | |
| 397 | .macro_param_has_attribute, | |
| 398 | .macro_param_has_warning, | |
| 399 | .macro_param_has_feature, | |
| 400 | .macro_param_has_extension, | |
| 401 | .macro_param_has_builtin, | |
| 402 | .macro_param_is_identifier, | |
| 403 | .macro_file, | |
| 404 | .macro_line, | |
| 405 | .macro_counter, | |
| 406 | .macro_param_pragma_operator, | |
| 407 | => "", | |
| 408 | .macro_ws => " ", | |
| 409 | ||
| 410 | .macro_func => "__func__", | |
| 411 | .macro_function => "__FUNCTION__", | |
| 412 | .macro_pretty_func => "__PRETTY_FUNCTION__", | |
| 413 | ||
| 414 | .bang => "!", | |
| 415 | .bang_equal => "!=", | |
| 416 | .pipe => "|", | |
| 417 | .pipe_pipe => "||", | |
| 418 | .pipe_equal => "|=", | |
| 419 | .equal => "=", | |
| 420 | .equal_equal => "==", | |
| 421 | .l_paren => "(", | |
| 422 | .r_paren => ")", | |
| 423 | .l_brace => "{", | |
| 424 | .r_brace => "}", | |
| 425 | .l_bracket => "[", | |
| 426 | .r_bracket => "]", | |
| 427 | .period => ".", | |
| 428 | .ellipsis => "...", | |
| 429 | .caret => "^", | |
| 430 | .caret_equal => "^=", | |
| 431 | .plus => "+", | |
| 432 | .plus_plus => "++", | |
| 433 | .plus_equal => "+=", | |
| 434 | .minus => "-", | |
| 435 | .minus_minus => "--", | |
| 436 | .minus_equal => "-=", | |
| 437 | .asterisk => "*", | |
| 438 | .asterisk_equal => "*=", | |
| 439 | .percent => "%", | |
| 440 | .percent_equal => "%=", | |
| 441 | .arrow => "->", | |
| 442 | .colon => ":", | |
| 443 | .colon_colon => "::", | |
| 444 | .semicolon => ";", | |
| 445 | .slash => "/", | |
| 446 | .slash_equal => "/=", | |
| 447 | .comma => ",", | |
| 448 | .ampersand => "&", | |
| 449 | .ampersand_ampersand => "&&", | |
| 450 | .ampersand_equal => "&=", | |
| 451 | .question_mark => "?", | |
| 452 | .angle_bracket_left => "<", | |
| 453 | .angle_bracket_left_equal => "<=", | |
| 454 | .angle_bracket_angle_bracket_left => "<<", | |
| 455 | .angle_bracket_angle_bracket_left_equal => "<<=", | |
| 456 | .angle_bracket_right => ">", | |
| 457 | .angle_bracket_right_equal => ">=", | |
| 458 | .angle_bracket_angle_bracket_right => ">>", | |
| 459 | .angle_bracket_angle_bracket_right_equal => ">>=", | |
| 460 | .tilde => "~", | |
| 461 | .hash => "#", | |
| 462 | .hash_hash => "##", | |
| 463 | ||
| 464 | .keyword_auto => "auto", | |
| 465 | .keyword_break => "break", | |
| 466 | .keyword_case => "case", | |
| 467 | .keyword_char => "char", | |
| 468 | .keyword_const => "const", | |
| 469 | .keyword_continue => "continue", | |
| 470 | .keyword_default => "default", | |
| 471 | .keyword_do => "do", | |
| 472 | .keyword_double => "double", | |
| 473 | .keyword_else => "else", | |
| 474 | .keyword_enum => "enum", | |
| 475 | .keyword_extern => "extern", | |
| 476 | .keyword_float => "float", | |
| 477 | .keyword_for => "for", | |
| 478 | .keyword_goto => "goto", | |
| 479 | .keyword_if => "if", | |
| 480 | .keyword_int => "int", | |
| 481 | .keyword_long => "long", | |
| 482 | .keyword_register => "register", | |
| 483 | .keyword_return => "return", | |
| 484 | .keyword_short => "short", | |
| 485 | .keyword_signed => "signed", | |
| 486 | .keyword_sizeof => "sizeof", | |
| 487 | .keyword_static => "static", | |
| 488 | .keyword_struct => "struct", | |
| 489 | .keyword_switch => "switch", | |
| 490 | .keyword_typedef => "typedef", | |
| 491 | .keyword_typeof => "typeof", | |
| 492 | .keyword_union => "union", | |
| 493 | .keyword_unsigned => "unsigned", | |
| 494 | .keyword_void => "void", | |
| 495 | .keyword_volatile => "volatile", | |
| 496 | .keyword_while => "while", | |
| 497 | .keyword_bool => "_Bool", | |
| 498 | .keyword_complex => "_Complex", | |
| 499 | .keyword_imaginary => "_Imaginary", | |
| 500 | .keyword_inline => "inline", | |
| 501 | .keyword_restrict => "restrict", | |
| 502 | .keyword_alignas => "_Alignas", | |
| 503 | .keyword_alignof => "_Alignof", | |
| 504 | .keyword_atomic => "_Atomic", | |
| 505 | .keyword_generic => "_Generic", | |
| 506 | .keyword_noreturn => "_Noreturn", | |
| 507 | .keyword_static_assert => "_Static_assert", | |
| 508 | .keyword_thread_local => "_Thread_local", | |
| 509 | .keyword_include => "include", | |
| 510 | .keyword_define => "define", | |
| 511 | .keyword_defined => "defined", | |
| 512 | .keyword_undef => "undef", | |
| 513 | .keyword_ifdef => "ifdef", | |
| 514 | .keyword_ifndef => "ifndef", | |
| 515 | .keyword_elif => "elif", | |
| 516 | .keyword_endif => "endif", | |
| 517 | .keyword_error => "error", | |
| 518 | .keyword_warning => "warning", | |
| 519 | .keyword_pragma => "pragma", | |
| 520 | .keyword_line => "line", | |
| 521 | .keyword_va_args => "__VA_ARGS__", | |
| 522 | .keyword_const1 => "__const", | |
| 523 | .keyword_const2 => "__const__", | |
| 524 | .keyword_inline1 => "__inline", | |
| 525 | .keyword_inline2 => "__inline__", | |
| 526 | .keyword_volatile1 => "__volatile", | |
| 527 | .keyword_volatile2 => "__volatile__", | |
| 528 | .keyword_restrict1 => "__restrict", | |
| 529 | .keyword_restrict2 => "__restrict__", | |
| 530 | .keyword_alignof1 => "__alignof", | |
| 531 | .keyword_alignof2 => "__alignof__", | |
| 532 | .keyword_typeof1 => "__typeof", | |
| 533 | .keyword_typeof2 => "__typeof__", | |
| 534 | .builtin_choose_expr => "__builtin_choose_expr", | |
| 535 | .builtin_va_arg => "__builtin_va_arg", | |
| 536 | .keyword_attribute1 => "__attribute", | |
| 537 | .keyword_attribute2 => "__attribute__", | |
| 538 | .keyword_extension => "__extension__", | |
| 539 | .keyword_asm => "asm", | |
| 540 | .keyword_asm1 => "__asm", | |
| 541 | .keyword_asm2 => "__asm__", | |
| 542 | .keyword_declspec => "__declspec", | |
| 543 | }; | |
| 544 | } | |
| 545 | ||
| 546 | pub fn symbol(id: Id) []const u8 { | |
| 547 | return switch (id) { | |
| 548 | .macro_string, .invalid => unreachable, | |
| 549 | .identifier, | |
| 550 | .extended_identifier, | |
| 551 | .macro_func, | |
| 552 | .macro_function, | |
| 553 | .macro_pretty_func, | |
| 554 | .builtin_choose_expr, | |
| 555 | .builtin_va_arg, | |
| 556 | => "an identifier", | |
| 557 | .string_literal, | |
| 558 | .string_literal_utf_16, | |
| 559 | .string_literal_utf_8, | |
| 560 | .string_literal_utf_32, | |
| 561 | .string_literal_wide, | |
| 562 | => "a string literal", | |
| 563 | .char_literal, | |
| 564 | .char_literal_utf_16, | |
| 565 | .char_literal_utf_32, | |
| 566 | .char_literal_wide, | |
| 567 | => "a character literal", | |
| 568 | .float_literal, | |
| 569 | .float_literal_f, | |
| 570 | .float_literal_l, | |
| 571 | => "a float literal", | |
| 572 | .imaginary_literal, | |
| 573 | .imaginary_literal_f, | |
| 574 | .imaginary_literal_l, | |
| 575 | => "an imaginary literal", | |
| 576 | .integer_literal, | |
| 577 | .integer_literal_u, | |
| 578 | .integer_literal_l, | |
| 579 | .integer_literal_lu, | |
| 580 | .integer_literal_ll, | |
| 581 | .integer_literal_llu, | |
| 582 | => "an integer literal", | |
| 583 | else => id.lexeme().?, | |
| 584 | }; | |
| 585 | } | |
| 586 | ||
| 587 | /// tokens that can start an expression parsed by Preprocessor.expr | |
| 588 | /// Note that eof, r_paren, and string literals cannot actually start a | |
| 589 | /// preprocessor expression, but we include them here so that a nicer | |
| 590 | /// error message can be generated by the parser. | |
| 591 | pub fn validPreprocessorExprStart(id: Id) bool { | |
| 592 | return switch (id) { | |
| 593 | .eof, | |
| 594 | .r_paren, | |
| 595 | .string_literal, | |
| 596 | .string_literal_utf_16, | |
| 597 | .string_literal_utf_8, | |
| 598 | .string_literal_utf_32, | |
| 599 | .string_literal_wide, | |
| 600 | ||
| 601 | .integer_literal, | |
| 602 | .integer_literal_u, | |
| 603 | .integer_literal_l, | |
| 604 | .integer_literal_lu, | |
| 605 | .integer_literal_ll, | |
| 606 | .integer_literal_llu, | |
| 607 | .float_literal, | |
| 608 | .float_literal_f, | |
| 609 | .float_literal_l, | |
| 610 | .imaginary_literal, | |
| 611 | .imaginary_literal_f, | |
| 612 | .imaginary_literal_l, | |
| 613 | .char_literal, | |
| 614 | .char_literal_utf_16, | |
| 615 | .char_literal_utf_32, | |
| 616 | .char_literal_wide, | |
| 617 | .l_paren, | |
| 618 | .plus, | |
| 619 | .minus, | |
| 620 | .tilde, | |
| 621 | .bang, | |
| 622 | .identifier, | |
| 623 | .extended_identifier, | |
| 624 | .one, | |
| 625 | .zero, | |
| 626 | => true, | |
| 627 | else => false, | |
| 628 | }; | |
| 629 | } | |
| 630 | }; | |
| 631 | ||
| 632 | /// double underscore and underscore + capital letter identifiers | |
| 633 | /// belong to the implementation namespace, so we always convert them | |
| 634 | /// to keywords. | |
| 635 | pub fn getTokenId(comp: *const Compilation, str: []const u8) Token.Id { | |
| 636 | const kw = all_kws.get(str) orelse return .identifier; | |
| 637 | const standard = comp.langopts.standard; | |
| 638 | return switch (kw) { | |
| 639 | .keyword_inline => if (standard.isGNU() or standard.atLeast(.c99)) kw else .identifier, | |
| 640 | .keyword_restrict => if (standard.atLeast(.c99)) kw else .identifier, | |
| 641 | .keyword_typeof => if (standard.isGNU()) kw else .identifier, | |
| 642 | .keyword_asm => if (standard.isGNU()) kw else .identifier, | |
| 643 | else => kw, | |
| 644 | }; | |
| 645 | } | |
| 646 | ||
| 647 | /// Check if codepoint may appear in specified context | |
| 648 | /// does not check basic character set chars because the tokenizer handles them separately to keep the common | |
| 649 | /// case on the fast path | |
| 650 | pub fn mayAppearInIdent(comp: *const Compilation, codepoint: u21, where: enum { start, inside }) bool { | |
| 651 | if (codepoint == '$') return comp.langopts.dollars_in_identifiers; | |
| 652 | if (codepoint <= 0x7F) return false; | |
| 653 | return switch (where) { | |
| 654 | .start => if (comp.langopts.standard.atLeast(.c11)) | |
| 655 | CharInfo.isC11IdChar(codepoint) and !CharInfo.isC11DisallowedInitialIdChar(codepoint) | |
| 656 | else | |
| 657 | CharInfo.isC99IdChar(codepoint) and !CharInfo.isC99DisallowedInitialIDChar(codepoint), | |
| 658 | .inside => if (comp.langopts.standard.atLeast(.c11)) | |
| 659 | CharInfo.isC11IdChar(codepoint) | |
| 660 | else | |
| 661 | CharInfo.isC99IdChar(codepoint), | |
| 662 | }; | |
| 663 | } | |
| 664 | ||
| 665 | const all_kws = std.ComptimeStringMap(Id, .{ | |
| 666 | .{ "auto", .keyword_auto }, | |
| 667 | .{ "break", .keyword_break }, | |
| 668 | .{ "case", .keyword_case }, | |
| 669 | .{ "char", .keyword_char }, | |
| 670 | .{ "const", .keyword_const }, | |
| 671 | .{ "continue", .keyword_continue }, | |
| 672 | .{ "default", .keyword_default }, | |
| 673 | .{ "do", .keyword_do }, | |
| 674 | .{ "double", .keyword_double }, | |
| 675 | .{ "else", .keyword_else }, | |
| 676 | .{ "enum", .keyword_enum }, | |
| 677 | .{ "extern", .keyword_extern }, | |
| 678 | .{ "float", .keyword_float }, | |
| 679 | .{ "for", .keyword_for }, | |
| 680 | .{ "goto", .keyword_goto }, | |
| 681 | .{ "if", .keyword_if }, | |
| 682 | .{ "int", .keyword_int }, | |
| 683 | .{ "long", .keyword_long }, | |
| 684 | .{ "register", .keyword_register }, | |
| 685 | .{ "return", .keyword_return }, | |
| 686 | .{ "short", .keyword_short }, | |
| 687 | .{ "signed", .keyword_signed }, | |
| 688 | .{ "sizeof", .keyword_sizeof }, | |
| 689 | .{ "static", .keyword_static }, | |
| 690 | .{ "struct", .keyword_struct }, | |
| 691 | .{ "switch", .keyword_switch }, | |
| 692 | .{ "typedef", .keyword_typedef }, | |
| 693 | .{ "union", .keyword_union }, | |
| 694 | .{ "unsigned", .keyword_unsigned }, | |
| 695 | .{ "void", .keyword_void }, | |
| 696 | .{ "volatile", .keyword_volatile }, | |
| 697 | .{ "while", .keyword_while }, | |
| 698 | .{ "__typeof__", .keyword_typeof2 }, | |
| 699 | .{ "__typeof", .keyword_typeof1 }, | |
| 700 | ||
| 701 | // ISO C99 | |
| 702 | .{ "_Bool", .keyword_bool }, | |
| 703 | .{ "_Complex", .keyword_complex }, | |
| 704 | .{ "_Imaginary", .keyword_imaginary }, | |
| 705 | .{ "inline", .keyword_inline }, | |
| 706 | .{ "restrict", .keyword_restrict }, | |
| 707 | ||
| 708 | // ISO C11 | |
| 709 | .{ "_Alignas", .keyword_alignas }, | |
| 710 | .{ "_Alignof", .keyword_alignof }, | |
| 711 | .{ "_Atomic", .keyword_atomic }, | |
| 712 | .{ "_Generic", .keyword_generic }, | |
| 713 | .{ "_Noreturn", .keyword_noreturn }, | |
| 714 | .{ "_Static_assert", .keyword_static_assert }, | |
| 715 | .{ "_Thread_local", .keyword_thread_local }, | |
| 716 | ||
| 717 | // Preprocessor directives | |
| 718 | .{ "include", .keyword_include }, | |
| 719 | .{ "define", .keyword_define }, | |
| 720 | .{ "defined", .keyword_defined }, | |
| 721 | .{ "undef", .keyword_undef }, | |
| 722 | .{ "ifdef", .keyword_ifdef }, | |
| 723 | .{ "ifndef", .keyword_ifndef }, | |
| 724 | .{ "elif", .keyword_elif }, | |
| 725 | .{ "endif", .keyword_endif }, | |
| 726 | .{ "error", .keyword_error }, | |
| 727 | .{ "warning", .keyword_warning }, | |
| 728 | .{ "pragma", .keyword_pragma }, | |
| 729 | .{ "line", .keyword_line }, | |
| 730 | .{ "__VA_ARGS__", .keyword_va_args }, | |
| 731 | .{ "__func__", .macro_func }, | |
| 732 | .{ "__FUNCTION__", .macro_function }, | |
| 733 | .{ "__PRETTY_FUNCTION__", .macro_pretty_func }, | |
| 734 | ||
| 735 | // gcc keywords | |
| 736 | .{ "__const", .keyword_const1 }, | |
| 737 | .{ "__const__", .keyword_const2 }, | |
| 738 | .{ "__inline", .keyword_inline1 }, | |
| 739 | .{ "__inline__", .keyword_inline2 }, | |
| 740 | .{ "__volatile", .keyword_volatile1 }, | |
| 741 | .{ "__volatile__", .keyword_volatile2 }, | |
| 742 | .{ "__restrict", .keyword_restrict1 }, | |
| 743 | .{ "__restrict__", .keyword_restrict2 }, | |
| 744 | .{ "__alignof", .keyword_alignof1 }, | |
| 745 | .{ "__alignof__", .keyword_alignof2 }, | |
| 746 | .{ "typeof", .keyword_typeof }, | |
| 747 | .{ "__attribute", .keyword_attribute1 }, | |
| 748 | .{ "__attribute__", .keyword_attribute2 }, | |
| 749 | .{ "__extension__", .keyword_extension }, | |
| 750 | .{ "asm", .keyword_asm }, | |
| 751 | .{ "__asm", .keyword_asm1 }, | |
| 752 | .{ "__asm__", .keyword_asm2 }, | |
| 753 | ||
| 754 | // ms keywords | |
| 755 | .{ "__declspec", .keyword_declspec }, | |
| 756 | ||
| 757 | // builtins that require special parsing | |
| 758 | .{ "__builtin_choose_expr", .builtin_choose_expr }, | |
| 759 | .{ "__builtin_va_arg", .builtin_va_arg }, | |
| 760 | }); | |
| 761 | }; | |
| 762 | ||
| 763 | buf: []const u8, | |
| 764 | index: u32 = 0, | |
| 765 | source: Source.Id, | |
| 766 | comp: *const Compilation, | |
| 767 | line: u32 = 1, | |
| 768 | ||
| 769 | pub fn next(self: *Tokenizer) Token { | |
| 770 | var state: enum { | |
| 771 | start, | |
| 772 | whitespace, | |
| 773 | u, | |
| 774 | u8, | |
| 775 | U, | |
| 776 | L, | |
| 777 | string_literal, | |
| 778 | char_literal_start, | |
| 779 | char_literal, | |
| 780 | escape_sequence, | |
| 781 | octal_escape, | |
| 782 | hex_escape, | |
| 783 | unicode_escape, | |
| 784 | identifier, | |
| 785 | extended_identifier, | |
| 786 | equal, | |
| 787 | bang, | |
| 788 | pipe, | |
| 789 | colon, | |
| 790 | percent, | |
| 791 | asterisk, | |
| 792 | plus, | |
| 793 | angle_bracket_left, | |
| 794 | angle_bracket_angle_bracket_left, | |
| 795 | angle_bracket_right, | |
| 796 | angle_bracket_angle_bracket_right, | |
| 797 | caret, | |
| 798 | period, | |
| 799 | period2, | |
| 800 | minus, | |
| 801 | slash, | |
| 802 | ampersand, | |
| 803 | hash, | |
| 804 | line_comment, | |
| 805 | multi_line_comment, | |
| 806 | multi_line_comment_asterisk, | |
| 807 | multi_line_comment_done, | |
| 808 | zero, | |
| 809 | integer_literal_oct, | |
| 810 | integer_literal_binary, | |
| 811 | integer_literal_binary_first, | |
| 812 | integer_literal_hex, | |
| 813 | integer_literal_hex_first, | |
| 814 | integer_literal, | |
| 815 | integer_suffix, | |
| 816 | integer_suffix_u, | |
| 817 | integer_suffix_l, | |
| 818 | integer_suffix_ll, | |
| 819 | integer_suffix_ul, | |
| 820 | float_fraction, | |
| 821 | float_fraction_hex, | |
| 822 | float_exponent, | |
| 823 | float_exponent_digits, | |
| 824 | float_suffix, | |
| 825 | float_suffix_f, | |
| 826 | float_suffix_i, | |
| 827 | float_suffix_l, | |
| 828 | } = .start; | |
| 829 | ||
| 830 | var start = self.index; | |
| 831 | var id: Token.Id = .eof; | |
| 832 | ||
| 833 | var return_state = state; | |
| 834 | var counter: u32 = 0; | |
| 835 | var codepoint_len: u3 = undefined; | |
| 836 | while (self.index < self.buf.len) : (self.index += codepoint_len) { | |
| 837 | codepoint_len = std.unicode.utf8ByteSequenceLength(self.buf[self.index]) catch unreachable; | |
| 838 | const c = std.unicode.utf8Decode(self.buf[self.index .. self.index + codepoint_len]) catch unreachable; | |
| 839 | switch (state) { | |
| 840 | .start => switch (c) { | |
| 841 | '\n' => { | |
| 842 | id = .nl; | |
| 843 | self.index += 1; | |
| 844 | self.line += 1; | |
| 845 | break; | |
| 846 | }, | |
| 847 | '"' => { | |
| 848 | id = .string_literal; | |
| 849 | state = .string_literal; | |
| 850 | }, | |
| 851 | '\'' => { | |
| 852 | id = .char_literal; | |
| 853 | state = .char_literal_start; | |
| 854 | }, | |
| 855 | 'u' => state = .u, | |
| 856 | 'U' => state = .U, | |
| 857 | 'L' => state = .L, | |
| 858 | 'a'...'t', 'v'...'z', 'A'...'K', 'M'...'T', 'V'...'Z', '_' => state = .identifier, | |
| 859 | '=' => state = .equal, | |
| 860 | '!' => state = .bang, | |
| 861 | '|' => state = .pipe, | |
| 862 | '(' => { | |
| 863 | id = .l_paren; | |
| 864 | self.index += 1; | |
| 865 | break; | |
| 866 | }, | |
| 867 | ')' => { | |
| 868 | id = .r_paren; | |
| 869 | self.index += 1; | |
| 870 | break; | |
| 871 | }, | |
| 872 | '[' => { | |
| 873 | id = .l_bracket; | |
| 874 | self.index += 1; | |
| 875 | break; | |
| 876 | }, | |
| 877 | ']' => { | |
| 878 | id = .r_bracket; | |
| 879 | self.index += 1; | |
| 880 | break; | |
| 881 | }, | |
| 882 | ';' => { | |
| 883 | id = .semicolon; | |
| 884 | self.index += 1; | |
| 885 | break; | |
| 886 | }, | |
| 887 | ',' => { | |
| 888 | id = .comma; | |
| 889 | self.index += 1; | |
| 890 | break; | |
| 891 | }, | |
| 892 | '?' => { | |
| 893 | id = .question_mark; | |
| 894 | self.index += 1; | |
| 895 | break; | |
| 896 | }, | |
| 897 | ':' => if (self.comp.langopts.standard.atLeast(.c2x)) { | |
| 898 | state = .colon; | |
| 899 | } else { | |
| 900 | id = .colon; | |
| 901 | self.index += 1; | |
| 902 | break; | |
| 903 | }, | |
| 904 | '%' => state = .percent, | |
| 905 | '*' => state = .asterisk, | |
| 906 | '+' => state = .plus, | |
| 907 | '<' => state = .angle_bracket_left, | |
| 908 | '>' => state = .angle_bracket_right, | |
| 909 | '^' => state = .caret, | |
| 910 | '{' => { | |
| 911 | id = .l_brace; | |
| 912 | self.index += 1; | |
| 913 | break; | |
| 914 | }, | |
| 915 | '}' => { | |
| 916 | id = .r_brace; | |
| 917 | self.index += 1; | |
| 918 | break; | |
| 919 | }, | |
| 920 | '~' => { | |
| 921 | id = .tilde; | |
| 922 | self.index += 1; | |
| 923 | break; | |
| 924 | }, | |
| 925 | '.' => state = .period, | |
| 926 | '-' => state = .minus, | |
| 927 | '/' => state = .slash, | |
| 928 | '&' => state = .ampersand, | |
| 929 | '#' => state = .hash, | |
| 930 | '0' => state = .zero, | |
| 931 | '1'...'9' => state = .integer_literal, | |
| 932 | '\t', '\x0B', '\x0C', ' ' => state = .whitespace, | |
| 933 | else => if (Token.mayAppearInIdent(self.comp, c, .start)) { | |
| 934 | state = .extended_identifier; | |
| 935 | } else { | |
| 936 | id = .invalid; | |
| 937 | self.index += codepoint_len; | |
| 938 | break; | |
| 939 | }, | |
| 940 | }, | |
| 941 | .whitespace => switch (c) { | |
| 942 | '\t', '\x0B', '\x0C', ' ' => {}, | |
| 943 | else => { | |
| 944 | id = .whitespace; | |
| 945 | break; | |
| 946 | }, | |
| 947 | }, | |
| 948 | .u => switch (c) { | |
| 949 | '8' => { | |
| 950 | state = .u8; | |
| 951 | }, | |
| 952 | '\'' => { | |
| 953 | id = .char_literal_utf_16; | |
| 954 | state = .char_literal_start; | |
| 955 | }, | |
| 956 | '\"' => { | |
| 957 | id = .string_literal_utf_16; | |
| 958 | state = .string_literal; | |
| 959 | }, | |
| 960 | else => { | |
| 961 | codepoint_len = 0; | |
| 962 | state = .identifier; | |
| 963 | }, | |
| 964 | }, | |
| 965 | .u8 => switch (c) { | |
| 966 | '\"' => { | |
| 967 | id = .string_literal_utf_8; | |
| 968 | state = .string_literal; | |
| 969 | }, | |
| 970 | else => { | |
| 971 | codepoint_len = 0; | |
| 972 | state = .identifier; | |
| 973 | }, | |
| 974 | }, | |
| 975 | .U => switch (c) { | |
| 976 | '\'' => { | |
| 977 | id = .char_literal_utf_32; | |
| 978 | state = .char_literal_start; | |
| 979 | }, | |
| 980 | '\"' => { | |
| 981 | id = .string_literal_utf_32; | |
| 982 | state = .string_literal; | |
| 983 | }, | |
| 984 | else => { | |
| 985 | codepoint_len = 0; | |
| 986 | state = .identifier; | |
| 987 | }, | |
| 988 | }, | |
| 989 | .L => switch (c) { | |
| 990 | '\'' => { | |
| 991 | id = .char_literal_wide; | |
| 992 | state = .char_literal_start; | |
| 993 | }, | |
| 994 | '\"' => { | |
| 995 | id = .string_literal_wide; | |
| 996 | state = .string_literal; | |
| 997 | }, | |
| 998 | else => { | |
| 999 | codepoint_len = 0; | |
| 1000 | state = .identifier; | |
| 1001 | }, | |
| 1002 | }, | |
| 1003 | .string_literal => switch (c) { | |
| 1004 | '\\' => { | |
| 1005 | return_state = .string_literal; | |
| 1006 | state = .escape_sequence; | |
| 1007 | }, | |
| 1008 | '"' => { | |
| 1009 | self.index += 1; | |
| 1010 | break; | |
| 1011 | }, | |
| 1012 | '\n' => { | |
| 1013 | id = .invalid; | |
| 1014 | break; | |
| 1015 | }, | |
| 1016 | '\r' => unreachable, | |
| 1017 | else => {}, | |
| 1018 | }, | |
| 1019 | .char_literal_start => switch (c) { | |
| 1020 | '\\' => { | |
| 1021 | return_state = .char_literal; | |
| 1022 | state = .escape_sequence; | |
| 1023 | }, | |
| 1024 | ||
| 1025 | '\'', '\n' => { | |
| 1026 | id = .invalid; | |
| 1027 | break; | |
| 1028 | }, | |
| 1029 | else => { | |
| 1030 | state = .char_literal; | |
| 1031 | }, | |
| 1032 | }, | |
| 1033 | .char_literal => switch (c) { | |
| 1034 | '\\' => { | |
| 1035 | return_state = .char_literal; | |
| 1036 | state = .escape_sequence; | |
| 1037 | }, | |
| 1038 | '\'' => { | |
| 1039 | self.index += 1; | |
| 1040 | break; | |
| 1041 | }, | |
| 1042 | '\n' => { | |
| 1043 | id = .invalid; | |
| 1044 | break; | |
| 1045 | }, | |
| 1046 | else => {}, | |
| 1047 | }, | |
| 1048 | .escape_sequence => switch (c) { | |
| 1049 | '\'', '"', '?', '\\', 'a', 'b', 'e', 'f', 'n', 'r', 't', 'v' => { | |
| 1050 | state = return_state; | |
| 1051 | }, | |
| 1052 | '\n' => { | |
| 1053 | state = return_state; | |
| 1054 | self.line += 1; | |
| 1055 | }, | |
| 1056 | '0'...'7' => { | |
| 1057 | counter = 1; | |
| 1058 | state = .octal_escape; | |
| 1059 | }, | |
| 1060 | 'x' => state = .hex_escape, | |
| 1061 | 'u' => { | |
| 1062 | counter = 4; | |
| 1063 | state = .unicode_escape; | |
| 1064 | }, | |
| 1065 | 'U' => { | |
| 1066 | counter = 8; | |
| 1067 | state = .unicode_escape; | |
| 1068 | }, | |
| 1069 | else => { | |
| 1070 | id = .invalid; | |
| 1071 | break; | |
| 1072 | }, | |
| 1073 | }, | |
| 1074 | .octal_escape => switch (c) { | |
| 1075 | '0'...'7' => { | |
| 1076 | counter += 1; | |
| 1077 | if (counter == 3) state = return_state; | |
| 1078 | }, | |
| 1079 | else => { | |
| 1080 | codepoint_len = 0; | |
| 1081 | state = return_state; | |
| 1082 | }, | |
| 1083 | }, | |
| 1084 | .hex_escape => switch (c) { | |
| 1085 | '0'...'9', 'a'...'f', 'A'...'F' => {}, | |
| 1086 | else => { | |
| 1087 | codepoint_len = 0; | |
| 1088 | state = return_state; | |
| 1089 | }, | |
| 1090 | }, | |
| 1091 | .unicode_escape => switch (c) { | |
| 1092 | '0'...'9', 'a'...'f', 'A'...'F' => { | |
| 1093 | counter -= 1; | |
| 1094 | if (counter == 0) state = return_state; | |
| 1095 | }, | |
| 1096 | else => { | |
| 1097 | id = .invalid; | |
| 1098 | break; | |
| 1099 | }, | |
| 1100 | }, | |
| 1101 | .identifier, .extended_identifier => switch (c) { | |
| 1102 | 'a'...'z', 'A'...'Z', '_', '0'...'9' => {}, | |
| 1103 | else => { | |
| 1104 | if (!Token.mayAppearInIdent(self.comp, c, .inside)) { | |
| 1105 | id = if (state == .identifier) Token.getTokenId(self.comp, self.buf[start..self.index]) else .extended_identifier; | |
| 1106 | break; | |
| 1107 | } | |
| 1108 | state = .extended_identifier; | |
| 1109 | }, | |
| 1110 | }, | |
| 1111 | .equal => switch (c) { | |
| 1112 | '=' => { | |
| 1113 | id = .equal_equal; | |
| 1114 | self.index += 1; | |
| 1115 | break; | |
| 1116 | }, | |
| 1117 | else => { | |
| 1118 | id = .equal; | |
| 1119 | break; | |
| 1120 | }, | |
| 1121 | }, | |
| 1122 | .bang => switch (c) { | |
| 1123 | '=' => { | |
| 1124 | id = .bang_equal; | |
| 1125 | self.index += 1; | |
| 1126 | break; | |
| 1127 | }, | |
| 1128 | else => { | |
| 1129 | id = .bang; | |
| 1130 | break; | |
| 1131 | }, | |
| 1132 | }, | |
| 1133 | .pipe => switch (c) { | |
| 1134 | '=' => { | |
| 1135 | id = .pipe_equal; | |
| 1136 | self.index += 1; | |
| 1137 | break; | |
| 1138 | }, | |
| 1139 | '|' => { | |
| 1140 | id = .pipe_pipe; | |
| 1141 | self.index += 1; | |
| 1142 | break; | |
| 1143 | }, | |
| 1144 | else => { | |
| 1145 | id = .pipe; | |
| 1146 | break; | |
| 1147 | }, | |
| 1148 | }, | |
| 1149 | .colon => switch (c) { | |
| 1150 | ':' => { | |
| 1151 | id = .colon_colon; | |
| 1152 | self.index += 1; | |
| 1153 | break; | |
| 1154 | }, | |
| 1155 | else => { | |
| 1156 | id = .colon; | |
| 1157 | break; | |
| 1158 | }, | |
| 1159 | }, | |
| 1160 | .percent => switch (c) { | |
| 1161 | '=' => { | |
| 1162 | id = .percent_equal; | |
| 1163 | self.index += 1; | |
| 1164 | break; | |
| 1165 | }, | |
| 1166 | else => { | |
| 1167 | id = .percent; | |
| 1168 | break; | |
| 1169 | }, | |
| 1170 | }, | |
| 1171 | .asterisk => switch (c) { | |
| 1172 | '=' => { | |
| 1173 | id = .asterisk_equal; | |
| 1174 | self.index += 1; | |
| 1175 | break; | |
| 1176 | }, | |
| 1177 | else => { | |
| 1178 | id = .asterisk; | |
| 1179 | break; | |
| 1180 | }, | |
| 1181 | }, | |
| 1182 | .plus => switch (c) { | |
| 1183 | '=' => { | |
| 1184 | id = .plus_equal; | |
| 1185 | self.index += 1; | |
| 1186 | break; | |
| 1187 | }, | |
| 1188 | '+' => { | |
| 1189 | id = .plus_plus; | |
| 1190 | self.index += 1; | |
| 1191 | break; | |
| 1192 | }, | |
| 1193 | else => { | |
| 1194 | id = .plus; | |
| 1195 | break; | |
| 1196 | }, | |
| 1197 | }, | |
| 1198 | .angle_bracket_left => switch (c) { | |
| 1199 | '<' => state = .angle_bracket_angle_bracket_left, | |
| 1200 | '=' => { | |
| 1201 | id = .angle_bracket_left_equal; | |
| 1202 | self.index += 1; | |
| 1203 | break; | |
| 1204 | }, | |
| 1205 | else => { | |
| 1206 | id = .angle_bracket_left; | |
| 1207 | break; | |
| 1208 | }, | |
| 1209 | }, | |
| 1210 | .angle_bracket_angle_bracket_left => switch (c) { | |
| 1211 | '=' => { | |
| 1212 | id = .angle_bracket_angle_bracket_left_equal; | |
| 1213 | self.index += 1; | |
| 1214 | break; | |
| 1215 | }, | |
| 1216 | else => { | |
| 1217 | id = .angle_bracket_angle_bracket_left; | |
| 1218 | break; | |
| 1219 | }, | |
| 1220 | }, | |
| 1221 | .angle_bracket_right => switch (c) { | |
| 1222 | '>' => state = .angle_bracket_angle_bracket_right, | |
| 1223 | '=' => { | |
| 1224 | id = .angle_bracket_right_equal; | |
| 1225 | self.index += 1; | |
| 1226 | break; | |
| 1227 | }, | |
| 1228 | else => { | |
| 1229 | id = .angle_bracket_right; | |
| 1230 | break; | |
| 1231 | }, | |
| 1232 | }, | |
| 1233 | .angle_bracket_angle_bracket_right => switch (c) { | |
| 1234 | '=' => { | |
| 1235 | id = .angle_bracket_angle_bracket_right_equal; | |
| 1236 | self.index += 1; | |
| 1237 | break; | |
| 1238 | }, | |
| 1239 | else => { | |
| 1240 | id = .angle_bracket_angle_bracket_right; | |
| 1241 | break; | |
| 1242 | }, | |
| 1243 | }, | |
| 1244 | .caret => switch (c) { | |
| 1245 | '=' => { | |
| 1246 | id = .caret_equal; | |
| 1247 | self.index += 1; | |
| 1248 | break; | |
| 1249 | }, | |
| 1250 | else => { | |
| 1251 | id = .caret; | |
| 1252 | break; | |
| 1253 | }, | |
| 1254 | }, | |
| 1255 | .period => switch (c) { | |
| 1256 | '.' => state = .period2, | |
| 1257 | '0'...'9' => state = .float_fraction, | |
| 1258 | else => { | |
| 1259 | id = .period; | |
| 1260 | break; | |
| 1261 | }, | |
| 1262 | }, | |
| 1263 | .period2 => switch (c) { | |
| 1264 | '.' => { | |
| 1265 | id = .ellipsis; | |
| 1266 | self.index += 1; | |
| 1267 | break; | |
| 1268 | }, | |
| 1269 | else => { | |
| 1270 | id = .period; | |
| 1271 | self.index -= 1; | |
| 1272 | break; | |
| 1273 | }, | |
| 1274 | }, | |
| 1275 | .minus => switch (c) { | |
| 1276 | '>' => { | |
| 1277 | id = .arrow; | |
| 1278 | self.index += 1; | |
| 1279 | break; | |
| 1280 | }, | |
| 1281 | '=' => { | |
| 1282 | id = .minus_equal; | |
| 1283 | self.index += 1; | |
| 1284 | break; | |
| 1285 | }, | |
| 1286 | '-' => { | |
| 1287 | id = .minus_minus; | |
| 1288 | self.index += 1; | |
| 1289 | break; | |
| 1290 | }, | |
| 1291 | else => { | |
| 1292 | id = .minus; | |
| 1293 | break; | |
| 1294 | }, | |
| 1295 | }, | |
| 1296 | .ampersand => switch (c) { | |
| 1297 | '&' => { | |
| 1298 | id = .ampersand_ampersand; | |
| 1299 | self.index += 1; | |
| 1300 | break; | |
| 1301 | }, | |
| 1302 | '=' => { | |
| 1303 | id = .ampersand_equal; | |
| 1304 | self.index += 1; | |
| 1305 | break; | |
| 1306 | }, | |
| 1307 | else => { | |
| 1308 | id = .ampersand; | |
| 1309 | break; | |
| 1310 | }, | |
| 1311 | }, | |
| 1312 | .hash => switch (c) { | |
| 1313 | '#' => { | |
| 1314 | id = .hash_hash; | |
| 1315 | self.index += 1; | |
| 1316 | break; | |
| 1317 | }, | |
| 1318 | else => { | |
| 1319 | id = .hash; | |
| 1320 | break; | |
| 1321 | }, | |
| 1322 | }, | |
| 1323 | .slash => switch (c) { | |
| 1324 | '/' => state = .line_comment, | |
| 1325 | '*' => state = .multi_line_comment, | |
| 1326 | '=' => { | |
| 1327 | id = .slash_equal; | |
| 1328 | self.index += 1; | |
| 1329 | break; | |
| 1330 | }, | |
| 1331 | else => { | |
| 1332 | id = .slash; | |
| 1333 | break; | |
| 1334 | }, | |
| 1335 | }, | |
| 1336 | .line_comment => switch (c) { | |
| 1337 | '\n' => { | |
| 1338 | self.index -= 1; | |
| 1339 | state = .start; | |
| 1340 | }, | |
| 1341 | else => {}, | |
| 1342 | }, | |
| 1343 | .multi_line_comment => switch (c) { | |
| 1344 | '*' => state = .multi_line_comment_asterisk, | |
| 1345 | '\n' => self.line += 1, | |
| 1346 | else => {}, | |
| 1347 | }, | |
| 1348 | .multi_line_comment_asterisk => switch (c) { | |
| 1349 | '/' => state = .multi_line_comment_done, | |
| 1350 | '\n' => { | |
| 1351 | self.line += 1; | |
| 1352 | state = .multi_line_comment; | |
| 1353 | }, | |
| 1354 | '*' => {}, | |
| 1355 | else => state = .multi_line_comment, | |
| 1356 | }, | |
| 1357 | .multi_line_comment_done => switch (c) { | |
| 1358 | '\n' => { | |
| 1359 | start = self.index; | |
| 1360 | id = .nl; | |
| 1361 | self.index += 1; | |
| 1362 | self.line += 1; | |
| 1363 | break; | |
| 1364 | }, | |
| 1365 | '\r' => unreachable, | |
| 1366 | '\t', '\x0B', '\x0C', ' ' => { | |
| 1367 | start = self.index; | |
| 1368 | state = .whitespace; | |
| 1369 | }, | |
| 1370 | else => { | |
| 1371 | id = .whitespace; | |
| 1372 | break; | |
| 1373 | }, | |
| 1374 | }, | |
| 1375 | .zero => switch (c) { | |
| 1376 | '0'...'9' => state = .integer_literal_oct, | |
| 1377 | 'b', 'B' => state = .integer_literal_binary_first, | |
| 1378 | 'x', 'X' => state = .integer_literal_hex_first, | |
| 1379 | '.' => state = .float_fraction, | |
| 1380 | else => { | |
| 1381 | if (c <= 0x7F) { | |
| 1382 | state = .integer_suffix; | |
| 1383 | self.index -= 1; | |
| 1384 | } else { | |
| 1385 | id = .integer_literal; | |
| 1386 | break; | |
| 1387 | } | |
| 1388 | }, | |
| 1389 | }, | |
| 1390 | .integer_literal_oct => switch (c) { | |
| 1391 | '0'...'7' => {}, | |
| 1392 | else => if (c <= 0x7F) { | |
| 1393 | state = .integer_suffix; | |
| 1394 | self.index -= 1; | |
| 1395 | } else { | |
| 1396 | id = .integer_literal; | |
| 1397 | break; | |
| 1398 | }, | |
| 1399 | }, | |
| 1400 | .integer_literal_binary_first => switch (c) { | |
| 1401 | '0', '1' => state = .integer_literal_binary, | |
| 1402 | else => { | |
| 1403 | id = .invalid; | |
| 1404 | break; | |
| 1405 | }, | |
| 1406 | }, | |
| 1407 | .integer_literal_binary => switch (c) { | |
| 1408 | '0', '1' => {}, | |
| 1409 | else => if (c <= 0x7F) { | |
| 1410 | state = .integer_suffix; | |
| 1411 | self.index -= 1; | |
| 1412 | } else { | |
| 1413 | id = .integer_literal; | |
| 1414 | break; | |
| 1415 | }, | |
| 1416 | }, | |
| 1417 | .integer_literal_hex_first => switch (c) { | |
| 1418 | '0'...'9', 'a'...'f', 'A'...'F' => state = .integer_literal_hex, | |
| 1419 | '.' => state = .float_fraction_hex, | |
| 1420 | 'p', 'P' => state = .float_exponent, | |
| 1421 | else => { | |
| 1422 | id = .invalid; | |
| 1423 | break; | |
| 1424 | }, | |
| 1425 | }, | |
| 1426 | .integer_literal_hex => switch (c) { | |
| 1427 | '0'...'9', 'a'...'f', 'A'...'F' => {}, | |
| 1428 | '.' => state = .float_fraction_hex, | |
| 1429 | 'p', 'P' => state = .float_exponent, | |
| 1430 | else => if (c <= 0x7F) { | |
| 1431 | state = .integer_suffix; | |
| 1432 | self.index -= 1; | |
| 1433 | } else { | |
| 1434 | id = .integer_literal; | |
| 1435 | break; | |
| 1436 | }, | |
| 1437 | }, | |
| 1438 | .integer_literal => switch (c) { | |
| 1439 | '0'...'9' => {}, | |
| 1440 | '.' => state = .float_fraction, | |
| 1441 | 'e', 'E' => state = .float_exponent, | |
| 1442 | else => if (c <= 0x7F) { | |
| 1443 | state = .integer_suffix; | |
| 1444 | self.index -= 1; | |
| 1445 | } else { | |
| 1446 | id = .integer_literal; | |
| 1447 | break; | |
| 1448 | }, | |
| 1449 | }, | |
| 1450 | .integer_suffix => switch (c) { | |
| 1451 | 'u', 'U' => state = .integer_suffix_u, | |
| 1452 | 'l', 'L' => state = .integer_suffix_l, | |
| 1453 | else => { | |
| 1454 | id = .integer_literal; | |
| 1455 | break; | |
| 1456 | }, | |
| 1457 | }, | |
| 1458 | .integer_suffix_u => switch (c) { | |
| 1459 | 'l', 'L' => state = .integer_suffix_ul, | |
| 1460 | else => { | |
| 1461 | id = .integer_literal_u; | |
| 1462 | break; | |
| 1463 | }, | |
| 1464 | }, | |
| 1465 | .integer_suffix_l => switch (c) { | |
| 1466 | 'l', 'L' => state = .integer_suffix_ll, | |
| 1467 | 'u', 'U' => { | |
| 1468 | id = .integer_literal_lu; | |
| 1469 | self.index += 1; | |
| 1470 | break; | |
| 1471 | }, | |
| 1472 | else => { | |
| 1473 | id = .integer_literal_l; | |
| 1474 | break; | |
| 1475 | }, | |
| 1476 | }, | |
| 1477 | .integer_suffix_ll => switch (c) { | |
| 1478 | 'u', 'U' => { | |
| 1479 | id = .integer_literal_llu; | |
| 1480 | self.index += 1; | |
| 1481 | break; | |
| 1482 | }, | |
| 1483 | else => { | |
| 1484 | id = .integer_literal_ll; | |
| 1485 | break; | |
| 1486 | }, | |
| 1487 | }, | |
| 1488 | .integer_suffix_ul => switch (c) { | |
| 1489 | 'l', 'L' => { | |
| 1490 | id = .integer_literal_llu; | |
| 1491 | self.index += 1; | |
| 1492 | break; | |
| 1493 | }, | |
| 1494 | else => { | |
| 1495 | id = .integer_literal_lu; | |
| 1496 | break; | |
| 1497 | }, | |
| 1498 | }, | |
| 1499 | .float_fraction => switch (c) { | |
| 1500 | '0'...'9' => {}, | |
| 1501 | 'e', 'E' => state = .float_exponent, | |
| 1502 | else => if (c <= 0x7F) { | |
| 1503 | self.index -= 1; | |
| 1504 | state = .float_suffix; | |
| 1505 | } else { | |
| 1506 | id = .float_literal; | |
| 1507 | break; | |
| 1508 | }, | |
| 1509 | }, | |
| 1510 | .float_fraction_hex => switch (c) { | |
| 1511 | '0'...'9', 'a'...'f', 'A'...'F' => {}, | |
| 1512 | 'p', 'P' => state = .float_exponent, | |
| 1513 | else => { | |
| 1514 | id = .invalid; | |
| 1515 | break; | |
| 1516 | }, | |
| 1517 | }, | |
| 1518 | .float_exponent => switch (c) { | |
| 1519 | '+', '-' => state = .float_exponent_digits, | |
| 1520 | else => { | |
| 1521 | codepoint_len = 0; | |
| 1522 | state = .float_exponent_digits; | |
| 1523 | }, | |
| 1524 | }, | |
| 1525 | .float_exponent_digits => switch (c) { | |
| 1526 | '0'...'9' => counter += 1, | |
| 1527 | else => { | |
| 1528 | if (counter == 0) { | |
| 1529 | id = .invalid; | |
| 1530 | break; | |
| 1531 | } | |
| 1532 | codepoint_len = 0; | |
| 1533 | state = .float_suffix; | |
| 1534 | }, | |
| 1535 | }, | |
| 1536 | .float_suffix => switch (c) { | |
| 1537 | 'f', 'F' => state = .float_suffix_f, | |
| 1538 | 'i', 'I' => state = .float_suffix_i, | |
| 1539 | 'l', 'L' => state = .float_suffix_l, | |
| 1540 | else => { | |
| 1541 | id = .float_literal; | |
| 1542 | break; | |
| 1543 | }, | |
| 1544 | }, | |
| 1545 | .float_suffix_f => switch (c) { | |
| 1546 | 'i', 'I' => { | |
| 1547 | id = .imaginary_literal_f; | |
| 1548 | self.index += 1; | |
| 1549 | break; | |
| 1550 | }, | |
| 1551 | else => { | |
| 1552 | id = .float_literal_f; | |
| 1553 | break; | |
| 1554 | }, | |
| 1555 | }, | |
| 1556 | .float_suffix_i => switch (c) { | |
| 1557 | 'f', 'F' => { | |
| 1558 | id = .imaginary_literal_f; | |
| 1559 | self.index += 1; | |
| 1560 | break; | |
| 1561 | }, | |
| 1562 | 'l', 'L' => { | |
| 1563 | id = .imaginary_literal_l; | |
| 1564 | self.index += 1; | |
| 1565 | break; | |
| 1566 | }, | |
| 1567 | else => { | |
| 1568 | id = .imaginary_literal; | |
| 1569 | break; | |
| 1570 | }, | |
| 1571 | }, | |
| 1572 | .float_suffix_l => switch (c) { | |
| 1573 | 'i', 'I' => { | |
| 1574 | id = .imaginary_literal_l; | |
| 1575 | self.index += 1; | |
| 1576 | break; | |
| 1577 | }, | |
| 1578 | else => { | |
| 1579 | id = .float_literal_l; | |
| 1580 | break; | |
| 1581 | }, | |
| 1582 | }, | |
| 1583 | } | |
| 1584 | } else if (self.index == self.buf.len) { | |
| 1585 | switch (state) { | |
| 1586 | .start, .line_comment => {}, | |
| 1587 | .u, .u8, .U, .L, .identifier => id = Token.getTokenId(self.comp, self.buf[start..self.index]), | |
| 1588 | .extended_identifier => id = .extended_identifier, | |
| 1589 | .period2, | |
| 1590 | .string_literal, | |
| 1591 | .char_literal_start, | |
| 1592 | .char_literal, | |
| 1593 | .escape_sequence, | |
| 1594 | .octal_escape, | |
| 1595 | .hex_escape, | |
| 1596 | .unicode_escape, | |
| 1597 | .multi_line_comment, | |
| 1598 | .multi_line_comment_asterisk, | |
| 1599 | .float_exponent, | |
| 1600 | .integer_literal_binary_first, | |
| 1601 | .integer_literal_hex_first, | |
| 1602 | => id = .invalid, | |
| 1603 | ||
| 1604 | .whitespace => id = .whitespace, | |
| 1605 | .multi_line_comment_done => id = .whitespace, | |
| 1606 | .float_exponent_digits => id = if (counter == 0) .invalid else .float_literal, | |
| 1607 | .float_fraction, | |
| 1608 | .float_fraction_hex, | |
| 1609 | => id = .float_literal, | |
| 1610 | .integer_literal_oct, | |
| 1611 | .integer_literal_binary, | |
| 1612 | .integer_literal_hex, | |
| 1613 | .integer_literal, | |
| 1614 | .integer_suffix, | |
| 1615 | .zero, | |
| 1616 | => id = .integer_literal, | |
| 1617 | .integer_suffix_u => id = .integer_literal_u, | |
| 1618 | .integer_suffix_l => id = .integer_literal_l, | |
| 1619 | .integer_suffix_ll => id = .integer_literal_ll, | |
| 1620 | .integer_suffix_ul => id = .integer_literal_lu, | |
| 1621 | ||
| 1622 | .float_suffix => id = .float_literal, | |
| 1623 | .float_suffix_f => id = .float_literal_f, | |
| 1624 | .float_suffix_i => id = .imaginary_literal, | |
| 1625 | .float_suffix_l => id = .float_literal_l, | |
| 1626 | .equal => id = .equal, | |
| 1627 | .bang => id = .bang, | |
| 1628 | .minus => id = .minus, | |
| 1629 | .slash => id = .slash, | |
| 1630 | .ampersand => id = .ampersand, | |
| 1631 | .hash => id = .hash, | |
| 1632 | .period => id = .period, | |
| 1633 | .pipe => id = .pipe, | |
| 1634 | .angle_bracket_angle_bracket_right => id = .angle_bracket_angle_bracket_right, | |
| 1635 | .angle_bracket_right => id = .angle_bracket_right, | |
| 1636 | .angle_bracket_angle_bracket_left => id = .angle_bracket_angle_bracket_left, | |
| 1637 | .angle_bracket_left => id = .angle_bracket_left, | |
| 1638 | .plus => id = .plus, | |
| 1639 | .colon => id = .colon, | |
| 1640 | .percent => id = .percent, | |
| 1641 | .caret => id = .caret, | |
| 1642 | .asterisk => id = .asterisk, | |
| 1643 | } | |
| 1644 | } | |
| 1645 | ||
| 1646 | return .{ | |
| 1647 | .id = id, | |
| 1648 | .start = start, | |
| 1649 | .end = self.index, | |
| 1650 | .line = self.line, | |
| 1651 | .source = self.source, | |
| 1652 | }; | |
| 1653 | } | |
| 1654 | ||
| 1655 | pub fn nextNoWS(self: *Tokenizer) Token { | |
| 1656 | var tok = self.next(); | |
| 1657 | while (tok.id == .whitespace) tok = self.next(); | |
| 1658 | return tok; | |
| 1659 | } | |
| 1660 | ||
| 1661 | test "operators" { | |
| 1662 | try expectTokens( | |
| 1663 | \\ ! != | || |= = == | |
| 1664 | \\ ( ) { } [ ] . .. ... | |
| 1665 | \\ ^ ^= + ++ += - -- -= | |
| 1666 | \\ * *= % %= -> : ; / /= | |
| 1667 | \\ , & && &= ? < <= << | |
| 1668 | \\ <<= > >= >> >>= ~ # ## | |
| 1669 | \\ | |
| 1670 | , &.{ | |
| 1671 | .bang, | |
| 1672 | .bang_equal, | |
| 1673 | .pipe, | |
| 1674 | .pipe_pipe, | |
| 1675 | .pipe_equal, | |
| 1676 | .equal, | |
| 1677 | .equal_equal, | |
| 1678 | .nl, | |
| 1679 | .l_paren, | |
| 1680 | .r_paren, | |
| 1681 | .l_brace, | |
| 1682 | .r_brace, | |
| 1683 | .l_bracket, | |
| 1684 | .r_bracket, | |
| 1685 | .period, | |
| 1686 | .period, | |
| 1687 | .period, | |
| 1688 | .ellipsis, | |
| 1689 | .nl, | |
| 1690 | .caret, | |
| 1691 | .caret_equal, | |
| 1692 | .plus, | |
| 1693 | .plus_plus, | |
| 1694 | .plus_equal, | |
| 1695 | .minus, | |
| 1696 | .minus_minus, | |
| 1697 | .minus_equal, | |
| 1698 | .nl, | |
| 1699 | .asterisk, | |
| 1700 | .asterisk_equal, | |
| 1701 | .percent, | |
| 1702 | .percent_equal, | |
| 1703 | .arrow, | |
| 1704 | .colon, | |
| 1705 | .semicolon, | |
| 1706 | .slash, | |
| 1707 | .slash_equal, | |
| 1708 | .nl, | |
| 1709 | .comma, | |
| 1710 | .ampersand, | |
| 1711 | .ampersand_ampersand, | |
| 1712 | .ampersand_equal, | |
| 1713 | .question_mark, | |
| 1714 | .angle_bracket_left, | |
| 1715 | .angle_bracket_left_equal, | |
| 1716 | .angle_bracket_angle_bracket_left, | |
| 1717 | .nl, | |
| 1718 | .angle_bracket_angle_bracket_left_equal, | |
| 1719 | .angle_bracket_right, | |
| 1720 | .angle_bracket_right_equal, | |
| 1721 | .angle_bracket_angle_bracket_right, | |
| 1722 | .angle_bracket_angle_bracket_right_equal, | |
| 1723 | .tilde, | |
| 1724 | .hash, | |
| 1725 | .hash_hash, | |
| 1726 | .nl, | |
| 1727 | }); | |
| 1728 | } | |
| 1729 | ||
| 1730 | test "keywords" { | |
| 1731 | try expectTokens( | |
| 1732 | \\auto break case char const continue default do | |
| 1733 | \\double else enum extern float for goto if int | |
| 1734 | \\long register return short signed sizeof static | |
| 1735 | \\struct switch typedef union unsigned void volatile | |
| 1736 | \\while _Bool _Complex _Imaginary inline restrict _Alignas | |
| 1737 | \\_Alignof _Atomic _Generic _Noreturn _Static_assert _Thread_local | |
| 1738 | \\__attribute __attribute__ __declspec | |
| 1739 | \\ | |
| 1740 | , &.{ | |
| 1741 | .keyword_auto, | |
| 1742 | .keyword_break, | |
| 1743 | .keyword_case, | |
| 1744 | .keyword_char, | |
| 1745 | .keyword_const, | |
| 1746 | .keyword_continue, | |
| 1747 | .keyword_default, | |
| 1748 | .keyword_do, | |
| 1749 | .nl, | |
| 1750 | .keyword_double, | |
| 1751 | .keyword_else, | |
| 1752 | .keyword_enum, | |
| 1753 | .keyword_extern, | |
| 1754 | .keyword_float, | |
| 1755 | .keyword_for, | |
| 1756 | .keyword_goto, | |
| 1757 | .keyword_if, | |
| 1758 | .keyword_int, | |
| 1759 | .nl, | |
| 1760 | .keyword_long, | |
| 1761 | .keyword_register, | |
| 1762 | .keyword_return, | |
| 1763 | .keyword_short, | |
| 1764 | .keyword_signed, | |
| 1765 | .keyword_sizeof, | |
| 1766 | .keyword_static, | |
| 1767 | .nl, | |
| 1768 | .keyword_struct, | |
| 1769 | .keyword_switch, | |
| 1770 | .keyword_typedef, | |
| 1771 | .keyword_union, | |
| 1772 | .keyword_unsigned, | |
| 1773 | .keyword_void, | |
| 1774 | .keyword_volatile, | |
| 1775 | .nl, | |
| 1776 | .keyword_while, | |
| 1777 | .keyword_bool, | |
| 1778 | .keyword_complex, | |
| 1779 | .keyword_imaginary, | |
| 1780 | .keyword_inline, | |
| 1781 | .keyword_restrict, | |
| 1782 | .keyword_alignas, | |
| 1783 | .nl, | |
| 1784 | .keyword_alignof, | |
| 1785 | .keyword_atomic, | |
| 1786 | .keyword_generic, | |
| 1787 | .keyword_noreturn, | |
| 1788 | .keyword_static_assert, | |
| 1789 | .keyword_thread_local, | |
| 1790 | .nl, | |
| 1791 | .keyword_attribute1, | |
| 1792 | .keyword_attribute2, | |
| 1793 | .keyword_declspec, | |
| 1794 | .nl, | |
| 1795 | }); | |
| 1796 | } | |
| 1797 | ||
| 1798 | test "preprocessor keywords" { | |
| 1799 | try expectTokens( | |
| 1800 | \\#include | |
| 1801 | \\#define | |
| 1802 | \\#ifdef | |
| 1803 | \\#ifndef | |
| 1804 | \\#error | |
| 1805 | \\#pragma | |
| 1806 | \\ | |
| 1807 | , &.{ | |
| 1808 | .hash, | |
| 1809 | .keyword_include, | |
| 1810 | .nl, | |
| 1811 | .hash, | |
| 1812 | .keyword_define, | |
| 1813 | .nl, | |
| 1814 | .hash, | |
| 1815 | .keyword_ifdef, | |
| 1816 | .nl, | |
| 1817 | .hash, | |
| 1818 | .keyword_ifndef, | |
| 1819 | .nl, | |
| 1820 | .hash, | |
| 1821 | .keyword_error, | |
| 1822 | .nl, | |
| 1823 | .hash, | |
| 1824 | .keyword_pragma, | |
| 1825 | .nl, | |
| 1826 | }); | |
| 1827 | } | |
| 1828 | ||
| 1829 | test "line continuation" { | |
| 1830 | try expectTokens( | |
| 1831 | \\#define foo \ | |
| 1832 | \\ bar | |
| 1833 | \\"foo\ | |
| 1834 | \\ bar" | |
| 1835 | \\#define "foo" | |
| 1836 | \\ "bar" | |
| 1837 | \\#define "foo" \ | |
| 1838 | \\ "bar" | |
| 1839 | , &.{ | |
| 1840 | .hash, | |
| 1841 | .keyword_define, | |
| 1842 | .identifier, | |
| 1843 | .identifier, | |
| 1844 | .nl, | |
| 1845 | .string_literal, | |
| 1846 | .nl, | |
| 1847 | .hash, | |
| 1848 | .keyword_define, | |
| 1849 | .string_literal, | |
| 1850 | .nl, | |
| 1851 | .string_literal, | |
| 1852 | .nl, | |
| 1853 | .hash, | |
| 1854 | .keyword_define, | |
| 1855 | .string_literal, | |
| 1856 | .string_literal, | |
| 1857 | }); | |
| 1858 | } | |
| 1859 | ||
| 1860 | test "string prefix" { | |
| 1861 | try expectTokens( | |
| 1862 | \\"foo" | |
| 1863 | \\u"foo" | |
| 1864 | \\u8"foo" | |
| 1865 | \\U"foo" | |
| 1866 | \\L"foo" | |
| 1867 | \\'foo' | |
| 1868 | \\u'foo' | |
| 1869 | \\U'foo' | |
| 1870 | \\L'foo' | |
| 1871 | \\ | |
| 1872 | , &.{ | |
| 1873 | .string_literal, | |
| 1874 | .nl, | |
| 1875 | .string_literal_utf_16, | |
| 1876 | .nl, | |
| 1877 | .string_literal_utf_8, | |
| 1878 | .nl, | |
| 1879 | .string_literal_utf_32, | |
| 1880 | .nl, | |
| 1881 | .string_literal_wide, | |
| 1882 | .nl, | |
| 1883 | .char_literal, | |
| 1884 | .nl, | |
| 1885 | .char_literal_utf_16, | |
| 1886 | .nl, | |
| 1887 | .char_literal_utf_32, | |
| 1888 | .nl, | |
| 1889 | .char_literal_wide, | |
| 1890 | .nl, | |
| 1891 | }); | |
| 1892 | } | |
| 1893 | ||
| 1894 | test "num suffixes" { | |
| 1895 | try expectTokens( | |
| 1896 | \\ 1.0f 1.0L 1.0 .0 1. 0x1p0f 0X1p0 | |
| 1897 | \\ 0l 0lu 0ll 0llu 0 | |
| 1898 | \\ 1u 1ul 1ull 1 | |
| 1899 | \\ 1.0i 1.0I | |
| 1900 | \\ 1.0if 1.0If 1.0fi 1.0fI | |
| 1901 | \\ 1.0il 1.0Il 1.0li 1.0lI | |
| 1902 | \\ | |
| 1903 | , &.{ | |
| 1904 | .float_literal_f, | |
| 1905 | .float_literal_l, | |
| 1906 | .float_literal, | |
| 1907 | .float_literal, | |
| 1908 | .float_literal, | |
| 1909 | .float_literal_f, | |
| 1910 | .float_literal, | |
| 1911 | .nl, | |
| 1912 | .integer_literal_l, | |
| 1913 | .integer_literal_lu, | |
| 1914 | .integer_literal_ll, | |
| 1915 | .integer_literal_llu, | |
| 1916 | .integer_literal, | |
| 1917 | .nl, | |
| 1918 | .integer_literal_u, | |
| 1919 | .integer_literal_lu, | |
| 1920 | .integer_literal_llu, | |
| 1921 | .integer_literal, | |
| 1922 | .nl, | |
| 1923 | .imaginary_literal, | |
| 1924 | .imaginary_literal, | |
| 1925 | .nl, | |
| 1926 | .imaginary_literal_f, | |
| 1927 | .imaginary_literal_f, | |
| 1928 | .imaginary_literal_f, | |
| 1929 | .imaginary_literal_f, | |
| 1930 | .nl, | |
| 1931 | .imaginary_literal_l, | |
| 1932 | .imaginary_literal_l, | |
| 1933 | .imaginary_literal_l, | |
| 1934 | .imaginary_literal_l, | |
| 1935 | .nl, | |
| 1936 | }); | |
| 1937 | } | |
| 1938 | ||
| 1939 | test "comments" { | |
| 1940 | try expectTokens( | |
| 1941 | \\//foo | |
| 1942 | \\#foo | |
| 1943 | , &.{ | |
| 1944 | .nl, | |
| 1945 | .hash, | |
| 1946 | .identifier, | |
| 1947 | }); | |
| 1948 | } | |
| 1949 | ||
| 1950 | test "extended identifiers" { | |
| 1951 | try expectTokens("𝓪𝓻𝓸𝓬𝓬", &.{.extended_identifier}); | |
| 1952 | try expectTokens("u𝓪𝓻𝓸𝓬𝓬", &.{.extended_identifier}); | |
| 1953 | try expectTokens("u8𝓪𝓻𝓸𝓬𝓬", &.{.extended_identifier}); | |
| 1954 | try expectTokens("U𝓪𝓻𝓸𝓬𝓬", &.{.extended_identifier}); | |
| 1955 | try expectTokens("L𝓪𝓻𝓸𝓬𝓬", &.{.extended_identifier}); | |
| 1956 | try expectTokens("1™", &.{ .integer_literal, .extended_identifier }); | |
| 1957 | try expectTokens("1.™", &.{ .float_literal, .extended_identifier }); | |
| 1958 | try expectTokens("..™", &.{ .period, .period, .extended_identifier }); | |
| 1959 | try expectTokens("0™", &.{ .integer_literal, .extended_identifier }); | |
| 1960 | try expectTokens("0b\u{E0000}", &.{ .invalid, .extended_identifier }); | |
| 1961 | try expectTokens("0b0\u{E0000}", &.{ .integer_literal, .extended_identifier }); | |
| 1962 | try expectTokens("01\u{E0000}", &.{ .integer_literal, .extended_identifier }); | |
| 1963 | try expectTokens("010\u{E0000}", &.{ .integer_literal, .extended_identifier }); | |
| 1964 | try expectTokens("0x\u{E0000}", &.{ .invalid, .extended_identifier }); | |
| 1965 | try expectTokens("0x0\u{E0000}", &.{ .integer_literal, .extended_identifier }); | |
| 1966 | try expectTokens("\"\\0\u{E0000}\"", &.{.string_literal}); | |
| 1967 | try expectTokens("\"\\x\u{E0000}\"", &.{.string_literal}); | |
| 1968 | try expectTokens("\"\\u\u{E0000}\"", &.{ .invalid, .extended_identifier, .invalid }); | |
| 1969 | try expectTokens("1e\u{E0000}", &.{ .invalid, .extended_identifier }); | |
| 1970 | try expectTokens("1e1\u{E0000}", &.{ .float_literal, .extended_identifier }); | |
| 1971 | } | |
| 1972 | ||
| 1973 | fn expectTokens(contents: []const u8, expected_tokens: []const Token.Id) !void { | |
| 1974 | var comp = Compilation.init(std.testing.allocator); | |
| 1975 | defer comp.deinit(); | |
| 1976 | const source = try comp.addSourceFromBuffer("path", contents); | |
| 1977 | var tokenizer = Tokenizer{ | |
| 1978 | .buf = source.buf, | |
| 1979 | .source = source.id, | |
| 1980 | .comp = &comp, | |
| 1981 | }; | |
| 1982 | var i: usize = 0; | |
| 1983 | while (i < expected_tokens.len) { | |
| 1984 | const token = tokenizer.next(); | |
| 1985 | if (token.id == .whitespace) continue; | |
| 1986 | const expected_token_id = expected_tokens[i]; | |
| 1987 | i += 1; | |
| 1988 | if (!std.meta.eql(token.id, expected_token_id)) { | |
| 1989 | std.debug.print("expected {s}, found {s}\n", .{ @tagName(expected_token_id), @tagName(token.id) }); | |
| 1990 | return error.TokensDoNotEqual; | |
| 1991 | } | |
| 1992 | } | |
| 1993 | const last_token = tokenizer.next(); | |
| 1994 | try std.testing.expect(last_token.id == .eof); | |
| 1995 | } |
src/aro/Tree.zig created+1148| ... | ... | @@ -0,0 +1,1148 @@ |
| 1 | const std = @import("std"); | |
| 2 | const Type = @import("Type.zig"); | |
| 3 | const Tokenizer = @import("Tokenizer.zig"); | |
| 4 | const Compilation = @import("Compilation.zig"); | |
| 5 | const Source = @import("Source.zig"); | |
| 6 | const Attribute = @import("Attribute.zig"); | |
| 7 | const Value = @import("Value.zig"); | |
| 8 | ||
| 9 | const Tree = @This(); | |
| 10 | ||
| 11 | pub const Token = struct { | |
| 12 | id: Id, | |
| 13 | /// This location contains the actual token slice which might be generated. | |
| 14 | /// If it is generated then there is guaranteed to be at least one | |
| 15 | /// expansion location. | |
| 16 | loc: Source.Location, | |
| 17 | expansion_locs: ?[*]Source.Location = null, | |
| 18 | ||
| 19 | pub fn expansionSlice(tok: Token) []const Source.Location { | |
| 20 | const locs = tok.expansion_locs orelse return &[0]Source.Location{}; | |
| 21 | var i: usize = 0; | |
| 22 | while (locs[i].id != .unused) : (i += 1) {} | |
| 23 | return locs[0..i]; | |
| 24 | } | |
| 25 | ||
| 26 | pub fn addExpansionLocation(tok: *Token, gpa: std.mem.Allocator, new: []const Source.Location) !void { | |
| 27 | if (new.len == 0 or tok.id == .whitespace) return; | |
| 28 | var list = std.ArrayList(Source.Location).init(gpa); | |
| 29 | defer { | |
| 30 | std.mem.set(Source.Location, list.items.ptr[list.items.len..list.capacity], .{}); | |
| 31 | // add a sentinel since the allocator is not guaranteed | |
| 32 | // to return the exact desired size | |
| 33 | list.items.ptr[list.capacity - 1].byte_offset = 1; | |
| 34 | tok.expansion_locs = list.items.ptr; | |
| 35 | } | |
| 36 | ||
| 37 | if (tok.expansion_locs) |locs| { | |
| 38 | var i: usize = 0; | |
| 39 | while (locs[i].id != .unused) : (i += 1) {} | |
| 40 | list.items = locs[0..i]; | |
| 41 | while (locs[i].byte_offset != 1) : (i += 1) {} | |
| 42 | list.capacity = i + 1; | |
| 43 | } | |
| 44 | ||
| 45 | const min_len = std.math.max(list.items.len + new.len + 1, 4); | |
| 46 | const wanted_len = std.math.ceilPowerOfTwo(usize, min_len) catch | |
| 47 | return error.OutOfMemory; | |
| 48 | try list.ensureTotalCapacity(wanted_len); | |
| 49 | ||
| 50 | for (new) |new_loc| { | |
| 51 | if (new_loc.id == .generated) continue; | |
| 52 | list.appendAssumeCapacity(new_loc); | |
| 53 | } | |
| 54 | } | |
| 55 | ||
| 56 | pub fn free(expansion_locs: ?[*]Source.Location, gpa: std.mem.Allocator) void { | |
| 57 | const locs = expansion_locs orelse return; | |
| 58 | var i: usize = 0; | |
| 59 | while (locs[i].id != .unused) : (i += 1) {} | |
| 60 | while (locs[i].byte_offset != 1) : (i += 1) {} | |
| 61 | gpa.free(locs[0 .. i + 1]); | |
| 62 | } | |
| 63 | ||
| 64 | pub fn dupe(tok: Token, gpa: std.mem.Allocator) !Token { | |
| 65 | var copy = tok; | |
| 66 | copy.expansion_locs = null; | |
| 67 | try copy.addExpansionLocation(gpa, tok.expansionSlice()); | |
| 68 | return copy; | |
| 69 | } | |
| 70 | ||
| 71 | pub const List = std.MultiArrayList(Token); | |
| 72 | pub const Id = Tokenizer.Token.Id; | |
| 73 | }; | |
| 74 | ||
| 75 | pub const TokenIndex = u32; | |
| 76 | pub const NodeIndex = enum(u32) { none, _ }; | |
| 77 | pub const ValueMap = std.AutoHashMap(NodeIndex, Value); | |
| 78 | ||
| 79 | comp: *Compilation, | |
| 80 | arena: std.heap.ArenaAllocator, | |
| 81 | generated: []const u8, | |
| 82 | tokens: Token.List.Slice, | |
| 83 | nodes: Node.List.Slice, | |
| 84 | data: []const NodeIndex, | |
| 85 | root_decls: []const NodeIndex, | |
| 86 | strings: []const u8, | |
| 87 | value_map: ValueMap, | |
| 88 | ||
| 89 | pub fn deinit(tree: *Tree) void { | |
| 90 | tree.comp.gpa.free(tree.root_decls); | |
| 91 | tree.comp.gpa.free(tree.data); | |
| 92 | tree.comp.gpa.free(tree.strings); | |
| 93 | tree.nodes.deinit(tree.comp.gpa); | |
| 94 | tree.arena.deinit(); | |
| 95 | tree.value_map.deinit(); | |
| 96 | } | |
| 97 | ||
| 98 | pub const Node = struct { | |
| 99 | tag: Tag, | |
| 100 | ty: Type = .{ .specifier = .void }, | |
| 101 | data: Data, | |
| 102 | ||
| 103 | pub const Range = struct { start: u32, end: u32 }; | |
| 104 | ||
| 105 | pub const Data = union { | |
| 106 | decl: struct { | |
| 107 | name: TokenIndex, | |
| 108 | node: NodeIndex = .none, | |
| 109 | }, | |
| 110 | decl_ref: TokenIndex, | |
| 111 | range: Range, | |
| 112 | if3: struct { | |
| 113 | cond: NodeIndex, | |
| 114 | body: u32, | |
| 115 | }, | |
| 116 | un: NodeIndex, | |
| 117 | bin: struct { | |
| 118 | lhs: NodeIndex, | |
| 119 | rhs: NodeIndex, | |
| 120 | }, | |
| 121 | member: struct { | |
| 122 | lhs: NodeIndex, | |
| 123 | index: u32, | |
| 124 | }, | |
| 125 | union_init: struct { | |
| 126 | field_index: u32, | |
| 127 | node: NodeIndex, | |
| 128 | }, | |
| 129 | int: u64, | |
| 130 | ||
| 131 | pub fn forDecl(data: Data, tree: Tree) struct { | |
| 132 | decls: []const NodeIndex, | |
| 133 | cond: NodeIndex, | |
| 134 | incr: NodeIndex, | |
| 135 | body: NodeIndex, | |
| 136 | } { | |
| 137 | const items = tree.data[data.range.start..data.range.end]; | |
| 138 | const decls = items[0 .. items.len - 3]; | |
| 139 | ||
| 140 | return .{ | |
| 141 | .decls = decls, | |
| 142 | .cond = items[items.len - 3], | |
| 143 | .incr = items[items.len - 2], | |
| 144 | .body = items[items.len - 1], | |
| 145 | }; | |
| 146 | } | |
| 147 | ||
| 148 | pub fn forStmt(data: Data, tree: Tree) struct { | |
| 149 | init: NodeIndex, | |
| 150 | cond: NodeIndex, | |
| 151 | incr: NodeIndex, | |
| 152 | body: NodeIndex, | |
| 153 | } { | |
| 154 | const items = tree.data[data.if3.body..]; | |
| 155 | ||
| 156 | return .{ | |
| 157 | .init = items[0], | |
| 158 | .cond = items[1], | |
| 159 | .incr = items[2], | |
| 160 | .body = data.if3.cond, | |
| 161 | }; | |
| 162 | } | |
| 163 | }; | |
| 164 | ||
| 165 | pub const List = std.MultiArrayList(Node); | |
| 166 | }; | |
| 167 | ||
| 168 | pub const Tag = enum(u8) { | |
| 169 | /// Only appears at index 0 and reaching it is always a result of a bug. | |
| 170 | invalid, | |
| 171 | ||
| 172 | // ====== Decl ====== | |
| 173 | ||
| 174 | // _Static_assert | |
| 175 | static_assert, | |
| 176 | ||
| 177 | // function prototype | |
| 178 | fn_proto, | |
| 179 | static_fn_proto, | |
| 180 | inline_fn_proto, | |
| 181 | inline_static_fn_proto, | |
| 182 | ||
| 183 | // function definition | |
| 184 | fn_def, | |
| 185 | static_fn_def, | |
| 186 | inline_fn_def, | |
| 187 | inline_static_fn_def, | |
| 188 | ||
| 189 | // variable declaration | |
| 190 | @"var", | |
| 191 | extern_var, | |
| 192 | static_var, | |
| 193 | // same as static_var, used for __func__, __FUNCTION__ and __PRETTY_FUNCTION__ | |
| 194 | implicit_static_var, | |
| 195 | threadlocal_var, | |
| 196 | threadlocal_extern_var, | |
| 197 | threadlocal_static_var, | |
| 198 | ||
| 199 | // typedef declaration | |
| 200 | typedef, | |
| 201 | ||
| 202 | // container declarations | |
| 203 | /// { lhs; rhs; } | |
| 204 | struct_decl_two, | |
| 205 | /// { lhs; rhs; } | |
| 206 | union_decl_two, | |
| 207 | /// { lhs, rhs, } | |
| 208 | enum_decl_two, | |
| 209 | /// { range } | |
| 210 | struct_decl, | |
| 211 | /// { range } | |
| 212 | union_decl, | |
| 213 | /// { range } | |
| 214 | enum_decl, | |
| 215 | ||
| 216 | /// name = node | |
| 217 | enum_field_decl, | |
| 218 | /// ty name : node | |
| 219 | /// name == 0 means unnamed | |
| 220 | record_field_decl, | |
| 221 | /// Used when a record has an unnamed record as a field | |
| 222 | indirect_record_field_decl, | |
| 223 | ||
| 224 | // ====== Stmt ====== | |
| 225 | ||
| 226 | labeled_stmt, | |
| 227 | /// { first; second; } first and second may be null | |
| 228 | compound_stmt_two, | |
| 229 | /// { data } | |
| 230 | compound_stmt, | |
| 231 | /// if (first) data[second] else data[second+1]; | |
| 232 | if_then_else_stmt, | |
| 233 | /// if (first); else second; | |
| 234 | if_else_stmt, | |
| 235 | /// if (first) second; second may be null | |
| 236 | if_then_stmt, | |
| 237 | /// switch (first) second | |
| 238 | switch_stmt, | |
| 239 | /// case first: second | |
| 240 | case_stmt, | |
| 241 | /// default: first | |
| 242 | default_stmt, | |
| 243 | /// while (first) second | |
| 244 | while_stmt, | |
| 245 | /// do second while(first); | |
| 246 | do_while_stmt, | |
| 247 | /// for (data[..]; data[len-3]; data[len-2]) data[len-1] | |
| 248 | for_decl_stmt, | |
| 249 | /// for (;;;) first | |
| 250 | forever_stmt, | |
| 251 | /// for (data[first]; data[first+1]; data[first+2]) second | |
| 252 | for_stmt, | |
| 253 | /// goto first; | |
| 254 | goto_stmt, | |
| 255 | /// goto *un; | |
| 256 | computed_goto_stmt, | |
| 257 | // continue; first and second unused | |
| 258 | continue_stmt, | |
| 259 | // break; first and second unused | |
| 260 | break_stmt, | |
| 261 | // null statement (just a semicolon); first and second unused | |
| 262 | null_stmt, | |
| 263 | /// return first; first may be null | |
| 264 | return_stmt, | |
| 265 | ||
| 266 | // ====== Expr ====== | |
| 267 | ||
| 268 | /// lhs , rhs | |
| 269 | comma_expr, | |
| 270 | /// lhs ?: rhs | |
| 271 | binary_cond_expr, | |
| 272 | /// lhs ? data[0] : data[1] | |
| 273 | cond_expr, | |
| 274 | /// lhs = rhs | |
| 275 | assign_expr, | |
| 276 | /// lhs *= rhs | |
| 277 | mul_assign_expr, | |
| 278 | /// lhs /= rhs | |
| 279 | div_assign_expr, | |
| 280 | /// lhs %= rhs | |
| 281 | mod_assign_expr, | |
| 282 | /// lhs += rhs | |
| 283 | add_assign_expr, | |
| 284 | /// lhs -= rhs | |
| 285 | sub_assign_expr, | |
| 286 | /// lhs <<= rhs | |
| 287 | shl_assign_expr, | |
| 288 | /// lhs >>= rhs | |
| 289 | shr_assign_expr, | |
| 290 | /// lhs &= rhs | |
| 291 | bit_and_assign_expr, | |
| 292 | /// lhs ^= rhs | |
| 293 | bit_xor_assign_expr, | |
| 294 | /// lhs |= rhs | |
| 295 | bit_or_assign_expr, | |
| 296 | /// lhs || rhs | |
| 297 | bool_or_expr, | |
| 298 | /// lhs && rhs | |
| 299 | bool_and_expr, | |
| 300 | /// lhs | rhs | |
| 301 | bit_or_expr, | |
| 302 | /// lhs ^ rhs | |
| 303 | bit_xor_expr, | |
| 304 | /// lhs & rhs | |
| 305 | bit_and_expr, | |
| 306 | /// lhs == rhs | |
| 307 | equal_expr, | |
| 308 | /// lhs != rhs | |
| 309 | not_equal_expr, | |
| 310 | /// lhs < rhs | |
| 311 | less_than_expr, | |
| 312 | /// lhs <= rhs | |
| 313 | less_than_equal_expr, | |
| 314 | /// lhs > rhs | |
| 315 | greater_than_expr, | |
| 316 | /// lhs >= rhs | |
| 317 | greater_than_equal_expr, | |
| 318 | /// lhs << rhs | |
| 319 | shl_expr, | |
| 320 | /// lhs >> rhs | |
| 321 | shr_expr, | |
| 322 | /// lhs + rhs | |
| 323 | add_expr, | |
| 324 | /// lhs - rhs | |
| 325 | sub_expr, | |
| 326 | /// lhs * rhs | |
| 327 | mul_expr, | |
| 328 | /// lhs / rhs | |
| 329 | div_expr, | |
| 330 | /// lhs % rhs | |
| 331 | mod_expr, | |
| 332 | /// Explicit (type)un | |
| 333 | cast_expr, | |
| 334 | /// &un | |
| 335 | addr_of_expr, | |
| 336 | /// &&decl_ref | |
| 337 | addr_of_label, | |
| 338 | /// *un | |
| 339 | deref_expr, | |
| 340 | /// +un | |
| 341 | plus_expr, | |
| 342 | /// -un | |
| 343 | negate_expr, | |
| 344 | /// ~un | |
| 345 | bit_not_expr, | |
| 346 | /// !un | |
| 347 | bool_not_expr, | |
| 348 | /// ++un | |
| 349 | pre_inc_expr, | |
| 350 | /// --un | |
| 351 | pre_dec_expr, | |
| 352 | /// lhs[rhs] lhs is pointer/array type, rhs is integer type | |
| 353 | array_access_expr, | |
| 354 | /// first(second) second may be 0 | |
| 355 | call_expr_one, | |
| 356 | /// data[0](data[1..]) | |
| 357 | call_expr, | |
| 358 | /// decl | |
| 359 | builtin_call_expr_one, | |
| 360 | builtin_call_expr, | |
| 361 | /// lhs.member | |
| 362 | member_access_expr, | |
| 363 | /// lhs->member | |
| 364 | member_access_ptr_expr, | |
| 365 | /// un++ | |
| 366 | post_inc_expr, | |
| 367 | /// un-- | |
| 368 | post_dec_expr, | |
| 369 | /// (un) | |
| 370 | paren_expr, | |
| 371 | /// decl_ref | |
| 372 | decl_ref_expr, | |
| 373 | /// decl_ref | |
| 374 | enumeration_ref, | |
| 375 | /// integer literal, always unsigned | |
| 376 | int_literal, | |
| 377 | /// Same as int_literal, but originates from a char literal | |
| 378 | char_literal, | |
| 379 | /// f32 literal | |
| 380 | float_literal, | |
| 381 | /// f64 literal | |
| 382 | double_literal, | |
| 383 | /// wraps a float or double literal: un | |
| 384 | imaginary_literal, | |
| 385 | /// tree.str[index..][0..len] | |
| 386 | string_literal_expr, | |
| 387 | /// sizeof(un?) | |
| 388 | sizeof_expr, | |
| 389 | /// _Alignof(un?) | |
| 390 | alignof_expr, | |
| 391 | /// _Generic(controlling lhs, chosen rhs) | |
| 392 | generic_expr_one, | |
| 393 | /// _Generic(controlling range[0], chosen range[1], rest range[2..]) | |
| 394 | generic_expr, | |
| 395 | /// ty: un | |
| 396 | generic_association_expr, | |
| 397 | // default: un | |
| 398 | generic_default_expr, | |
| 399 | /// __builtin_choose_expr(lhs, data[0], data[1]) | |
| 400 | builtin_choose_expr, | |
| 401 | /// ({ un }) | |
| 402 | stmt_expr, | |
| 403 | ||
| 404 | // ====== Initializer expressions ====== | |
| 405 | ||
| 406 | /// { lhs, rhs } | |
| 407 | array_init_expr_two, | |
| 408 | /// { range } | |
| 409 | array_init_expr, | |
| 410 | /// { lhs, rhs } | |
| 411 | struct_init_expr_two, | |
| 412 | /// { range } | |
| 413 | struct_init_expr, | |
| 414 | /// { union_init } | |
| 415 | union_init_expr, | |
| 416 | /// (ty){ un } | |
| 417 | compound_literal_expr, | |
| 418 | ||
| 419 | // ====== Implicit casts ====== | |
| 420 | ||
| 421 | /// Convert T[] to T * | |
| 422 | array_to_pointer, | |
| 423 | /// Converts an lvalue to an rvalue | |
| 424 | lval_to_rval, | |
| 425 | /// Convert a function type to a pointer to a function | |
| 426 | function_to_pointer, | |
| 427 | /// Convert a pointer type to a _Bool | |
| 428 | pointer_to_bool, | |
| 429 | /// Convert a pointer type to an integer type | |
| 430 | pointer_to_int, | |
| 431 | /// Convert _Bool to an integer type | |
| 432 | bool_to_int, | |
| 433 | /// Convert _Bool to a floating type | |
| 434 | bool_to_float, | |
| 435 | /// Convert a _Bool to a pointer; will cause a warning | |
| 436 | bool_to_pointer, | |
| 437 | /// Convert an integer type to _Bool | |
| 438 | int_to_bool, | |
| 439 | /// Convert an integer to a floating | |
| 440 | int_to_float, | |
| 441 | /// Convert an integer type to a pointer type | |
| 442 | int_to_pointer, | |
| 443 | /// Convert a floating type to a _Bool | |
| 444 | float_to_bool, | |
| 445 | /// Convert a floating type to an integer | |
| 446 | float_to_int, | |
| 447 | /// Convert one integer type to another | |
| 448 | int_cast, | |
| 449 | /// Convert one floating type to another | |
| 450 | float_cast, | |
| 451 | /// Convert pointer to one with same child type but more CV-quals, | |
| 452 | /// OR to appropriately-qualified void * | |
| 453 | /// only appears on the branches of a conditional expr | |
| 454 | qual_cast, | |
| 455 | /// Convert type to void; only appears on the branches of a conditional expr | |
| 456 | to_void, | |
| 457 | ||
| 458 | /// Convert a literal 0 to a null pointer | |
| 459 | null_to_pointer, | |
| 460 | ||
| 461 | /// Inserted at the end of a function body if no return stmt is found. | |
| 462 | /// ty is the functions return type | |
| 463 | implicit_return, | |
| 464 | ||
| 465 | /// Inserted in array_init_expr to represent unspecified elements. | |
| 466 | /// data.int contains the amount of elements. | |
| 467 | array_filler_expr, | |
| 468 | /// Inserted in record and scalar initializers for unspecified elements. | |
| 469 | default_init_expr, | |
| 470 | ||
| 471 | /// attribute argument identifier (see `mode` attribute) | |
| 472 | attr_arg_ident, | |
| 473 | /// rhs can be none | |
| 474 | attr_params_two, | |
| 475 | /// range | |
| 476 | attr_params, | |
| 477 | ||
| 478 | pub fn isImplicit(tag: Tag) bool { | |
| 479 | return switch (tag) { | |
| 480 | .array_to_pointer, | |
| 481 | .lval_to_rval, | |
| 482 | .function_to_pointer, | |
| 483 | .pointer_to_bool, | |
| 484 | .pointer_to_int, | |
| 485 | .bool_to_int, | |
| 486 | .bool_to_float, | |
| 487 | .bool_to_pointer, | |
| 488 | .int_to_bool, | |
| 489 | .int_to_float, | |
| 490 | .int_to_pointer, | |
| 491 | .float_to_bool, | |
| 492 | .float_to_int, | |
| 493 | .int_cast, | |
| 494 | .float_cast, | |
| 495 | .to_void, | |
| 496 | .implicit_return, | |
| 497 | .qual_cast, | |
| 498 | .null_to_pointer, | |
| 499 | .array_filler_expr, | |
| 500 | .default_init_expr, | |
| 501 | .implicit_static_var, | |
| 502 | => true, | |
| 503 | else => false, | |
| 504 | }; | |
| 505 | } | |
| 506 | }; | |
| 507 | ||
| 508 | pub fn isLval(nodes: Node.List.Slice, extra: []const NodeIndex, value_map: ValueMap, node: NodeIndex) bool { | |
| 509 | var is_const: bool = undefined; | |
| 510 | return isLvalExtra(nodes, extra, value_map, node, &is_const); | |
| 511 | } | |
| 512 | ||
| 513 | pub fn isLvalExtra(nodes: Node.List.Slice, extra: []const NodeIndex, value_map: ValueMap, node: NodeIndex, is_const: *bool) bool { | |
| 514 | is_const.* = false; | |
| 515 | switch (nodes.items(.tag)[@enumToInt(node)]) { | |
| 516 | .compound_literal_expr => { | |
| 517 | is_const.* = nodes.items(.ty)[@enumToInt(node)].isConst(); | |
| 518 | return true; | |
| 519 | }, | |
| 520 | .string_literal_expr => return true, | |
| 521 | .member_access_ptr_expr => { | |
| 522 | const lhs_expr = nodes.items(.data)[@enumToInt(node)].member.lhs; | |
| 523 | const ptr_ty = nodes.items(.ty)[@enumToInt(lhs_expr)]; | |
| 524 | if (ptr_ty.isPtr()) is_const.* = ptr_ty.elemType().isConst(); | |
| 525 | return true; | |
| 526 | }, | |
| 527 | .array_access_expr => { | |
| 528 | const lhs_expr = nodes.items(.data)[@enumToInt(node)].bin.lhs; | |
| 529 | if (lhs_expr != .none) { | |
| 530 | const array_ty = nodes.items(.ty)[@enumToInt(lhs_expr)]; | |
| 531 | if (array_ty.isPtr() or array_ty.isArray()) is_const.* = array_ty.elemType().isConst(); | |
| 532 | } | |
| 533 | return true; | |
| 534 | }, | |
| 535 | .decl_ref_expr => { | |
| 536 | const decl_ty = nodes.items(.ty)[@enumToInt(node)]; | |
| 537 | is_const.* = decl_ty.isConst(); | |
| 538 | return true; | |
| 539 | }, | |
| 540 | .deref_expr => { | |
| 541 | const data = nodes.items(.data)[@enumToInt(node)]; | |
| 542 | const operand_ty = nodes.items(.ty)[@enumToInt(data.un)]; | |
| 543 | if (operand_ty.isFunc()) return false; | |
| 544 | if (operand_ty.isPtr() or operand_ty.isArray()) is_const.* = operand_ty.elemType().isConst(); | |
| 545 | return true; | |
| 546 | }, | |
| 547 | .member_access_expr => { | |
| 548 | const data = nodes.items(.data)[@enumToInt(node)]; | |
| 549 | return isLvalExtra(nodes, extra, value_map, data.member.lhs, is_const); | |
| 550 | }, | |
| 551 | .paren_expr => { | |
| 552 | const data = nodes.items(.data)[@enumToInt(node)]; | |
| 553 | return isLvalExtra(nodes, extra, value_map, data.un, is_const); | |
| 554 | }, | |
| 555 | .builtin_choose_expr => { | |
| 556 | const data = nodes.items(.data)[@enumToInt(node)]; | |
| 557 | ||
| 558 | if (value_map.get(data.if3.cond)) |val| { | |
| 559 | const offset = @boolToInt(val.isZero()); | |
| 560 | return isLvalExtra(nodes, extra, value_map, extra[data.if3.body + offset], is_const); | |
| 561 | } | |
| 562 | return false; | |
| 563 | }, | |
| 564 | else => return false, | |
| 565 | } | |
| 566 | } | |
| 567 | ||
| 568 | pub fn dumpStr(bytes: []const u8, tag: Tag, writer: anytype) !void { | |
| 569 | switch (tag) { | |
| 570 | .string_literal_expr => try writer.print("\"{}\"", .{std.zig.fmtEscapes(bytes[0 .. bytes.len - 1])}), | |
| 571 | else => unreachable, | |
| 572 | } | |
| 573 | } | |
| 574 | ||
| 575 | pub fn tokSlice(tree: Tree, tok_i: TokenIndex) []const u8 { | |
| 576 | if (tree.tokens.items(.id)[tok_i].lexeme()) |some| return some; | |
| 577 | const loc = tree.tokens.items(.loc)[tok_i]; | |
| 578 | var tmp_tokenizer = Tokenizer{ | |
| 579 | .buf = tree.comp.getSource(loc.id).buf, | |
| 580 | .comp = tree.comp, | |
| 581 | .index = loc.byte_offset, | |
| 582 | .source = .generated, | |
| 583 | }; | |
| 584 | const tok = tmp_tokenizer.next(); | |
| 585 | return tmp_tokenizer.buf[tok.start..tok.end]; | |
| 586 | } | |
| 587 | ||
| 588 | pub fn dump(tree: Tree, writer: anytype) @TypeOf(writer).Error!void { | |
| 589 | for (tree.root_decls) |i| { | |
| 590 | try tree.dumpNode(i, 0, writer); | |
| 591 | try writer.writeByte('\n'); | |
| 592 | } | |
| 593 | } | |
| 594 | ||
| 595 | fn dumpAttribute(attr: Attribute, writer: anytype) !void { | |
| 596 | inline for (std.meta.fields(Attribute.Tag)) |e| { | |
| 597 | if (e.value == @enumToInt(attr.tag)) { | |
| 598 | const args = @field(attr.args, e.name); | |
| 599 | if (@TypeOf(args) == void) { | |
| 600 | try writer.writeByte('\n'); | |
| 601 | return; | |
| 602 | } | |
| 603 | inline for (@typeInfo(@TypeOf(args)).Struct.fields) |f, i| { | |
| 604 | if (comptime std.mem.eql(u8, f.name, "__name_tok")) continue; | |
| 605 | if (i != 0) { | |
| 606 | try writer.writeAll(", "); | |
| 607 | } | |
| 608 | try writer.writeAll(f.name); | |
| 609 | try writer.writeAll(": "); | |
| 610 | switch (f.field_type) { | |
| 611 | []const u8, ?[]const u8 => try writer.print("\"{s}\"", .{@field(args, f.name)}), | |
| 612 | else => switch (@typeInfo(f.field_type)) { | |
| 613 | .Enum => try writer.writeAll(@tagName(@field(args, f.name))), | |
| 614 | else => try writer.print("{}", .{@field(args, f.name)}), | |
| 615 | }, | |
| 616 | } | |
| 617 | } | |
| 618 | try writer.writeByte('\n'); | |
| 619 | return; | |
| 620 | } | |
| 621 | } | |
| 622 | } | |
| 623 | ||
| 624 | fn dumpNode(tree: Tree, node: NodeIndex, level: u32, w: anytype) @TypeOf(w).Error!void { | |
| 625 | const delta = 2; | |
| 626 | const half = delta / 2; | |
| 627 | const util = @import("util.zig"); | |
| 628 | const TYPE = util.Color.purple; | |
| 629 | const TAG = util.Color.cyan; | |
| 630 | const IMPLICIT = util.Color.blue; | |
| 631 | const NAME = util.Color.red; | |
| 632 | const LITERAL = util.Color.green; | |
| 633 | const ATTRIBUTE = util.Color.yellow; | |
| 634 | std.debug.assert(node != .none); | |
| 635 | ||
| 636 | const tag = tree.nodes.items(.tag)[@enumToInt(node)]; | |
| 637 | const data = tree.nodes.items(.data)[@enumToInt(node)]; | |
| 638 | const ty = tree.nodes.items(.ty)[@enumToInt(node)]; | |
| 639 | try w.writeByteNTimes(' ', level); | |
| 640 | ||
| 641 | util.setColor(if (tag.isImplicit()) IMPLICIT else TAG, w); | |
| 642 | try w.print("{s}: ", .{@tagName(tag)}); | |
| 643 | util.setColor(TYPE, w); | |
| 644 | try w.writeByte('\''); | |
| 645 | try ty.dump(w); | |
| 646 | try w.writeByte('\''); | |
| 647 | ||
| 648 | if (isLval(tree.nodes, tree.data, tree.value_map, node)) { | |
| 649 | util.setColor(ATTRIBUTE, w); | |
| 650 | try w.writeAll(" lvalue"); | |
| 651 | } | |
| 652 | if (tree.value_map.get(node)) |val| { | |
| 653 | util.setColor(LITERAL, w); | |
| 654 | try w.writeAll(" (value: "); | |
| 655 | try val.dump(ty, tree.comp, w); | |
| 656 | try w.writeByte(')'); | |
| 657 | } | |
| 658 | try w.writeAll("\n"); | |
| 659 | util.setColor(.reset, w); | |
| 660 | ||
| 661 | if (ty.specifier == .attributed) { | |
| 662 | util.setColor(ATTRIBUTE, w); | |
| 663 | for (ty.data.attributed.attributes) |attr| { | |
| 664 | try w.writeByteNTimes(' ', level + half); | |
| 665 | try w.print("attr: {s} ", .{@tagName(attr.tag)}); | |
| 666 | try dumpAttribute(attr, w); | |
| 667 | } | |
| 668 | util.setColor(.reset, w); | |
| 669 | } | |
| 670 | ||
| 671 | switch (tag) { | |
| 672 | .invalid => unreachable, | |
| 673 | .static_assert => { | |
| 674 | try w.writeByteNTimes(' ', level + 1); | |
| 675 | try w.writeAll("condition:\n"); | |
| 676 | try tree.dumpNode(data.bin.lhs, level + delta, w); | |
| 677 | if (data.bin.rhs != .none) { | |
| 678 | try w.writeByteNTimes(' ', level + 1); | |
| 679 | try w.writeAll("diagnostic:\n"); | |
| 680 | try tree.dumpNode(data.bin.rhs, level + delta, w); | |
| 681 | } | |
| 682 | }, | |
| 683 | .fn_proto, | |
| 684 | .static_fn_proto, | |
| 685 | .inline_fn_proto, | |
| 686 | .inline_static_fn_proto, | |
| 687 | => { | |
| 688 | try w.writeByteNTimes(' ', level + half); | |
| 689 | try w.writeAll("name: "); | |
| 690 | util.setColor(NAME, w); | |
| 691 | try w.print("{s}\n", .{tree.tokSlice(data.decl.name)}); | |
| 692 | util.setColor(.reset, w); | |
| 693 | }, | |
| 694 | .fn_def, | |
| 695 | .static_fn_def, | |
| 696 | .inline_fn_def, | |
| 697 | .inline_static_fn_def, | |
| 698 | => { | |
| 699 | try w.writeByteNTimes(' ', level + half); | |
| 700 | try w.writeAll("name: "); | |
| 701 | util.setColor(NAME, w); | |
| 702 | try w.print("{s}\n", .{tree.tokSlice(data.decl.name)}); | |
| 703 | util.setColor(.reset, w); | |
| 704 | try w.writeByteNTimes(' ', level + half); | |
| 705 | try w.writeAll("body:\n"); | |
| 706 | try tree.dumpNode(data.decl.node, level + delta, w); | |
| 707 | }, | |
| 708 | .typedef, | |
| 709 | .@"var", | |
| 710 | .extern_var, | |
| 711 | .static_var, | |
| 712 | .implicit_static_var, | |
| 713 | .threadlocal_var, | |
| 714 | .threadlocal_extern_var, | |
| 715 | .threadlocal_static_var, | |
| 716 | => { | |
| 717 | try w.writeByteNTimes(' ', level + half); | |
| 718 | try w.writeAll("name: "); | |
| 719 | util.setColor(NAME, w); | |
| 720 | try w.print("{s}\n", .{tree.tokSlice(data.decl.name)}); | |
| 721 | util.setColor(.reset, w); | |
| 722 | if (data.decl.node != .none) { | |
| 723 | try w.writeByteNTimes(' ', level + half); | |
| 724 | try w.writeAll("init:\n"); | |
| 725 | try tree.dumpNode(data.decl.node, level + delta, w); | |
| 726 | } | |
| 727 | }, | |
| 728 | .enum_field_decl => { | |
| 729 | try w.writeByteNTimes(' ', level + half); | |
| 730 | try w.writeAll("name: "); | |
| 731 | util.setColor(NAME, w); | |
| 732 | try w.print("{s}\n", .{tree.tokSlice(data.decl.name)}); | |
| 733 | util.setColor(.reset, w); | |
| 734 | if (data.decl.node != .none) { | |
| 735 | try w.writeByteNTimes(' ', level + half); | |
| 736 | try w.writeAll("value:\n"); | |
| 737 | try tree.dumpNode(data.decl.node, level + delta, w); | |
| 738 | } | |
| 739 | }, | |
| 740 | .record_field_decl => { | |
| 741 | if (data.decl.name != 0) { | |
| 742 | try w.writeByteNTimes(' ', level + half); | |
| 743 | try w.writeAll("name: "); | |
| 744 | util.setColor(NAME, w); | |
| 745 | try w.print("{s}\n", .{tree.tokSlice(data.decl.name)}); | |
| 746 | util.setColor(.reset, w); | |
| 747 | } | |
| 748 | if (data.decl.node != .none) { | |
| 749 | try w.writeByteNTimes(' ', level + half); | |
| 750 | try w.writeAll("bits:\n"); | |
| 751 | try tree.dumpNode(data.decl.node, level + delta, w); | |
| 752 | } | |
| 753 | }, | |
| 754 | .indirect_record_field_decl => {}, | |
| 755 | .compound_stmt, | |
| 756 | .array_init_expr, | |
| 757 | .struct_init_expr, | |
| 758 | .enum_decl, | |
| 759 | .struct_decl, | |
| 760 | .union_decl, | |
| 761 | .attr_params, | |
| 762 | => { | |
| 763 | for (tree.data[data.range.start..data.range.end]) |stmt, i| { | |
| 764 | if (i != 0) try w.writeByte('\n'); | |
| 765 | try tree.dumpNode(stmt, level + delta, w); | |
| 766 | } | |
| 767 | }, | |
| 768 | .compound_stmt_two, | |
| 769 | .array_init_expr_two, | |
| 770 | .struct_init_expr_two, | |
| 771 | .enum_decl_two, | |
| 772 | .struct_decl_two, | |
| 773 | .union_decl_two, | |
| 774 | .attr_params_two, | |
| 775 | => { | |
| 776 | if (data.bin.lhs != .none) try tree.dumpNode(data.bin.lhs, level + delta, w); | |
| 777 | if (data.bin.rhs != .none) try tree.dumpNode(data.bin.rhs, level + delta, w); | |
| 778 | }, | |
| 779 | .union_init_expr => { | |
| 780 | try w.writeByteNTimes(' ', level + half); | |
| 781 | try w.writeAll("field index: "); | |
| 782 | util.setColor(LITERAL, w); | |
| 783 | try w.print("{d}\n", .{data.union_init.field_index}); | |
| 784 | util.setColor(.reset, w); | |
| 785 | if (data.union_init.node != .none) { | |
| 786 | try tree.dumpNode(data.union_init.node, level + delta, w); | |
| 787 | } | |
| 788 | }, | |
| 789 | .compound_literal_expr => { | |
| 790 | try tree.dumpNode(data.un, level + half, w); | |
| 791 | }, | |
| 792 | .labeled_stmt => { | |
| 793 | try w.writeByteNTimes(' ', level + half); | |
| 794 | try w.writeAll("label: "); | |
| 795 | util.setColor(LITERAL, w); | |
| 796 | try w.print("{s}\n", .{tree.tokSlice(data.decl.name)}); | |
| 797 | util.setColor(.reset, w); | |
| 798 | if (data.decl.node != .none) { | |
| 799 | try w.writeByteNTimes(' ', level + half); | |
| 800 | try w.writeAll("stmt:\n"); | |
| 801 | try tree.dumpNode(data.decl.node, level + delta, w); | |
| 802 | } | |
| 803 | }, | |
| 804 | .case_stmt => { | |
| 805 | try w.writeByteNTimes(' ', level + half); | |
| 806 | try w.writeAll("value:\n"); | |
| 807 | try tree.dumpNode(data.bin.lhs, level + delta, w); | |
| 808 | if (data.bin.rhs != .none) { | |
| 809 | try w.writeByteNTimes(' ', level + half); | |
| 810 | try w.writeAll("stmt:\n"); | |
| 811 | try tree.dumpNode(data.bin.rhs, level + delta, w); | |
| 812 | } | |
| 813 | }, | |
| 814 | .default_stmt => { | |
| 815 | if (data.un != .none) { | |
| 816 | try w.writeByteNTimes(' ', level + half); | |
| 817 | try w.writeAll("stmt:\n"); | |
| 818 | try tree.dumpNode(data.un, level + delta, w); | |
| 819 | } | |
| 820 | }, | |
| 821 | .cond_expr, .if_then_else_stmt, .builtin_choose_expr => { | |
| 822 | try w.writeByteNTimes(' ', level + half); | |
| 823 | try w.writeAll("cond:\n"); | |
| 824 | try tree.dumpNode(data.if3.cond, level + delta, w); | |
| 825 | ||
| 826 | try w.writeByteNTimes(' ', level + half); | |
| 827 | try w.writeAll("then:\n"); | |
| 828 | try tree.dumpNode(tree.data[data.if3.body], level + delta, w); | |
| 829 | ||
| 830 | try w.writeByteNTimes(' ', level + half); | |
| 831 | try w.writeAll("else:\n"); | |
| 832 | try tree.dumpNode(tree.data[data.if3.body + 1], level + delta, w); | |
| 833 | }, | |
| 834 | .if_else_stmt => { | |
| 835 | try w.writeByteNTimes(' ', level + half); | |
| 836 | try w.writeAll("cond:\n"); | |
| 837 | try tree.dumpNode(data.bin.lhs, level + delta, w); | |
| 838 | ||
| 839 | try w.writeByteNTimes(' ', level + half); | |
| 840 | try w.writeAll("else:\n"); | |
| 841 | try tree.dumpNode(data.bin.rhs, level + delta, w); | |
| 842 | }, | |
| 843 | .if_then_stmt => { | |
| 844 | try w.writeByteNTimes(' ', level + half); | |
| 845 | try w.writeAll("cond:\n"); | |
| 846 | try tree.dumpNode(data.bin.lhs, level + delta, w); | |
| 847 | ||
| 848 | if (data.bin.rhs != .none) { | |
| 849 | try w.writeByteNTimes(' ', level + half); | |
| 850 | try w.writeAll("then:\n"); | |
| 851 | try tree.dumpNode(data.bin.rhs, level + delta, w); | |
| 852 | } | |
| 853 | }, | |
| 854 | .switch_stmt, .while_stmt, .do_while_stmt => { | |
| 855 | try w.writeByteNTimes(' ', level + half); | |
| 856 | try w.writeAll("cond:\n"); | |
| 857 | try tree.dumpNode(data.bin.lhs, level + delta, w); | |
| 858 | ||
| 859 | if (data.bin.rhs != .none) { | |
| 860 | try w.writeByteNTimes(' ', level + half); | |
| 861 | try w.writeAll("body:\n"); | |
| 862 | try tree.dumpNode(data.bin.rhs, level + delta, w); | |
| 863 | } | |
| 864 | }, | |
| 865 | .for_decl_stmt => { | |
| 866 | const for_decl = data.forDecl(tree); | |
| 867 | ||
| 868 | try w.writeByteNTimes(' ', level + half); | |
| 869 | try w.writeAll("decl:\n"); | |
| 870 | for (for_decl.decls) |decl| { | |
| 871 | try tree.dumpNode(decl, level + delta, w); | |
| 872 | try w.writeByte('\n'); | |
| 873 | } | |
| 874 | if (for_decl.cond != .none) { | |
| 875 | try w.writeByteNTimes(' ', level + half); | |
| 876 | try w.writeAll("cond:\n"); | |
| 877 | try tree.dumpNode(for_decl.cond, level + delta, w); | |
| 878 | } | |
| 879 | if (for_decl.incr != .none) { | |
| 880 | try w.writeByteNTimes(' ', level + half); | |
| 881 | try w.writeAll("incr:\n"); | |
| 882 | try tree.dumpNode(for_decl.incr, level + delta, w); | |
| 883 | } | |
| 884 | if (for_decl.body != .none) { | |
| 885 | try w.writeByteNTimes(' ', level + half); | |
| 886 | try w.writeAll("body:\n"); | |
| 887 | try tree.dumpNode(for_decl.body, level + delta, w); | |
| 888 | } | |
| 889 | }, | |
| 890 | .forever_stmt => { | |
| 891 | if (data.un != .none) { | |
| 892 | try w.writeByteNTimes(' ', level + half); | |
| 893 | try w.writeAll("body:\n"); | |
| 894 | try tree.dumpNode(data.un, level + delta, w); | |
| 895 | } | |
| 896 | }, | |
| 897 | .for_stmt => { | |
| 898 | const for_stmt = data.forStmt(tree); | |
| 899 | ||
| 900 | if (for_stmt.init != .none) { | |
| 901 | try w.writeByteNTimes(' ', level + half); | |
| 902 | try w.writeAll("init:\n"); | |
| 903 | try tree.dumpNode(for_stmt.init, level + delta, w); | |
| 904 | } | |
| 905 | if (for_stmt.cond != .none) { | |
| 906 | try w.writeByteNTimes(' ', level + half); | |
| 907 | try w.writeAll("cond:\n"); | |
| 908 | try tree.dumpNode(for_stmt.cond, level + delta, w); | |
| 909 | } | |
| 910 | if (for_stmt.incr != .none) { | |
| 911 | try w.writeByteNTimes(' ', level + half); | |
| 912 | try w.writeAll("incr:\n"); | |
| 913 | try tree.dumpNode(for_stmt.incr, level + delta, w); | |
| 914 | } | |
| 915 | if (for_stmt.body != .none) { | |
| 916 | try w.writeByteNTimes(' ', level + half); | |
| 917 | try w.writeAll("body:\n"); | |
| 918 | try tree.dumpNode(for_stmt.body, level + delta, w); | |
| 919 | } | |
| 920 | }, | |
| 921 | .goto_stmt, .addr_of_label => { | |
| 922 | try w.writeByteNTimes(' ', level + half); | |
| 923 | try w.writeAll("label: "); | |
| 924 | util.setColor(LITERAL, w); | |
| 925 | try w.print("{s}\n", .{tree.tokSlice(data.decl_ref)}); | |
| 926 | util.setColor(.reset, w); | |
| 927 | }, | |
| 928 | .continue_stmt, .break_stmt, .implicit_return, .null_stmt => {}, | |
| 929 | .return_stmt => { | |
| 930 | if (data.un != .none) { | |
| 931 | try w.writeByteNTimes(' ', level + half); | |
| 932 | try w.writeAll("expr:\n"); | |
| 933 | try tree.dumpNode(data.un, level + delta, w); | |
| 934 | } | |
| 935 | }, | |
| 936 | .attr_arg_ident => { | |
| 937 | try w.writeByteNTimes(' ', level + half); | |
| 938 | util.setColor(ATTRIBUTE, w); | |
| 939 | try w.print("name: {s}\n", .{tree.tokSlice(data.decl_ref)}); | |
| 940 | util.setColor(.reset, w); | |
| 941 | }, | |
| 942 | .call_expr => { | |
| 943 | try w.writeByteNTimes(' ', level + half); | |
| 944 | try w.writeAll("lhs:\n"); | |
| 945 | try tree.dumpNode(tree.data[data.range.start], level + delta, w); | |
| 946 | ||
| 947 | try w.writeByteNTimes(' ', level + half); | |
| 948 | try w.writeAll("args:\n"); | |
| 949 | for (tree.data[data.range.start + 1 .. data.range.end]) |arg| try tree.dumpNode(arg, level + delta, w); | |
| 950 | }, | |
| 951 | .call_expr_one => { | |
| 952 | try w.writeByteNTimes(' ', level + half); | |
| 953 | try w.writeAll("lhs:\n"); | |
| 954 | try tree.dumpNode(data.bin.lhs, level + delta, w); | |
| 955 | if (data.bin.rhs != .none) { | |
| 956 | try w.writeByteNTimes(' ', level + half); | |
| 957 | try w.writeAll("arg:\n"); | |
| 958 | try tree.dumpNode(data.bin.rhs, level + delta, w); | |
| 959 | } | |
| 960 | }, | |
| 961 | .builtin_call_expr => { | |
| 962 | try w.writeByteNTimes(' ', level + half); | |
| 963 | try w.writeAll("name: "); | |
| 964 | util.setColor(NAME, w); | |
| 965 | try w.print("{s}\n", .{tree.tokSlice(@enumToInt(tree.data[data.range.start]))}); | |
| 966 | util.setColor(.reset, w); | |
| 967 | ||
| 968 | try w.writeByteNTimes(' ', level + half); | |
| 969 | try w.writeAll("args:\n"); | |
| 970 | for (tree.data[data.range.start + 1 .. data.range.end]) |arg| try tree.dumpNode(arg, level + delta, w); | |
| 971 | }, | |
| 972 | .builtin_call_expr_one => { | |
| 973 | try w.writeByteNTimes(' ', level + half); | |
| 974 | try w.writeAll("name: "); | |
| 975 | util.setColor(NAME, w); | |
| 976 | try w.print("{s}\n", .{tree.tokSlice(data.decl.name)}); | |
| 977 | util.setColor(.reset, w); | |
| 978 | if (data.decl.node != .none) { | |
| 979 | try w.writeByteNTimes(' ', level + half); | |
| 980 | try w.writeAll("arg:\n"); | |
| 981 | try tree.dumpNode(data.decl.node, level + delta, w); | |
| 982 | } | |
| 983 | }, | |
| 984 | .comma_expr, | |
| 985 | .binary_cond_expr, | |
| 986 | .assign_expr, | |
| 987 | .mul_assign_expr, | |
| 988 | .div_assign_expr, | |
| 989 | .mod_assign_expr, | |
| 990 | .add_assign_expr, | |
| 991 | .sub_assign_expr, | |
| 992 | .shl_assign_expr, | |
| 993 | .shr_assign_expr, | |
| 994 | .bit_and_assign_expr, | |
| 995 | .bit_xor_assign_expr, | |
| 996 | .bit_or_assign_expr, | |
| 997 | .bool_or_expr, | |
| 998 | .bool_and_expr, | |
| 999 | .bit_or_expr, | |
| 1000 | .bit_xor_expr, | |
| 1001 | .bit_and_expr, | |
| 1002 | .equal_expr, | |
| 1003 | .not_equal_expr, | |
| 1004 | .less_than_expr, | |
| 1005 | .less_than_equal_expr, | |
| 1006 | .greater_than_expr, | |
| 1007 | .greater_than_equal_expr, | |
| 1008 | .shl_expr, | |
| 1009 | .shr_expr, | |
| 1010 | .add_expr, | |
| 1011 | .sub_expr, | |
| 1012 | .mul_expr, | |
| 1013 | .div_expr, | |
| 1014 | .mod_expr, | |
| 1015 | => { | |
| 1016 | try w.writeByteNTimes(' ', level + 1); | |
| 1017 | try w.writeAll("lhs:\n"); | |
| 1018 | try tree.dumpNode(data.bin.lhs, level + delta, w); | |
| 1019 | try w.writeByteNTimes(' ', level + 1); | |
| 1020 | try w.writeAll("rhs:\n"); | |
| 1021 | try tree.dumpNode(data.bin.rhs, level + delta, w); | |
| 1022 | }, | |
| 1023 | .cast_expr, | |
| 1024 | .addr_of_expr, | |
| 1025 | .computed_goto_stmt, | |
| 1026 | .deref_expr, | |
| 1027 | .plus_expr, | |
| 1028 | .negate_expr, | |
| 1029 | .bit_not_expr, | |
| 1030 | .bool_not_expr, | |
| 1031 | .pre_inc_expr, | |
| 1032 | .pre_dec_expr, | |
| 1033 | .post_inc_expr, | |
| 1034 | .post_dec_expr, | |
| 1035 | .paren_expr, | |
| 1036 | => { | |
| 1037 | try w.writeByteNTimes(' ', level + 1); | |
| 1038 | try w.writeAll("operand:\n"); | |
| 1039 | try tree.dumpNode(data.un, level + delta, w); | |
| 1040 | }, | |
| 1041 | .decl_ref_expr => { | |
| 1042 | try w.writeByteNTimes(' ', level + 1); | |
| 1043 | try w.writeAll("name: "); | |
| 1044 | util.setColor(NAME, w); | |
| 1045 | try w.print("{s}\n", .{tree.tokSlice(data.decl_ref)}); | |
| 1046 | util.setColor(.reset, w); | |
| 1047 | }, | |
| 1048 | .enumeration_ref => { | |
| 1049 | try w.writeByteNTimes(' ', level + 1); | |
| 1050 | try w.writeAll("name: "); | |
| 1051 | util.setColor(NAME, w); | |
| 1052 | try w.print("{s}\n", .{tree.tokSlice(data.decl_ref)}); | |
| 1053 | util.setColor(.reset, w); | |
| 1054 | }, | |
| 1055 | .int_literal, | |
| 1056 | .char_literal, | |
| 1057 | .float_literal, | |
| 1058 | .double_literal, | |
| 1059 | .string_literal_expr, | |
| 1060 | => {}, | |
| 1061 | .member_access_expr, .member_access_ptr_expr => { | |
| 1062 | try w.writeByteNTimes(' ', level + 1); | |
| 1063 | try w.writeAll("lhs:\n"); | |
| 1064 | try tree.dumpNode(data.member.lhs, level + delta, w); | |
| 1065 | ||
| 1066 | var lhs_ty = tree.nodes.items(.ty)[@enumToInt(data.member.lhs)]; | |
| 1067 | if (lhs_ty.isPtr()) lhs_ty = lhs_ty.elemType(); | |
| 1068 | lhs_ty = lhs_ty.canonicalize(.standard); | |
| 1069 | ||
| 1070 | try w.writeByteNTimes(' ', level + 1); | |
| 1071 | try w.writeAll("name: "); | |
| 1072 | util.setColor(NAME, w); | |
| 1073 | try w.print("{s}\n", .{lhs_ty.data.record.fields[data.member.index].name}); | |
| 1074 | util.setColor(.reset, w); | |
| 1075 | }, | |
| 1076 | .array_access_expr => { | |
| 1077 | if (data.bin.lhs != .none) { | |
| 1078 | try w.writeByteNTimes(' ', level + 1); | |
| 1079 | try w.writeAll("lhs:\n"); | |
| 1080 | try tree.dumpNode(data.bin.lhs, level + delta, w); | |
| 1081 | } | |
| 1082 | try w.writeByteNTimes(' ', level + 1); | |
| 1083 | try w.writeAll("index:\n"); | |
| 1084 | try tree.dumpNode(data.bin.rhs, level + delta, w); | |
| 1085 | }, | |
| 1086 | .sizeof_expr, .alignof_expr => { | |
| 1087 | if (data.un != .none) { | |
| 1088 | try w.writeByteNTimes(' ', level + 1); | |
| 1089 | try w.writeAll("expr:\n"); | |
| 1090 | try tree.dumpNode(data.un, level + delta, w); | |
| 1091 | } | |
| 1092 | }, | |
| 1093 | .generic_expr_one => { | |
| 1094 | try w.writeByteNTimes(' ', level + 1); | |
| 1095 | try w.writeAll("controlling:\n"); | |
| 1096 | try tree.dumpNode(data.bin.lhs, level + delta, w); | |
| 1097 | try w.writeByteNTimes(' ', level + 1); | |
| 1098 | try w.writeAll("chosen:\n"); | |
| 1099 | try tree.dumpNode(data.bin.rhs, level + delta, w); | |
| 1100 | }, | |
| 1101 | .generic_expr => { | |
| 1102 | const nodes = tree.data[data.range.start..data.range.end]; | |
| 1103 | try w.writeByteNTimes(' ', level + 1); | |
| 1104 | try w.writeAll("controlling:\n"); | |
| 1105 | try tree.dumpNode(nodes[0], level + delta, w); | |
| 1106 | try w.writeByteNTimes(' ', level + 1); | |
| 1107 | try w.writeAll("chosen:\n"); | |
| 1108 | try tree.dumpNode(nodes[1], level + delta, w); | |
| 1109 | try w.writeByteNTimes(' ', level + 1); | |
| 1110 | try w.writeAll("rest:\n"); | |
| 1111 | for (nodes[2..]) |expr| { | |
| 1112 | try tree.dumpNode(expr, level + delta, w); | |
| 1113 | } | |
| 1114 | }, | |
| 1115 | .generic_association_expr, .generic_default_expr, .stmt_expr, .imaginary_literal => { | |
| 1116 | try tree.dumpNode(data.un, level + delta, w); | |
| 1117 | }, | |
| 1118 | .array_to_pointer, | |
| 1119 | .lval_to_rval, | |
| 1120 | .function_to_pointer, | |
| 1121 | .pointer_to_bool, | |
| 1122 | .pointer_to_int, | |
| 1123 | .bool_to_int, | |
| 1124 | .bool_to_float, | |
| 1125 | .bool_to_pointer, | |
| 1126 | .int_to_bool, | |
| 1127 | .int_to_float, | |
| 1128 | .int_to_pointer, | |
| 1129 | .float_to_bool, | |
| 1130 | .float_to_int, | |
| 1131 | .int_cast, | |
| 1132 | .float_cast, | |
| 1133 | .to_void, | |
| 1134 | .qual_cast, | |
| 1135 | .null_to_pointer, | |
| 1136 | => { | |
| 1137 | try tree.dumpNode(data.un, level + delta, w); | |
| 1138 | }, | |
| 1139 | .array_filler_expr => { | |
| 1140 | try w.writeByteNTimes(' ', level + 1); | |
| 1141 | try w.writeAll("count: "); | |
| 1142 | util.setColor(LITERAL, w); | |
| 1143 | try w.print("{d}\n", .{data.int}); | |
| 1144 | util.setColor(.reset, w); | |
| 1145 | }, | |
| 1146 | .default_init_expr => {}, | |
| 1147 | } | |
| 1148 | } |
src/aro/Type.zig created+1676| ... | ... | @@ -0,0 +1,1676 @@ |
| 1 | const std = @import("std"); | |
| 2 | const Tree = @import("Tree.zig"); | |
| 3 | const TokenIndex = Tree.TokenIndex; | |
| 4 | const NodeIndex = Tree.NodeIndex; | |
| 5 | const Parser = @import("Parser.zig"); | |
| 6 | const Compilation = @import("Compilation.zig"); | |
| 7 | const Attribute = @import("Attribute.zig"); | |
| 8 | ||
| 9 | const Type = @This(); | |
| 10 | ||
| 11 | pub const Qualifiers = packed struct { | |
| 12 | @"const": bool = false, | |
| 13 | atomic: bool = false, | |
| 14 | @"volatile": bool = false, | |
| 15 | restrict: bool = false, | |
| 16 | ||
| 17 | // for function parameters only, stored here since it fits in the padding | |
| 18 | register: bool = false, | |
| 19 | ||
| 20 | pub fn any(quals: Qualifiers) bool { | |
| 21 | return quals.@"const" or quals.restrict or quals.@"volatile" or quals.atomic; | |
| 22 | } | |
| 23 | ||
| 24 | pub fn dump(quals: Qualifiers, w: anytype) !void { | |
| 25 | if (quals.@"const") try w.writeAll("const "); | |
| 26 | if (quals.atomic) try w.writeAll("_Atomic "); | |
| 27 | if (quals.@"volatile") try w.writeAll("volatile "); | |
| 28 | if (quals.restrict) try w.writeAll("restrict "); | |
| 29 | if (quals.register) try w.writeAll("register "); | |
| 30 | } | |
| 31 | ||
| 32 | /// Merge the const/volatile qualifiers, used by type resolution | |
| 33 | /// of the conditional operator | |
| 34 | pub fn mergeCV(a: Qualifiers, b: Qualifiers) Qualifiers { | |
| 35 | return .{ | |
| 36 | .@"const" = a.@"const" or b.@"const", | |
| 37 | .@"volatile" = a.@"volatile" or b.@"volatile", | |
| 38 | }; | |
| 39 | } | |
| 40 | ||
| 41 | /// Merge all qualifiers, used by typeof() | |
| 42 | fn mergeAll(a: Qualifiers, b: Qualifiers) Qualifiers { | |
| 43 | return .{ | |
| 44 | .@"const" = a.@"const" or b.@"const", | |
| 45 | .atomic = a.atomic or b.atomic, | |
| 46 | .@"volatile" = a.@"volatile" or b.@"volatile", | |
| 47 | .restrict = a.restrict or b.restrict, | |
| 48 | .register = a.register or b.register, | |
| 49 | }; | |
| 50 | } | |
| 51 | ||
| 52 | /// Checks if a has all the qualifiers of b | |
| 53 | pub fn hasQuals(a: Qualifiers, b: Qualifiers) bool { | |
| 54 | if (b.@"const" and !a.@"const") return false; | |
| 55 | if (b.@"volatile" and !a.@"volatile") return false; | |
| 56 | if (b.atomic and !a.atomic) return false; | |
| 57 | return true; | |
| 58 | } | |
| 59 | ||
| 60 | /// register is a storage class and not actually a qualifier | |
| 61 | /// so it is not preserved by typeof() | |
| 62 | pub fn inheritFromTypeof(quals: Qualifiers) Qualifiers { | |
| 63 | var res = quals; | |
| 64 | res.register = false; | |
| 65 | return res; | |
| 66 | } | |
| 67 | ||
| 68 | pub const Builder = struct { | |
| 69 | @"const": ?TokenIndex = null, | |
| 70 | atomic: ?TokenIndex = null, | |
| 71 | @"volatile": ?TokenIndex = null, | |
| 72 | restrict: ?TokenIndex = null, | |
| 73 | ||
| 74 | pub fn finish(b: Qualifiers.Builder, p: *Parser, ty: *Type) !void { | |
| 75 | if (ty.specifier != .pointer and b.restrict != null) { | |
| 76 | try p.errStr(.restrict_non_pointer, b.restrict.?, try p.typeStr(ty.*)); | |
| 77 | } | |
| 78 | if (b.atomic) |some| { | |
| 79 | if (ty.isArray()) try p.errStr(.atomic_array, some, try p.typeStr(ty.*)); | |
| 80 | if (ty.isFunc()) try p.errStr(.atomic_func, some, try p.typeStr(ty.*)); | |
| 81 | if (ty.hasIncompleteSize()) try p.errStr(.atomic_incomplete, some, try p.typeStr(ty.*)); | |
| 82 | } | |
| 83 | ||
| 84 | ty.qual = .{ | |
| 85 | .@"const" = b.@"const" != null, | |
| 86 | .atomic = b.atomic != null, | |
| 87 | .@"volatile" = b.@"volatile" != null, | |
| 88 | .restrict = b.restrict != null, | |
| 89 | }; | |
| 90 | } | |
| 91 | }; | |
| 92 | }; | |
| 93 | ||
| 94 | // TODO improve memory usage | |
| 95 | pub const Func = struct { | |
| 96 | return_type: Type, | |
| 97 | params: []Param, | |
| 98 | ||
| 99 | pub const Param = struct { | |
| 100 | name: []const u8, | |
| 101 | ty: Type, | |
| 102 | name_tok: TokenIndex, | |
| 103 | }; | |
| 104 | }; | |
| 105 | ||
| 106 | pub const Array = struct { | |
| 107 | len: u64, | |
| 108 | elem: Type, | |
| 109 | }; | |
| 110 | ||
| 111 | pub const Expr = struct { | |
| 112 | node: NodeIndex, | |
| 113 | ty: Type, | |
| 114 | }; | |
| 115 | ||
| 116 | pub const Attributed = struct { | |
| 117 | attributes: []Attribute, | |
| 118 | base: Type, | |
| 119 | ||
| 120 | fn create(allocator: std.mem.Allocator, base: Type, attributes: []const Attribute) !*Attributed { | |
| 121 | var attributed_type = try allocator.create(Attributed); | |
| 122 | errdefer allocator.destroy(attributed_type); | |
| 123 | ||
| 124 | const existing = base.getAttributes(); | |
| 125 | var all_attrs = try allocator.alloc(Attribute, existing.len + attributes.len); | |
| 126 | std.mem.copy(Attribute, all_attrs, existing); | |
| 127 | std.mem.copy(Attribute, all_attrs[existing.len..], attributes); | |
| 128 | ||
| 129 | attributed_type.* = .{ | |
| 130 | .attributes = all_attrs, | |
| 131 | .base = base, | |
| 132 | }; | |
| 133 | return attributed_type; | |
| 134 | } | |
| 135 | }; | |
| 136 | ||
| 137 | // TODO improve memory usage | |
| 138 | pub const Enum = struct { | |
| 139 | name: []const u8, | |
| 140 | tag_ty: Type, | |
| 141 | fields: []Field, | |
| 142 | ||
| 143 | pub const Field = struct { | |
| 144 | name: []const u8, | |
| 145 | ty: Type, | |
| 146 | name_tok: TokenIndex, | |
| 147 | node: NodeIndex, | |
| 148 | }; | |
| 149 | ||
| 150 | pub fn isIncomplete(e: Enum) bool { | |
| 151 | return e.fields.len == std.math.maxInt(usize); | |
| 152 | } | |
| 153 | ||
| 154 | pub fn create(allocator: std.mem.Allocator, name: []const u8) !*Enum { | |
| 155 | var e = try allocator.create(Enum); | |
| 156 | e.name = name; | |
| 157 | e.fields.len = std.math.maxInt(usize); | |
| 158 | return e; | |
| 159 | } | |
| 160 | }; | |
| 161 | ||
| 162 | // TODO improve memory usage | |
| 163 | pub const Record = struct { | |
| 164 | name: []const u8, | |
| 165 | fields: []Field, | |
| 166 | size: u64, | |
| 167 | alignment: u29, | |
| 168 | ||
| 169 | pub const Field = struct { | |
| 170 | name: []const u8, | |
| 171 | ty: Type, | |
| 172 | /// zero for anonymous fields | |
| 173 | name_tok: TokenIndex = 0, | |
| 174 | bit_width: u32 = 0, | |
| 175 | ||
| 176 | pub fn isAnonymousRecord(f: Field) bool { | |
| 177 | return f.name_tok == 0 and f.ty.isRecord(); | |
| 178 | } | |
| 179 | }; | |
| 180 | ||
| 181 | pub fn isIncomplete(r: Record) bool { | |
| 182 | return r.fields.len == std.math.maxInt(usize); | |
| 183 | } | |
| 184 | ||
| 185 | pub fn create(allocator: std.mem.Allocator, name: []const u8) !*Record { | |
| 186 | var r = try allocator.create(Record); | |
| 187 | r.name = name; | |
| 188 | r.fields.len = std.math.maxInt(usize); | |
| 189 | return r; | |
| 190 | } | |
| 191 | }; | |
| 192 | ||
| 193 | pub const Specifier = enum { | |
| 194 | void, | |
| 195 | bool, | |
| 196 | ||
| 197 | // integers | |
| 198 | char, | |
| 199 | schar, | |
| 200 | uchar, | |
| 201 | short, | |
| 202 | ushort, | |
| 203 | int, | |
| 204 | uint, | |
| 205 | long, | |
| 206 | ulong, | |
| 207 | long_long, | |
| 208 | ulong_long, | |
| 209 | ||
| 210 | // floating point numbers | |
| 211 | float, | |
| 212 | double, | |
| 213 | long_double, | |
| 214 | complex_float, | |
| 215 | complex_double, | |
| 216 | complex_long_double, | |
| 217 | ||
| 218 | // data.sub_type | |
| 219 | pointer, | |
| 220 | unspecified_variable_len_array, | |
| 221 | decayed_unspecified_variable_len_array, | |
| 222 | // data.func | |
| 223 | /// int foo(int bar, char baz) and int (void) | |
| 224 | func, | |
| 225 | /// int foo(int bar, char baz, ...) | |
| 226 | var_args_func, | |
| 227 | /// int foo(bar, baz) and int foo() | |
| 228 | /// is also var args, but we can give warnings about incorrect amounts of parameters | |
| 229 | old_style_func, | |
| 230 | ||
| 231 | // data.array | |
| 232 | array, | |
| 233 | decayed_array, | |
| 234 | static_array, | |
| 235 | decayed_static_array, | |
| 236 | incomplete_array, | |
| 237 | decayed_incomplete_array, | |
| 238 | // data.expr | |
| 239 | variable_len_array, | |
| 240 | decayed_variable_len_array, | |
| 241 | ||
| 242 | // data.record | |
| 243 | @"struct", | |
| 244 | @"union", | |
| 245 | ||
| 246 | // data.enum | |
| 247 | @"enum", | |
| 248 | ||
| 249 | /// typeof(type-name) | |
| 250 | typeof_type, | |
| 251 | /// decayed array created with typeof(type-name) | |
| 252 | decayed_typeof_type, | |
| 253 | ||
| 254 | /// typeof(expression) | |
| 255 | typeof_expr, | |
| 256 | /// decayed array created with typeof(expression) | |
| 257 | decayed_typeof_expr, | |
| 258 | ||
| 259 | /// data.attributed | |
| 260 | attributed, | |
| 261 | ||
| 262 | /// special type used to implement __builtin_va_start | |
| 263 | special_va_start, | |
| 264 | }; | |
| 265 | ||
| 266 | /// All fields of Type except data may be mutated | |
| 267 | data: union { | |
| 268 | sub_type: *Type, | |
| 269 | func: *Func, | |
| 270 | array: *Array, | |
| 271 | expr: *Expr, | |
| 272 | @"enum": *Enum, | |
| 273 | record: *Record, | |
| 274 | attributed: *Attributed, | |
| 275 | none: void, | |
| 276 | } = .{ .none = {} }, | |
| 277 | specifier: Specifier, | |
| 278 | qual: Qualifiers = .{}, | |
| 279 | ||
| 280 | /// Determine if type matches the given specifier, recursing into typeof | |
| 281 | /// types if necessary. | |
| 282 | pub fn is(ty: Type, specifier: Specifier) bool { | |
| 283 | std.debug.assert(specifier != .typeof_type and specifier != .typeof_expr); | |
| 284 | return ty.get(specifier) != null; | |
| 285 | } | |
| 286 | ||
| 287 | pub fn withAttributes(self: Type, allocator: std.mem.Allocator, attributes: []const Attribute) !Type { | |
| 288 | if (attributes.len == 0) return self; | |
| 289 | const attributed_type = try Type.Attributed.create(allocator, self, attributes); | |
| 290 | return Type{ .specifier = .attributed, .data = .{ .attributed = attributed_type } }; | |
| 291 | } | |
| 292 | ||
| 293 | pub fn isCallable(ty: Type) ?Type { | |
| 294 | return switch (ty.specifier) { | |
| 295 | .func, .var_args_func, .old_style_func => ty, | |
| 296 | .pointer => if (ty.data.sub_type.isFunc()) ty.data.sub_type.* else null, | |
| 297 | .typeof_type => ty.data.sub_type.isCallable(), | |
| 298 | .typeof_expr => ty.data.expr.ty.isCallable(), | |
| 299 | .attributed => ty.data.attributed.base.isCallable(), | |
| 300 | else => null, | |
| 301 | }; | |
| 302 | } | |
| 303 | ||
| 304 | pub fn isFunc(ty: Type) bool { | |
| 305 | return switch (ty.specifier) { | |
| 306 | .func, .var_args_func, .old_style_func => true, | |
| 307 | .typeof_type => ty.data.sub_type.isFunc(), | |
| 308 | .typeof_expr => ty.data.expr.ty.isFunc(), | |
| 309 | .attributed => ty.data.attributed.base.isFunc(), | |
| 310 | else => false, | |
| 311 | }; | |
| 312 | } | |
| 313 | ||
| 314 | pub fn isArray(ty: Type) bool { | |
| 315 | return switch (ty.specifier) { | |
| 316 | .array, .static_array, .incomplete_array, .variable_len_array, .unspecified_variable_len_array => true, | |
| 317 | .typeof_type => ty.data.sub_type.isArray(), | |
| 318 | .typeof_expr => ty.data.expr.ty.isArray(), | |
| 319 | .attributed => ty.data.attributed.base.isArray(), | |
| 320 | else => false, | |
| 321 | }; | |
| 322 | } | |
| 323 | ||
| 324 | pub fn isPtr(ty: Type) bool { | |
| 325 | return switch (ty.specifier) { | |
| 326 | .pointer, | |
| 327 | .decayed_array, | |
| 328 | .decayed_static_array, | |
| 329 | .decayed_incomplete_array, | |
| 330 | .decayed_variable_len_array, | |
| 331 | .decayed_unspecified_variable_len_array, | |
| 332 | .decayed_typeof_type, | |
| 333 | .decayed_typeof_expr, | |
| 334 | => true, | |
| 335 | .typeof_type => ty.data.sub_type.isPtr(), | |
| 336 | .typeof_expr => ty.data.expr.ty.isPtr(), | |
| 337 | .attributed => ty.data.attributed.base.isPtr(), | |
| 338 | else => false, | |
| 339 | }; | |
| 340 | } | |
| 341 | ||
| 342 | pub fn isInt(ty: Type) bool { | |
| 343 | return switch (ty.specifier) { | |
| 344 | .@"enum", .bool, .char, .schar, .uchar, .short, .ushort, .int, .uint, .long, .ulong, .long_long, .ulong_long => true, | |
| 345 | .typeof_type => ty.data.sub_type.isInt(), | |
| 346 | .typeof_expr => ty.data.expr.ty.isInt(), | |
| 347 | .attributed => ty.data.attributed.base.isInt(), | |
| 348 | else => false, | |
| 349 | }; | |
| 350 | } | |
| 351 | ||
| 352 | pub fn isFloat(ty: Type) bool { | |
| 353 | return switch (ty.specifier) { | |
| 354 | .float, .double, .long_double, .complex_float, .complex_double, .complex_long_double => true, | |
| 355 | .typeof_type => ty.data.sub_type.isFloat(), | |
| 356 | .typeof_expr => ty.data.expr.ty.isFloat(), | |
| 357 | .attributed => ty.data.attributed.base.isFloat(), | |
| 358 | else => false, | |
| 359 | }; | |
| 360 | } | |
| 361 | ||
| 362 | pub fn isReal(ty: Type) bool { | |
| 363 | return switch (ty.specifier) { | |
| 364 | .complex_float, .complex_double, .complex_long_double => false, | |
| 365 | .typeof_type => ty.data.sub_type.isReal(), | |
| 366 | .typeof_expr => ty.data.expr.ty.isReal(), | |
| 367 | .attributed => ty.data.attributed.base.isReal(), | |
| 368 | else => true, | |
| 369 | }; | |
| 370 | } | |
| 371 | ||
| 372 | pub fn isVoidStar(ty: Type) bool { | |
| 373 | return switch (ty.specifier) { | |
| 374 | .pointer => ty.data.sub_type.specifier == .void, | |
| 375 | .typeof_type => ty.data.sub_type.isVoidStar(), | |
| 376 | .typeof_expr => ty.data.expr.ty.isVoidStar(), | |
| 377 | .attributed => ty.data.attributed.base.isVoidStar(), | |
| 378 | else => false, | |
| 379 | }; | |
| 380 | } | |
| 381 | ||
| 382 | pub fn isTypeof(ty: Type) bool { | |
| 383 | return switch (ty.specifier) { | |
| 384 | .typeof_type, .typeof_expr, .decayed_typeof_type, .decayed_typeof_expr => true, | |
| 385 | else => false, | |
| 386 | }; | |
| 387 | } | |
| 388 | ||
| 389 | pub fn isConst(ty: Type) bool { | |
| 390 | return switch (ty.specifier) { | |
| 391 | .typeof_type, .decayed_typeof_type => ty.qual.@"const" or ty.data.sub_type.isConst(), | |
| 392 | .typeof_expr, .decayed_typeof_expr => ty.qual.@"const" or ty.data.expr.ty.isConst(), | |
| 393 | .attributed => ty.data.attributed.base.isConst(), | |
| 394 | else => ty.qual.@"const", | |
| 395 | }; | |
| 396 | } | |
| 397 | ||
| 398 | pub fn isUnsignedInt(ty: Type, comp: *Compilation) bool { | |
| 399 | return switch (ty.specifier) { | |
| 400 | .char => return getCharSignedness(comp) == .unsigned, | |
| 401 | .uchar, .ushort, .uint, .ulong, .ulong_long, .bool => true, | |
| 402 | .typeof_type => ty.data.sub_type.isUnsignedInt(comp), | |
| 403 | .typeof_expr => ty.data.expr.ty.isUnsignedInt(comp), | |
| 404 | .attributed => ty.data.attributed.base.isUnsignedInt(comp), | |
| 405 | else => false, | |
| 406 | }; | |
| 407 | } | |
| 408 | ||
| 409 | pub fn isEnumOrRecord(ty: Type) bool { | |
| 410 | return switch (ty.specifier) { | |
| 411 | .@"enum", .@"struct", .@"union" => true, | |
| 412 | .typeof_type => ty.data.sub_type.isEnumOrRecord(), | |
| 413 | .typeof_expr => ty.data.expr.ty.isEnumOrRecord(), | |
| 414 | .attributed => ty.data.attributed.base.isEnumOrRecord(), | |
| 415 | else => false, | |
| 416 | }; | |
| 417 | } | |
| 418 | ||
| 419 | pub fn isRecord(ty: Type) bool { | |
| 420 | return switch (ty.specifier) { | |
| 421 | .@"struct", .@"union" => true, | |
| 422 | .typeof_type => ty.data.sub_type.isRecord(), | |
| 423 | .typeof_expr => ty.data.expr.ty.isRecord(), | |
| 424 | .attributed => ty.data.attributed.base.isRecord(), | |
| 425 | else => false, | |
| 426 | }; | |
| 427 | } | |
| 428 | ||
| 429 | pub fn isAnonymousRecord(ty: Type) bool { | |
| 430 | return switch (ty.specifier) { | |
| 431 | // anonymous records can be recognized by their names which are in | |
| 432 | // the format "(anonymous TAG at path:line:col)". | |
| 433 | .@"struct", .@"union" => ty.data.record.name[0] == '(', | |
| 434 | .typeof_type => ty.data.sub_type.isAnonymousRecord(), | |
| 435 | .typeof_expr => ty.data.expr.ty.isAnonymousRecord(), | |
| 436 | .attributed => ty.data.attributed.base.isAnonymousRecord(), | |
| 437 | else => false, | |
| 438 | }; | |
| 439 | } | |
| 440 | ||
| 441 | pub fn elemType(ty: Type) Type { | |
| 442 | return switch (ty.specifier) { | |
| 443 | .pointer, .unspecified_variable_len_array, .decayed_unspecified_variable_len_array => ty.data.sub_type.*, | |
| 444 | .array, .static_array, .incomplete_array, .decayed_array, .decayed_static_array, .decayed_incomplete_array => ty.data.array.elem, | |
| 445 | .variable_len_array, .decayed_variable_len_array => ty.data.expr.ty, | |
| 446 | .typeof_type, .decayed_typeof_type, .typeof_expr, .decayed_typeof_expr => { | |
| 447 | const unwrapped = ty.canonicalize(.preserve_quals); | |
| 448 | var elem = unwrapped.elemType(); | |
| 449 | elem.qual = elem.qual.mergeAll(unwrapped.qual); | |
| 450 | return elem; | |
| 451 | }, | |
| 452 | .attributed => ty.data.attributed.base, | |
| 453 | else => unreachable, | |
| 454 | }; | |
| 455 | } | |
| 456 | ||
| 457 | pub fn returnType(ty: Type) Type { | |
| 458 | return switch (ty.specifier) { | |
| 459 | .func, .var_args_func, .old_style_func => ty.data.func.return_type, | |
| 460 | .typeof_type, .decayed_typeof_type => ty.data.sub_type.returnType(), | |
| 461 | .typeof_expr, .decayed_typeof_expr => ty.data.expr.ty.returnType(), | |
| 462 | .attributed => ty.data.attributed.base.returnType(), | |
| 463 | else => unreachable, | |
| 464 | }; | |
| 465 | } | |
| 466 | ||
| 467 | pub fn params(ty: Type) []Func.Param { | |
| 468 | return switch (ty.specifier) { | |
| 469 | .func, .var_args_func, .old_style_func => ty.data.func.params, | |
| 470 | .typeof_type, .decayed_typeof_type => ty.data.sub_type.params(), | |
| 471 | .typeof_expr, .decayed_typeof_expr => ty.data.expr.ty.params(), | |
| 472 | .attributed => ty.data.attributed.base.params(), | |
| 473 | else => unreachable, | |
| 474 | }; | |
| 475 | } | |
| 476 | ||
| 477 | pub fn arrayLen(ty: Type) ?usize { | |
| 478 | return switch (ty.specifier) { | |
| 479 | .array, .static_array, .decayed_array, .decayed_static_array => ty.data.array.len, | |
| 480 | .typeof_type, .decayed_typeof_type => ty.data.sub_type.arrayLen(), | |
| 481 | .typeof_expr, .decayed_typeof_expr => ty.data.expr.ty.arrayLen(), | |
| 482 | .attributed => ty.data.attributed.base.arrayLen(), | |
| 483 | else => null, | |
| 484 | }; | |
| 485 | } | |
| 486 | ||
| 487 | pub fn anyQual(ty: Type) bool { | |
| 488 | return switch (ty.specifier) { | |
| 489 | .typeof_type => ty.qual.any() or ty.data.sub_type.anyQual(), | |
| 490 | .typeof_expr => ty.qual.any() or ty.data.expr.ty.anyQual(), | |
| 491 | else => ty.qual.any(), | |
| 492 | }; | |
| 493 | } | |
| 494 | ||
| 495 | pub fn getAttributes(ty: Type) []const Attribute { | |
| 496 | return switch (ty.specifier) { | |
| 497 | .attributed => ty.data.attributed.attributes, | |
| 498 | .typeof_type, .decayed_typeof_type => ty.data.sub_type.getAttributes(), | |
| 499 | .typeof_expr, .decayed_typeof_expr => ty.data.expr.ty.getAttributes(), | |
| 500 | else => &.{}, | |
| 501 | }; | |
| 502 | } | |
| 503 | ||
| 504 | pub fn integerPromotion(ty: Type, comp: *Compilation) Type { | |
| 505 | var specifier = ty.specifier; | |
| 506 | if (specifier == .@"enum") { | |
| 507 | if (ty.hasIncompleteSize()) return .{ .specifier = .int }; | |
| 508 | specifier = ty.data.@"enum".tag_ty.specifier; | |
| 509 | } | |
| 510 | return .{ | |
| 511 | .specifier = switch (specifier) { | |
| 512 | .bool, .char, .schar, .uchar, .short => .int, | |
| 513 | .ushort => if (ty.sizeof(comp).? == sizeof(.{ .specifier = .int }, comp)) Specifier.uint else .int, | |
| 514 | .int => .int, | |
| 515 | .uint => .uint, | |
| 516 | .long => .long, | |
| 517 | .ulong => .ulong, | |
| 518 | .long_long => .long_long, | |
| 519 | .ulong_long => .ulong_long, | |
| 520 | .typeof_type => return ty.data.sub_type.integerPromotion(comp), | |
| 521 | .typeof_expr => return ty.data.expr.ty.integerPromotion(comp), | |
| 522 | .attributed => return ty.data.attributed.base.integerPromotion(comp), | |
| 523 | else => unreachable, // not an integer type | |
| 524 | }, | |
| 525 | }; | |
| 526 | } | |
| 527 | ||
| 528 | pub fn hasIncompleteSize(ty: Type) bool { | |
| 529 | return switch (ty.specifier) { | |
| 530 | .void, .incomplete_array => true, | |
| 531 | .@"enum" => ty.data.@"enum".isIncomplete(), | |
| 532 | .@"struct", .@"union" => ty.data.record.isIncomplete(), | |
| 533 | .array, .static_array => ty.data.array.elem.hasIncompleteSize(), | |
| 534 | .typeof_type => ty.data.sub_type.hasIncompleteSize(), | |
| 535 | .typeof_expr => ty.data.expr.ty.hasIncompleteSize(), | |
| 536 | .attributed => ty.data.attributed.base.hasIncompleteSize(), | |
| 537 | else => false, | |
| 538 | }; | |
| 539 | } | |
| 540 | ||
| 541 | pub fn hasUnboundVLA(ty: Type) bool { | |
| 542 | var cur = ty; | |
| 543 | while (true) { | |
| 544 | switch (cur.specifier) { | |
| 545 | .unspecified_variable_len_array, | |
| 546 | .decayed_unspecified_variable_len_array, | |
| 547 | => return true, | |
| 548 | .array, | |
| 549 | .static_array, | |
| 550 | .incomplete_array, | |
| 551 | .variable_len_array, | |
| 552 | .decayed_array, | |
| 553 | .decayed_static_array, | |
| 554 | .decayed_incomplete_array, | |
| 555 | .decayed_variable_len_array, | |
| 556 | => cur = cur.elemType(), | |
| 557 | .typeof_type, .decayed_typeof_type => cur = cur.data.sub_type.*, | |
| 558 | .typeof_expr, .decayed_typeof_expr => cur = cur.data.expr.ty, | |
| 559 | .attributed => cur = cur.data.attributed.base, | |
| 560 | else => return false, | |
| 561 | } | |
| 562 | } | |
| 563 | } | |
| 564 | ||
| 565 | pub fn hasField(ty: Type, name: []const u8) bool { | |
| 566 | switch (ty.specifier) { | |
| 567 | .@"struct" => { | |
| 568 | std.debug.assert(!ty.data.record.isIncomplete()); | |
| 569 | for (ty.data.record.fields) |f| { | |
| 570 | if (f.isAnonymousRecord() and f.ty.hasField(name)) return true; | |
| 571 | if (std.mem.eql(u8, name, f.name)) return true; | |
| 572 | } | |
| 573 | }, | |
| 574 | .@"union" => { | |
| 575 | std.debug.assert(!ty.data.record.isIncomplete()); | |
| 576 | for (ty.data.record.fields) |f| { | |
| 577 | if (f.isAnonymousRecord() and f.ty.hasField(name)) return true; | |
| 578 | if (std.mem.eql(u8, name, f.name)) return true; | |
| 579 | } | |
| 580 | }, | |
| 581 | .typeof_type => return ty.data.sub_type.hasField(name), | |
| 582 | .typeof_expr => return ty.data.expr.ty.hasField(name), | |
| 583 | .attributed => return ty.data.attributed.base.hasField(name), | |
| 584 | else => unreachable, | |
| 585 | } | |
| 586 | return false; | |
| 587 | } | |
| 588 | ||
| 589 | pub fn getCharSignedness(comp: *Compilation) std.builtin.Signedness { | |
| 590 | switch (comp.target.cpu.arch) { | |
| 591 | .aarch64, | |
| 592 | .aarch64_32, | |
| 593 | .aarch64_be, | |
| 594 | .arm, | |
| 595 | .armeb, | |
| 596 | .thumb, | |
| 597 | .thumbeb, | |
| 598 | => return if (comp.target.os.tag.isDarwin() or comp.target.os.tag == .windows) .signed else .unsigned, | |
| 599 | .powerpc, .powerpc64 => return if (comp.target.os.tag.isDarwin()) .signed else .unsigned, | |
| 600 | .powerpc64le, | |
| 601 | .s390x, | |
| 602 | .xcore, | |
| 603 | .arc, | |
| 604 | => return .unsigned, | |
| 605 | else => return .signed, | |
| 606 | } | |
| 607 | } | |
| 608 | ||
| 609 | /// Size of type as reported by sizeof | |
| 610 | pub fn sizeof(ty: Type, comp: *Compilation) ?u64 { | |
| 611 | // TODO get target from compilation | |
| 612 | return switch (ty.specifier) { | |
| 613 | .variable_len_array, .unspecified_variable_len_array, .incomplete_array => return null, | |
| 614 | .func, .var_args_func, .old_style_func, .void, .bool => 1, | |
| 615 | .char, .schar, .uchar => 1, | |
| 616 | .short, .ushort => 2, | |
| 617 | .int, .uint => 4, | |
| 618 | .long, .ulong => switch (comp.target.os.tag) { | |
| 619 | .linux, | |
| 620 | .macos, | |
| 621 | .freebsd, | |
| 622 | .netbsd, | |
| 623 | .dragonfly, | |
| 624 | .openbsd, | |
| 625 | .wasi, | |
| 626 | .emscripten, | |
| 627 | => comp.target.cpu.arch.ptrBitWidth() >> 3, | |
| 628 | .windows, .uefi => 4, | |
| 629 | else => 4, | |
| 630 | }, | |
| 631 | .long_long, .ulong_long => 8, | |
| 632 | .float => 4, | |
| 633 | .double => 8, | |
| 634 | .long_double => 16, | |
| 635 | .complex_float => 8, | |
| 636 | .complex_double => 16, | |
| 637 | .complex_long_double => 32, | |
| 638 | .pointer, | |
| 639 | .decayed_array, | |
| 640 | .decayed_static_array, | |
| 641 | .decayed_incomplete_array, | |
| 642 | .decayed_variable_len_array, | |
| 643 | .decayed_unspecified_variable_len_array, | |
| 644 | .decayed_typeof_type, | |
| 645 | .decayed_typeof_expr, | |
| 646 | .static_array, | |
| 647 | => comp.target.cpu.arch.ptrBitWidth() >> 3, | |
| 648 | .array => ty.data.array.elem.sizeof(comp).? * ty.data.array.len, | |
| 649 | .@"struct", .@"union" => if (ty.data.record.isIncomplete()) null else ty.data.record.size, | |
| 650 | .@"enum" => if (ty.data.@"enum".isIncomplete()) null else ty.data.@"enum".tag_ty.sizeof(comp), | |
| 651 | .typeof_type => ty.data.sub_type.sizeof(comp), | |
| 652 | .typeof_expr => ty.data.expr.ty.sizeof(comp), | |
| 653 | .attributed => ty.data.attributed.base.sizeof(comp), | |
| 654 | else => unreachable, | |
| 655 | }; | |
| 656 | } | |
| 657 | ||
| 658 | pub fn bitSizeof(ty: Type, comp: *Compilation) ?u64 { | |
| 659 | return switch (ty.specifier) { | |
| 660 | .bool => 1, | |
| 661 | .typeof_type, .decayed_typeof_type => ty.data.sub_type.bitSizeof(comp), | |
| 662 | .typeof_expr, .decayed_typeof_expr => ty.data.expr.ty.bitSizeof(comp), | |
| 663 | .attributed => ty.data.attributed.base.bitSizeof(comp), | |
| 664 | else => 8 * (ty.sizeof(comp) orelse return null), | |
| 665 | }; | |
| 666 | } | |
| 667 | ||
| 668 | /// Get the alignment of a type | |
| 669 | pub fn alignof(ty: Type, comp: *const Compilation) u29 { | |
| 670 | if (ty.requestedAlignment(comp)) |requested| return requested; | |
| 671 | ||
| 672 | // TODO get target from compilation | |
| 673 | return switch (ty.specifier) { | |
| 674 | .unspecified_variable_len_array => unreachable, // must be bound in function definition | |
| 675 | .variable_len_array, .incomplete_array => ty.elemType().alignof(comp), | |
| 676 | .func, .var_args_func, .old_style_func => 4, // TODO check target | |
| 677 | .char, .schar, .uchar, .void, .bool => 1, | |
| 678 | .short, .ushort => 2, | |
| 679 | .int, .uint => 4, | |
| 680 | .long, .ulong => switch (comp.target.os.tag) { | |
| 681 | .linux, | |
| 682 | .macos, | |
| 683 | .freebsd, | |
| 684 | .netbsd, | |
| 685 | .dragonfly, | |
| 686 | .openbsd, | |
| 687 | .wasi, | |
| 688 | .emscripten, | |
| 689 | => comp.target.cpu.arch.ptrBitWidth() >> 3, | |
| 690 | .windows, .uefi => 4, | |
| 691 | else => 4, | |
| 692 | }, | |
| 693 | .long_long, .ulong_long => 8, | |
| 694 | .float, .complex_float => 4, | |
| 695 | .double, .complex_double => 8, | |
| 696 | .long_double, .complex_long_double => 16, | |
| 697 | .pointer, | |
| 698 | .decayed_array, | |
| 699 | .decayed_static_array, | |
| 700 | .decayed_incomplete_array, | |
| 701 | .decayed_variable_len_array, | |
| 702 | .decayed_unspecified_variable_len_array, | |
| 703 | .static_array, | |
| 704 | => comp.target.cpu.arch.ptrBitWidth() >> 3, | |
| 705 | .array => ty.data.array.elem.alignof(comp), | |
| 706 | .@"struct", .@"union" => if (ty.data.record.isIncomplete()) 0 else ty.data.record.alignment, | |
| 707 | .@"enum" => if (ty.data.@"enum".isIncomplete()) 0 else ty.data.@"enum".tag_ty.alignof(comp), | |
| 708 | .typeof_type, .decayed_typeof_type => ty.data.sub_type.alignof(comp), | |
| 709 | .typeof_expr, .decayed_typeof_expr => ty.data.expr.ty.alignof(comp), | |
| 710 | .attributed => ty.data.attributed.base.alignof(comp), | |
| 711 | else => unreachable, | |
| 712 | }; | |
| 713 | } | |
| 714 | ||
| 715 | /// Canonicalize a possibly-typeof() type. If the type is not a typeof() type, simply | |
| 716 | /// return it. Otherwise, determine the actual qualified type. | |
| 717 | /// The `qual_handling` parameter can be used to return the full set of qualifiers | |
| 718 | /// added by typeof() operations, which is useful when determining the elemType of | |
| 719 | /// arrays and pointers. | |
| 720 | pub fn canonicalize(ty: Type, qual_handling: enum { standard, preserve_quals }) Type { | |
| 721 | var cur = ty; | |
| 722 | if (cur.specifier == .attributed) cur = cur.data.attributed.base; | |
| 723 | if (!cur.isTypeof()) return cur; | |
| 724 | ||
| 725 | var qual = cur.qual; | |
| 726 | while (true) { | |
| 727 | switch (cur.specifier) { | |
| 728 | .typeof_type => cur = cur.data.sub_type.*, | |
| 729 | .typeof_expr => cur = cur.data.expr.ty, | |
| 730 | .decayed_typeof_type => { | |
| 731 | cur = cur.data.sub_type.*; | |
| 732 | cur.decayArray(); | |
| 733 | }, | |
| 734 | .decayed_typeof_expr => { | |
| 735 | cur = cur.data.expr.ty; | |
| 736 | cur.decayArray(); | |
| 737 | }, | |
| 738 | else => break, | |
| 739 | } | |
| 740 | qual = qual.mergeAll(cur.qual); | |
| 741 | } | |
| 742 | if ((cur.isArray() or cur.isPtr()) and qual_handling == .standard) { | |
| 743 | cur.qual = .{}; | |
| 744 | } else { | |
| 745 | cur.qual = qual; | |
| 746 | } | |
| 747 | return cur; | |
| 748 | } | |
| 749 | ||
| 750 | pub fn get(ty: *const Type, specifier: Specifier) ?*const Type { | |
| 751 | std.debug.assert(specifier != .typeof_type and specifier != .typeof_expr); | |
| 752 | return switch (ty.specifier) { | |
| 753 | .typeof_type => ty.data.sub_type.get(specifier), | |
| 754 | .typeof_expr => ty.data.expr.ty.get(specifier), | |
| 755 | .attributed => ty.data.attributed.base.get(specifier), | |
| 756 | else => if (ty.specifier == specifier) ty else null, | |
| 757 | }; | |
| 758 | } | |
| 759 | ||
| 760 | fn requestedAlignment(ty: Type, comp: *const Compilation) ?u29 { | |
| 761 | return switch (ty.specifier) { | |
| 762 | .typeof_type, .decayed_typeof_type => ty.data.sub_type.requestedAlignment(comp), | |
| 763 | .typeof_expr, .decayed_typeof_expr => ty.data.expr.ty.requestedAlignment(comp), | |
| 764 | .attributed => { | |
| 765 | var max_requested: ?u29 = null; | |
| 766 | for (ty.data.attributed.attributes) |attribute| { | |
| 767 | if (attribute.tag != .aligned) continue; | |
| 768 | const requested = if (attribute.args.aligned.alignment) |alignment| | |
| 769 | alignment.requested | |
| 770 | else | |
| 771 | comp.defaultAlignment(); | |
| 772 | ||
| 773 | if (max_requested == null or max_requested.? < requested) { | |
| 774 | max_requested = requested; | |
| 775 | } | |
| 776 | } | |
| 777 | return max_requested; | |
| 778 | }, | |
| 779 | else => null, | |
| 780 | }; | |
| 781 | } | |
| 782 | ||
| 783 | pub fn eql(a_param: Type, b_param: Type, comp: *const Compilation, check_qualifiers: bool) bool { | |
| 784 | const a = a_param.canonicalize(.standard); | |
| 785 | const b = b_param.canonicalize(.standard); | |
| 786 | ||
| 787 | if (a.alignof(comp) != b.alignof(comp)) return false; | |
| 788 | if (a.isPtr()) { | |
| 789 | if (!b.isPtr()) return false; | |
| 790 | } else if (a.isFunc()) { | |
| 791 | if (!b.isFunc()) return false; | |
| 792 | } else if (a.isArray()) { | |
| 793 | if (!b.isArray()) return false; | |
| 794 | } else if (a.specifier != b.specifier) return false; | |
| 795 | ||
| 796 | if (a.qual.atomic != b.qual.atomic) return false; | |
| 797 | if (check_qualifiers) { | |
| 798 | if (a.qual.@"const" != b.qual.@"const") return false; | |
| 799 | if (a.qual.@"volatile" != b.qual.@"volatile") return false; | |
| 800 | } | |
| 801 | ||
| 802 | switch (a.specifier) { | |
| 803 | .pointer, | |
| 804 | .decayed_array, | |
| 805 | .decayed_static_array, | |
| 806 | .decayed_incomplete_array, | |
| 807 | .decayed_variable_len_array, | |
| 808 | .decayed_unspecified_variable_len_array, | |
| 809 | => if (!a_param.elemType().eql(b_param.elemType(), comp, check_qualifiers)) return false, | |
| 810 | ||
| 811 | .func, | |
| 812 | .var_args_func, | |
| 813 | .old_style_func, | |
| 814 | => { | |
| 815 | // TODO validate this | |
| 816 | if (a.data.func.params.len != b.data.func.params.len) return false; | |
| 817 | // return type cannot have qualifiers | |
| 818 | if (!a.returnType().eql(b.returnType(), comp, false)) return false; | |
| 819 | for (a.data.func.params) |param, i| { | |
| 820 | var a_unqual = param.ty; | |
| 821 | a_unqual.qual.@"const" = false; | |
| 822 | a_unqual.qual.@"volatile" = false; | |
| 823 | var b_unqual = b.data.func.params[i].ty; | |
| 824 | b_unqual.qual.@"const" = false; | |
| 825 | b_unqual.qual.@"volatile" = false; | |
| 826 | if (!a_unqual.eql(b_unqual, comp, check_qualifiers)) return false; | |
| 827 | } | |
| 828 | }, | |
| 829 | ||
| 830 | .array, | |
| 831 | .static_array, | |
| 832 | .incomplete_array, | |
| 833 | => { | |
| 834 | if (!std.meta.eql(a.arrayLen(), b.arrayLen())) return false; | |
| 835 | if (!a.elemType().eql(b.elemType(), comp, check_qualifiers)) return false; | |
| 836 | }, | |
| 837 | .variable_len_array => if (!a.elemType().eql(b.elemType(), comp, check_qualifiers)) return false, | |
| 838 | ||
| 839 | .@"struct", .@"union" => if (a.data.record != b.data.record) return false, | |
| 840 | .@"enum" => if (a.data.@"enum" != b.data.@"enum") return false, | |
| 841 | ||
| 842 | else => {}, | |
| 843 | } | |
| 844 | return true; | |
| 845 | } | |
| 846 | ||
| 847 | /// Decays an array to a pointer | |
| 848 | pub fn decayArray(ty: *Type) void { | |
| 849 | // the decayed array type is the current specifier +1 | |
| 850 | ty.specifier = @intToEnum(Type.Specifier, @enumToInt(ty.specifier) + 1); | |
| 851 | } | |
| 852 | ||
| 853 | pub fn combine(inner: *Type, outer: Type, p: *Parser, source_tok: TokenIndex) Parser.Error!void { | |
| 854 | switch (inner.specifier) { | |
| 855 | .pointer => return inner.data.sub_type.combine(outer, p, source_tok), | |
| 856 | .unspecified_variable_len_array => { | |
| 857 | try inner.data.sub_type.combine(outer, p, source_tok); | |
| 858 | }, | |
| 859 | .variable_len_array => { | |
| 860 | try inner.data.expr.ty.combine(outer, p, source_tok); | |
| 861 | }, | |
| 862 | .array, .static_array, .incomplete_array => { | |
| 863 | try inner.data.array.elem.combine(outer, p, source_tok); | |
| 864 | }, | |
| 865 | .func, .var_args_func, .old_style_func => { | |
| 866 | try inner.data.func.return_type.combine(outer, p, source_tok); | |
| 867 | }, | |
| 868 | .decayed_array, | |
| 869 | .decayed_static_array, | |
| 870 | .decayed_incomplete_array, | |
| 871 | .decayed_variable_len_array, | |
| 872 | .decayed_unspecified_variable_len_array, | |
| 873 | .decayed_typeof_type, | |
| 874 | .decayed_typeof_expr, | |
| 875 | => unreachable, // type should not be able to decay before being combined | |
| 876 | else => inner.* = outer, | |
| 877 | } | |
| 878 | } | |
| 879 | ||
| 880 | pub fn validateCombinedType(ty: Type, p: *Parser, source_tok: TokenIndex) Parser.Error!void { | |
| 881 | switch (ty.specifier) { | |
| 882 | .pointer => return ty.data.sub_type.validateCombinedType(p, source_tok), | |
| 883 | .unspecified_variable_len_array, | |
| 884 | .variable_len_array, | |
| 885 | .array, | |
| 886 | .static_array, | |
| 887 | .incomplete_array, | |
| 888 | => { | |
| 889 | const elem_ty = ty.elemType(); | |
| 890 | if (elem_ty.hasIncompleteSize()) { | |
| 891 | try p.errStr(.array_incomplete_elem, source_tok, try p.typeStr(elem_ty)); | |
| 892 | return error.ParsingFailed; | |
| 893 | } | |
| 894 | if (elem_ty.isFunc()) { | |
| 895 | try p.errTok(.array_func_elem, source_tok); | |
| 896 | return error.ParsingFailed; | |
| 897 | } | |
| 898 | if (elem_ty.specifier == .static_array and elem_ty.isArray()) { | |
| 899 | try p.errTok(.static_non_outermost_array, source_tok); | |
| 900 | } | |
| 901 | if (elem_ty.anyQual() and elem_ty.isArray()) { | |
| 902 | try p.errTok(.qualifier_non_outermost_array, source_tok); | |
| 903 | } | |
| 904 | }, | |
| 905 | .func, .var_args_func, .old_style_func => { | |
| 906 | const ret_ty = &ty.data.func.return_type; | |
| 907 | if (ret_ty.isArray()) try p.errTok(.func_cannot_return_array, source_tok); | |
| 908 | if (ret_ty.isFunc()) try p.errTok(.func_cannot_return_func, source_tok); | |
| 909 | if (ret_ty.qual.@"const") { | |
| 910 | try p.errStr(.qual_on_ret_type, source_tok, "const"); | |
| 911 | ret_ty.qual.@"const" = false; | |
| 912 | } | |
| 913 | if (ret_ty.qual.@"volatile") { | |
| 914 | try p.errStr(.qual_on_ret_type, source_tok, "volatile"); | |
| 915 | ret_ty.qual.@"volatile" = false; | |
| 916 | } | |
| 917 | if (ret_ty.qual.atomic) { | |
| 918 | try p.errStr(.qual_on_ret_type, source_tok, "atomic"); | |
| 919 | ret_ty.qual.atomic = false; | |
| 920 | } | |
| 921 | }, | |
| 922 | .typeof_type, .decayed_typeof_type => return ty.data.sub_type.validateCombinedType(p, source_tok), | |
| 923 | .typeof_expr, .decayed_typeof_expr => return ty.data.expr.ty.validateCombinedType(p, source_tok), | |
| 924 | .attributed => return ty.data.attributed.base.validateCombinedType(p, source_tok), | |
| 925 | else => {}, | |
| 926 | } | |
| 927 | } | |
| 928 | ||
| 929 | /// An unfinished Type | |
| 930 | pub const Builder = struct { | |
| 931 | typedef: ?struct { | |
| 932 | tok: TokenIndex, | |
| 933 | ty: Type, | |
| 934 | } = null, | |
| 935 | specifier: Builder.Specifier = .none, | |
| 936 | qual: Qualifiers.Builder = .{}, | |
| 937 | typeof: ?Type = null, | |
| 938 | /// When true an error is returned instead of adding a diagnostic message. | |
| 939 | /// Used for trying to combine typedef types. | |
| 940 | error_on_invalid: bool = false, | |
| 941 | ||
| 942 | pub const Specifier = union(enum) { | |
| 943 | none, | |
| 944 | void, | |
| 945 | bool, | |
| 946 | char, | |
| 947 | schar, | |
| 948 | uchar, | |
| 949 | ||
| 950 | unsigned, | |
| 951 | signed, | |
| 952 | short, | |
| 953 | sshort, | |
| 954 | ushort, | |
| 955 | short_int, | |
| 956 | sshort_int, | |
| 957 | ushort_int, | |
| 958 | int, | |
| 959 | sint, | |
| 960 | uint, | |
| 961 | long, | |
| 962 | slong, | |
| 963 | ulong, | |
| 964 | long_int, | |
| 965 | slong_int, | |
| 966 | ulong_int, | |
| 967 | long_long, | |
| 968 | slong_long, | |
| 969 | ulong_long, | |
| 970 | long_long_int, | |
| 971 | slong_long_int, | |
| 972 | ulong_long_int, | |
| 973 | ||
| 974 | float, | |
| 975 | double, | |
| 976 | long_double, | |
| 977 | complex, | |
| 978 | complex_long, | |
| 979 | complex_float, | |
| 980 | complex_double, | |
| 981 | complex_long_double, | |
| 982 | ||
| 983 | pointer: *Type, | |
| 984 | unspecified_variable_len_array: *Type, | |
| 985 | decayed_unspecified_variable_len_array: *Type, | |
| 986 | func: *Func, | |
| 987 | var_args_func: *Func, | |
| 988 | old_style_func: *Func, | |
| 989 | array: *Array, | |
| 990 | decayed_array: *Array, | |
| 991 | static_array: *Array, | |
| 992 | decayed_static_array: *Array, | |
| 993 | incomplete_array: *Array, | |
| 994 | decayed_incomplete_array: *Array, | |
| 995 | variable_len_array: *Expr, | |
| 996 | decayed_variable_len_array: *Expr, | |
| 997 | @"struct": *Record, | |
| 998 | @"union": *Record, | |
| 999 | @"enum": *Enum, | |
| 1000 | typeof_type: *Type, | |
| 1001 | decayed_typeof_type: *Type, | |
| 1002 | typeof_expr: *Expr, | |
| 1003 | decayed_typeof_expr: *Expr, | |
| 1004 | ||
| 1005 | attributed: *Attributed, | |
| 1006 | ||
| 1007 | pub fn str(spec: Builder.Specifier) ?[]const u8 { | |
| 1008 | return switch (spec) { | |
| 1009 | .none => unreachable, | |
| 1010 | .void => "void", | |
| 1011 | .bool => "_Bool", | |
| 1012 | .char => "char", | |
| 1013 | .schar => "signed char", | |
| 1014 | .uchar => "unsigned char", | |
| 1015 | .unsigned => "unsigned", | |
| 1016 | .signed => "signed", | |
| 1017 | .short => "short", | |
| 1018 | .ushort => "unsigned short", | |
| 1019 | .sshort => "signed short", | |
| 1020 | .short_int => "short int", | |
| 1021 | .sshort_int => "signed short int", | |
| 1022 | .ushort_int => "unsigned short int", | |
| 1023 | .int => "int", | |
| 1024 | .sint => "signed int", | |
| 1025 | .uint => "unsigned int", | |
| 1026 | .long => "long", | |
| 1027 | .slong => "signed long", | |
| 1028 | .ulong => "unsigned long", | |
| 1029 | .long_int => "long int", | |
| 1030 | .slong_int => "signed long int", | |
| 1031 | .ulong_int => "unsigned long int", | |
| 1032 | .long_long => "long long", | |
| 1033 | .slong_long => "signed long long", | |
| 1034 | .ulong_long => "unsigned long long", | |
| 1035 | .long_long_int => "long long int", | |
| 1036 | .slong_long_int => "signed long long int", | |
| 1037 | .ulong_long_int => "unsigned long long int", | |
| 1038 | ||
| 1039 | .float => "float", | |
| 1040 | .double => "double", | |
| 1041 | .long_double => "long double", | |
| 1042 | .complex => "_Complex", | |
| 1043 | .complex_long => "_Complex long", | |
| 1044 | .complex_float => "_Complex float", | |
| 1045 | .complex_double => "_Complex double", | |
| 1046 | .complex_long_double => "_Complex long double", | |
| 1047 | ||
| 1048 | .attributed => |attributed| Builder.fromType(attributed.base).str(), | |
| 1049 | ||
| 1050 | else => null, | |
| 1051 | }; | |
| 1052 | } | |
| 1053 | }; | |
| 1054 | ||
| 1055 | pub fn finish(b: Builder, p: *Parser, attr_buf_start: usize) Parser.Error!Type { | |
| 1056 | var ty: Type = .{ .specifier = undefined }; | |
| 1057 | switch (b.specifier) { | |
| 1058 | .none => { | |
| 1059 | if (b.typeof) |typeof| { | |
| 1060 | ty = typeof; | |
| 1061 | } else { | |
| 1062 | ty.specifier = .int; | |
| 1063 | try p.err(.missing_type_specifier); | |
| 1064 | } | |
| 1065 | }, | |
| 1066 | .void => ty.specifier = .void, | |
| 1067 | .bool => ty.specifier = .bool, | |
| 1068 | .char => ty.specifier = .char, | |
| 1069 | .schar => ty.specifier = .schar, | |
| 1070 | .uchar => ty.specifier = .uchar, | |
| 1071 | ||
| 1072 | .unsigned => ty.specifier = .uint, | |
| 1073 | .signed => ty.specifier = .int, | |
| 1074 | .short_int, .sshort_int, .short, .sshort => ty.specifier = .short, | |
| 1075 | .ushort, .ushort_int => ty.specifier = .ushort, | |
| 1076 | .int, .sint => ty.specifier = .int, | |
| 1077 | .uint => ty.specifier = .uint, | |
| 1078 | .long, .slong, .long_int, .slong_int => ty.specifier = .long, | |
| 1079 | .ulong, .ulong_int => ty.specifier = .ulong, | |
| 1080 | .long_long, .slong_long, .long_long_int, .slong_long_int => ty.specifier = .long_long, | |
| 1081 | .ulong_long, .ulong_long_int => ty.specifier = .ulong_long, | |
| 1082 | ||
| 1083 | .float => ty.specifier = .float, | |
| 1084 | .double => ty.specifier = .double, | |
| 1085 | .long_double => ty.specifier = .long_double, | |
| 1086 | .complex_float => ty.specifier = .complex_float, | |
| 1087 | .complex_double => ty.specifier = .complex_double, | |
| 1088 | .complex_long_double => ty.specifier = .complex_long_double, | |
| 1089 | .complex => { | |
| 1090 | try p.errTok(.plain_complex, p.tok_i - 1); | |
| 1091 | ty.specifier = .complex_double; | |
| 1092 | }, | |
| 1093 | .complex_long => { | |
| 1094 | try p.errExtra(.type_is_invalid, p.tok_i, .{ .str = b.specifier.str().? }); | |
| 1095 | return error.ParsingFailed; | |
| 1096 | }, | |
| 1097 | ||
| 1098 | .pointer => |data| { | |
| 1099 | ty.specifier = .pointer; | |
| 1100 | ty.data = .{ .sub_type = data }; | |
| 1101 | }, | |
| 1102 | .unspecified_variable_len_array => |data| { | |
| 1103 | ty.specifier = .unspecified_variable_len_array; | |
| 1104 | ty.data = .{ .sub_type = data }; | |
| 1105 | }, | |
| 1106 | .decayed_unspecified_variable_len_array => |data| { | |
| 1107 | ty.specifier = .decayed_unspecified_variable_len_array; | |
| 1108 | ty.data = .{ .sub_type = data }; | |
| 1109 | }, | |
| 1110 | .func => |data| { | |
| 1111 | ty.specifier = .func; | |
| 1112 | ty.data = .{ .func = data }; | |
| 1113 | }, | |
| 1114 | .var_args_func => |data| { | |
| 1115 | ty.specifier = .var_args_func; | |
| 1116 | ty.data = .{ .func = data }; | |
| 1117 | }, | |
| 1118 | .old_style_func => |data| { | |
| 1119 | ty.specifier = .old_style_func; | |
| 1120 | ty.data = .{ .func = data }; | |
| 1121 | }, | |
| 1122 | .array => |data| { | |
| 1123 | ty.specifier = .array; | |
| 1124 | ty.data = .{ .array = data }; | |
| 1125 | }, | |
| 1126 | .decayed_array => |data| { | |
| 1127 | ty.specifier = .decayed_array; | |
| 1128 | ty.data = .{ .array = data }; | |
| 1129 | }, | |
| 1130 | .static_array => |data| { | |
| 1131 | ty.specifier = .static_array; | |
| 1132 | ty.data = .{ .array = data }; | |
| 1133 | }, | |
| 1134 | .decayed_static_array => |data| { | |
| 1135 | ty.specifier = .decayed_static_array; | |
| 1136 | ty.data = .{ .array = data }; | |
| 1137 | }, | |
| 1138 | .incomplete_array => |data| { | |
| 1139 | ty.specifier = .incomplete_array; | |
| 1140 | ty.data = .{ .array = data }; | |
| 1141 | }, | |
| 1142 | .decayed_incomplete_array => |data| { | |
| 1143 | ty.specifier = .decayed_incomplete_array; | |
| 1144 | ty.data = .{ .array = data }; | |
| 1145 | }, | |
| 1146 | .variable_len_array => |data| { | |
| 1147 | ty.specifier = .variable_len_array; | |
| 1148 | ty.data = .{ .expr = data }; | |
| 1149 | }, | |
| 1150 | .decayed_variable_len_array => |data| { | |
| 1151 | ty.specifier = .decayed_variable_len_array; | |
| 1152 | ty.data = .{ .expr = data }; | |
| 1153 | }, | |
| 1154 | .@"struct" => |data| { | |
| 1155 | ty.specifier = .@"struct"; | |
| 1156 | ty.data = .{ .record = data }; | |
| 1157 | }, | |
| 1158 | .@"union" => |data| { | |
| 1159 | ty.specifier = .@"union"; | |
| 1160 | ty.data = .{ .record = data }; | |
| 1161 | }, | |
| 1162 | .@"enum" => |data| { | |
| 1163 | ty.specifier = .@"enum"; | |
| 1164 | ty.data = .{ .@"enum" = data }; | |
| 1165 | }, | |
| 1166 | .typeof_type => |data| { | |
| 1167 | ty.specifier = .typeof_type; | |
| 1168 | ty.data = .{ .sub_type = data }; | |
| 1169 | }, | |
| 1170 | .decayed_typeof_type => |data| { | |
| 1171 | ty.specifier = .decayed_typeof_type; | |
| 1172 | ty.data = .{ .sub_type = data }; | |
| 1173 | }, | |
| 1174 | .typeof_expr => |data| { | |
| 1175 | ty.specifier = .typeof_expr; | |
| 1176 | ty.data = .{ .expr = data }; | |
| 1177 | }, | |
| 1178 | .decayed_typeof_expr => |data| { | |
| 1179 | ty.specifier = .decayed_typeof_expr; | |
| 1180 | ty.data = .{ .expr = data }; | |
| 1181 | }, | |
| 1182 | .attributed => |data| { | |
| 1183 | ty.specifier = .attributed; | |
| 1184 | ty.data = .{ .attributed = data }; | |
| 1185 | }, | |
| 1186 | } | |
| 1187 | try b.qual.finish(p, &ty); | |
| 1188 | ||
| 1189 | return p.withAttributes(ty, attr_buf_start); | |
| 1190 | } | |
| 1191 | ||
| 1192 | fn cannotCombine(b: Builder, p: *Parser, source_tok: TokenIndex) !void { | |
| 1193 | if (b.error_on_invalid) return error.CannotCombine; | |
| 1194 | const ty_str = b.specifier.str() orelse try p.typeStr(try b.finish(p, p.attr_buf.len)); | |
| 1195 | try p.errExtra(.cannot_combine_spec, source_tok, .{ .str = ty_str }); | |
| 1196 | if (b.typedef) |some| try p.errStr(.spec_from_typedef, some.tok, try p.typeStr(some.ty)); | |
| 1197 | } | |
| 1198 | ||
| 1199 | fn duplicateSpec(b: *Builder, p: *Parser, spec: []const u8) !void { | |
| 1200 | if (b.error_on_invalid) return error.CannotCombine; | |
| 1201 | try p.errStr(.duplicate_decl_spec, p.tok_i, spec); | |
| 1202 | } | |
| 1203 | ||
| 1204 | pub fn combineFromTypeof(b: *Builder, p: *Parser, new: Type, source_tok: TokenIndex) Compilation.Error!void { | |
| 1205 | if (b.typeof != null) return p.errStr(.cannot_combine_spec, source_tok, "typeof"); | |
| 1206 | if (b.specifier != .none) return p.errStr(.invalid_typeof, source_tok, @tagName(b.specifier)); | |
| 1207 | const inner = switch (new.specifier) { | |
| 1208 | .typeof_type => new.data.sub_type.*, | |
| 1209 | .typeof_expr => new.data.expr.ty, | |
| 1210 | else => unreachable, | |
| 1211 | }; | |
| 1212 | ||
| 1213 | b.typeof = switch (inner.specifier) { | |
| 1214 | .attributed => inner.data.attributed.base, | |
| 1215 | else => new, | |
| 1216 | }; | |
| 1217 | } | |
| 1218 | ||
| 1219 | /// Try to combine type from typedef, returns true if successful. | |
| 1220 | pub fn combineTypedef(b: *Builder, p: *Parser, typedef_ty: Type, name_tok: TokenIndex) bool { | |
| 1221 | b.error_on_invalid = true; | |
| 1222 | defer b.error_on_invalid = false; | |
| 1223 | ||
| 1224 | const new_spec = fromType(typedef_ty); | |
| 1225 | b.combineExtra(p, new_spec, 0) catch |err| switch (err) { | |
| 1226 | error.FatalError => unreachable, // we do not add any diagnostics | |
| 1227 | error.OutOfMemory => unreachable, // we do not add any diagnostics | |
| 1228 | error.ParsingFailed => unreachable, // we do not add any diagnostics | |
| 1229 | error.CannotCombine => return false, | |
| 1230 | }; | |
| 1231 | b.typedef = .{ .tok = name_tok, .ty = typedef_ty }; | |
| 1232 | return true; | |
| 1233 | } | |
| 1234 | ||
| 1235 | pub fn combine(b: *Builder, p: *Parser, new: Builder.Specifier, source_tok: TokenIndex) !void { | |
| 1236 | b.combineExtra(p, new, source_tok) catch |err| switch (err) { | |
| 1237 | error.CannotCombine => unreachable, | |
| 1238 | else => |e| return e, | |
| 1239 | }; | |
| 1240 | } | |
| 1241 | ||
| 1242 | fn combineExtra(b: *Builder, p: *Parser, new: Builder.Specifier, source_tok: TokenIndex) !void { | |
| 1243 | if (b.typeof != null) { | |
| 1244 | if (b.error_on_invalid) return error.CannotCombine; | |
| 1245 | try p.errStr(.invalid_typeof, source_tok, @tagName(new)); | |
| 1246 | } | |
| 1247 | ||
| 1248 | switch (new) { | |
| 1249 | else => switch (b.specifier) { | |
| 1250 | .none => b.specifier = new, | |
| 1251 | else => return b.cannotCombine(p, source_tok), | |
| 1252 | }, | |
| 1253 | .signed => b.specifier = switch (b.specifier) { | |
| 1254 | .none => .signed, | |
| 1255 | .char => .schar, | |
| 1256 | .short => .sshort, | |
| 1257 | .short_int => .sshort_int, | |
| 1258 | .int => .sint, | |
| 1259 | .long => .slong, | |
| 1260 | .long_int => .slong_int, | |
| 1261 | .long_long => .slong_long, | |
| 1262 | .long_long_int => .slong_long_int, | |
| 1263 | .sshort, | |
| 1264 | .sshort_int, | |
| 1265 | .sint, | |
| 1266 | .slong, | |
| 1267 | .slong_int, | |
| 1268 | .slong_long, | |
| 1269 | .slong_long_int, | |
| 1270 | => return b.duplicateSpec(p, "signed"), | |
| 1271 | else => return b.cannotCombine(p, source_tok), | |
| 1272 | }, | |
| 1273 | .unsigned => b.specifier = switch (b.specifier) { | |
| 1274 | .none => .unsigned, | |
| 1275 | .char => .uchar, | |
| 1276 | .short => .ushort, | |
| 1277 | .short_int => .ushort_int, | |
| 1278 | .int => .uint, | |
| 1279 | .long => .ulong, | |
| 1280 | .long_int => .ulong_int, | |
| 1281 | .long_long => .ulong_long, | |
| 1282 | .long_long_int => .ulong_long_int, | |
| 1283 | .ushort, | |
| 1284 | .ushort_int, | |
| 1285 | .uint, | |
| 1286 | .ulong, | |
| 1287 | .ulong_int, | |
| 1288 | .ulong_long, | |
| 1289 | .ulong_long_int, | |
| 1290 | => return b.duplicateSpec(p, "unsigned"), | |
| 1291 | else => return b.cannotCombine(p, source_tok), | |
| 1292 | }, | |
| 1293 | .char => b.specifier = switch (b.specifier) { | |
| 1294 | .none => .char, | |
| 1295 | .unsigned => .uchar, | |
| 1296 | .signed => .schar, | |
| 1297 | .char, .schar, .uchar => return b.duplicateSpec(p, "char"), | |
| 1298 | else => return b.cannotCombine(p, source_tok), | |
| 1299 | }, | |
| 1300 | .short => b.specifier = switch (b.specifier) { | |
| 1301 | .none => .short, | |
| 1302 | .unsigned => .ushort, | |
| 1303 | .signed => .sshort, | |
| 1304 | else => return b.cannotCombine(p, source_tok), | |
| 1305 | }, | |
| 1306 | .int => b.specifier = switch (b.specifier) { | |
| 1307 | .none => .int, | |
| 1308 | .signed => .sint, | |
| 1309 | .unsigned => .uint, | |
| 1310 | .short => .short_int, | |
| 1311 | .sshort => .sshort_int, | |
| 1312 | .ushort => .ushort_int, | |
| 1313 | .long => .long_int, | |
| 1314 | .slong => .slong_int, | |
| 1315 | .ulong => .ulong_int, | |
| 1316 | .long_long => .long_long_int, | |
| 1317 | .slong_long => .slong_long_int, | |
| 1318 | .ulong_long => .ulong_long_int, | |
| 1319 | .int, | |
| 1320 | .sint, | |
| 1321 | .uint, | |
| 1322 | .short_int, | |
| 1323 | .sshort_int, | |
| 1324 | .ushort_int, | |
| 1325 | .long_int, | |
| 1326 | .slong_int, | |
| 1327 | .ulong_int, | |
| 1328 | .long_long_int, | |
| 1329 | .slong_long_int, | |
| 1330 | .ulong_long_int, | |
| 1331 | => return b.duplicateSpec(p, "int"), | |
| 1332 | else => return b.cannotCombine(p, source_tok), | |
| 1333 | }, | |
| 1334 | .long => b.specifier = switch (b.specifier) { | |
| 1335 | .none => .long, | |
| 1336 | .long => .long_long, | |
| 1337 | .unsigned => .ulong, | |
| 1338 | .signed => .long, | |
| 1339 | .int => .long_int, | |
| 1340 | .sint => .slong_int, | |
| 1341 | .ulong => .ulong_long, | |
| 1342 | .long_long, .ulong_long => return b.duplicateSpec(p, "long"), | |
| 1343 | .complex => .complex_long, | |
| 1344 | else => return b.cannotCombine(p, source_tok), | |
| 1345 | }, | |
| 1346 | .float => b.specifier = switch (b.specifier) { | |
| 1347 | .none => .float, | |
| 1348 | .complex => .complex_float, | |
| 1349 | .complex_float, .float => return b.duplicateSpec(p, "float"), | |
| 1350 | else => return b.cannotCombine(p, source_tok), | |
| 1351 | }, | |
| 1352 | .double => b.specifier = switch (b.specifier) { | |
| 1353 | .none => .double, | |
| 1354 | .long => .long_double, | |
| 1355 | .complex_long => .complex_long_double, | |
| 1356 | .complex => .complex_double, | |
| 1357 | .long_double, | |
| 1358 | .complex_long_double, | |
| 1359 | .complex_double, | |
| 1360 | .double, | |
| 1361 | => return b.duplicateSpec(p, "double"), | |
| 1362 | else => return b.cannotCombine(p, source_tok), | |
| 1363 | }, | |
| 1364 | .complex => b.specifier = switch (b.specifier) { | |
| 1365 | .none => .complex, | |
| 1366 | .long => .complex_long, | |
| 1367 | .float => .complex_float, | |
| 1368 | .double => .complex_double, | |
| 1369 | .long_double => .complex_long_double, | |
| 1370 | .complex, | |
| 1371 | .complex_long, | |
| 1372 | .complex_float, | |
| 1373 | .complex_double, | |
| 1374 | .complex_long_double, | |
| 1375 | => return b.duplicateSpec(p, "_Complex"), | |
| 1376 | else => return b.cannotCombine(p, source_tok), | |
| 1377 | }, | |
| 1378 | } | |
| 1379 | } | |
| 1380 | ||
| 1381 | pub fn fromType(ty: Type) Builder.Specifier { | |
| 1382 | return switch (ty.specifier) { | |
| 1383 | .void => .void, | |
| 1384 | .bool => .bool, | |
| 1385 | .char => .char, | |
| 1386 | .schar => .schar, | |
| 1387 | .uchar => .uchar, | |
| 1388 | .short => .short, | |
| 1389 | .ushort => .ushort, | |
| 1390 | .int => .int, | |
| 1391 | .uint => .uint, | |
| 1392 | .long => .long, | |
| 1393 | .ulong => .ulong, | |
| 1394 | .long_long => .long_long, | |
| 1395 | .ulong_long => .ulong_long, | |
| 1396 | .float => .float, | |
| 1397 | .double => .double, | |
| 1398 | .long_double => .long_double, | |
| 1399 | .complex_float => .complex_float, | |
| 1400 | .complex_double => .complex_double, | |
| 1401 | .complex_long_double => .complex_long_double, | |
| 1402 | ||
| 1403 | .pointer => .{ .pointer = ty.data.sub_type }, | |
| 1404 | .unspecified_variable_len_array => .{ .unspecified_variable_len_array = ty.data.sub_type }, | |
| 1405 | .decayed_unspecified_variable_len_array => .{ .decayed_unspecified_variable_len_array = ty.data.sub_type }, | |
| 1406 | .func => .{ .func = ty.data.func }, | |
| 1407 | .var_args_func => .{ .var_args_func = ty.data.func }, | |
| 1408 | .old_style_func => .{ .old_style_func = ty.data.func }, | |
| 1409 | .array => .{ .array = ty.data.array }, | |
| 1410 | .decayed_array => .{ .decayed_array = ty.data.array }, | |
| 1411 | .static_array => .{ .static_array = ty.data.array }, | |
| 1412 | .decayed_static_array => .{ .decayed_static_array = ty.data.array }, | |
| 1413 | .incomplete_array => .{ .incomplete_array = ty.data.array }, | |
| 1414 | .decayed_incomplete_array => .{ .decayed_incomplete_array = ty.data.array }, | |
| 1415 | .variable_len_array => .{ .variable_len_array = ty.data.expr }, | |
| 1416 | .decayed_variable_len_array => .{ .decayed_variable_len_array = ty.data.expr }, | |
| 1417 | .@"struct" => .{ .@"struct" = ty.data.record }, | |
| 1418 | .@"union" => .{ .@"union" = ty.data.record }, | |
| 1419 | .@"enum" => .{ .@"enum" = ty.data.@"enum" }, | |
| 1420 | ||
| 1421 | .typeof_type => .{ .typeof_type = ty.data.sub_type }, | |
| 1422 | .decayed_typeof_type => .{ .decayed_typeof_type = ty.data.sub_type }, | |
| 1423 | .typeof_expr => .{ .typeof_expr = ty.data.expr }, | |
| 1424 | .decayed_typeof_expr => .{ .decayed_typeof_expr = ty.data.expr }, | |
| 1425 | ||
| 1426 | .attributed => .{ .attributed = ty.data.attributed }, | |
| 1427 | else => unreachable, | |
| 1428 | }; | |
| 1429 | } | |
| 1430 | }; | |
| 1431 | ||
| 1432 | pub fn getAttribute(ty: Type, comptime tag: Attribute.Tag) ?Attribute.ArgumentsForTag(tag) { | |
| 1433 | switch (ty.specifier) { | |
| 1434 | .typeof_type => return ty.data.sub_type.getAttribute(tag), | |
| 1435 | .typeof_expr => return ty.data.expr.ty.getAttribute(tag), | |
| 1436 | .attributed => { | |
| 1437 | for (ty.data.attributed.attributes) |attribute| { | |
| 1438 | if (attribute.tag == tag) return @field(attribute.args, @tagName(tag)); | |
| 1439 | } | |
| 1440 | return null; | |
| 1441 | }, | |
| 1442 | else => return null, | |
| 1443 | } | |
| 1444 | } | |
| 1445 | ||
| 1446 | /// Print type in C style | |
| 1447 | pub fn print(ty: Type, w: anytype) @TypeOf(w).Error!void { | |
| 1448 | _ = try ty.printPrologue(w); | |
| 1449 | try ty.printEpilogue(w); | |
| 1450 | } | |
| 1451 | ||
| 1452 | pub fn printNamed(ty: Type, name: []const u8, w: anytype) @TypeOf(w).Error!void { | |
| 1453 | const simple = try ty.printPrologue(w); | |
| 1454 | if (simple) try w.writeByte(' '); | |
| 1455 | try w.writeAll(name); | |
| 1456 | try ty.printEpilogue(w); | |
| 1457 | } | |
| 1458 | ||
| 1459 | /// return true if `ty` is simple | |
| 1460 | fn printPrologue(ty: Type, w: anytype) @TypeOf(w).Error!bool { | |
| 1461 | if (ty.qual.atomic) { | |
| 1462 | var non_atomic_ty = ty; | |
| 1463 | non_atomic_ty.qual.atomic = false; | |
| 1464 | try w.writeAll("_Atomic("); | |
| 1465 | try non_atomic_ty.print(w); | |
| 1466 | try w.writeAll(")"); | |
| 1467 | return true; | |
| 1468 | } | |
| 1469 | switch (ty.specifier) { | |
| 1470 | .pointer, | |
| 1471 | .decayed_array, | |
| 1472 | .decayed_static_array, | |
| 1473 | .decayed_incomplete_array, | |
| 1474 | .decayed_variable_len_array, | |
| 1475 | .decayed_unspecified_variable_len_array, | |
| 1476 | .decayed_typeof_type, | |
| 1477 | .decayed_typeof_expr, | |
| 1478 | => { | |
| 1479 | const elem_ty = ty.elemType(); | |
| 1480 | const simple = try elem_ty.printPrologue(w); | |
| 1481 | if (simple) try w.writeByte(' '); | |
| 1482 | if (elem_ty.isFunc() or elem_ty.isArray()) try w.writeByte('('); | |
| 1483 | try w.writeByte('*'); | |
| 1484 | try ty.qual.dump(w); | |
| 1485 | return false; | |
| 1486 | }, | |
| 1487 | .func, .var_args_func, .old_style_func => { | |
| 1488 | const ret_ty = ty.data.func.return_type; | |
| 1489 | const simple = try ret_ty.printPrologue(w); | |
| 1490 | if (simple) try w.writeByte(' '); | |
| 1491 | return false; | |
| 1492 | }, | |
| 1493 | .array, .static_array, .incomplete_array, .unspecified_variable_len_array, .variable_len_array => { | |
| 1494 | const elem_ty = ty.elemType(); | |
| 1495 | const simple = try elem_ty.printPrologue(w); | |
| 1496 | if (simple) try w.writeByte(' '); | |
| 1497 | return false; | |
| 1498 | }, | |
| 1499 | .typeof_type, .typeof_expr => { | |
| 1500 | const actual = ty.canonicalize(.standard); | |
| 1501 | return actual.printPrologue(w); | |
| 1502 | }, | |
| 1503 | .attributed => { | |
| 1504 | const actual = ty.canonicalize(.standard); | |
| 1505 | return actual.printPrologue(w); | |
| 1506 | }, | |
| 1507 | else => {}, | |
| 1508 | } | |
| 1509 | try ty.qual.dump(w); | |
| 1510 | ||
| 1511 | switch (ty.specifier) { | |
| 1512 | .@"enum" => try w.print("enum {s}", .{ty.data.@"enum".name}), | |
| 1513 | .@"struct" => try w.print("struct {s}", .{ty.data.record.name}), | |
| 1514 | .@"union" => try w.print("union {s}", .{ty.data.record.name}), | |
| 1515 | else => try w.writeAll(Builder.fromType(ty).str().?), | |
| 1516 | } | |
| 1517 | return true; | |
| 1518 | } | |
| 1519 | ||
| 1520 | fn printEpilogue(ty: Type, w: anytype) @TypeOf(w).Error!void { | |
| 1521 | if (ty.qual.atomic) return; | |
| 1522 | switch (ty.specifier) { | |
| 1523 | .pointer, | |
| 1524 | .decayed_array, | |
| 1525 | .decayed_static_array, | |
| 1526 | .decayed_incomplete_array, | |
| 1527 | .decayed_variable_len_array, | |
| 1528 | .decayed_unspecified_variable_len_array, | |
| 1529 | .decayed_typeof_type, | |
| 1530 | .decayed_typeof_expr, | |
| 1531 | => { | |
| 1532 | const elem_ty = ty.elemType(); | |
| 1533 | if (elem_ty.isFunc() or elem_ty.isArray()) try w.writeByte(')'); | |
| 1534 | try elem_ty.printEpilogue(w); | |
| 1535 | }, | |
| 1536 | .func, .var_args_func, .old_style_func => { | |
| 1537 | try w.writeByte('('); | |
| 1538 | for (ty.data.func.params) |param, i| { | |
| 1539 | if (i != 0) try w.writeAll(", "); | |
| 1540 | _ = try param.ty.printPrologue(w); | |
| 1541 | try param.ty.printEpilogue(w); | |
| 1542 | } | |
| 1543 | if (ty.specifier != .func) { | |
| 1544 | if (ty.data.func.params.len != 0) try w.writeAll(", "); | |
| 1545 | try w.writeAll("..."); | |
| 1546 | } else if (ty.data.func.params.len == 0) { | |
| 1547 | try w.writeAll("void"); | |
| 1548 | } | |
| 1549 | try w.writeByte(')'); | |
| 1550 | try ty.data.func.return_type.printEpilogue(w); | |
| 1551 | }, | |
| 1552 | .array, .static_array => { | |
| 1553 | try w.writeByte('['); | |
| 1554 | if (ty.specifier == .static_array) try w.writeAll("static "); | |
| 1555 | try ty.qual.dump(w); | |
| 1556 | try w.print("{d}]", .{ty.data.array.len}); | |
| 1557 | try ty.data.array.elem.printEpilogue(w); | |
| 1558 | }, | |
| 1559 | .incomplete_array => { | |
| 1560 | try w.writeByte('['); | |
| 1561 | try ty.qual.dump(w); | |
| 1562 | try w.writeByte(']'); | |
| 1563 | try ty.data.array.elem.printEpilogue(w); | |
| 1564 | }, | |
| 1565 | .unspecified_variable_len_array => { | |
| 1566 | try w.writeByte('['); | |
| 1567 | try ty.qual.dump(w); | |
| 1568 | try w.writeAll("*]"); | |
| 1569 | try ty.data.sub_type.printEpilogue(w); | |
| 1570 | }, | |
| 1571 | .variable_len_array => { | |
| 1572 | try w.writeByte('['); | |
| 1573 | try ty.qual.dump(w); | |
| 1574 | try w.writeAll("<expr>]"); | |
| 1575 | try ty.data.expr.ty.printEpilogue(w); | |
| 1576 | }, | |
| 1577 | else => {}, | |
| 1578 | } | |
| 1579 | } | |
| 1580 | ||
| 1581 | /// Useful for debugging, too noisy to be enabled by default. | |
| 1582 | const dump_detailed_containers = false; | |
| 1583 | ||
| 1584 | // Print as Zig types since those are actually readable | |
| 1585 | pub fn dump(ty: Type, w: anytype) @TypeOf(w).Error!void { | |
| 1586 | try ty.qual.dump(w); | |
| 1587 | switch (ty.specifier) { | |
| 1588 | .pointer => { | |
| 1589 | try w.writeAll("*"); | |
| 1590 | try ty.data.sub_type.dump(w); | |
| 1591 | }, | |
| 1592 | .func, .var_args_func, .old_style_func => { | |
| 1593 | try w.writeAll("fn ("); | |
| 1594 | for (ty.data.func.params) |param, i| { | |
| 1595 | if (i != 0) try w.writeAll(", "); | |
| 1596 | if (param.name.len != 0) try w.print("{s}: ", .{param.name}); | |
| 1597 | try param.ty.dump(w); | |
| 1598 | } | |
| 1599 | if (ty.specifier != .func) { | |
| 1600 | if (ty.data.func.params.len != 0) try w.writeAll(", "); | |
| 1601 | try w.writeAll("..."); | |
| 1602 | } | |
| 1603 | try w.writeAll(") "); | |
| 1604 | try ty.data.func.return_type.dump(w); | |
| 1605 | }, | |
| 1606 | .array, .static_array, .decayed_array, .decayed_static_array => { | |
| 1607 | if (ty.specifier == .decayed_array or ty.specifier == .decayed_static_array) try w.writeByte('d'); | |
| 1608 | try w.writeByte('['); | |
| 1609 | if (ty.specifier == .static_array or ty.specifier == .decayed_static_array) try w.writeAll("static "); | |
| 1610 | try w.print("{d}]", .{ty.data.array.len}); | |
| 1611 | try ty.data.array.elem.dump(w); | |
| 1612 | }, | |
| 1613 | .incomplete_array, .decayed_incomplete_array => { | |
| 1614 | if (ty.specifier == .decayed_incomplete_array) try w.writeByte('d'); | |
| 1615 | try w.writeAll("[]"); | |
| 1616 | try ty.data.array.elem.dump(w); | |
| 1617 | }, | |
| 1618 | .@"enum" => { | |
| 1619 | try w.print("enum {s}", .{ty.data.@"enum".name}); | |
| 1620 | if (dump_detailed_containers) try dumpEnum(ty.data.@"enum", w); | |
| 1621 | }, | |
| 1622 | .@"struct" => { | |
| 1623 | try w.print("struct {s}", .{ty.data.record.name}); | |
| 1624 | if (dump_detailed_containers) try dumpRecord(ty.data.record, w); | |
| 1625 | }, | |
| 1626 | .@"union" => { | |
| 1627 | try w.print("union {s}", .{ty.data.record.name}); | |
| 1628 | if (dump_detailed_containers) try dumpRecord(ty.data.record, w); | |
| 1629 | }, | |
| 1630 | .unspecified_variable_len_array, .decayed_unspecified_variable_len_array => { | |
| 1631 | if (ty.specifier == .decayed_unspecified_variable_len_array) try w.writeByte('d'); | |
| 1632 | try w.writeAll("[*]"); | |
| 1633 | try ty.data.sub_type.dump(w); | |
| 1634 | }, | |
| 1635 | .variable_len_array, .decayed_variable_len_array => { | |
| 1636 | if (ty.specifier == .decayed_variable_len_array) try w.writeByte('d'); | |
| 1637 | try w.writeAll("[<expr>]"); | |
| 1638 | try ty.data.expr.ty.dump(w); | |
| 1639 | }, | |
| 1640 | .typeof_type, .decayed_typeof_type => { | |
| 1641 | try w.writeAll("typeof("); | |
| 1642 | try ty.data.sub_type.dump(w); | |
| 1643 | try w.writeAll(")"); | |
| 1644 | }, | |
| 1645 | .typeof_expr, .decayed_typeof_expr => { | |
| 1646 | try w.writeAll("typeof(<expr>: "); | |
| 1647 | try ty.data.expr.ty.dump(w); | |
| 1648 | try w.writeAll(")"); | |
| 1649 | }, | |
| 1650 | .attributed => { | |
| 1651 | try w.writeAll("attributed("); | |
| 1652 | try ty.data.attributed.base.dump(w); | |
| 1653 | try w.writeAll(")"); | |
| 1654 | }, | |
| 1655 | .special_va_start => try w.writeAll("(va start param)"), | |
| 1656 | else => try w.writeAll(Builder.fromType(ty).str().?), | |
| 1657 | } | |
| 1658 | } | |
| 1659 | ||
| 1660 | fn dumpEnum(@"enum": *Enum, w: anytype) @TypeOf(w).Error!void { | |
| 1661 | try w.writeAll(" {"); | |
| 1662 | for (@"enum".fields) |field| { | |
| 1663 | try w.print(" {s} = {d},", .{ field.name, field.value }); | |
| 1664 | } | |
| 1665 | try w.writeAll(" }"); | |
| 1666 | } | |
| 1667 | ||
| 1668 | fn dumpRecord(record: *Record, w: anytype) @TypeOf(w).Error!void { | |
| 1669 | try w.writeAll(" {"); | |
| 1670 | for (record.fields) |field| { | |
| 1671 | try w.writeByte(' '); | |
| 1672 | try field.ty.dump(w); | |
| 1673 | try w.print(" {s}: {d};", .{ field.name, field.bit_width }); | |
| 1674 | } | |
| 1675 | try w.writeAll(" }"); | |
| 1676 | } |
src/aro/Value.zig created+445| ... | ... | @@ -0,0 +1,445 @@ |
| 1 | const std = @import("std"); | |
| 2 | const assert = std.debug.assert; | |
| 3 | const Compilation = @import("Compilation.zig"); | |
| 4 | const Type = @import("Type.zig"); | |
| 5 | ||
| 6 | const Value = @This(); | |
| 7 | ||
| 8 | tag: Tag = .unavailable, | |
| 9 | data: union { | |
| 10 | none: void, | |
| 11 | int: u64, | |
| 12 | float: f64, | |
| 13 | array: []Value, | |
| 14 | bytes: []u8, | |
| 15 | } = .{ .none = {} }, | |
| 16 | ||
| 17 | const Tag = enum { | |
| 18 | unavailable, | |
| 19 | /// int is used to store integer, boolean and pointer values | |
| 20 | int, | |
| 21 | float, | |
| 22 | array, | |
| 23 | bytes, | |
| 24 | }; | |
| 25 | ||
| 26 | pub fn zero(v: Value) Value { | |
| 27 | return switch (v.tag) { | |
| 28 | .int => int(0), | |
| 29 | .float => float(0), | |
| 30 | else => unreachable, | |
| 31 | }; | |
| 32 | } | |
| 33 | ||
| 34 | pub fn one(v: Value) Value { | |
| 35 | return switch (v.tag) { | |
| 36 | .int => int(1), | |
| 37 | .float => float(1), | |
| 38 | else => unreachable, | |
| 39 | }; | |
| 40 | } | |
| 41 | ||
| 42 | pub fn int(v: anytype) Value { | |
| 43 | if (@TypeOf(v) == comptime_int or @typeInfo(@TypeOf(v)).Int.signedness == .unsigned) | |
| 44 | return .{ .tag = .int, .data = .{ .int = v } } | |
| 45 | else | |
| 46 | return .{ .tag = .int, .data = .{ .int = @bitCast(u64, @as(i64, v)) } }; | |
| 47 | } | |
| 48 | ||
| 49 | pub fn float(v: anytype) Value { | |
| 50 | return .{ .tag = .float, .data = .{ .float = v } }; | |
| 51 | } | |
| 52 | ||
| 53 | pub fn bytes(v: anytype) Value { | |
| 54 | return .{ .tag = .bytes, .data = .{ .bytes = v } }; | |
| 55 | } | |
| 56 | ||
| 57 | pub fn signExtend(v: Value, old_ty: Type, comp: *Compilation) i64 { | |
| 58 | const size = old_ty.sizeof(comp).?; | |
| 59 | return switch (size) { | |
| 60 | 4 => v.getInt(i32), | |
| 61 | 8 => v.getInt(i64), | |
| 62 | else => unreachable, | |
| 63 | }; | |
| 64 | } | |
| 65 | ||
| 66 | /// Converts the stored value from a float to an integer. | |
| 67 | /// `.unavailable` value remains unchanged. | |
| 68 | pub fn floatToInt(v: *Value, old_ty: Type, new_ty: Type, comp: *Compilation) void { | |
| 69 | assert(old_ty.isFloat()); | |
| 70 | if (v.tag == .unavailable) return; | |
| 71 | if (new_ty.isUnsignedInt(comp) and v.data.float < 0) { | |
| 72 | v.* = int(0); | |
| 73 | return; | |
| 74 | } else if (!std.math.isFinite(v.data.float)) { | |
| 75 | v.tag = .unavailable; | |
| 76 | return; | |
| 77 | } | |
| 78 | const size = old_ty.sizeof(comp).?; | |
| 79 | v.* = int(switch (size) { | |
| 80 | 4 => @floatToInt(i32, v.getFloat(f32)), | |
| 81 | 8 => @floatToInt(i64, v.getFloat(f64)), | |
| 82 | else => unreachable, | |
| 83 | }); | |
| 84 | } | |
| 85 | ||
| 86 | /// Converts the stored value from an integer to a float. | |
| 87 | /// `.unavailable` value remains unchanged. | |
| 88 | pub fn intToFloat(v: *Value, old_ty: Type, new_ty: Type, comp: *Compilation) void { | |
| 89 | assert(old_ty.isInt()); | |
| 90 | if (v.tag == .unavailable) return; | |
| 91 | if (!new_ty.isReal() or new_ty.sizeof(comp).? > 8) { | |
| 92 | v.tag = .unavailable; | |
| 93 | } else if (old_ty.isUnsignedInt(comp)) { | |
| 94 | v.* = float(@intToFloat(f64, v.data.int)); | |
| 95 | } else { | |
| 96 | v.* = float(@intToFloat(f64, @bitCast(i64, v.data.int))); | |
| 97 | } | |
| 98 | } | |
| 99 | ||
| 100 | /// Truncates or extends bits based on type. | |
| 101 | /// old_ty is only used for size. | |
| 102 | pub fn intCast(v: *Value, old_ty: Type, new_ty: Type, comp: *Compilation) void { | |
| 103 | // assert(old_ty.isInt() and new_ty.isInt()); | |
| 104 | if (v.tag == .unavailable) return; | |
| 105 | if (new_ty.is(.bool)) return v.toBool(); | |
| 106 | if (!old_ty.isUnsignedInt(comp)) { | |
| 107 | const size = new_ty.sizeof(comp).?; | |
| 108 | switch (size) { | |
| 109 | 1 => v.* = int(@bitCast(u8, v.getInt(i8))), | |
| 110 | 2 => v.* = int(@bitCast(u16, v.getInt(i16))), | |
| 111 | 4 => v.* = int(@bitCast(u32, v.getInt(i32))), | |
| 112 | 8 => return, | |
| 113 | else => unreachable, | |
| 114 | } | |
| 115 | } | |
| 116 | } | |
| 117 | ||
| 118 | /// Converts the stored value from an integer to a float. | |
| 119 | /// `.unavailable` value remains unchanged. | |
| 120 | pub fn floatCast(v: *Value, old_ty: Type, new_ty: Type, comp: *Compilation) void { | |
| 121 | assert(old_ty.isFloat() and new_ty.isFloat()); | |
| 122 | if (v.tag == .unavailable) return; | |
| 123 | const size = new_ty.sizeof(comp).?; | |
| 124 | if (!new_ty.isReal() or size > 8) { | |
| 125 | v.tag = .unavailable; | |
| 126 | } else if (size == 32) { | |
| 127 | v.* = float(@floatCast(f32, v.data.float)); | |
| 128 | } | |
| 129 | } | |
| 130 | ||
| 131 | /// Truncates data.int to one bit | |
| 132 | pub fn toBool(v: *Value) void { | |
| 133 | if (v.tag == .unavailable) return; | |
| 134 | const res = v.getBool(); | |
| 135 | v.* = int(@boolToInt(res)); | |
| 136 | } | |
| 137 | ||
| 138 | pub fn isZero(v: Value) bool { | |
| 139 | return switch (v.tag) { | |
| 140 | .unavailable => false, | |
| 141 | .int => v.data.int == 0, | |
| 142 | .float => v.data.float == 0, | |
| 143 | .array => false, | |
| 144 | .bytes => false, | |
| 145 | }; | |
| 146 | } | |
| 147 | ||
| 148 | pub fn getBool(v: Value) bool { | |
| 149 | return switch (v.tag) { | |
| 150 | .unavailable => unreachable, | |
| 151 | .int => v.data.int != 0, | |
| 152 | .float => v.data.float != 0, | |
| 153 | .array => true, | |
| 154 | .bytes => true, | |
| 155 | }; | |
| 156 | } | |
| 157 | ||
| 158 | pub fn getInt(v: Value, comptime T: type) T { | |
| 159 | if (T == u64) return v.data.int; | |
| 160 | return if (@typeInfo(T).Int.signedness == .unsigned) | |
| 161 | @truncate(T, v.data.int) | |
| 162 | else | |
| 163 | @truncate(T, @bitCast(i64, v.data.int)); | |
| 164 | } | |
| 165 | ||
| 166 | pub fn getFloat(v: Value, comptime T: type) T { | |
| 167 | if (T == f64) return v.data.float; | |
| 168 | return @floatCast(T, v.data.float); | |
| 169 | } | |
| 170 | ||
| 171 | const bin_overflow = struct { | |
| 172 | inline fn addInt(comptime T: type, out: *Value, a: Value, b: Value) bool { | |
| 173 | const a_val = a.getInt(T); | |
| 174 | const b_val = b.getInt(T); | |
| 175 | var c: T = undefined; | |
| 176 | const overflow = @addWithOverflow(T, a_val, b_val, &c); | |
| 177 | out.* = int(c); | |
| 178 | return overflow; | |
| 179 | } | |
| 180 | inline fn addFloat(comptime T: type, aa: Value, bb: Value) Value { | |
| 181 | const a_val = aa.getFloat(T); | |
| 182 | const b_val = bb.getFloat(T); | |
| 183 | return float(a_val + b_val); | |
| 184 | } | |
| 185 | ||
| 186 | inline fn subInt(comptime T: type, out: *Value, a: Value, b: Value) bool { | |
| 187 | const a_val = a.getInt(T); | |
| 188 | const b_val = b.getInt(T); | |
| 189 | var c: T = undefined; | |
| 190 | const overflow = @subWithOverflow(T, a_val, b_val, &c); | |
| 191 | out.* = int(c); | |
| 192 | return overflow; | |
| 193 | } | |
| 194 | inline fn subFloat(comptime T: type, aa: Value, bb: Value) Value { | |
| 195 | const a_val = aa.getFloat(T); | |
| 196 | const b_val = bb.getFloat(T); | |
| 197 | return float(a_val - b_val); | |
| 198 | } | |
| 199 | ||
| 200 | inline fn mulInt(comptime T: type, out: *Value, a: Value, b: Value) bool { | |
| 201 | const a_val = a.getInt(T); | |
| 202 | const b_val = b.getInt(T); | |
| 203 | var c: T = undefined; | |
| 204 | const overflow = @mulWithOverflow(T, a_val, b_val, &c); | |
| 205 | out.* = int(c); | |
| 206 | return overflow; | |
| 207 | } | |
| 208 | inline fn mulFloat(comptime T: type, aa: Value, bb: Value) Value { | |
| 209 | const a_val = aa.getFloat(T); | |
| 210 | const b_val = bb.getFloat(T); | |
| 211 | return float(a_val * b_val); | |
| 212 | } | |
| 213 | ||
| 214 | const FT = fn (*Value, Value, Value, Type, *Compilation) bool; | |
| 215 | fn getOp(intFunc: anytype, floatFunc: anytype) FT { | |
| 216 | return struct { | |
| 217 | fn op(res: *Value, a: Value, b: Value, ty: Type, comp: *Compilation) bool { | |
| 218 | const size = ty.sizeof(comp).?; | |
| 219 | if (@TypeOf(floatFunc) != @TypeOf(null) and ty.isFloat()) { | |
| 220 | res.* = switch (size) { | |
| 221 | 4 => floatFunc(f32, a, b), | |
| 222 | 8 => floatFunc(f64, a, b), | |
| 223 | else => unreachable, | |
| 224 | }; | |
| 225 | return false; | |
| 226 | } | |
| 227 | ||
| 228 | if (ty.isUnsignedInt(comp)) switch (size) { | |
| 229 | 1 => unreachable, // promoted to int | |
| 230 | 2 => unreachable, // promoted to int | |
| 231 | 4 => return intFunc(u32, res, a, b), | |
| 232 | 8 => return intFunc(u64, res, a, b), | |
| 233 | else => unreachable, | |
| 234 | } else switch (size) { | |
| 235 | 1 => unreachable, // promoted to int | |
| 236 | 2 => unreachable, // promoted to int | |
| 237 | 4 => return intFunc(i32, res, a, b), | |
| 238 | 8 => return intFunc(i64, res, a, b), | |
| 239 | else => unreachable, | |
| 240 | } | |
| 241 | } | |
| 242 | }.op; | |
| 243 | } | |
| 244 | }; | |
| 245 | ||
| 246 | pub const add = bin_overflow.getOp(bin_overflow.addInt, bin_overflow.addFloat); | |
| 247 | pub const sub = bin_overflow.getOp(bin_overflow.subInt, bin_overflow.subFloat); | |
| 248 | pub const mul = bin_overflow.getOp(bin_overflow.mulInt, bin_overflow.mulFloat); | |
| 249 | ||
| 250 | const bin_ops = struct { | |
| 251 | inline fn divInt(comptime T: type, aa: Value, bb: Value) Value { | |
| 252 | const a_val = aa.getInt(T); | |
| 253 | const b_val = bb.getInt(T); | |
| 254 | return int(@divTrunc(a_val, b_val)); | |
| 255 | } | |
| 256 | inline fn divFloat(comptime T: type, aa: Value, bb: Value) Value { | |
| 257 | const a_val = aa.getFloat(T); | |
| 258 | const b_val = bb.getFloat(T); | |
| 259 | return float(a_val / b_val); | |
| 260 | } | |
| 261 | ||
| 262 | inline fn remInt(comptime T: type, a: Value, b: Value) Value { | |
| 263 | const a_val = a.getInt(T); | |
| 264 | const b_val = b.getInt(T); | |
| 265 | ||
| 266 | if (@typeInfo(T).Int.signedness == .signed) { | |
| 267 | if (a_val == std.math.minInt(T) and b_val == -1) { | |
| 268 | return Value{ .tag = .unavailable, .data = .{ .none = {} } }; | |
| 269 | } else { | |
| 270 | if (b_val > 0) return int(@rem(a_val, b_val)); | |
| 271 | return int(a_val - @divTrunc(a_val, b_val) * b_val); | |
| 272 | } | |
| 273 | } else { | |
| 274 | return int(a_val % b_val); | |
| 275 | } | |
| 276 | } | |
| 277 | ||
| 278 | inline fn orInt(comptime T: type, a: Value, b: Value) Value { | |
| 279 | const a_val = a.getInt(T); | |
| 280 | const b_val = b.getInt(T); | |
| 281 | return int(a_val | b_val); | |
| 282 | } | |
| 283 | inline fn xorInt(comptime T: type, a: Value, b: Value) Value { | |
| 284 | const a_val = a.getInt(T); | |
| 285 | const b_val = b.getInt(T); | |
| 286 | return int(a_val ^ b_val); | |
| 287 | } | |
| 288 | inline fn andInt(comptime T: type, a: Value, b: Value) Value { | |
| 289 | const a_val = a.getInt(T); | |
| 290 | const b_val = b.getInt(T); | |
| 291 | return int(a_val & b_val); | |
| 292 | } | |
| 293 | ||
| 294 | inline fn shl(comptime T: type, a: Value, b: Value) Value { | |
| 295 | const ShiftT = std.math.Log2Int(T); | |
| 296 | const info = @typeInfo(T).Int; | |
| 297 | const UT = std.meta.Int(.unsigned, info.bits); | |
| 298 | const b_val = b.getInt(T); | |
| 299 | ||
| 300 | if (b_val > std.math.maxInt(ShiftT)) { | |
| 301 | return if (info.signedness == .unsigned) | |
| 302 | int(@as(UT, std.math.maxInt(UT))) | |
| 303 | else | |
| 304 | int(@as(T, std.math.minInt(T))); | |
| 305 | } | |
| 306 | const amt = @truncate(ShiftT, @bitCast(UT, b_val)); | |
| 307 | const a_val = a.getInt(T); | |
| 308 | return int(a_val << amt); | |
| 309 | } | |
| 310 | inline fn shr(comptime T: type, a: Value, b: Value) Value { | |
| 311 | const ShiftT = std.math.Log2Int(T); | |
| 312 | const UT = std.meta.Int(.unsigned, @typeInfo(T).Int.bits); | |
| 313 | ||
| 314 | const b_val = b.getInt(T); | |
| 315 | if (b_val > std.math.maxInt(ShiftT)) return Value.int(0); | |
| 316 | ||
| 317 | const amt = @truncate(ShiftT, @intCast(UT, b_val)); | |
| 318 | const a_val = a.getInt(T); | |
| 319 | return int(a_val >> amt); | |
| 320 | } | |
| 321 | ||
| 322 | const FT = fn (Value, Value, Type, *Compilation) Value; | |
| 323 | fn getOp(intFunc: anytype, floatFunc: anytype) FT { | |
| 324 | return struct { | |
| 325 | fn op(a: Value, b: Value, ty: Type, comp: *Compilation) Value { | |
| 326 | const size = ty.sizeof(comp).?; | |
| 327 | if (@TypeOf(floatFunc) != @TypeOf(null) and ty.isFloat()) { | |
| 328 | switch (size) { | |
| 329 | 4 => return floatFunc(f32, a, b), | |
| 330 | 8 => return floatFunc(f64, a, b), | |
| 331 | else => unreachable, | |
| 332 | } | |
| 333 | } | |
| 334 | ||
| 335 | if (ty.isUnsignedInt(comp)) switch (size) { | |
| 336 | 1 => unreachable, // promoted to int | |
| 337 | 2 => unreachable, // promoted to int | |
| 338 | 4 => return intFunc(u32, a, b), | |
| 339 | 8 => return intFunc(u64, a, b), | |
| 340 | else => unreachable, | |
| 341 | } else switch (size) { | |
| 342 | 1 => unreachable, // promoted to int | |
| 343 | 2 => unreachable, // promoted to int | |
| 344 | 4 => return intFunc(i32, a, b), | |
| 345 | 8 => return intFunc(i64, a, b), | |
| 346 | else => unreachable, | |
| 347 | } | |
| 348 | } | |
| 349 | }.op; | |
| 350 | } | |
| 351 | }; | |
| 352 | ||
| 353 | /// caller guarantees rhs != 0 | |
| 354 | pub const div = bin_ops.getOp(bin_ops.divInt, bin_ops.divFloat); | |
| 355 | /// caller guarantees rhs != 0 | |
| 356 | /// caller guarantees lhs != std.math.minInt(T) OR rhs != -1 | |
| 357 | pub const rem = bin_ops.getOp(bin_ops.remInt, null); | |
| 358 | ||
| 359 | pub const bitOr = bin_ops.getOp(bin_ops.orInt, null); | |
| 360 | pub const bitXor = bin_ops.getOp(bin_ops.xorInt, null); | |
| 361 | pub const bitAnd = bin_ops.getOp(bin_ops.andInt, null); | |
| 362 | ||
| 363 | pub const shl = bin_ops.getOp(bin_ops.shl, null); | |
| 364 | pub const shr = bin_ops.getOp(bin_ops.shr, null); | |
| 365 | ||
| 366 | pub fn bitNot(v: Value, ty: Type, comp: *Compilation) Value { | |
| 367 | const size = ty.sizeof(comp).?; | |
| 368 | var out: Value = undefined; | |
| 369 | if (ty.isUnsignedInt(comp)) switch (size) { | |
| 370 | 1 => unreachable, // promoted to int | |
| 371 | 2 => unreachable, // promoted to int | |
| 372 | 4 => out = int(~v.getInt(u32)), | |
| 373 | 8 => out = int(~v.getInt(u64)), | |
| 374 | else => unreachable, | |
| 375 | } else switch (size) { | |
| 376 | 1 => unreachable, // promoted to int | |
| 377 | 2 => unreachable, // promoted to int | |
| 378 | 4 => out = int(~v.getInt(i32)), | |
| 379 | 8 => out = int(~v.getInt(i64)), | |
| 380 | else => unreachable, | |
| 381 | } | |
| 382 | return out; | |
| 383 | } | |
| 384 | ||
| 385 | pub fn compare(a: Value, op: std.math.CompareOperator, b: Value, ty: Type, comp: *Compilation) bool { | |
| 386 | assert(a.tag == b.tag); | |
| 387 | const S = struct { | |
| 388 | inline fn doICompare(comptime T: type, aa: Value, opp: std.math.CompareOperator, bb: Value) bool { | |
| 389 | const a_val = aa.getInt(T); | |
| 390 | const b_val = bb.getInt(T); | |
| 391 | return std.math.compare(a_val, opp, b_val); | |
| 392 | } | |
| 393 | inline fn doFCompare(comptime T: type, aa: Value, opp: std.math.CompareOperator, bb: Value) bool { | |
| 394 | const a_val = aa.getFloat(T); | |
| 395 | const b_val = bb.getFloat(T); | |
| 396 | return std.math.compare(a_val, opp, b_val); | |
| 397 | } | |
| 398 | }; | |
| 399 | const size = ty.sizeof(comp).?; | |
| 400 | switch (a.tag) { | |
| 401 | .unavailable => return true, | |
| 402 | .int => if (ty.isUnsignedInt(comp)) switch (size) { | |
| 403 | 1 => unreachable, // promoted to int | |
| 404 | 2 => unreachable, // promoted to int | |
| 405 | 4 => return S.doICompare(u32, a, op, b), | |
| 406 | 8 => return S.doICompare(u64, a, op, b), | |
| 407 | else => unreachable, | |
| 408 | } else switch (size) { | |
| 409 | 1 => unreachable, // promoted to int | |
| 410 | 2 => unreachable, // promoted to int | |
| 411 | 4 => return S.doICompare(i32, a, op, b), | |
| 412 | 8 => return S.doICompare(i64, a, op, b), | |
| 413 | else => unreachable, | |
| 414 | }, | |
| 415 | .float => switch (size) { | |
| 416 | 4 => return S.doFCompare(f32, a, op, b), | |
| 417 | 8 => return S.doFCompare(f64, a, op, b), | |
| 418 | else => unreachable, | |
| 419 | }, | |
| 420 | else => @panic("TODO"), | |
| 421 | } | |
| 422 | return false; | |
| 423 | } | |
| 424 | ||
| 425 | pub fn hash(v: Value) u64 { | |
| 426 | switch (v.tag) { | |
| 427 | .unavailable => unreachable, | |
| 428 | .int => return std.hash.Wyhash.hash(0, std.mem.asBytes(&v.data.int)), | |
| 429 | else => @panic("TODO"), | |
| 430 | } | |
| 431 | } | |
| 432 | ||
| 433 | pub fn dump(v: Value, ty: Type, comp: *Compilation, w: anytype) !void { | |
| 434 | switch (v.tag) { | |
| 435 | .unavailable => try w.writeAll("unavailable"), | |
| 436 | .int => if (ty.isUnsignedInt(comp)) | |
| 437 | try w.print("{d}", .{v.data.int}) | |
| 438 | else { | |
| 439 | try w.print("{d}", .{v.signExtend(ty, comp)}); | |
| 440 | }, | |
| 441 | // std.fmt does @as instead of @floatCast | |
| 442 | .float => try w.print("{d}", .{@floatCast(f64, v.data.float)}), | |
| 443 | else => try w.print("({s})", .{@tagName(v.tag)}), | |
| 444 | } | |
| 445 | } |
src/aro/features.zig created+75| ... | ... | @@ -0,0 +1,75 @@ |
| 1 | const std = @import("std"); | |
| 2 | const Compilation = @import("Compilation.zig"); | |
| 3 | ||
| 4 | /// Used to implement the __has_feature macro. | |
| 5 | pub fn hasFeature(comp: *Compilation, ext: []const u8) bool { | |
| 6 | const list = .{ | |
| 7 | .assume_nonnull = true, | |
| 8 | .attribute_analyzer_noreturn = true, | |
| 9 | .attribute_availability = true, | |
| 10 | .attribute_availability_with_message = true, | |
| 11 | .attribute_availability_app_extension = true, | |
| 12 | .attribute_availability_with_version_underscores = true, | |
| 13 | .attribute_availability_tvos = true, | |
| 14 | .attribute_availability_watchos = true, | |
| 15 | .attribute_availability_with_strict = true, | |
| 16 | .attribute_availability_with_replacement = true, | |
| 17 | .attribute_availability_in_templates = true, | |
| 18 | .attribute_availability_swift = true, | |
| 19 | .attribute_cf_returns_not_retained = true, | |
| 20 | .attribute_cf_returns_retained = true, | |
| 21 | .attribute_cf_returns_on_parameters = true, | |
| 22 | .attribute_deprecated_with_message = true, | |
| 23 | .attribute_deprecated_with_replacement = true, | |
| 24 | .attribute_ext_vector_type = true, | |
| 25 | .attribute_ns_returns_not_retained = true, | |
| 26 | .attribute_ns_returns_retained = true, | |
| 27 | .attribute_ns_consumes_self = true, | |
| 28 | .attribute_ns_consumed = true, | |
| 29 | .attribute_cf_consumed = true, | |
| 30 | .attribute_overloadable = true, | |
| 31 | .attribute_unavailable_with_message = true, | |
| 32 | .attribute_unused_on_fields = true, | |
| 33 | .attribute_diagnose_if_objc = true, | |
| 34 | .blocks = false, // TODO | |
| 35 | .c_thread_safety_attributes = true, | |
| 36 | .enumerator_attributes = true, | |
| 37 | .nullability = true, | |
| 38 | .nullability_on_arrays = true, | |
| 39 | .nullability_nullable_result = true, | |
| 40 | .c_alignas = comp.langopts.standard.atLeast(.c11), | |
| 41 | .c_alignof = comp.langopts.standard.atLeast(.c11), | |
| 42 | .c_atomic = comp.langopts.standard.atLeast(.c11), | |
| 43 | .c_generic_selections = comp.langopts.standard.atLeast(.c11), | |
| 44 | .c_static_assert = comp.langopts.standard.atLeast(.c11), | |
| 45 | .c_thread_local = comp.langopts.standard.atLeast(.c11) and comp.isTlsSupported(), | |
| 46 | }; | |
| 47 | inline for (std.meta.fields(@TypeOf(list))) |f| { | |
| 48 | if (std.mem.eql(u8, f.name, ext)) return @field(list, f.name); | |
| 49 | } | |
| 50 | return false; | |
| 51 | } | |
| 52 | ||
| 53 | /// Used to implement the __has_extension macro. | |
| 54 | pub fn hasExtension(comp: *Compilation, ext: []const u8) bool { | |
| 55 | const list = .{ | |
| 56 | // C11 features | |
| 57 | .c_alignas = true, | |
| 58 | .c_alignof = true, | |
| 59 | .c_atomic = false, // TODO | |
| 60 | .c_generic_selections = true, | |
| 61 | .c_static_assert = true, | |
| 62 | .c_thread_local = comp.isTlsSupported(), | |
| 63 | // misc | |
| 64 | .overloadable_unmarked = false, // TODO | |
| 65 | .statement_attributes_with_gnu_syntax = false, // TODO | |
| 66 | .gnu_asm = true, | |
| 67 | .gnu_asm_goto_with_outputs = true, | |
| 68 | .matrix_types = false, // TODO | |
| 69 | .matrix_types_scalar_division = false, // TODO | |
| 70 | }; | |
| 71 | inline for (std.meta.fields(@TypeOf(list))) |f| { | |
| 72 | if (std.mem.eql(u8, f.name, ext)) return @field(list, f.name); | |
| 73 | } | |
| 74 | return false; | |
| 75 | } |
src/aro/lib.zig created+13| ... | ... | @@ -0,0 +1,13 @@ |
| 1 | pub const Codegen = @import("Codegen.zig"); | |
| 2 | pub const Compilation = @import("Compilation.zig"); | |
| 3 | pub const Diagnostics = @import("Diagnostics.zig"); | |
| 4 | pub const Parser = @import("Parser.zig"); | |
| 5 | pub const Preprocessor = @import("Preprocessor.zig"); | |
| 6 | pub const Source = @import("Source.zig"); | |
| 7 | pub const Tokenizer = @import("Tokenizer.zig"); | |
| 8 | pub const Tree = @import("Tree.zig"); | |
| 9 | pub const Type = @import("Type.zig"); | |
| 10 | pub const Value = @import("Value.zig"); | |
| 11 | ||
| 12 | pub const version_str = "0.0.0-dev"; | |
| 13 | pub const version = @import("std").SemanticVersion.parse(version_str) catch unreachable; |
src/aro/pragmas/gcc.zig created+199| ... | ... | @@ -0,0 +1,199 @@ |
| 1 | const std = @import("std"); | |
| 2 | const mem = std.mem; | |
| 3 | const Compilation = @import("../Compilation.zig"); | |
| 4 | const Pragma = @import("../Pragma.zig"); | |
| 5 | const Diagnostics = @import("../Diagnostics.zig"); | |
| 6 | const Preprocessor = @import("../Preprocessor.zig"); | |
| 7 | const Parser = @import("../Parser.zig"); | |
| 8 | const TokenIndex = @import("../Tree.zig").TokenIndex; | |
| 9 | ||
| 10 | const GCC = @This(); | |
| 11 | ||
| 12 | pragma: Pragma = .{ | |
| 13 | .beforeParse = beforeParse, | |
| 14 | .beforePreprocess = beforePreprocess, | |
| 15 | .afterParse = afterParse, | |
| 16 | .deinit = deinit, | |
| 17 | .preprocessorHandler = preprocessorHandler, | |
| 18 | .parserHandler = parserHandler, | |
| 19 | .preserveTokens = preserveTokens, | |
| 20 | }, | |
| 21 | original_options: Diagnostics.Options = .{}, | |
| 22 | options_stack: std.ArrayListUnmanaged(Diagnostics.Options) = .{}, | |
| 23 | ||
| 24 | const Directive = enum { | |
| 25 | warning, | |
| 26 | @"error", | |
| 27 | diagnostic, | |
| 28 | poison, | |
| 29 | const Diagnostics = enum { | |
| 30 | ignored, | |
| 31 | warning, | |
| 32 | @"error", | |
| 33 | fatal, | |
| 34 | push, | |
| 35 | pop, | |
| 36 | }; | |
| 37 | }; | |
| 38 | ||
| 39 | fn beforePreprocess(pragma: *Pragma, comp: *Compilation) void { | |
| 40 | var self = @fieldParentPtr(GCC, "pragma", pragma); | |
| 41 | self.original_options = comp.diag.options; | |
| 42 | } | |
| 43 | ||
| 44 | fn beforeParse(pragma: *Pragma, comp: *Compilation) void { | |
| 45 | var self = @fieldParentPtr(GCC, "pragma", pragma); | |
| 46 | comp.diag.options = self.original_options; | |
| 47 | self.options_stack.items.len = 0; | |
| 48 | } | |
| 49 | ||
| 50 | fn afterParse(pragma: *Pragma, comp: *Compilation) void { | |
| 51 | var self = @fieldParentPtr(GCC, "pragma", pragma); | |
| 52 | comp.diag.options = self.original_options; | |
| 53 | self.options_stack.items.len = 0; | |
| 54 | } | |
| 55 | ||
| 56 | pub fn init(allocator: mem.Allocator) !*Pragma { | |
| 57 | var gcc = try allocator.create(GCC); | |
| 58 | gcc.* = .{}; | |
| 59 | return &gcc.pragma; | |
| 60 | } | |
| 61 | ||
| 62 | fn deinit(pragma: *Pragma, comp: *Compilation) void { | |
| 63 | var self = @fieldParentPtr(GCC, "pragma", pragma); | |
| 64 | self.options_stack.deinit(comp.gpa); | |
| 65 | comp.gpa.destroy(self); | |
| 66 | } | |
| 67 | ||
| 68 | fn diagnosticHandler(self: *GCC, pp: *Preprocessor, start_idx: TokenIndex) Pragma.Error!void { | |
| 69 | const diagnostic_tok = pp.tokens.get(start_idx); | |
| 70 | if (diagnostic_tok.id == .nl) return; | |
| 71 | ||
| 72 | const diagnostic = std.meta.stringToEnum(Directive.Diagnostics, pp.expandedSlice(diagnostic_tok)) orelse | |
| 73 | return error.UnknownPragma; | |
| 74 | ||
| 75 | switch (diagnostic) { | |
| 76 | .ignored, .warning, .@"error", .fatal => { | |
| 77 | const str = Pragma.pasteTokens(pp, start_idx + 1) catch |err| switch (err) { | |
| 78 | error.ExpectedStringLiteral => { | |
| 79 | return pp.comp.diag.add(.{ | |
| 80 | .tag = .pragma_requires_string_literal, | |
| 81 | .loc = diagnostic_tok.loc, | |
| 82 | .extra = .{ .str = "GCC diagnostic" }, | |
| 83 | }, diagnostic_tok.expansionSlice()); | |
| 84 | }, | |
| 85 | else => |e| return e, | |
| 86 | }; | |
| 87 | if (!mem.startsWith(u8, str, "-W")) { | |
| 88 | const next = pp.tokens.get(start_idx + 1); | |
| 89 | return pp.comp.diag.add(.{ | |
| 90 | .tag = .malformed_warning_check, | |
| 91 | .loc = next.loc, | |
| 92 | .extra = .{ .str = "GCC diagnostic" }, | |
| 93 | }, next.expansionSlice()); | |
| 94 | } | |
| 95 | const new_kind = switch (diagnostic) { | |
| 96 | .ignored => Diagnostics.Kind.off, | |
| 97 | .warning => Diagnostics.Kind.warning, | |
| 98 | .@"error" => Diagnostics.Kind.@"error", | |
| 99 | .fatal => Diagnostics.Kind.@"fatal error", | |
| 100 | else => unreachable, | |
| 101 | }; | |
| 102 | ||
| 103 | try pp.comp.diag.set(str[2..], new_kind); | |
| 104 | }, | |
| 105 | .push => try self.options_stack.append(pp.comp.gpa, pp.comp.diag.options), | |
| 106 | .pop => pp.comp.diag.options = self.options_stack.popOrNull() orelse self.original_options, | |
| 107 | } | |
| 108 | } | |
| 109 | ||
| 110 | fn preprocessorHandler(pragma: *Pragma, pp: *Preprocessor, start_idx: TokenIndex) Pragma.Error!void { | |
| 111 | var self = @fieldParentPtr(GCC, "pragma", pragma); | |
| 112 | const directive_tok = pp.tokens.get(start_idx + 1); | |
| 113 | if (directive_tok.id == .nl) return; | |
| 114 | ||
| 115 | const gcc_pragma = std.meta.stringToEnum(Directive, pp.expandedSlice(directive_tok)) orelse | |
| 116 | return pp.comp.diag.add(.{ | |
| 117 | .tag = .unknown_gcc_pragma, | |
| 118 | .loc = directive_tok.loc, | |
| 119 | }, directive_tok.expansionSlice()); | |
| 120 | ||
| 121 | switch (gcc_pragma) { | |
| 122 | .warning, .@"error" => { | |
| 123 | const text = Pragma.pasteTokens(pp, start_idx + 2) catch |err| switch (err) { | |
| 124 | error.ExpectedStringLiteral => { | |
| 125 | return pp.comp.diag.add(.{ | |
| 126 | .tag = .pragma_requires_string_literal, | |
| 127 | .loc = directive_tok.loc, | |
| 128 | .extra = .{ .str = @tagName(gcc_pragma) }, | |
| 129 | }, directive_tok.expansionSlice()); | |
| 130 | }, | |
| 131 | else => |e| return e, | |
| 132 | }; | |
| 133 | const extra = Diagnostics.Message.Extra{ .str = try pp.comp.diag.arena.allocator().dupe(u8, text) }; | |
| 134 | const diagnostic_tag: Diagnostics.Tag = if (gcc_pragma == .warning) .pragma_warning_message else .pragma_error_message; | |
| 135 | return pp.comp.diag.add( | |
| 136 | .{ .tag = diagnostic_tag, .loc = directive_tok.loc, .extra = extra }, | |
| 137 | directive_tok.expansionSlice(), | |
| 138 | ); | |
| 139 | }, | |
| 140 | .diagnostic => return self.diagnosticHandler(pp, start_idx + 2) catch |err| switch (err) { | |
| 141 | error.UnknownPragma => { | |
| 142 | const tok = pp.tokens.get(start_idx + 2); | |
| 143 | return pp.comp.diag.add(.{ | |
| 144 | .tag = .unknown_gcc_pragma_directive, | |
| 145 | .loc = tok.loc, | |
| 146 | }, tok.expansionSlice()); | |
| 147 | }, | |
| 148 | else => |e| return e, | |
| 149 | }, | |
| 150 | .poison => { | |
| 151 | var i: usize = 2; | |
| 152 | while (true) : (i += 1) { | |
| 153 | const tok = pp.tokens.get(start_idx + i); | |
| 154 | if (tok.id == .nl) break; | |
| 155 | ||
| 156 | if (!tok.id.isMacroIdentifier()) { | |
| 157 | return pp.comp.diag.add(.{ | |
| 158 | .tag = .pragma_poison_identifier, | |
| 159 | .loc = tok.loc, | |
| 160 | }, tok.expansionSlice()); | |
| 161 | } | |
| 162 | const str = pp.expandedSlice(tok); | |
| 163 | if (pp.defines.get(str) != null) { | |
| 164 | try pp.comp.diag.add(.{ | |
| 165 | .tag = .pragma_poison_macro, | |
| 166 | .loc = tok.loc, | |
| 167 | }, tok.expansionSlice()); | |
| 168 | } | |
| 169 | try pp.poisoned_identifiers.put(str, {}); | |
| 170 | } | |
| 171 | return; | |
| 172 | }, | |
| 173 | } | |
| 174 | } | |
| 175 | ||
| 176 | fn parserHandler(pragma: *Pragma, p: *Parser, start_idx: TokenIndex) Compilation.Error!void { | |
| 177 | var self = @fieldParentPtr(GCC, "pragma", pragma); | |
| 178 | const directive_tok = p.pp.tokens.get(start_idx + 1); | |
| 179 | if (directive_tok.id == .nl) return; | |
| 180 | const name = p.pp.expandedSlice(directive_tok); | |
| 181 | if (mem.eql(u8, name, "diagnostic")) { | |
| 182 | return self.diagnosticHandler(p.pp, start_idx + 2) catch |err| switch (err) { | |
| 183 | error.UnknownPragma => {}, // handled during preprocessing | |
| 184 | error.StopPreprocessing => unreachable, // Only used by #pragma once | |
| 185 | else => |e| return e, | |
| 186 | }; | |
| 187 | } | |
| 188 | } | |
| 189 | ||
| 190 | fn preserveTokens(_: *Pragma, pp: *Preprocessor, start_idx: TokenIndex) bool { | |
| 191 | const next = pp.tokens.get(start_idx + 1); | |
| 192 | if (next.id != .nl) { | |
| 193 | const name = pp.expandedSlice(next); | |
| 194 | if (mem.eql(u8, name, "poison")) { | |
| 195 | return false; | |
| 196 | } | |
| 197 | } | |
| 198 | return true; | |
| 199 | } |
src/aro/pragmas/message.zig created+50| ... | ... | @@ -0,0 +1,50 @@ |
| 1 | const std = @import("std"); | |
| 2 | const mem = std.mem; | |
| 3 | const Compilation = @import("../Compilation.zig"); | |
| 4 | const Pragma = @import("../Pragma.zig"); | |
| 5 | const Diagnostics = @import("../Diagnostics.zig"); | |
| 6 | const Preprocessor = @import("../Preprocessor.zig"); | |
| 7 | const Parser = @import("../Parser.zig"); | |
| 8 | const TokenIndex = @import("../Tree.zig").TokenIndex; | |
| 9 | const Source = @import("../Source.zig"); | |
| 10 | ||
| 11 | const Message = @This(); | |
| 12 | ||
| 13 | pragma: Pragma = .{ | |
| 14 | .deinit = deinit, | |
| 15 | .preprocessorHandler = preprocessorHandler, | |
| 16 | }, | |
| 17 | ||
| 18 | pub fn init(allocator: mem.Allocator) !*Pragma { | |
| 19 | var once = try allocator.create(Message); | |
| 20 | once.* = .{}; | |
| 21 | return &once.pragma; | |
| 22 | } | |
| 23 | ||
| 24 | fn deinit(pragma: *Pragma, comp: *Compilation) void { | |
| 25 | var self = @fieldParentPtr(Message, "pragma", pragma); | |
| 26 | comp.gpa.destroy(self); | |
| 27 | } | |
| 28 | ||
| 29 | fn preprocessorHandler(_: *Pragma, pp: *Preprocessor, start_idx: TokenIndex) Pragma.Error!void { | |
| 30 | const message_tok = pp.tokens.get(start_idx); | |
| 31 | const message_expansion_locs = message_tok.expansionSlice(); | |
| 32 | ||
| 33 | const str = Pragma.pasteTokens(pp, start_idx + 1) catch |err| switch (err) { | |
| 34 | error.ExpectedStringLiteral => { | |
| 35 | return pp.comp.diag.add(.{ | |
| 36 | .tag = .pragma_requires_string_literal, | |
| 37 | .loc = message_tok.loc, | |
| 38 | .extra = .{ .str = "message" }, | |
| 39 | }, message_expansion_locs); | |
| 40 | }, | |
| 41 | else => |e| return e, | |
| 42 | }; | |
| 43 | ||
| 44 | const loc = if (message_expansion_locs.len != 0) | |
| 45 | message_expansion_locs[message_expansion_locs.len - 1] | |
| 46 | else | |
| 47 | message_tok.loc; | |
| 48 | const extra = Diagnostics.Message.Extra{ .str = try pp.comp.diag.arena.allocator().dupe(u8, str) }; | |
| 49 | return pp.comp.diag.add(.{ .tag = .pragma_message, .loc = loc, .extra = extra }, &.{}); | |
| 50 | } |
src/aro/pragmas/once.zig created+56| ... | ... | @@ -0,0 +1,56 @@ |
| 1 | const std = @import("std"); | |
| 2 | const mem = std.mem; | |
| 3 | const Compilation = @import("../Compilation.zig"); | |
| 4 | const Pragma = @import("../Pragma.zig"); | |
| 5 | const Diagnostics = @import("../Diagnostics.zig"); | |
| 6 | const Preprocessor = @import("../Preprocessor.zig"); | |
| 7 | const Parser = @import("../Parser.zig"); | |
| 8 | const TokenIndex = @import("../Tree.zig").TokenIndex; | |
| 9 | const Source = @import("../Source.zig"); | |
| 10 | ||
| 11 | const Once = @This(); | |
| 12 | ||
| 13 | pragma: Pragma = .{ | |
| 14 | .afterParse = afterParse, | |
| 15 | .deinit = deinit, | |
| 16 | .preprocessorHandler = preprocessorHandler, | |
| 17 | }, | |
| 18 | pragma_once: std.AutoHashMap(Source.Id, void), | |
| 19 | preprocess_count: u32 = 0, | |
| 20 | ||
| 21 | pub fn init(allocator: mem.Allocator) !*Pragma { | |
| 22 | var once = try allocator.create(Once); | |
| 23 | once.* = .{ | |
| 24 | .pragma_once = std.AutoHashMap(Source.Id, void).init(allocator), | |
| 25 | }; | |
| 26 | return &once.pragma; | |
| 27 | } | |
| 28 | ||
| 29 | fn afterParse(pragma: *Pragma, _: *Compilation) void { | |
| 30 | var self = @fieldParentPtr(Once, "pragma", pragma); | |
| 31 | self.pragma_once.clearRetainingCapacity(); | |
| 32 | } | |
| 33 | ||
| 34 | fn deinit(pragma: *Pragma, comp: *Compilation) void { | |
| 35 | var self = @fieldParentPtr(Once, "pragma", pragma); | |
| 36 | self.pragma_once.deinit(); | |
| 37 | comp.gpa.destroy(self); | |
| 38 | } | |
| 39 | ||
| 40 | fn preprocessorHandler(pragma: *Pragma, pp: *Preprocessor, start_idx: TokenIndex) Pragma.Error!void { | |
| 41 | var self = @fieldParentPtr(Once, "pragma", pragma); | |
| 42 | const name_tok = pp.tokens.get(start_idx); | |
| 43 | const next = pp.tokens.get(start_idx + 1); | |
| 44 | if (next.id != .nl) { | |
| 45 | try pp.comp.diag.add(.{ | |
| 46 | .tag = .extra_tokens_directive_end, | |
| 47 | .loc = name_tok.loc, | |
| 48 | }, next.expansionSlice()); | |
| 49 | } | |
| 50 | const seen = self.preprocess_count == pp.preprocess_count; | |
| 51 | const prev = try self.pragma_once.fetchPut(name_tok.loc.id, {}); | |
| 52 | if (prev != null and !seen) { | |
| 53 | return error.StopPreprocessing; | |
| 54 | } | |
| 55 | self.preprocess_count = pp.preprocess_count; | |
| 56 | } |
src/aro/util.zig created+56| ... | ... | @@ -0,0 +1,56 @@ |
| 1 | const std = @import("std"); | |
| 2 | const is_windows = @import("builtin").os.tag == .windows; | |
| 3 | ||
| 4 | pub const Color = enum { | |
| 5 | reset, | |
| 6 | red, | |
| 7 | green, | |
| 8 | blue, | |
| 9 | cyan, | |
| 10 | purple, | |
| 11 | yellow, | |
| 12 | white, | |
| 13 | }; | |
| 14 | ||
| 15 | pub fn setColor(color: Color, w: anytype) void { | |
| 16 | if (is_windows) { | |
| 17 | const stderr_file = std.io.getStdErr(); | |
| 18 | if (!stderr_file.isTty()) return; | |
| 19 | const windows = std.os.windows; | |
| 20 | const S = struct { | |
| 21 | var attrs: windows.WORD = undefined; | |
| 22 | var init_attrs = false; | |
| 23 | }; | |
| 24 | if (!S.init_attrs) { | |
| 25 | S.init_attrs = true; | |
| 26 | var info: windows.CONSOLE_SCREEN_BUFFER_INFO = undefined; | |
| 27 | _ = windows.kernel32.GetConsoleScreenBufferInfo(stderr_file.handle, &info); | |
| 28 | S.attrs = info.wAttributes; | |
| 29 | _ = windows.kernel32.SetConsoleOutputCP(65001); | |
| 30 | } | |
| 31 | ||
| 32 | // need to flush bufferedWriter | |
| 33 | const T = if (@typeInfo(@TypeOf(w.context)) == .Pointer) @TypeOf(w.context.*) else @TypeOf(w.context); | |
| 34 | if (T != void and @hasDecl(T, "flush")) w.context.flush() catch {}; | |
| 35 | ||
| 36 | switch (color) { | |
| 37 | .reset => _ = windows.SetConsoleTextAttribute(stderr_file.handle, S.attrs) catch {}, | |
| 38 | .red => _ = windows.SetConsoleTextAttribute(stderr_file.handle, windows.FOREGROUND_RED | windows.FOREGROUND_INTENSITY) catch {}, | |
| 39 | .green => _ = windows.SetConsoleTextAttribute(stderr_file.handle, windows.FOREGROUND_GREEN | windows.FOREGROUND_INTENSITY) catch {}, | |
| 40 | .blue => _ = windows.SetConsoleTextAttribute(stderr_file.handle, windows.FOREGROUND_BLUE | windows.FOREGROUND_INTENSITY) catch {}, | |
| 41 | .cyan => _ = windows.SetConsoleTextAttribute(stderr_file.handle, windows.FOREGROUND_GREEN | windows.FOREGROUND_BLUE | windows.FOREGROUND_INTENSITY) catch {}, | |
| 42 | .purple => _ = windows.SetConsoleTextAttribute(stderr_file.handle, windows.FOREGROUND_RED | windows.FOREGROUND_BLUE | windows.FOREGROUND_INTENSITY) catch {}, | |
| 43 | .yellow => _ = windows.SetConsoleTextAttribute(stderr_file.handle, windows.FOREGROUND_RED | windows.FOREGROUND_GREEN | windows.FOREGROUND_INTENSITY) catch {}, | |
| 44 | .white => _ = windows.SetConsoleTextAttribute(stderr_file.handle, windows.FOREGROUND_RED | windows.FOREGROUND_GREEN | windows.FOREGROUND_BLUE | windows.FOREGROUND_INTENSITY) catch {}, | |
| 45 | } | |
| 46 | } else switch (color) { | |
| 47 | .reset => w.writeAll("\x1b[0m") catch {}, | |
| 48 | .red => w.writeAll("\x1b[31;1m") catch {}, | |
| 49 | .green => w.writeAll("\x1b[32;1m") catch {}, | |
| 50 | .blue => w.writeAll("\x1b[34;1m") catch {}, | |
| 51 | .cyan => w.writeAll("\x1b[36;1m") catch {}, | |
| 52 | .purple => w.writeAll("\x1b[35;1m") catch {}, | |
| 53 | .yellow => w.writeAll("\x1b[93;1m") catch {}, | |
| 54 | .white => w.writeAll("\x1b[0m\x1b[1m") catch {}, | |
| 55 | } | |
| 56 | } |