authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-02-04 19:42:03-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-02-04 19:42:03-07:00
log376d443ae314e6e999fb58fd6d8e8a4adc4fbdeb
treefdf08175822edd14d1a985691fe3bc4ed9c8b497
parent04f379dd414184a42412f4497b0573d7612d6730

add Aro as a C frontend

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");
3535const libtsan = @import("libtsan.zig");
3636const Zir = @import("Zir.zig");
3737const Color = @import("main.zig").Color;
38const aro = @import("aro/lib.zig");
3839
3940/// General-purpose allocator. Used for both temporary and long-term storage.
4041gpa: Allocator,
......@@ -237,6 +238,10 @@ const Job = union(enum) {
237238
238239 /// The value is the index into `link.File.Options.system_libs`.
239240 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,
240245};
241246
242247pub const CObject = struct {
......@@ -1677,17 +1682,20 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {
16771682 try comp.astgen_wait_group.init();
16781683 errdefer comp.astgen_wait_group.deinit();
16791684
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 }
16911699 }
16921700
16931701 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 {
20252033
20262034 // For compiling C objects, we rely on the cache hash system to avoid duplicating work.
20272035 // 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 }
20312047 }
20322048
20332049 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
28112827 return;
28122828 },
28132829 },
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 },
28142839 .emit_h_decl => |decl| switch (decl.analysis) {
28152840 .unreferenced => unreachable,
28162841 .in_progress => unreachable,
......@@ -3145,6 +3170,58 @@ fn processOneJob(comp: *Compilation, job: Job, main_progress_node: *std.Progress
31453170 }
31463171}
31473172
3173fn 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
31483225const AstGenSrc = union(enum) {
31493226 root,
31503227 import: struct {
src/Module.zig+25
......@@ -4709,6 +4709,31 @@ pub fn createAnonymousDecl(mod: *Module, block: *Sema.Block, typed_value: TypedV
47094709 return mod.createAnonymousDeclFromDecl(block.src_decl, block.namespace, block.wip_capture_scope, typed_value);
47104710}
47114711
4712/// TODO cleanup
4713pub 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
47124737pub fn createAnonymousDeclFromDecl(
47134738 mod: *Module,
47144739 src_decl: *Decl,
src/Sema.zig-5
......@@ -164,11 +164,6 @@ pub const Block = struct {
164164 br_list: std.ArrayListUnmanaged(Air.Inst.Index),
165165 };
166166
167 /// For debugging purposes.
168 pub fn dump(block: *Block, mod: Module) void {
169 Zir.dumpBlock(mod, block);
170 }
171
172167 pub fn makeSubBlock(parent: *Block) Block {
173168 return .{
174169 .parent = parent,
src/aro/Attribute.zig created+978
......@@ -0,0 +1,978 @@
1const std = @import("std");
2const mem = std.mem;
3const Tree = @import("Tree.zig");
4const Diagnostics = @import("Diagnostics.zig");
5const Value = @import("Value.zig");
6const Compilation = @import("Compilation.zig");
7const Type = @import("Type.zig");
8const NodeIndex = Tree.NodeIndex;
9const TokenIndex = Tree.TokenIndex;
10const TypeInfo = std.builtin.TypeInfo;
11
12const Attribute = @This();
13
14tag: Tag,
15args: Arguments,
16
17pub const Kind = enum {
18 c2x,
19 declspec,
20 gnu,
21};
22
23pub 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
67fn 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
72pub 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
88pub 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
103fn UnwrapOptional(comptime T: type) type {
104 return switch (@typeInfo(T)) {
105 .Optional => |optional| optional.child,
106 else => T,
107 };
108}
109
110pub 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
158pub 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
174pub 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
195pub 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
212pub 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
240fn 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
286pub 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
307const EnumTypes = enum {
308 string,
309 identifier,
310};
311pub const Alignment = struct {
312 node: NodeIndex = .none,
313 requested: u29,
314 alignas: bool = false,
315};
316pub const Identifier = struct {
317 tok: TokenIndex = 0,
318};
319
320const 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
877pub const Tag = std.meta.DeclEnum(attributes);
878
879pub 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
900pub 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
905pub 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
919pub 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
927fn 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
941fn 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
961fn 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
973fn 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 @@
1const std = @import("std");
2const Compilation = @import("Compilation.zig");
3const Type = @import("Type.zig");
4
5const Builtins = @This();
6
7const 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};
22const BuiltinMap = std.StringHashMapUnmanaged(Builtin);
23
24_builtins: BuiltinMap = .{},
25_params: []Type.Func.Param = &.{},
26
27pub fn deinit(b: *Builtins, gpa: std.mem.Allocator) void {
28 b._builtins.deinit(gpa);
29 gpa.free(b._params);
30}
31
32fn 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
55pub 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
78pub 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
84pub 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
6const assert = @import("std").debug.assert;
7
8/// C11 Standard Annex D
9pub 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
78pub 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
388pub 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
402pub 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
424pub 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
443pub 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 @@
1aro_comp: *aro.Compilation,
2tree: aro.Tree,
3bin_file: *link.File,
4arena: Allocator,
5gpa: Allocator,
6verbose_air: bool,
7
8const builtin = @import("builtin");
9const std = @import("std");
10const Allocator = std.mem.Allocator;
11const log = std.log.scoped(.aro);
12
13const Codegen = @This();
14const aro = @import("lib.zig");
15const link = @import("../link.zig");
16const NodeIndex = aro.Tree.NodeIndex;
17const Value = @import("../value.zig").Value;
18const Type = @import("../type.zig").Type;
19const TypedValue = @import("../TypedValue.zig");
20const Air = @import("../Air.zig");
21const Compilation = @import("../Compilation.zig");
22const Module = @import("../Module.zig");
23const Liveness = @import("../Liveness.zig");
24
25pub 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
81const 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
218fn 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
284fn 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
358fn 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
376const Error = error{OutOfMemory};
377
378fn 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
468fn 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
492fn 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
498pub 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 @@
1const std = @import("std");
2const assert = std.debug.assert;
3const mem = std.mem;
4const Allocator = mem.Allocator;
5const EpochSeconds = std.time.epoch.EpochSeconds;
6const Builtins = @import("Builtins.zig");
7const Diagnostics = @import("Diagnostics.zig");
8const LangOpts = @import("LangOpts.zig");
9const Source = @import("Source.zig");
10const Tokenizer = @import("Tokenizer.zig");
11const Token = Tokenizer.Token;
12const Type = @import("Type.zig");
13const Pragma = @import("Pragma.zig");
14
15const Compilation = @This();
16
17pub const Error = error{
18 /// A fatal error has ocurred and compilation has stopped.
19 FatalError,
20} || Allocator.Error;
21
22gpa: Allocator,
23sources: std.StringArrayHashMap(Source),
24diag: Diagnostics,
25include_dirs: std.ArrayList([]const u8),
26system_include_dirs: std.ArrayList([]const u8),
27output_name: ?[]const u8 = null,
28builtin_header_path: ?[]u8 = null,
29target: std.Target = @import("builtin").target,
30pragma_handlers: std.StringArrayHashMap(*Pragma),
31only_preprocess: bool = false,
32only_compile: bool = false,
33verbose_ast: bool = false,
34langopts: LangOpts = .{},
35generated_buf: std.ArrayList(u8),
36builtins: Builtins = .{},
37types: struct {
38 wchar: Type,
39 ptrdiff: Type,
40 size: Type,
41 va_list: Type,
42} = undefined,
43
44pub 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
56pub 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
75fn 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.
114pub 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
345fn 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
351fn 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
392fn 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
469fn 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
479fn 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
483pub 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
500pub 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`.
518pub 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`.
616pub 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`
629pub 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
648pub 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
687pub fn addPragmaHandler(comp: *Compilation, name: []const u8, handler: *Pragma) Allocator.Error!void {
688 try comp.pragma_handlers.putNoClobber(name, handler);
689}
690
691pub 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
709pub fn getPragma(comp: *Compilation, name: []const u8) ?*Pragma {
710 return comp.pragma_handlers.get(name);
711}
712
713const PragmaEvent = enum {
714 before_preprocess,
715 before_parse,
716 after_parse,
717};
718
719pub 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
730pub const renderErrors = Diagnostics.render;
731
732pub 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
748pub 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
764test "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
808test "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 @@
1const std = @import("std");
2const mem = std.mem;
3const Allocator = mem.Allocator;
4const Source = @import("Source.zig");
5const Compilation = @import("Compilation.zig");
6const Attribute = @import("Attribute.zig");
7const Tree = @import("Tree.zig");
8const util = @import("util.zig");
9const is_windows = @import("builtin").os.tag == .windows;
10
11const Diagnostics = @This();
12
13pub 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
56pub const Tag = std.meta.DeclEnum(messages);
57
58// u4 to avoid any possible packed struct issues
59pub const Kind = enum(u4) { @"fatal error", @"error", note, warning, off, default };
60
61pub 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
126const 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
1595list: std.ArrayListUnmanaged(Message) = .{},
1596arena: std.heap.ArenaAllocator,
1597color: bool = true,
1598fatal_errors: bool = false,
1599options: Options = .{},
1600errors: u32 = 0,
1601macro_backtrace_limit: u32 = 6,
1602
1603pub 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
1610pub 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
1623pub 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
1630pub fn deinit(diag: *Diagnostics) void {
1631 diag.list.deinit(diag.arena.allocator());
1632 diag.arena.deinit();
1633}
1634
1635pub 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
1687pub 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
1706pub 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
1720pub 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
1728pub 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
1833fn 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
1866const 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.
3const std = @import("std");
4const Allocator = std.mem.Allocator;
5const testing = std.testing;
6const Tree = @import("Tree.zig");
7const Token = Tree.Token;
8const TokenIndex = Tree.TokenIndex;
9const NodeIndex = Tree.NodeIndex;
10const Type = @import("Type.zig");
11const Diagnostics = @import("Diagnostics.zig");
12const NodeList = std.ArrayList(NodeIndex);
13const Parser = @import("Parser.zig");
14
15const InitList = @This();
16
17const 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
26list: std.ArrayListUnmanaged(Item) = .{},
27node: NodeIndex = .none,
28tok: TokenIndex = 0,
29
30/// Deinitialize freeing all memory.
31pub 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.
38pub 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.
82pub 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
116test "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 @@
1const std = @import("std");
2const DiagnosticTag = @import("Diagnostics.zig").Tag;
3
4const LangOpts = @This();
5
6const 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
72standard: Standard = .default,
73/// -fshort-enums option, makes enums only take up as much space as they need to hold all the values.
74short_enums: bool = false,
75dollars_in_identifiers: bool = true,
76declspec_attrs: bool = false,
77
78pub fn setStandard(self: *LangOpts, name: []const u8) error{InvalidStandard}!void {
79 self.standard = Standard.NameMap.get(name) orelse return error.InvalidStandard;
80}
81
82pub fn enableMSExtensions(self: *LangOpts) void {
83 self.declspec_attrs = true;
84}
85
86pub fn disableMSExtensions(self: *LangOpts) void {
87 self.declspec_attrs = false;
88}
src/aro/Parser.zig created+6269
......@@ -0,0 +1,6269 @@
1const std = @import("std");
2const mem = std.mem;
3const Allocator = mem.Allocator;
4const assert = std.debug.assert;
5const Compilation = @import("Compilation.zig");
6const Source = @import("Source.zig");
7const Tokenizer = @import("Tokenizer.zig");
8const Preprocessor = @import("Preprocessor.zig");
9const Tree = @import("Tree.zig");
10const Token = Tree.Token;
11const TokenIndex = Tree.TokenIndex;
12const NodeIndex = Tree.NodeIndex;
13const Type = @import("Type.zig");
14const Diagnostics = @import("Diagnostics.zig");
15const NodeList = std.ArrayList(NodeIndex);
16const InitList = @import("InitList.zig");
17const Attribute = @import("Attribute.zig");
18const CharInfo = @import("CharInfo.zig");
19const Value = @import("Value.zig");
20
21const Parser = @This();
22
23const 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
71const Label = union(enum) {
72 unresolved_goto: TokenIndex,
73 label: TokenIndex,
74};
75
76pub const Error = Compilation.Error || error{ParsingFailed};
77
78/// An attribute that has been parsed but not yet validated in its context
79const TentativeAttribute = struct {
80 attr: Attribute,
81 tok: TokenIndex,
82};
83
84// values from preprocessor
85pp: *Preprocessor,
86tok_ids: []const Token.Id,
87tok_i: TokenIndex = 0,
88
89// values of the incomplete Tree
90arena: Allocator,
91nodes: Tree.Node.List = .{},
92data: NodeList,
93strings: std.ArrayList(u8),
94value_map: Tree.ValueMap,
95
96// buffers used during compilation
97scopes: std.ArrayList(Scope),
98labels: std.ArrayList(Label),
99list_buf: NodeList,
100decl_buf: NodeList,
101param_buf: std.ArrayList(Type.Func.Param),
102enum_buf: std.ArrayList(Type.Enum.Field),
103record_buf: std.ArrayList(Type.Record.Field),
104attr_buf: std.MultiArrayList(TentativeAttribute) = .{},
105
106// configuration and miscellaneous info
107no_eval: bool = false,
108in_macro: bool = false,
109extension_suppressed: bool = false,
110contains_address_of_label: bool = false,
111label_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)
115computed_goto_tok: ?TokenIndex = null,
116
117/// Various variables that are different for each function.
118func: 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.
126record: 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
166fn 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
195fn 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
233fn 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
242fn 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
250fn 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
258fn 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
271fn 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
285fn 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
293fn 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
305pub 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
310pub 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
327pub 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
332pub fn err(p: *Parser, tag: Diagnostics.Tag) Compilation.Error!void {
333 @setCold(true);
334 return p.errTok(tag, p.tok_i);
335}
336
337pub fn todo(p: *Parser, msg: []const u8) Error {
338 try p.errStr(.todo, p.tok_i, msg);
339 return error.ParsingFailed;
340}
341
342pub 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
350pub 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
359pub fn typePairStr(p: *Parser, a: Type, b: Type) ![]const u8 {
360 return p.typePairStrExtra(a, " and ", b);
361}
362
363pub 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
377fn 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
388fn 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
407fn 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
414fn 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
422fn 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
451fn 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
467fn 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
505fn 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
517fn 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
529fn 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
539fn 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
551fn nodeIs(p: *Parser, node: NodeIndex, tag: Tree.Tag) bool {
552 return p.getNode(node, tag) != null;
553}
554
555fn 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
571fn 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)*
590pub 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
693fn 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
703fn 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
713fn 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
765fn 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
784pub 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
794fn 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 ')' ';'
1020fn 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
1079pub 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 ')'
1186fn 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
1235fn 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
1308fn 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
1327const InitDeclarator = struct { d: Declarator, initializer: NodeIndex = .none };
1328
1329/// attribute
1330/// : attrIdentifier
1331/// | attrIdentifier '(' identifier ')'
1332/// | attrIdentifier '(' identifier (',' expr)+ ')'
1333/// | attrIdentifier '(' (expr (',' expr)*)? ')'
1334fn 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
1403fn 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)*)?
1412fn 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
1422fn 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
1436fn 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
1443fn 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
1459fn 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
1472fn 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 ) ')'
1488fn 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 ')' ')')*
1515fn 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)?
1526fn 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 ')'
1635fn 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
1707fn 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
1727fn 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
1842fn 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)?
1862fn 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
1975fn 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)+
1987fn 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
2002fn 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
2100const 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
2126const EnumFieldAndNode = struct { field: Type.Enum.Field, node: NodeIndex };
2127
2128/// enumerator : IDENTIFIER ('=' constExpr)
2129fn 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
2191fn 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
2229const Declarator = struct {
2230 name: TokenIndex,
2231 ty: Type,
2232 func_declarator: ?TokenIndex = null,
2233 old_style_func: ?TokenIndex = null,
2234};
2235const DeclaratorKind = enum { normal, abstract, param, record };
2236
2237/// declarator : pointer? (IDENTIFIER | '(' declarator ')') directDeclarator*
2238/// abstractDeclarator
2239/// : pointer? ('(' abstractDeclarator ')')? directAbstractDeclarator*
2240fn 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? ')'
2300fn 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?
2460fn 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)
2478fn 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
2569fn 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 '}'
2580fn 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
2607fn 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.
2788fn 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.
2837fn 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
2895fn 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
2940fn 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
2979fn 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
3038fn 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
3056fn 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 ')'
3191fn 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
3237fn 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? ';'
3270fn 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
3534fn 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
3623const 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)* '}'
3629fn 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
3715fn 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
3740fn 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
3750fn 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
3815fn 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
3878pub 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
3891const 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)*
4319fn 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
4338fn 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
4371fn 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
4501fn 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)?
4516fn 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)*
4554fn 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)*
4576fn 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)*
4598fn 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)*
4614fn 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)*
4630fn 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)*
4646fn 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)*
4668fn 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)*
4698fn 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)*
4721fn 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)*´
4744fn 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
4792fn 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
4811fn 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
4901fn 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
4938fn 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 ')'
4975fn 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)*
5238fn 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
5324fn 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
5364fn 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
5388fn 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
5563fn 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
5590fn 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
5821fn 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
5839fn 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
5922fn 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
5938fn 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
5950fn 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
6064fn 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
6085fn 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
6159fn 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
6191fn 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
6204fn 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 @@
1const std = @import("std");
2const Compilation = @import("Compilation.zig");
3const Preprocessor = @import("Preprocessor.zig");
4const Parser = @import("Parser.zig");
5const TokenIndex = @import("Tree.zig").TokenIndex;
6
7const Pragma = @This();
8
9pub const Error = Compilation.Error || error{ UnknownPragma, StopPreprocessing };
10
11/// Called during Preprocessor.init
12beforePreprocess: ?fn (*Pragma, *Compilation) void = null,
13
14/// Called at the beginning of Parser.parse
15beforeParse: ?fn (*Pragma, *Compilation) void = null,
16
17/// Called at the end of Parser.parse if a Tree was successfully parsed
18afterParse: ?fn (*Pragma, *Compilation) void = null,
19
20/// Called during Compilation.deinit
21deinit: 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)
31preprocessorHandler: ?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
35preserveTokens: ?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
39parserHandler: ?fn (*Pragma, *Parser, start_idx: TokenIndex) Compilation.Error!void = null,
40
41pub 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
70pub 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
75pub fn preprocessorCB(self: *Pragma, pp: *Preprocessor, start_idx: TokenIndex) Error!void {
76 if (self.preprocessorHandler) |func| return func(self, pp, start_idx);
77}
78
79pub 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 @@
1const std = @import("std");
2const mem = std.mem;
3const Allocator = mem.Allocator;
4const assert = std.debug.assert;
5const Compilation = @import("Compilation.zig");
6const Error = Compilation.Error;
7const Source = @import("Source.zig");
8const Tokenizer = @import("Tokenizer.zig");
9const RawToken = Tokenizer.Token;
10const Parser = @import("Parser.zig");
11const Diagnostics = @import("Diagnostics.zig");
12const Token = @import("Tree.zig").Token;
13const Attribute = @import("Attribute.zig");
14const features = @import("features.zig");
15
16const Preprocessor = @This();
17const DefineMap = std.StringHashMap(Macro);
18const RawTokenList = std.ArrayList(RawToken);
19const 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
24const MacroError = Error || error{StopPreprocessing};
25
26const 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
66comp: *Compilation,
67arena: std.heap.ArenaAllocator,
68defines: DefineMap,
69tokens: Token.List = .{},
70token_buf: RawTokenList,
71char_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
74preprocess_count: u32 = 0,
75generated_line: u32 = 1,
76add_expansion_nl: u32 = 0,
77include_depth: u8 = 0,
78counter: u32 = 0,
79expansion_source_loc: Source.Location = undefined,
80poisoned_identifiers: std.StringHashMap(void),
81/// Memory is retained to avoid allocation on every single token.
82top_expansion_buf: ExpandBuf,
83
84pub 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
98const 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
146fn 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
157pub 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
171pub 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.
183pub 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
191fn 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.
401pub 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.
408fn 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
419fn 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
430fn 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.
437fn 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.
448fn 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.
462fn 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.
600fn 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.
659fn 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
666const ExpandBuf = std.ArrayList(Token);
667const MacroArguments = std.ArrayList([]const Token);
668fn 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
676fn 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 '"')
729fn 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)
748fn 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
783fn 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
802fn 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
841fn 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
927fn 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
1051fn 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
1067fn 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
1076fn 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
1106fn 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
1191fn 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
1322fn 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.
1351pub 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
1370fn 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
1421fn 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
1434fn 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.
1449fn 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.
1533fn 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.
1685fn 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
1708fn 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
1718fn 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
1753fn 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.
1812pub 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
1863test "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
1917test "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 @@
1const std = @import("std");
2const Source = @This();
3
4pub const Id = enum(u32) {
5 unused = 0,
6 generated = 1,
7 _,
8};
9
10pub 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
20path: []const u8,
21buf: []const u8,
22id: Id,
23invalid_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
27splice_locs: []const u32,
28
29/// Todo: binary search instead of scanning entire `splice_locs`.
30pub 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
39pub fn physicalLine(source: Source, loc: Location) u32 {
40 return loc.line + source.numSplicesBefore(loc.byte_offset);
41}
42
43const LineCol = struct { line: []const u8, line_no: u32, col: u32, width: u32, end_with_splice: bool };
44
45pub 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
86fn 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
114fn 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
127pub 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 @@
1const std = @import("std");
2const assert = std.debug.assert;
3const Compilation = @import("Compilation.zig");
4const Source = @import("Source.zig");
5const LangOpts = @import("LangOpts.zig");
6const CharInfo = @import("CharInfo.zig");
7
8const Tokenizer = @This();
9
10pub 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
763buf: []const u8,
764index: u32 = 0,
765source: Source.Id,
766comp: *const Compilation,
767line: u32 = 1,
768
769pub 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
1655pub fn nextNoWS(self: *Tokenizer) Token {
1656 var tok = self.next();
1657 while (tok.id == .whitespace) tok = self.next();
1658 return tok;
1659}
1660
1661test "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
1730test "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
1798test "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
1829test "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
1860test "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
1894test "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
1939test "comments" {
1940 try expectTokens(
1941 \\//foo
1942 \\#foo
1943 , &.{
1944 .nl,
1945 .hash,
1946 .identifier,
1947 });
1948}
1949
1950test "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
1973fn 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 @@
1const std = @import("std");
2const Type = @import("Type.zig");
3const Tokenizer = @import("Tokenizer.zig");
4const Compilation = @import("Compilation.zig");
5const Source = @import("Source.zig");
6const Attribute = @import("Attribute.zig");
7const Value = @import("Value.zig");
8
9const Tree = @This();
10
11pub 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
75pub const TokenIndex = u32;
76pub const NodeIndex = enum(u32) { none, _ };
77pub const ValueMap = std.AutoHashMap(NodeIndex, Value);
78
79comp: *Compilation,
80arena: std.heap.ArenaAllocator,
81generated: []const u8,
82tokens: Token.List.Slice,
83nodes: Node.List.Slice,
84data: []const NodeIndex,
85root_decls: []const NodeIndex,
86strings: []const u8,
87value_map: ValueMap,
88
89pub 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
98pub 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
168pub 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
508pub 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
513pub 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
568pub 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
575pub 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
588pub 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
595fn 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
624fn 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 @@
1const std = @import("std");
2const Tree = @import("Tree.zig");
3const TokenIndex = Tree.TokenIndex;
4const NodeIndex = Tree.NodeIndex;
5const Parser = @import("Parser.zig");
6const Compilation = @import("Compilation.zig");
7const Attribute = @import("Attribute.zig");
8
9const Type = @This();
10
11pub 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
95pub 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
106pub const Array = struct {
107 len: u64,
108 elem: Type,
109};
110
111pub const Expr = struct {
112 node: NodeIndex,
113 ty: Type,
114};
115
116pub 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
138pub 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
163pub 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
193pub 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
267data: 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 = {} },
277specifier: Specifier,
278qual: Qualifiers = .{},
279
280/// Determine if type matches the given specifier, recursing into typeof
281/// types if necessary.
282pub 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
287pub 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
293pub 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
304pub 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
314pub 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
324pub 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
342pub 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
352pub 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
362pub 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
372pub 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
382pub 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
389pub 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
398pub 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
409pub 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
419pub 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
429pub 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
441pub 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
457pub 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
467pub 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
477pub 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
487pub 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
495pub 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
504pub 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
528pub 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
541pub 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
565pub 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
589pub 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
610pub 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
658pub 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
669pub 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.
720pub 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
750pub 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
760fn 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
783pub 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
848pub 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
853pub 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
880pub 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
930pub 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
1432pub 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
1447pub fn print(ty: Type, w: anytype) @TypeOf(w).Error!void {
1448 _ = try ty.printPrologue(w);
1449 try ty.printEpilogue(w);
1450}
1451
1452pub 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
1460fn 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
1520fn 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.
1582const dump_detailed_containers = false;
1583
1584// Print as Zig types since those are actually readable
1585pub 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
1660fn 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
1668fn 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 @@
1const std = @import("std");
2const assert = std.debug.assert;
3const Compilation = @import("Compilation.zig");
4const Type = @import("Type.zig");
5
6const Value = @This();
7
8tag: Tag = .unavailable,
9data: union {
10 none: void,
11 int: u64,
12 float: f64,
13 array: []Value,
14 bytes: []u8,
15} = .{ .none = {} },
16
17const 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
26pub fn zero(v: Value) Value {
27 return switch (v.tag) {
28 .int => int(0),
29 .float => float(0),
30 else => unreachable,
31 };
32}
33
34pub fn one(v: Value) Value {
35 return switch (v.tag) {
36 .int => int(1),
37 .float => float(1),
38 else => unreachable,
39 };
40}
41
42pub 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
49pub fn float(v: anytype) Value {
50 return .{ .tag = .float, .data = .{ .float = v } };
51}
52
53pub fn bytes(v: anytype) Value {
54 return .{ .tag = .bytes, .data = .{ .bytes = v } };
55}
56
57pub 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.
68pub 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.
88pub 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.
102pub 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.
120pub 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
132pub fn toBool(v: *Value) void {
133 if (v.tag == .unavailable) return;
134 const res = v.getBool();
135 v.* = int(@boolToInt(res));
136}
137
138pub 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
148pub 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
158pub 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
166pub 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
171const 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
246pub const add = bin_overflow.getOp(bin_overflow.addInt, bin_overflow.addFloat);
247pub const sub = bin_overflow.getOp(bin_overflow.subInt, bin_overflow.subFloat);
248pub const mul = bin_overflow.getOp(bin_overflow.mulInt, bin_overflow.mulFloat);
249
250const 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
354pub 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
357pub const rem = bin_ops.getOp(bin_ops.remInt, null);
358
359pub const bitOr = bin_ops.getOp(bin_ops.orInt, null);
360pub const bitXor = bin_ops.getOp(bin_ops.xorInt, null);
361pub const bitAnd = bin_ops.getOp(bin_ops.andInt, null);
362
363pub const shl = bin_ops.getOp(bin_ops.shl, null);
364pub const shr = bin_ops.getOp(bin_ops.shr, null);
365
366pub 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
385pub 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
425pub 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
433pub 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 @@
1const std = @import("std");
2const Compilation = @import("Compilation.zig");
3
4/// Used to implement the __has_feature macro.
5pub 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.
54pub 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 @@
1pub const Codegen = @import("Codegen.zig");
2pub const Compilation = @import("Compilation.zig");
3pub const Diagnostics = @import("Diagnostics.zig");
4pub const Parser = @import("Parser.zig");
5pub const Preprocessor = @import("Preprocessor.zig");
6pub const Source = @import("Source.zig");
7pub const Tokenizer = @import("Tokenizer.zig");
8pub const Tree = @import("Tree.zig");
9pub const Type = @import("Type.zig");
10pub const Value = @import("Value.zig");
11
12pub const version_str = "0.0.0-dev";
13pub const version = @import("std").SemanticVersion.parse(version_str) catch unreachable;
src/aro/pragmas/gcc.zig created+199
......@@ -0,0 +1,199 @@
1const std = @import("std");
2const mem = std.mem;
3const Compilation = @import("../Compilation.zig");
4const Pragma = @import("../Pragma.zig");
5const Diagnostics = @import("../Diagnostics.zig");
6const Preprocessor = @import("../Preprocessor.zig");
7const Parser = @import("../Parser.zig");
8const TokenIndex = @import("../Tree.zig").TokenIndex;
9
10const GCC = @This();
11
12pragma: Pragma = .{
13 .beforeParse = beforeParse,
14 .beforePreprocess = beforePreprocess,
15 .afterParse = afterParse,
16 .deinit = deinit,
17 .preprocessorHandler = preprocessorHandler,
18 .parserHandler = parserHandler,
19 .preserveTokens = preserveTokens,
20},
21original_options: Diagnostics.Options = .{},
22options_stack: std.ArrayListUnmanaged(Diagnostics.Options) = .{},
23
24const 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
39fn beforePreprocess(pragma: *Pragma, comp: *Compilation) void {
40 var self = @fieldParentPtr(GCC, "pragma", pragma);
41 self.original_options = comp.diag.options;
42}
43
44fn 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
50fn 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
56pub fn init(allocator: mem.Allocator) !*Pragma {
57 var gcc = try allocator.create(GCC);
58 gcc.* = .{};
59 return &gcc.pragma;
60}
61
62fn 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
68fn 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
110fn 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
176fn 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
190fn 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 @@
1const std = @import("std");
2const mem = std.mem;
3const Compilation = @import("../Compilation.zig");
4const Pragma = @import("../Pragma.zig");
5const Diagnostics = @import("../Diagnostics.zig");
6const Preprocessor = @import("../Preprocessor.zig");
7const Parser = @import("../Parser.zig");
8const TokenIndex = @import("../Tree.zig").TokenIndex;
9const Source = @import("../Source.zig");
10
11const Message = @This();
12
13pragma: Pragma = .{
14 .deinit = deinit,
15 .preprocessorHandler = preprocessorHandler,
16},
17
18pub fn init(allocator: mem.Allocator) !*Pragma {
19 var once = try allocator.create(Message);
20 once.* = .{};
21 return &once.pragma;
22}
23
24fn deinit(pragma: *Pragma, comp: *Compilation) void {
25 var self = @fieldParentPtr(Message, "pragma", pragma);
26 comp.gpa.destroy(self);
27}
28
29fn 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 @@
1const std = @import("std");
2const mem = std.mem;
3const Compilation = @import("../Compilation.zig");
4const Pragma = @import("../Pragma.zig");
5const Diagnostics = @import("../Diagnostics.zig");
6const Preprocessor = @import("../Preprocessor.zig");
7const Parser = @import("../Parser.zig");
8const TokenIndex = @import("../Tree.zig").TokenIndex;
9const Source = @import("../Source.zig");
10
11const Once = @This();
12
13pragma: Pragma = .{
14 .afterParse = afterParse,
15 .deinit = deinit,
16 .preprocessorHandler = preprocessorHandler,
17},
18pragma_once: std.AutoHashMap(Source.Id, void),
19preprocess_count: u32 = 0,
20
21pub 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
29fn afterParse(pragma: *Pragma, _: *Compilation) void {
30 var self = @fieldParentPtr(Once, "pragma", pragma);
31 self.pragma_once.clearRetainingCapacity();
32}
33
34fn 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
40fn 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 @@
1const std = @import("std");
2const is_windows = @import("builtin").os.tag == .windows;
3
4pub const Color = enum {
5 reset,
6 red,
7 green,
8 blue,
9 cyan,
10 purple,
11 yellow,
12 white,
13};
14
15pub 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}