Compilation.zig (66441B) - Raw
1 const std = @import("std"); 2 const Allocator = mem.Allocator; 3 const assert = std.debug.assert; 4 const EpochSeconds = std.time.epoch.EpochSeconds; 5 const mem = std.mem; 6 const Interner = @import("../backend.zig").Interner; 7 const Builtins = @import("Builtins.zig"); 8 const Builtin = Builtins.Builtin; 9 const Diagnostics = @import("Diagnostics.zig"); 10 const LangOpts = @import("LangOpts.zig"); 11 const Source = @import("Source.zig"); 12 const Tokenizer = @import("Tokenizer.zig"); 13 const Token = Tokenizer.Token; 14 const Type = @import("Type.zig"); 15 const Pragma = @import("Pragma.zig"); 16 const StrInt = @import("StringInterner.zig"); 17 const record_layout = @import("record_layout.zig"); 18 const target_util = @import("target.zig"); 19 20 pub const Error = error{ 21 /// A fatal error has ocurred and compilation has stopped. 22 FatalError, 23 } || Allocator.Error; 24 25 pub const bit_int_max_bits = std.math.maxInt(u16); 26 const path_buf_stack_limit = 1024; 27 28 /// Environment variables used during compilation / linking. 29 pub const Environment = struct { 30 /// Directory to use for temporary files 31 /// TODO: not implemented yet 32 tmpdir: ?[]const u8 = null, 33 34 /// PATH environment variable used to search for programs 35 path: ?[]const u8 = null, 36 37 /// Directories to try when searching for subprograms. 38 /// TODO: not implemented yet 39 compiler_path: ?[]const u8 = null, 40 41 /// Directories to try when searching for special linker files, if compiling for the native target 42 /// TODO: not implemented yet 43 library_path: ?[]const u8 = null, 44 45 /// List of directories to be searched as if specified with -I, but after any paths given with -I options on the command line 46 /// Used regardless of the language being compiled 47 /// TODO: not implemented yet 48 cpath: ?[]const u8 = null, 49 50 /// List of directories to be searched as if specified with -I, but after any paths given with -I options on the command line 51 /// Used if the language being compiled is C 52 /// TODO: not implemented yet 53 c_include_path: ?[]const u8 = null, 54 55 /// UNIX timestamp to be used instead of the current date and time in the __DATE__ and __TIME__ macros 56 source_date_epoch: ?[]const u8 = null, 57 58 /// Load all of the environment variables using the std.process API. Do not use if using Aro as a shared library on Linux without libc 59 /// See https://github.com/ziglang/zig/issues/4524 60 pub fn loadAll(allocator: std.mem.Allocator) !Environment { 61 var env: Environment = .{}; 62 errdefer env.deinit(allocator); 63 64 inline for (@typeInfo(@TypeOf(env)).@"struct".fields) |field| { 65 std.debug.assert(@field(env, field.name) == null); 66 67 var env_var_buf: [field.name.len]u8 = undefined; 68 const env_var_name = std.ascii.upperString(&env_var_buf, field.name); 69 const val: ?[]const u8 = std.process.getEnvVarOwned(allocator, env_var_name) catch |err| switch (err) { 70 error.OutOfMemory => |e| return e, 71 error.EnvironmentVariableNotFound => null, 72 error.InvalidWtf8 => null, 73 }; 74 @field(env, field.name) = val; 75 } 76 return env; 77 } 78 79 /// Use this only if environment slices were allocated with `allocator` (such as via `loadAll`) 80 pub fn deinit(self: *Environment, allocator: std.mem.Allocator) void { 81 inline for (@typeInfo(@TypeOf(self.*)).@"struct".fields) |field| { 82 if (@field(self, field.name)) |slice| { 83 allocator.free(slice); 84 } 85 } 86 self.* = undefined; 87 } 88 }; 89 90 const Compilation = @This(); 91 92 gpa: Allocator, 93 diagnostics: Diagnostics, 94 95 environment: Environment = .{}, 96 sources: std.StringArrayHashMapUnmanaged(Source) = .empty, 97 include_dirs: std.ArrayListUnmanaged([]const u8) = .empty, 98 system_include_dirs: std.ArrayListUnmanaged([]const u8) = .empty, 99 target: std.Target = @import("builtin").target, 100 pragma_handlers: std.StringArrayHashMapUnmanaged(*Pragma) = .empty, 101 langopts: LangOpts = .{}, 102 generated_buf: std.ArrayListUnmanaged(u8) = .empty, 103 builtins: Builtins = .{}, 104 types: struct { 105 wchar: Type = undefined, 106 uint_least16_t: Type = undefined, 107 uint_least32_t: Type = undefined, 108 ptrdiff: Type = undefined, 109 size: Type = undefined, 110 va_list: Type = undefined, 111 pid_t: Type = undefined, 112 ns_constant_string: struct { 113 ty: Type = undefined, 114 record: Type.Record = undefined, 115 fields: [4]Type.Record.Field = undefined, 116 int_ty: Type = .{ .specifier = .int, .qual = .{ .@"const" = true } }, 117 char_ty: Type = .{ .specifier = .char, .qual = .{ .@"const" = true } }, 118 } = .{}, 119 file: Type = .{ .specifier = .invalid }, 120 jmp_buf: Type = .{ .specifier = .invalid }, 121 sigjmp_buf: Type = .{ .specifier = .invalid }, 122 ucontext_t: Type = .{ .specifier = .invalid }, 123 intmax: Type = .{ .specifier = .invalid }, 124 intptr: Type = .{ .specifier = .invalid }, 125 int16: Type = .{ .specifier = .invalid }, 126 int64: Type = .{ .specifier = .invalid }, 127 } = .{}, 128 string_interner: StrInt = .{}, 129 interner: Interner = .{}, 130 /// If this is not null, the directory containing the specified Source will be searched for includes 131 /// Used by MS extensions which allow searching for includes relative to the directory of the main source file. 132 ms_cwd_source_id: ?Source.Id = null, 133 cwd: std.fs.Dir, 134 135 pub fn init(gpa: Allocator, cwd: std.fs.Dir) Compilation { 136 return .{ 137 .gpa = gpa, 138 .diagnostics = Diagnostics.init(gpa), 139 .cwd = cwd, 140 }; 141 } 142 143 /// Initialize Compilation with default environment, 144 /// pragma handlers and emulation mode set to target. 145 pub fn initDefault(gpa: Allocator, cwd: std.fs.Dir) !Compilation { 146 var comp: Compilation = .{ 147 .gpa = gpa, 148 .environment = try Environment.loadAll(gpa), 149 .diagnostics = Diagnostics.init(gpa), 150 .cwd = cwd, 151 }; 152 errdefer comp.deinit(); 153 try comp.addDefaultPragmaHandlers(); 154 comp.langopts.setEmulatedCompiler(target_util.systemCompiler(comp.target)); 155 return comp; 156 } 157 158 pub fn deinit(comp: *Compilation) void { 159 for (comp.pragma_handlers.values()) |pragma| { 160 pragma.deinit(pragma, comp); 161 } 162 for (comp.sources.values()) |source| { 163 comp.gpa.free(source.path); 164 comp.gpa.free(source.buf); 165 comp.gpa.free(source.splice_locs); 166 } 167 comp.sources.deinit(comp.gpa); 168 comp.diagnostics.deinit(); 169 comp.include_dirs.deinit(comp.gpa); 170 for (comp.system_include_dirs.items) |path| comp.gpa.free(path); 171 comp.system_include_dirs.deinit(comp.gpa); 172 comp.pragma_handlers.deinit(comp.gpa); 173 comp.generated_buf.deinit(comp.gpa); 174 comp.builtins.deinit(comp.gpa); 175 comp.string_interner.deinit(comp.gpa); 176 comp.interner.deinit(comp.gpa); 177 comp.environment.deinit(comp.gpa); 178 } 179 180 pub fn getSourceEpoch(self: *const Compilation, max: i64) !?i64 { 181 const provided = self.environment.source_date_epoch orelse return null; 182 const parsed = std.fmt.parseInt(i64, provided, 10) catch return error.InvalidEpoch; 183 if (parsed < 0 or parsed > max) return error.InvalidEpoch; 184 return parsed; 185 } 186 187 /// Dec 31 9999 23:59:59 188 const max_timestamp = 253402300799; 189 190 fn getTimestamp(comp: *Compilation) !u47 { 191 const provided: ?i64 = comp.getSourceEpoch(max_timestamp) catch blk: { 192 try comp.addDiagnostic(.{ 193 .tag = .invalid_source_epoch, 194 .loc = .{ .id = .unused, .byte_offset = 0, .line = 0 }, 195 }, &.{}); 196 break :blk null; 197 }; 198 const timestamp = provided orelse std.time.timestamp(); 199 return @intCast(std.math.clamp(timestamp, 0, max_timestamp)); 200 } 201 202 fn generateDateAndTime(w: anytype, timestamp: u47) !void { 203 const epoch_seconds = EpochSeconds{ .secs = timestamp }; 204 const epoch_day = epoch_seconds.getEpochDay(); 205 const day_seconds = epoch_seconds.getDaySeconds(); 206 const year_day = epoch_day.calculateYearDay(); 207 const month_day = year_day.calculateMonthDay(); 208 209 const month_names = [_][]const u8{ "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; 210 std.debug.assert(std.time.epoch.Month.jan.numeric() == 1); 211 212 const month_name = month_names[month_day.month.numeric() - 1]; 213 try w.print("#define __DATE__ \"{s} {d: >2} {d}\"\n", .{ 214 month_name, 215 month_day.day_index + 1, 216 year_day.year, 217 }); 218 try w.print("#define __TIME__ \"{d:0>2}:{d:0>2}:{d:0>2}\"\n", .{ 219 day_seconds.getHoursIntoDay(), 220 day_seconds.getMinutesIntoHour(), 221 day_seconds.getSecondsIntoMinute(), 222 }); 223 224 const day_names = [_][]const u8{ "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun" }; 225 const day_name = day_names[@intCast((epoch_day.day + 3) % 7)]; 226 try w.print("#define __TIMESTAMP__ \"{s} {s} {d: >2} {d:0>2}:{d:0>2}:{d:0>2} {d}\"\n", .{ 227 day_name, 228 month_name, 229 month_day.day_index + 1, 230 day_seconds.getHoursIntoDay(), 231 day_seconds.getMinutesIntoHour(), 232 day_seconds.getSecondsIntoMinute(), 233 year_day.year, 234 }); 235 } 236 237 /// Which set of system defines to generate via generateBuiltinMacros 238 pub const SystemDefinesMode = enum { 239 /// Only define macros required by the C standard (date/time macros and those beginning with `__STDC`) 240 no_system_defines, 241 /// Define the standard set of system macros 242 include_system_defines, 243 }; 244 245 fn generateSystemDefines(comp: *Compilation, w: anytype) !void { 246 const ptr_width = comp.target.ptrBitWidth(); 247 248 if (comp.langopts.gnuc_version > 0) { 249 try w.print("#define __GNUC__ {d}\n", .{comp.langopts.gnuc_version / 10_000}); 250 try w.print("#define __GNUC_MINOR__ {d}\n", .{comp.langopts.gnuc_version / 100 % 100}); 251 try w.print("#define __GNUC_PATCHLEVEL__ {d}\n", .{comp.langopts.gnuc_version % 100}); 252 } 253 254 // os macros 255 switch (comp.target.os.tag) { 256 .linux => try w.writeAll( 257 \\#define linux 1 258 \\#define __linux 1 259 \\#define __linux__ 1 260 \\ 261 ), 262 .windows => if (ptr_width == 32) try w.writeAll( 263 \\#define WIN32 1 264 \\#define _WIN32 1 265 \\#define __WIN32 1 266 \\#define __WIN32__ 1 267 \\ 268 ) else try w.writeAll( 269 \\#define WIN32 1 270 \\#define WIN64 1 271 \\#define _WIN32 1 272 \\#define _WIN64 1 273 \\#define __WIN32 1 274 \\#define __WIN64 1 275 \\#define __WIN32__ 1 276 \\#define __WIN64__ 1 277 \\ 278 ), 279 .freebsd => try w.print("#define __FreeBSD__ {d}\n", .{comp.target.os.version_range.semver.min.major}), 280 .netbsd => try w.writeAll("#define __NetBSD__ 1\n"), 281 .openbsd => try w.writeAll("#define __OpenBSD__ 1\n"), 282 .dragonfly => try w.writeAll("#define __DragonFly__ 1\n"), 283 .solaris => try w.writeAll( 284 \\#define sun 1 285 \\#define __sun 1 286 \\ 287 ), 288 .macos => try w.writeAll( 289 \\#define __APPLE__ 1 290 \\#define __MACH__ 1 291 \\ 292 ), 293 else => {}, 294 } 295 296 // unix and other additional os macros 297 switch (comp.target.os.tag) { 298 .freebsd, 299 .netbsd, 300 .openbsd, 301 .dragonfly, 302 .linux, 303 => try w.writeAll( 304 \\#define unix 1 305 \\#define __unix 1 306 \\#define __unix__ 1 307 \\ 308 ), 309 else => {}, 310 } 311 if (comp.target.abi.isAndroid()) { 312 try w.writeAll("#define __ANDROID__ 1\n"); 313 } 314 315 // architecture macros 316 switch (comp.target.cpu.arch) { 317 .x86_64 => try w.writeAll( 318 \\#define __amd64__ 1 319 \\#define __amd64 1 320 \\#define __x86_64 1 321 \\#define __x86_64__ 1 322 \\ 323 ), 324 .x86 => try w.writeAll( 325 \\#define i386 1 326 \\#define __i386 1 327 \\#define __i386__ 1 328 \\ 329 ), 330 .mips, 331 .mipsel, 332 .mips64, 333 .mips64el, 334 => try w.writeAll( 335 \\#define __mips__ 1 336 \\#define mips 1 337 \\ 338 ), 339 .powerpc, 340 .powerpcle, 341 => try w.writeAll( 342 \\#define __powerpc__ 1 343 \\#define __POWERPC__ 1 344 \\#define __ppc__ 1 345 \\#define __PPC__ 1 346 \\#define _ARCH_PPC 1 347 \\ 348 ), 349 .powerpc64, 350 .powerpc64le, 351 => try w.writeAll( 352 \\#define __powerpc 1 353 \\#define __powerpc__ 1 354 \\#define __powerpc64__ 1 355 \\#define __POWERPC__ 1 356 \\#define __ppc__ 1 357 \\#define __ppc64__ 1 358 \\#define __PPC__ 1 359 \\#define __PPC64__ 1 360 \\#define _ARCH_PPC 1 361 \\#define _ARCH_PPC64 1 362 \\ 363 ), 364 .sparc64 => try w.writeAll( 365 \\#define __sparc__ 1 366 \\#define __sparc 1 367 \\#define __sparc_v9__ 1 368 \\ 369 ), 370 .sparc => try w.writeAll( 371 \\#define __sparc__ 1 372 \\#define __sparc 1 373 \\ 374 ), 375 .arm, .armeb => try w.writeAll( 376 \\#define __arm__ 1 377 \\#define __arm 1 378 \\ 379 ), 380 .thumb, .thumbeb => try w.writeAll( 381 \\#define __arm__ 1 382 \\#define __arm 1 383 \\#define __thumb__ 1 384 \\ 385 ), 386 .aarch64, .aarch64_be => try w.writeAll("#define __aarch64__ 1\n"), 387 .msp430 => try w.writeAll( 388 \\#define MSP430 1 389 \\#define __MSP430__ 1 390 \\ 391 ), 392 else => {}, 393 } 394 395 if (comp.target.os.tag != .windows) switch (ptr_width) { 396 64 => try w.writeAll( 397 \\#define _LP64 1 398 \\#define __LP64__ 1 399 \\ 400 ), 401 32 => try w.writeAll("#define _ILP32 1\n"), 402 else => {}, 403 }; 404 405 try w.writeAll( 406 \\#define __ORDER_LITTLE_ENDIAN__ 1234 407 \\#define __ORDER_BIG_ENDIAN__ 4321 408 \\#define __ORDER_PDP_ENDIAN__ 3412 409 \\ 410 ); 411 if (comp.target.cpu.arch.endian() == .little) try w.writeAll( 412 \\#define __BYTE_ORDER__ __ORDER_LITTLE_ENDIAN__ 413 \\#define __LITTLE_ENDIAN__ 1 414 \\ 415 ) else try w.writeAll( 416 \\#define __BYTE_ORDER__ __ORDER_BIG_ENDIAN__ 417 \\#define __BIG_ENDIAN__ 1 418 \\ 419 ); 420 421 // atomics 422 try w.writeAll( 423 \\#define __ATOMIC_RELAXED 0 424 \\#define __ATOMIC_CONSUME 1 425 \\#define __ATOMIC_ACQUIRE 2 426 \\#define __ATOMIC_RELEASE 3 427 \\#define __ATOMIC_ACQ_REL 4 428 \\#define __ATOMIC_SEQ_CST 5 429 \\ 430 ); 431 432 // TODO: Set these to target-specific constants depending on backend capabilities 433 // For now they are just set to the "may be lock-free" value 434 try w.writeAll( 435 \\#define __ATOMIC_BOOL_LOCK_FREE 1 436 \\#define __ATOMIC_CHAR_LOCK_FREE 1 437 \\#define __ATOMIC_CHAR16_T_LOCK_FREE 1 438 \\#define __ATOMIC_CHAR32_T_LOCK_FREE 1 439 \\#define __ATOMIC_WCHAR_T_LOCK_FREE 1 440 \\#define __ATOMIC_SHORT_LOCK_FREE 1 441 \\#define __ATOMIC_INT_LOCK_FREE 1 442 \\#define __ATOMIC_LONG_LOCK_FREE 1 443 \\#define __ATOMIC_LLONG_LOCK_FREE 1 444 \\#define __ATOMIC_POINTER_LOCK_FREE 1 445 \\ 446 ); 447 if (comp.langopts.hasChar8_T()) { 448 try w.writeAll("#define __ATOMIC_CHAR8_T_LOCK_FREE 1\n"); 449 } 450 451 // types 452 if (comp.getCharSignedness() == .unsigned) try w.writeAll("#define __CHAR_UNSIGNED__ 1\n"); 453 try w.writeAll("#define __CHAR_BIT__ 8\n"); 454 455 // int maxs 456 try comp.generateIntWidth(w, "BOOL", .{ .specifier = .bool }); 457 try comp.generateIntMaxAndWidth(w, "SCHAR", .{ .specifier = .schar }); 458 try comp.generateIntMaxAndWidth(w, "SHRT", .{ .specifier = .short }); 459 try comp.generateIntMaxAndWidth(w, "INT", .{ .specifier = .int }); 460 try comp.generateIntMaxAndWidth(w, "LONG", .{ .specifier = .long }); 461 try comp.generateIntMaxAndWidth(w, "LONG_LONG", .{ .specifier = .long_long }); 462 try comp.generateIntMaxAndWidth(w, "WCHAR", comp.types.wchar); 463 // try comp.generateIntMax(w, "WINT", comp.types.wchar); 464 try comp.generateIntMaxAndWidth(w, "INTMAX", comp.types.intmax); 465 try comp.generateIntMaxAndWidth(w, "SIZE", comp.types.size); 466 try comp.generateIntMaxAndWidth(w, "UINTMAX", comp.types.intmax.makeIntegerUnsigned()); 467 try comp.generateIntMaxAndWidth(w, "PTRDIFF", comp.types.ptrdiff); 468 try comp.generateIntMaxAndWidth(w, "INTPTR", comp.types.intptr); 469 try comp.generateIntMaxAndWidth(w, "UINTPTR", comp.types.intptr.makeIntegerUnsigned()); 470 try comp.generateIntMaxAndWidth(w, "SIG_ATOMIC", target_util.sigAtomicType(comp.target)); 471 472 // int widths 473 try w.print("#define __BITINT_MAXWIDTH__ {d}\n", .{bit_int_max_bits}); 474 475 // sizeof types 476 try comp.generateSizeofType(w, "__SIZEOF_FLOAT__", .{ .specifier = .float }); 477 try comp.generateSizeofType(w, "__SIZEOF_DOUBLE__", .{ .specifier = .double }); 478 try comp.generateSizeofType(w, "__SIZEOF_LONG_DOUBLE__", .{ .specifier = .long_double }); 479 try comp.generateSizeofType(w, "__SIZEOF_SHORT__", .{ .specifier = .short }); 480 try comp.generateSizeofType(w, "__SIZEOF_INT__", .{ .specifier = .int }); 481 try comp.generateSizeofType(w, "__SIZEOF_LONG__", .{ .specifier = .long }); 482 try comp.generateSizeofType(w, "__SIZEOF_LONG_LONG__", .{ .specifier = .long_long }); 483 try comp.generateSizeofType(w, "__SIZEOF_POINTER__", .{ .specifier = .pointer }); 484 try comp.generateSizeofType(w, "__SIZEOF_PTRDIFF_T__", comp.types.ptrdiff); 485 try comp.generateSizeofType(w, "__SIZEOF_SIZE_T__", comp.types.size); 486 try comp.generateSizeofType(w, "__SIZEOF_WCHAR_T__", comp.types.wchar); 487 // try comp.generateSizeofType(w, "__SIZEOF_WINT_T__", .{ .specifier = .pointer }); 488 489 if (target_util.hasInt128(comp.target)) { 490 try comp.generateSizeofType(w, "__SIZEOF_INT128__", .{ .specifier = .int128 }); 491 } 492 493 // various int types 494 const mapper = comp.string_interner.getSlowTypeMapper(); 495 try generateTypeMacro(w, mapper, "__INTPTR_TYPE__", comp.types.intptr, comp.langopts); 496 try generateTypeMacro(w, mapper, "__UINTPTR_TYPE__", comp.types.intptr.makeIntegerUnsigned(), comp.langopts); 497 498 try generateTypeMacro(w, mapper, "__INTMAX_TYPE__", comp.types.intmax, comp.langopts); 499 try comp.generateSuffixMacro("__INTMAX", w, comp.types.intptr); 500 501 try generateTypeMacro(w, mapper, "__UINTMAX_TYPE__", comp.types.intmax.makeIntegerUnsigned(), comp.langopts); 502 try comp.generateSuffixMacro("__UINTMAX", w, comp.types.intptr.makeIntegerUnsigned()); 503 504 try generateTypeMacro(w, mapper, "__PTRDIFF_TYPE__", comp.types.ptrdiff, comp.langopts); 505 try generateTypeMacro(w, mapper, "__SIZE_TYPE__", comp.types.size, comp.langopts); 506 try generateTypeMacro(w, mapper, "__WCHAR_TYPE__", comp.types.wchar, comp.langopts); 507 try generateTypeMacro(w, mapper, "__CHAR16_TYPE__", comp.types.uint_least16_t, comp.langopts); 508 try generateTypeMacro(w, mapper, "__CHAR32_TYPE__", comp.types.uint_least32_t, comp.langopts); 509 510 try comp.generateExactWidthTypes(w, mapper); 511 try comp.generateFastAndLeastWidthTypes(w, mapper); 512 513 if (target_util.FPSemantics.halfPrecisionType(comp.target)) |half| { 514 try generateFloatMacros(w, "FLT16", half, "F16"); 515 } 516 try generateFloatMacros(w, "FLT", target_util.FPSemantics.forType(.float, comp.target), "F"); 517 try generateFloatMacros(w, "DBL", target_util.FPSemantics.forType(.double, comp.target), ""); 518 try generateFloatMacros(w, "LDBL", target_util.FPSemantics.forType(.longdouble, comp.target), "L"); 519 520 // TODO: clang treats __FLT_EVAL_METHOD__ as a special-cased macro because evaluating it within a scope 521 // where `#pragma clang fp eval_method(X)` has been called produces an error diagnostic. 522 const flt_eval_method = comp.langopts.fp_eval_method orelse target_util.defaultFpEvalMethod(comp.target); 523 try w.print("#define __FLT_EVAL_METHOD__ {d}\n", .{@intFromEnum(flt_eval_method)}); 524 525 try w.writeAll( 526 \\#define __FLT_RADIX__ 2 527 \\#define __DECIMAL_DIG__ __LDBL_DECIMAL_DIG__ 528 \\ 529 ); 530 } 531 532 /// Generate builtin macros that will be available to each source file. 533 pub fn generateBuiltinMacros(comp: *Compilation, system_defines_mode: SystemDefinesMode) !Source { 534 try comp.generateBuiltinTypes(); 535 536 var buf = std.array_list.Managed(u8).init(comp.gpa); 537 defer buf.deinit(); 538 539 if (system_defines_mode == .include_system_defines) { 540 try buf.appendSlice( 541 \\#define __VERSION__ "Aro 542 ++ " " ++ @import("../backend.zig").version_str ++ "\"\n" ++ 543 \\#define __Aro__ 544 \\ 545 ); 546 } 547 548 try buf.appendSlice("#define __STDC__ 1\n"); 549 try buf.writer().print("#define __STDC_HOSTED__ {d}\n", .{@intFromBool(comp.target.os.tag != .freestanding)}); 550 551 // standard macros 552 try buf.appendSlice( 553 \\#define __STDC_NO_COMPLEX__ 1 554 \\#define __STDC_NO_THREADS__ 1 555 \\#define __STDC_NO_VLA__ 1 556 \\#define __STDC_UTF_16__ 1 557 \\#define __STDC_UTF_32__ 1 558 \\#define __STDC_EMBED_NOT_FOUND__ 0 559 \\#define __STDC_EMBED_FOUND__ 1 560 \\#define __STDC_EMBED_EMPTY__ 2 561 \\ 562 ); 563 if (comp.langopts.standard.StdCVersionMacro()) |stdc_version| { 564 try buf.appendSlice("#define __STDC_VERSION__ "); 565 try buf.appendSlice(stdc_version); 566 try buf.append('\n'); 567 } 568 569 // timestamps 570 const timestamp = try comp.getTimestamp(); 571 try generateDateAndTime(buf.writer(), timestamp); 572 573 if (system_defines_mode == .include_system_defines) { 574 try comp.generateSystemDefines(buf.writer()); 575 } 576 577 return comp.addSourceFromBuffer("<builtin>", buf.items); 578 } 579 580 fn generateFloatMacros(w: anytype, prefix: []const u8, semantics: target_util.FPSemantics, ext: []const u8) !void { 581 const denormMin = semantics.chooseValue( 582 []const u8, 583 .{ 584 "5.9604644775390625e-8", 585 "1.40129846e-45", 586 "4.9406564584124654e-324", 587 "3.64519953188247460253e-4951", 588 "4.94065645841246544176568792868221e-324", 589 "6.47517511943802511092443895822764655e-4966", 590 }, 591 ); 592 const digits = semantics.chooseValue(i32, .{ 3, 6, 15, 18, 31, 33 }); 593 const decimalDigits = semantics.chooseValue(i32, .{ 5, 9, 17, 21, 33, 36 }); 594 const epsilon = semantics.chooseValue( 595 []const u8, 596 .{ 597 "9.765625e-4", 598 "1.19209290e-7", 599 "2.2204460492503131e-16", 600 "1.08420217248550443401e-19", 601 "4.94065645841246544176568792868221e-324", 602 "1.92592994438723585305597794258492732e-34", 603 }, 604 ); 605 const mantissaDigits = semantics.chooseValue(i32, .{ 11, 24, 53, 64, 106, 113 }); 606 607 const min10Exp = semantics.chooseValue(i32, .{ -4, -37, -307, -4931, -291, -4931 }); 608 const max10Exp = semantics.chooseValue(i32, .{ 4, 38, 308, 4932, 308, 4932 }); 609 610 const minExp = semantics.chooseValue(i32, .{ -13, -125, -1021, -16381, -968, -16381 }); 611 const maxExp = semantics.chooseValue(i32, .{ 16, 128, 1024, 16384, 1024, 16384 }); 612 613 const min = semantics.chooseValue( 614 []const u8, 615 .{ 616 "6.103515625e-5", 617 "1.17549435e-38", 618 "2.2250738585072014e-308", 619 "3.36210314311209350626e-4932", 620 "2.00416836000897277799610805135016e-292", 621 "3.36210314311209350626267781732175260e-4932", 622 }, 623 ); 624 const max = semantics.chooseValue( 625 []const u8, 626 .{ 627 "6.5504e+4", 628 "3.40282347e+38", 629 "1.7976931348623157e+308", 630 "1.18973149535723176502e+4932", 631 "1.79769313486231580793728971405301e+308", 632 "1.18973149535723176508575932662800702e+4932", 633 }, 634 ); 635 636 var def_prefix_buf: [32]u8 = undefined; 637 const prefix_slice = std.fmt.bufPrint(&def_prefix_buf, "__{s}_", .{prefix}) catch 638 return error.OutOfMemory; 639 640 try w.print("#define {s}DENORM_MIN__ {s}{s}\n", .{ prefix_slice, denormMin, ext }); 641 try w.print("#define {s}HAS_DENORM__\n", .{prefix_slice}); 642 try w.print("#define {s}DIG__ {d}\n", .{ prefix_slice, digits }); 643 try w.print("#define {s}DECIMAL_DIG__ {d}\n", .{ prefix_slice, decimalDigits }); 644 645 try w.print("#define {s}EPSILON__ {s}{s}\n", .{ prefix_slice, epsilon, ext }); 646 try w.print("#define {s}HAS_INFINITY__\n", .{prefix_slice}); 647 try w.print("#define {s}HAS_QUIET_NAN__\n", .{prefix_slice}); 648 try w.print("#define {s}MANT_DIG__ {d}\n", .{ prefix_slice, mantissaDigits }); 649 650 try w.print("#define {s}MAX_10_EXP__ {d}\n", .{ prefix_slice, max10Exp }); 651 try w.print("#define {s}MAX_EXP__ {d}\n", .{ prefix_slice, maxExp }); 652 try w.print("#define {s}MAX__ {s}{s}\n", .{ prefix_slice, max, ext }); 653 654 try w.print("#define {s}MIN_10_EXP__ ({d})\n", .{ prefix_slice, min10Exp }); 655 try w.print("#define {s}MIN_EXP__ ({d})\n", .{ prefix_slice, minExp }); 656 try w.print("#define {s}MIN__ {s}{s}\n", .{ prefix_slice, min, ext }); 657 } 658 659 fn generateTypeMacro(w: anytype, mapper: StrInt.TypeMapper, name: []const u8, ty: Type, langopts: LangOpts) !void { 660 try w.print("#define {s} ", .{name}); 661 try ty.print(mapper, langopts, w); 662 try w.writeByte('\n'); 663 } 664 665 fn generateBuiltinTypes(comp: *Compilation) !void { 666 const os = comp.target.os.tag; 667 const wchar: Type = switch (comp.target.cpu.arch) { 668 .xcore => .{ .specifier = .uchar }, 669 .ve, .msp430 => .{ .specifier = .uint }, 670 .arm, .armeb, .thumb, .thumbeb => .{ 671 .specifier = if (os != .windows and os != .netbsd and os != .openbsd) .uint else .int, 672 }, 673 .aarch64, .aarch64_be => .{ 674 .specifier = if (!os.isDarwin() and os != .netbsd) .uint else .int, 675 }, 676 .x86_64, .x86 => .{ .specifier = if (os == .windows) .ushort else .int }, 677 else => .{ .specifier = .int }, 678 }; 679 680 const ptr_width = comp.target.ptrBitWidth(); 681 const ptrdiff = if (os == .windows and ptr_width == 64) 682 Type{ .specifier = .long_long } 683 else switch (ptr_width) { 684 16 => Type{ .specifier = .int }, 685 32 => Type{ .specifier = .int }, 686 64 => Type{ .specifier = .long }, 687 else => unreachable, 688 }; 689 690 const size = if (os == .windows and ptr_width == 64) 691 Type{ .specifier = .ulong_long } 692 else switch (ptr_width) { 693 16 => Type{ .specifier = .uint }, 694 32 => Type{ .specifier = .uint }, 695 64 => Type{ .specifier = .ulong }, 696 else => unreachable, 697 }; 698 699 const va_list = try comp.generateVaListType(); 700 701 const pid_t: Type = switch (os) { 702 .haiku => .{ .specifier = .long }, 703 // Todo: pid_t is required to "a signed integer type"; are there any systems 704 // on which it is `short int`? 705 else => .{ .specifier = .int }, 706 }; 707 708 const intmax = target_util.intMaxType(comp.target); 709 const intptr = target_util.intPtrType(comp.target); 710 const int16 = target_util.int16Type(comp.target); 711 const int64 = target_util.int64Type(comp.target); 712 713 comp.types = .{ 714 .wchar = wchar, 715 .ptrdiff = ptrdiff, 716 .size = size, 717 .va_list = va_list, 718 .pid_t = pid_t, 719 .intmax = intmax, 720 .intptr = intptr, 721 .int16 = int16, 722 .int64 = int64, 723 .uint_least16_t = comp.intLeastN(16, .unsigned), 724 .uint_least32_t = comp.intLeastN(32, .unsigned), 725 }; 726 727 try comp.generateNsConstantStringType(); 728 } 729 730 pub fn float80Type(comp: *const Compilation) ?Type { 731 if (comp.langopts.emulate != .gcc) return null; 732 return target_util.float80Type(comp.target); 733 } 734 735 /// Smallest integer type with at least N bits 736 pub fn intLeastN(comp: *const Compilation, bits: usize, signedness: std.builtin.Signedness) Type { 737 if (bits == 64 and (comp.target.os.tag.isDarwin() or comp.target.cpu.arch.isWasm())) { 738 // WebAssembly and Darwin use `long long` for `int_least64_t` and `int_fast64_t`. 739 return .{ .specifier = if (signedness == .signed) .long_long else .ulong_long }; 740 } 741 if (bits == 16 and comp.target.cpu.arch == .avr) { 742 // AVR uses int for int_least16_t and int_fast16_t. 743 return .{ .specifier = if (signedness == .signed) .int else .uint }; 744 } 745 const candidates = switch (signedness) { 746 .signed => &[_]Type.Specifier{ .schar, .short, .int, .long, .long_long }, 747 .unsigned => &[_]Type.Specifier{ .uchar, .ushort, .uint, .ulong, .ulong_long }, 748 }; 749 for (candidates) |specifier| { 750 const ty: Type = .{ .specifier = specifier }; 751 if (ty.sizeof(comp).? * 8 >= bits) return ty; 752 } else unreachable; 753 } 754 755 fn intSize(comp: *const Compilation, specifier: Type.Specifier) u64 { 756 const ty = Type{ .specifier = specifier }; 757 return ty.sizeof(comp).?; 758 } 759 760 fn generateFastOrLeastType( 761 comp: *Compilation, 762 bits: usize, 763 kind: enum { least, fast }, 764 signedness: std.builtin.Signedness, 765 w: anytype, 766 mapper: StrInt.TypeMapper, 767 ) !void { 768 const ty = comp.intLeastN(bits, signedness); // defining the fast types as the least types is permitted 769 770 var buf: [32]u8 = undefined; 771 const suffix = "_TYPE__"; 772 const base_name = switch (signedness) { 773 .signed => "__INT_", 774 .unsigned => "__UINT_", 775 }; 776 const kind_str = switch (kind) { 777 .fast => "FAST", 778 .least => "LEAST", 779 }; 780 781 const full = std.fmt.bufPrint(&buf, "{s}{s}{d}{s}", .{ 782 base_name, kind_str, bits, suffix, 783 }) catch return error.OutOfMemory; 784 785 try generateTypeMacro(w, mapper, full, ty, comp.langopts); 786 787 const prefix = full[2 .. full.len - suffix.len]; // remove "__" and "_TYPE__" 788 789 switch (signedness) { 790 .signed => try comp.generateIntMaxAndWidth(w, prefix, ty), 791 .unsigned => try comp.generateIntMax(w, prefix, ty), 792 } 793 try comp.generateFmt(prefix, w, ty); 794 } 795 796 fn generateFastAndLeastWidthTypes(comp: *Compilation, w: anytype, mapper: StrInt.TypeMapper) !void { 797 const sizes = [_]usize{ 8, 16, 32, 64 }; 798 for (sizes) |size| { 799 try comp.generateFastOrLeastType(size, .least, .signed, w, mapper); 800 try comp.generateFastOrLeastType(size, .least, .unsigned, w, mapper); 801 try comp.generateFastOrLeastType(size, .fast, .signed, w, mapper); 802 try comp.generateFastOrLeastType(size, .fast, .unsigned, w, mapper); 803 } 804 } 805 806 fn generateExactWidthTypes(comp: *const Compilation, w: anytype, mapper: StrInt.TypeMapper) !void { 807 try comp.generateExactWidthType(w, mapper, .schar); 808 809 if (comp.intSize(.short) > comp.intSize(.char)) { 810 try comp.generateExactWidthType(w, mapper, .short); 811 } 812 813 if (comp.intSize(.int) > comp.intSize(.short)) { 814 try comp.generateExactWidthType(w, mapper, .int); 815 } 816 817 if (comp.intSize(.long) > comp.intSize(.int)) { 818 try comp.generateExactWidthType(w, mapper, .long); 819 } 820 821 if (comp.intSize(.long_long) > comp.intSize(.long)) { 822 try comp.generateExactWidthType(w, mapper, .long_long); 823 } 824 825 try comp.generateExactWidthType(w, mapper, .uchar); 826 try comp.generateExactWidthIntMax(w, .uchar); 827 try comp.generateExactWidthIntMax(w, .schar); 828 829 if (comp.intSize(.short) > comp.intSize(.char)) { 830 try comp.generateExactWidthType(w, mapper, .ushort); 831 try comp.generateExactWidthIntMax(w, .ushort); 832 try comp.generateExactWidthIntMax(w, .short); 833 } 834 835 if (comp.intSize(.int) > comp.intSize(.short)) { 836 try comp.generateExactWidthType(w, mapper, .uint); 837 try comp.generateExactWidthIntMax(w, .uint); 838 try comp.generateExactWidthIntMax(w, .int); 839 } 840 841 if (comp.intSize(.long) > comp.intSize(.int)) { 842 try comp.generateExactWidthType(w, mapper, .ulong); 843 try comp.generateExactWidthIntMax(w, .ulong); 844 try comp.generateExactWidthIntMax(w, .long); 845 } 846 847 if (comp.intSize(.long_long) > comp.intSize(.long)) { 848 try comp.generateExactWidthType(w, mapper, .ulong_long); 849 try comp.generateExactWidthIntMax(w, .ulong_long); 850 try comp.generateExactWidthIntMax(w, .long_long); 851 } 852 } 853 854 fn generateFmt(comp: *const Compilation, prefix: []const u8, w: anytype, ty: Type) !void { 855 const unsigned = ty.isUnsignedInt(comp); 856 const modifier = ty.formatModifier(); 857 const formats = if (unsigned) "ouxX" else "di"; 858 for (formats) |c| { 859 try w.print("#define {s}_FMT{c}__ \"{s}{c}\"\n", .{ prefix, c, modifier, c }); 860 } 861 } 862 863 fn generateSuffixMacro(comp: *const Compilation, prefix: []const u8, w: anytype, ty: Type) !void { 864 return w.print("#define {s}_C_SUFFIX__ {s}\n", .{ prefix, ty.intValueSuffix(comp) }); 865 } 866 867 /// Generate the following for ty: 868 /// Name macro (e.g. #define __UINT32_TYPE__ unsigned int) 869 /// Format strings (e.g. #define __UINT32_FMTu__ "u") 870 /// Suffix macro (e.g. #define __UINT32_C_SUFFIX__ U) 871 fn generateExactWidthType(comp: *const Compilation, w: anytype, mapper: StrInt.TypeMapper, specifier: Type.Specifier) !void { 872 var ty = Type{ .specifier = specifier }; 873 const width = 8 * ty.sizeof(comp).?; 874 const unsigned = ty.isUnsignedInt(comp); 875 876 if (width == 16) { 877 ty = if (unsigned) comp.types.int16.makeIntegerUnsigned() else comp.types.int16; 878 } else if (width == 64) { 879 ty = if (unsigned) comp.types.int64.makeIntegerUnsigned() else comp.types.int64; 880 } 881 882 var buffer: [16]u8 = undefined; 883 const suffix = "_TYPE__"; 884 const full = std.fmt.bufPrint(&buffer, "{s}{d}{s}", .{ 885 if (unsigned) "__UINT" else "__INT", width, suffix, 886 }) catch return error.OutOfMemory; 887 888 try generateTypeMacro(w, mapper, full, ty, comp.langopts); 889 890 const prefix = full[0 .. full.len - suffix.len]; // remove "_TYPE__" 891 892 try comp.generateFmt(prefix, w, ty); 893 try comp.generateSuffixMacro(prefix, w, ty); 894 } 895 896 pub fn hasFloat128(comp: *const Compilation) bool { 897 return target_util.hasFloat128(comp.target); 898 } 899 900 pub fn hasHalfPrecisionFloatABI(comp: *const Compilation) bool { 901 return comp.langopts.allow_half_args_and_returns or target_util.hasHalfPrecisionFloatABI(comp.target); 902 } 903 904 fn generateNsConstantStringType(comp: *Compilation) !void { 905 comp.types.ns_constant_string.record = .{ 906 .name = try StrInt.intern(comp, "__NSConstantString_tag"), 907 .fields = &comp.types.ns_constant_string.fields, 908 .field_attributes = null, 909 .type_layout = undefined, 910 }; 911 const const_int_ptr = Type{ .specifier = .pointer, .data = .{ .sub_type = &comp.types.ns_constant_string.int_ty } }; 912 const const_char_ptr = Type{ .specifier = .pointer, .data = .{ .sub_type = &comp.types.ns_constant_string.char_ty } }; 913 914 comp.types.ns_constant_string.fields[0] = .{ .name = try StrInt.intern(comp, "isa"), .ty = const_int_ptr }; 915 comp.types.ns_constant_string.fields[1] = .{ .name = try StrInt.intern(comp, "flags"), .ty = .{ .specifier = .int } }; 916 comp.types.ns_constant_string.fields[2] = .{ .name = try StrInt.intern(comp, "str"), .ty = const_char_ptr }; 917 comp.types.ns_constant_string.fields[3] = .{ .name = try StrInt.intern(comp, "length"), .ty = .{ .specifier = .long } }; 918 comp.types.ns_constant_string.ty = .{ .specifier = .@"struct", .data = .{ .record = &comp.types.ns_constant_string.record } }; 919 record_layout.compute(&comp.types.ns_constant_string.record, comp.types.ns_constant_string.ty, comp, null) catch unreachable; 920 } 921 922 fn generateVaListType(comp: *Compilation) !Type { 923 const Kind = enum { char_ptr, void_ptr, aarch64_va_list, x86_64_va_list }; 924 const kind: Kind = switch (comp.target.cpu.arch) { 925 .aarch64 => switch (comp.target.os.tag) { 926 .windows => @as(Kind, .char_ptr), 927 .ios, .macos, .tvos, .watchos => .char_ptr, 928 else => .aarch64_va_list, 929 }, 930 .sparc, .wasm32, .wasm64, .bpfel, .bpfeb, .riscv32, .riscv64, .avr, .spirv32, .spirv64 => .void_ptr, 931 .powerpc => switch (comp.target.os.tag) { 932 .ios, .macos, .tvos, .watchos, .aix => @as(Kind, .char_ptr), 933 else => return Type{ .specifier = .void }, // unknown 934 }, 935 .x86, .msp430 => .char_ptr, 936 .x86_64 => switch (comp.target.os.tag) { 937 .windows => @as(Kind, .char_ptr), 938 else => .x86_64_va_list, 939 }, 940 else => return Type{ .specifier = .void }, // unknown 941 }; 942 943 // TODO this might be bad? 944 const arena = comp.diagnostics.arena.allocator(); 945 946 var ty: Type = undefined; 947 switch (kind) { 948 .char_ptr => ty = .{ .specifier = .char }, 949 .void_ptr => ty = .{ .specifier = .void }, 950 .aarch64_va_list => { 951 const record_ty = try arena.create(Type.Record); 952 record_ty.* = .{ 953 .name = try StrInt.intern(comp, "__va_list_tag"), 954 .fields = try arena.alloc(Type.Record.Field, 5), 955 .field_attributes = null, 956 .type_layout = undefined, // computed below 957 }; 958 const void_ty = try arena.create(Type); 959 void_ty.* = .{ .specifier = .void }; 960 const void_ptr = Type{ .specifier = .pointer, .data = .{ .sub_type = void_ty } }; 961 record_ty.fields[0] = .{ .name = try StrInt.intern(comp, "__stack"), .ty = void_ptr }; 962 record_ty.fields[1] = .{ .name = try StrInt.intern(comp, "__gr_top"), .ty = void_ptr }; 963 record_ty.fields[2] = .{ .name = try StrInt.intern(comp, "__vr_top"), .ty = void_ptr }; 964 record_ty.fields[3] = .{ .name = try StrInt.intern(comp, "__gr_offs"), .ty = .{ .specifier = .int } }; 965 record_ty.fields[4] = .{ .name = try StrInt.intern(comp, "__vr_offs"), .ty = .{ .specifier = .int } }; 966 ty = .{ .specifier = .@"struct", .data = .{ .record = record_ty } }; 967 record_layout.compute(record_ty, ty, comp, null) catch unreachable; 968 }, 969 .x86_64_va_list => { 970 const record_ty = try arena.create(Type.Record); 971 record_ty.* = .{ 972 .name = try StrInt.intern(comp, "__va_list_tag"), 973 .fields = try arena.alloc(Type.Record.Field, 4), 974 .field_attributes = null, 975 .type_layout = undefined, // computed below 976 }; 977 const void_ty = try arena.create(Type); 978 void_ty.* = .{ .specifier = .void }; 979 const void_ptr = Type{ .specifier = .pointer, .data = .{ .sub_type = void_ty } }; 980 record_ty.fields[0] = .{ .name = try StrInt.intern(comp, "gp_offset"), .ty = .{ .specifier = .uint } }; 981 record_ty.fields[1] = .{ .name = try StrInt.intern(comp, "fp_offset"), .ty = .{ .specifier = .uint } }; 982 record_ty.fields[2] = .{ .name = try StrInt.intern(comp, "overflow_arg_area"), .ty = void_ptr }; 983 record_ty.fields[3] = .{ .name = try StrInt.intern(comp, "reg_save_area"), .ty = void_ptr }; 984 ty = .{ .specifier = .@"struct", .data = .{ .record = record_ty } }; 985 record_layout.compute(record_ty, ty, comp, null) catch unreachable; 986 }, 987 } 988 if (kind == .char_ptr or kind == .void_ptr) { 989 const elem_ty = try arena.create(Type); 990 elem_ty.* = ty; 991 ty = Type{ .specifier = .pointer, .data = .{ .sub_type = elem_ty } }; 992 } else { 993 const arr_ty = try arena.create(Type.Array); 994 arr_ty.* = .{ .len = 1, .elem = ty }; 995 ty = Type{ .specifier = .array, .data = .{ .array = arr_ty } }; 996 } 997 998 return ty; 999 } 1000 1001 fn generateIntMax(comp: *const Compilation, w: anytype, name: []const u8, ty: Type) !void { 1002 const bit_count: u8 = @intCast(ty.sizeof(comp).? * 8); 1003 const unsigned = ty.isUnsignedInt(comp); 1004 const max: u128 = switch (bit_count) { 1005 8 => if (unsigned) std.math.maxInt(u8) else std.math.maxInt(i8), 1006 16 => if (unsigned) std.math.maxInt(u16) else std.math.maxInt(i16), 1007 32 => if (unsigned) std.math.maxInt(u32) else std.math.maxInt(i32), 1008 64 => if (unsigned) std.math.maxInt(u64) else std.math.maxInt(i64), 1009 128 => if (unsigned) std.math.maxInt(u128) else std.math.maxInt(i128), 1010 else => unreachable, 1011 }; 1012 try w.print("#define __{s}_MAX__ {d}{s}\n", .{ name, max, ty.intValueSuffix(comp) }); 1013 } 1014 1015 /// Largest value that can be stored in wchar_t 1016 pub fn wcharMax(comp: *const Compilation) u32 { 1017 const unsigned = comp.types.wchar.isUnsignedInt(comp); 1018 return switch (comp.types.wchar.bitSizeof(comp).?) { 1019 8 => if (unsigned) std.math.maxInt(u8) else std.math.maxInt(i8), 1020 16 => if (unsigned) std.math.maxInt(u16) else std.math.maxInt(i16), 1021 32 => if (unsigned) std.math.maxInt(u32) else std.math.maxInt(i32), 1022 else => unreachable, 1023 }; 1024 } 1025 1026 fn generateExactWidthIntMax(comp: *const Compilation, w: anytype, specifier: Type.Specifier) !void { 1027 var ty = Type{ .specifier = specifier }; 1028 const bit_count: u8 = @intCast(ty.sizeof(comp).? * 8); 1029 const unsigned = ty.isUnsignedInt(comp); 1030 1031 if (bit_count == 64) { 1032 ty = if (unsigned) comp.types.int64.makeIntegerUnsigned() else comp.types.int64; 1033 } 1034 1035 var name_buffer: [6]u8 = undefined; 1036 const name = std.fmt.bufPrint(&name_buffer, "{s}{d}", .{ 1037 if (unsigned) "UINT" else "INT", bit_count, 1038 }) catch return error.OutOfMemory; 1039 1040 return comp.generateIntMax(w, name, ty); 1041 } 1042 1043 fn generateIntWidth(comp: *Compilation, w: anytype, name: []const u8, ty: Type) !void { 1044 try w.print("#define __{s}_WIDTH__ {d}\n", .{ name, 8 * ty.sizeof(comp).? }); 1045 } 1046 1047 fn generateIntMaxAndWidth(comp: *Compilation, w: anytype, name: []const u8, ty: Type) !void { 1048 try comp.generateIntMax(w, name, ty); 1049 try comp.generateIntWidth(w, name, ty); 1050 } 1051 1052 fn generateSizeofType(comp: *Compilation, w: anytype, name: []const u8, ty: Type) !void { 1053 try w.print("#define {s} {d}\n", .{ name, ty.sizeof(comp).? }); 1054 } 1055 1056 pub fn nextLargestIntSameSign(comp: *const Compilation, ty: Type) ?Type { 1057 assert(ty.isInt()); 1058 const specifiers = if (ty.isUnsignedInt(comp)) 1059 [_]Type.Specifier{ .short, .int, .long, .long_long } 1060 else 1061 [_]Type.Specifier{ .ushort, .uint, .ulong, .ulong_long }; 1062 const size = ty.sizeof(comp).?; 1063 for (specifiers) |specifier| { 1064 const candidate = Type{ .specifier = specifier }; 1065 if (candidate.sizeof(comp).? > size) return candidate; 1066 } 1067 return null; 1068 } 1069 1070 /// Maximum size of an array, in bytes 1071 pub fn maxArrayBytes(comp: *const Compilation) u64 { 1072 const max_bits = @min(61, comp.target.ptrBitWidth()); 1073 return (@as(u64, 1) << @truncate(max_bits)) - 1; 1074 } 1075 1076 /// If `enum E { ... }` syntax has a fixed underlying integer type regardless of the presence of 1077 /// __attribute__((packed)) or the range of values of the corresponding enumerator constants, 1078 /// specify it here. 1079 /// TODO: likely incomplete 1080 pub fn fixedEnumTagSpecifier(comp: *const Compilation) ?Type.Specifier { 1081 switch (comp.langopts.emulate) { 1082 .msvc => return .int, 1083 .clang => if (comp.target.os.tag == .windows) return .int, 1084 .gcc => {}, 1085 } 1086 return null; 1087 } 1088 1089 pub fn getCharSignedness(comp: *const Compilation) std.builtin.Signedness { 1090 return comp.langopts.char_signedness_override orelse comp.target.cCharSignedness(); 1091 } 1092 1093 /// Add built-in aro headers directory to system include paths 1094 pub fn addBuiltinIncludeDir(comp: *Compilation, aro_dir: []const u8) !void { 1095 var search_path = aro_dir; 1096 while (std.fs.path.dirname(search_path)) |dirname| : (search_path = dirname) { 1097 var base_dir = comp.cwd.openDir(dirname, .{}) catch continue; 1098 defer base_dir.close(); 1099 1100 base_dir.access("include/stddef.h", .{}) catch continue; 1101 const path = try std.fs.path.join(comp.gpa, &.{ dirname, "include" }); 1102 errdefer comp.gpa.free(path); 1103 try comp.system_include_dirs.append(comp.gpa, path); 1104 break; 1105 } else return error.AroIncludeNotFound; 1106 } 1107 1108 pub fn addSystemIncludeDir(comp: *Compilation, path: []const u8) !void { 1109 const duped = try comp.gpa.dupe(u8, path); 1110 errdefer comp.gpa.free(duped); 1111 try comp.system_include_dirs.append(comp.gpa, duped); 1112 } 1113 1114 pub fn getSource(comp: *const Compilation, id: Source.Id) Source { 1115 if (id == .generated) return .{ 1116 .path = "<scratch space>", 1117 .buf = comp.generated_buf.items, 1118 .id = .generated, 1119 .splice_locs = &.{}, 1120 .kind = .user, 1121 }; 1122 return comp.sources.values()[@intFromEnum(id) - 2]; 1123 } 1124 1125 /// Creates a Source from the contents of `reader` and adds it to the Compilation 1126 pub fn addSourceFromReader(comp: *Compilation, reader: anytype, path: []const u8, kind: Source.Kind) !Source { 1127 const contents = try reader.readAllAlloc(comp.gpa, std.math.maxInt(u32)); 1128 errdefer comp.gpa.free(contents); 1129 return comp.addSourceFromOwnedBuffer(contents, path, kind); 1130 } 1131 1132 /// Creates a Source from `buf` and adds it to the Compilation 1133 /// Performs newline splicing and line-ending normalization to '\n' 1134 /// `buf` will be modified and the allocation will be resized if newline splicing 1135 /// or line-ending changes happen. 1136 /// caller retains ownership of `path` 1137 /// To add the contents of an arbitrary reader as a Source, see addSourceFromReader 1138 /// To add a file's contents given its path, see addSourceFromPath 1139 pub fn addSourceFromOwnedBuffer(comp: *Compilation, buf: []u8, path: []const u8, kind: Source.Kind) !Source { 1140 try comp.sources.ensureUnusedCapacity(comp.gpa, 1); 1141 1142 var contents = buf; 1143 const duped_path = try comp.gpa.dupe(u8, path); 1144 errdefer comp.gpa.free(duped_path); 1145 1146 var splice_list = std.array_list.Managed(u32).init(comp.gpa); 1147 defer splice_list.deinit(); 1148 1149 const source_id: Source.Id = @enumFromInt(comp.sources.count() + 2); 1150 1151 var i: u32 = 0; 1152 var backslash_loc: u32 = undefined; 1153 var state: enum { 1154 beginning_of_file, 1155 bom1, 1156 bom2, 1157 start, 1158 back_slash, 1159 cr, 1160 back_slash_cr, 1161 trailing_ws, 1162 } = .beginning_of_file; 1163 var line: u32 = 1; 1164 1165 for (contents) |byte| { 1166 contents[i] = byte; 1167 1168 switch (byte) { 1169 '\r' => { 1170 switch (state) { 1171 .start, .cr, .beginning_of_file => { 1172 state = .start; 1173 line += 1; 1174 state = .cr; 1175 contents[i] = '\n'; 1176 i += 1; 1177 }, 1178 .back_slash, .trailing_ws, .back_slash_cr => { 1179 i = backslash_loc; 1180 try splice_list.append(i); 1181 if (state == .trailing_ws) { 1182 try comp.addDiagnostic(.{ 1183 .tag = .backslash_newline_escape, 1184 .loc = .{ .id = source_id, .byte_offset = i, .line = line }, 1185 }, &.{}); 1186 } 1187 state = if (state == .back_slash_cr) .cr else .back_slash_cr; 1188 }, 1189 .bom1, .bom2 => break, // invalid utf-8 1190 } 1191 }, 1192 '\n' => { 1193 switch (state) { 1194 .start, .beginning_of_file => { 1195 state = .start; 1196 line += 1; 1197 i += 1; 1198 }, 1199 .cr, .back_slash_cr => {}, 1200 .back_slash, .trailing_ws => { 1201 i = backslash_loc; 1202 if (state == .back_slash or state == .trailing_ws) { 1203 try splice_list.append(i); 1204 } 1205 if (state == .trailing_ws) { 1206 try comp.addDiagnostic(.{ 1207 .tag = .backslash_newline_escape, 1208 .loc = .{ .id = source_id, .byte_offset = i, .line = line }, 1209 }, &.{}); 1210 } 1211 }, 1212 .bom1, .bom2 => break, 1213 } 1214 state = .start; 1215 }, 1216 '\\' => { 1217 backslash_loc = i; 1218 state = .back_slash; 1219 i += 1; 1220 }, 1221 '\t', '\x0B', '\x0C', ' ' => { 1222 switch (state) { 1223 .start, .trailing_ws => {}, 1224 .beginning_of_file => state = .start, 1225 .cr, .back_slash_cr => state = .start, 1226 .back_slash => state = .trailing_ws, 1227 .bom1, .bom2 => break, 1228 } 1229 i += 1; 1230 }, 1231 '\xEF' => { 1232 i += 1; 1233 state = switch (state) { 1234 .beginning_of_file => .bom1, 1235 else => .start, 1236 }; 1237 }, 1238 '\xBB' => { 1239 i += 1; 1240 state = switch (state) { 1241 .bom1 => .bom2, 1242 else => .start, 1243 }; 1244 }, 1245 '\xBF' => { 1246 switch (state) { 1247 .bom2 => i = 0, // rewind and overwrite the BOM 1248 else => i += 1, 1249 } 1250 state = .start; 1251 }, 1252 else => { 1253 i += 1; 1254 state = .start; 1255 }, 1256 } 1257 } 1258 1259 const splice_locs = try splice_list.toOwnedSlice(); 1260 errdefer comp.gpa.free(splice_locs); 1261 1262 if (i != contents.len) contents = try comp.gpa.realloc(contents, i); 1263 errdefer @compileError("errdefers in callers would possibly free the realloced slice using the original len"); 1264 1265 const source = Source{ 1266 .id = source_id, 1267 .path = duped_path, 1268 .buf = contents, 1269 .splice_locs = splice_locs, 1270 .kind = kind, 1271 }; 1272 1273 comp.sources.putAssumeCapacityNoClobber(duped_path, source); 1274 return source; 1275 } 1276 1277 /// Caller retains ownership of `path` and `buf`. 1278 /// Dupes the source buffer; if it is acceptable to modify the source buffer and possibly resize 1279 /// the allocation, please use `addSourceFromOwnedBuffer` 1280 pub fn addSourceFromBuffer(comp: *Compilation, path: []const u8, buf: []const u8) !Source { 1281 if (comp.sources.get(path)) |some| return some; 1282 if (@as(u64, buf.len) > std.math.maxInt(u32)) return error.StreamTooLong; 1283 1284 const contents = try comp.gpa.dupe(u8, buf); 1285 errdefer comp.gpa.free(contents); 1286 1287 return comp.addSourceFromOwnedBuffer(contents, path, .user); 1288 } 1289 1290 /// Caller retains ownership of `path`. 1291 pub fn addSourceFromPath(comp: *Compilation, path: []const u8) !Source { 1292 return comp.addSourceFromPathExtra(path, .user); 1293 } 1294 1295 /// Caller retains ownership of `path`. 1296 fn addSourceFromPathExtra(comp: *Compilation, path: []const u8, kind: Source.Kind) !Source { 1297 if (comp.sources.get(path)) |some| return some; 1298 1299 if (mem.indexOfScalar(u8, path, 0) != null) { 1300 return error.FileNotFound; 1301 } 1302 1303 const file = try comp.cwd.openFile(path, .{}); 1304 defer file.close(); 1305 1306 const contents = file.readToEndAlloc(comp.gpa, std.math.maxInt(u32)) catch |err| switch (err) { 1307 error.FileTooBig => return error.StreamTooLong, 1308 else => |e| return e, 1309 }; 1310 errdefer comp.gpa.free(contents); 1311 1312 return comp.addSourceFromOwnedBuffer(contents, path, kind); 1313 } 1314 1315 pub const IncludeDirIterator = struct { 1316 comp: *const Compilation, 1317 cwd_source_id: ?Source.Id, 1318 include_dirs_idx: usize = 0, 1319 sys_include_dirs_idx: usize = 0, 1320 tried_ms_cwd: bool = false, 1321 1322 const FoundSource = struct { 1323 path: []const u8, 1324 kind: Source.Kind, 1325 }; 1326 1327 fn next(self: *IncludeDirIterator) ?FoundSource { 1328 if (self.cwd_source_id) |source_id| { 1329 self.cwd_source_id = null; 1330 const path = self.comp.getSource(source_id).path; 1331 return .{ .path = std.fs.path.dirname(path) orelse ".", .kind = .user }; 1332 } 1333 if (self.include_dirs_idx < self.comp.include_dirs.items.len) { 1334 defer self.include_dirs_idx += 1; 1335 return .{ .path = self.comp.include_dirs.items[self.include_dirs_idx], .kind = .user }; 1336 } 1337 if (self.sys_include_dirs_idx < self.comp.system_include_dirs.items.len) { 1338 defer self.sys_include_dirs_idx += 1; 1339 return .{ .path = self.comp.system_include_dirs.items[self.sys_include_dirs_idx], .kind = .system }; 1340 } 1341 if (self.comp.ms_cwd_source_id) |source_id| { 1342 if (self.tried_ms_cwd) return null; 1343 self.tried_ms_cwd = true; 1344 const path = self.comp.getSource(source_id).path; 1345 return .{ .path = std.fs.path.dirname(path) orelse ".", .kind = .user }; 1346 } 1347 return null; 1348 } 1349 1350 /// Returned value's path field must be freed by allocator 1351 fn nextWithFile(self: *IncludeDirIterator, filename: []const u8, allocator: Allocator) !?FoundSource { 1352 while (self.next()) |found| { 1353 const path = try std.fs.path.join(allocator, &.{ found.path, filename }); 1354 if (self.comp.langopts.ms_extensions) { 1355 std.mem.replaceScalar(u8, path, '\\', '/'); 1356 } 1357 return .{ .path = path, .kind = found.kind }; 1358 } 1359 return null; 1360 } 1361 1362 /// Advance the iterator until it finds an include directory that matches 1363 /// the directory which contains `source`. 1364 fn skipUntilDirMatch(self: *IncludeDirIterator, source: Source.Id) void { 1365 const path = self.comp.getSource(source).path; 1366 const includer_path = std.fs.path.dirname(path) orelse "."; 1367 while (self.next()) |found| { 1368 if (mem.eql(u8, includer_path, found.path)) break; 1369 } 1370 } 1371 }; 1372 1373 pub fn hasInclude( 1374 comp: *const Compilation, 1375 filename: []const u8, 1376 includer_token_source: Source.Id, 1377 /// angle bracket vs quotes 1378 include_type: IncludeType, 1379 /// __has_include vs __has_include_next 1380 which: WhichInclude, 1381 ) !bool { 1382 if (mem.indexOfScalar(u8, filename, 0) != null) { 1383 return false; 1384 } 1385 1386 if (std.fs.path.isAbsolute(filename)) { 1387 if (which == .next) return false; 1388 return !std.meta.isError(comp.cwd.access(filename, .{})); 1389 } 1390 1391 const cwd_source_id = switch (include_type) { 1392 .quotes => switch (which) { 1393 .first => includer_token_source, 1394 .next => null, 1395 }, 1396 .angle_brackets => null, 1397 }; 1398 var it = IncludeDirIterator{ .comp = comp, .cwd_source_id = cwd_source_id }; 1399 if (which == .next) { 1400 it.skipUntilDirMatch(includer_token_source); 1401 } 1402 1403 var stack_fallback = std.heap.stackFallback(path_buf_stack_limit, comp.gpa); 1404 const sf_allocator = stack_fallback.get(); 1405 1406 while (try it.nextWithFile(filename, sf_allocator)) |found| { 1407 defer sf_allocator.free(found.path); 1408 if (!std.meta.isError(comp.cwd.access(found.path, .{}))) return true; 1409 } 1410 return false; 1411 } 1412 1413 pub const WhichInclude = enum { 1414 first, 1415 next, 1416 }; 1417 1418 pub const IncludeType = enum { 1419 quotes, 1420 angle_brackets, 1421 }; 1422 1423 fn getFileContents(comp: *Compilation, path: []const u8, limit: ?u32) ![]const u8 { 1424 if (mem.indexOfScalar(u8, path, 0) != null) { 1425 return error.FileNotFound; 1426 } 1427 1428 const file = try comp.cwd.openFile(path, .{}); 1429 defer file.close(); 1430 1431 var buf = std.array_list.Managed(u8).init(comp.gpa); 1432 defer buf.deinit(); 1433 1434 const max = limit orelse std.math.maxInt(u32); 1435 file.deprecatedReader().readAllArrayList(&buf, max) catch |e| switch (e) { 1436 error.StreamTooLong => if (limit == null) return e, 1437 else => return e, 1438 }; 1439 1440 return buf.toOwnedSlice(); 1441 } 1442 1443 pub fn findEmbed( 1444 comp: *Compilation, 1445 filename: []const u8, 1446 includer_token_source: Source.Id, 1447 /// angle bracket vs quotes 1448 include_type: IncludeType, 1449 limit: ?u32, 1450 ) !?[]const u8 { 1451 if (std.fs.path.isAbsolute(filename)) { 1452 return if (comp.getFileContents(filename, limit)) |some| 1453 some 1454 else |err| switch (err) { 1455 error.OutOfMemory => |e| return e, 1456 else => null, 1457 }; 1458 } 1459 1460 const cwd_source_id = switch (include_type) { 1461 .quotes => includer_token_source, 1462 .angle_brackets => null, 1463 }; 1464 var it = IncludeDirIterator{ .comp = comp, .cwd_source_id = cwd_source_id }; 1465 var stack_fallback = std.heap.stackFallback(path_buf_stack_limit, comp.gpa); 1466 const sf_allocator = stack_fallback.get(); 1467 1468 while (try it.nextWithFile(filename, sf_allocator)) |found| { 1469 defer sf_allocator.free(found.path); 1470 if (comp.getFileContents(found.path, limit)) |some| 1471 return some 1472 else |err| switch (err) { 1473 error.OutOfMemory => return error.OutOfMemory, 1474 else => {}, 1475 } 1476 } 1477 return null; 1478 } 1479 1480 pub fn findInclude( 1481 comp: *Compilation, 1482 filename: []const u8, 1483 includer_token: Token, 1484 /// angle bracket vs quotes 1485 include_type: IncludeType, 1486 /// include vs include_next 1487 which: WhichInclude, 1488 ) !?Source { 1489 if (std.fs.path.isAbsolute(filename)) { 1490 if (which == .next) return null; 1491 // TODO: classify absolute file as belonging to system includes or not? 1492 return if (comp.addSourceFromPath(filename)) |some| 1493 some 1494 else |err| switch (err) { 1495 error.OutOfMemory => |e| return e, 1496 else => null, 1497 }; 1498 } 1499 const cwd_source_id = switch (include_type) { 1500 .quotes => switch (which) { 1501 .first => includer_token.source, 1502 .next => null, 1503 }, 1504 .angle_brackets => null, 1505 }; 1506 var it = IncludeDirIterator{ .comp = comp, .cwd_source_id = cwd_source_id }; 1507 1508 if (which == .next) { 1509 it.skipUntilDirMatch(includer_token.source); 1510 } 1511 1512 var stack_fallback = std.heap.stackFallback(path_buf_stack_limit, comp.gpa); 1513 const sf_allocator = stack_fallback.get(); 1514 1515 while (try it.nextWithFile(filename, sf_allocator)) |found| { 1516 defer sf_allocator.free(found.path); 1517 if (comp.addSourceFromPathExtra(found.path, found.kind)) |some| { 1518 if (it.tried_ms_cwd) { 1519 try comp.addDiagnostic(.{ 1520 .tag = .ms_search_rule, 1521 .extra = .{ .str = some.path }, 1522 .loc = .{ 1523 .id = includer_token.source, 1524 .byte_offset = includer_token.start, 1525 .line = includer_token.line, 1526 }, 1527 }, &.{}); 1528 } 1529 return some; 1530 } else |err| switch (err) { 1531 error.OutOfMemory => return error.OutOfMemory, 1532 else => {}, 1533 } 1534 } 1535 return null; 1536 } 1537 1538 pub fn addPragmaHandler(comp: *Compilation, name: []const u8, handler: *Pragma) Allocator.Error!void { 1539 try comp.pragma_handlers.putNoClobber(comp.gpa, name, handler); 1540 } 1541 1542 pub fn addDefaultPragmaHandlers(comp: *Compilation) Allocator.Error!void { 1543 const GCC = @import("pragmas/gcc.zig"); 1544 var gcc = try GCC.init(comp.gpa); 1545 errdefer gcc.deinit(gcc, comp); 1546 1547 const Once = @import("pragmas/once.zig"); 1548 var once = try Once.init(comp.gpa); 1549 errdefer once.deinit(once, comp); 1550 1551 const Message = @import("pragmas/message.zig"); 1552 var message = try Message.init(comp.gpa); 1553 errdefer message.deinit(message, comp); 1554 1555 const Pack = @import("pragmas/pack.zig"); 1556 var pack = try Pack.init(comp.gpa); 1557 errdefer pack.deinit(pack, comp); 1558 1559 try comp.addPragmaHandler("GCC", gcc); 1560 try comp.addPragmaHandler("once", once); 1561 try comp.addPragmaHandler("message", message); 1562 try comp.addPragmaHandler("pack", pack); 1563 } 1564 1565 pub fn getPragma(comp: *Compilation, name: []const u8) ?*Pragma { 1566 return comp.pragma_handlers.get(name); 1567 } 1568 1569 const PragmaEvent = enum { 1570 before_preprocess, 1571 before_parse, 1572 after_parse, 1573 }; 1574 1575 pub fn pragmaEvent(comp: *Compilation, event: PragmaEvent) void { 1576 for (comp.pragma_handlers.values()) |pragma| { 1577 const maybe_func = switch (event) { 1578 .before_preprocess => pragma.beforePreprocess, 1579 .before_parse => pragma.beforeParse, 1580 .after_parse => pragma.afterParse, 1581 }; 1582 if (maybe_func) |func| func(pragma, comp); 1583 } 1584 } 1585 1586 pub fn hasBuiltin(comp: *const Compilation, name: []const u8) bool { 1587 if (std.mem.eql(u8, name, "__builtin_va_arg") or 1588 std.mem.eql(u8, name, "__builtin_choose_expr") or 1589 std.mem.eql(u8, name, "__builtin_bitoffsetof") or 1590 std.mem.eql(u8, name, "__builtin_offsetof") or 1591 std.mem.eql(u8, name, "__builtin_types_compatible_p")) return true; 1592 1593 const builtin = Builtin.fromName(name) orelse return false; 1594 return comp.hasBuiltinFunction(builtin); 1595 } 1596 1597 pub fn hasBuiltinFunction(comp: *const Compilation, builtin: Builtin) bool { 1598 if (!target_util.builtinEnabled(comp.target, builtin.properties.target_set)) return false; 1599 1600 switch (builtin.properties.language) { 1601 .all_languages => return true, 1602 .all_ms_languages => return comp.langopts.emulate == .msvc, 1603 .gnu_lang, .all_gnu_languages => return comp.langopts.standard.isGNU(), 1604 } 1605 } 1606 1607 pub fn locSlice(comp: *const Compilation, loc: Source.Location) []const u8 { 1608 var tmp_tokenizer = Tokenizer{ 1609 .buf = comp.getSource(loc.id).buf, 1610 .langopts = comp.langopts, 1611 .index = loc.byte_offset, 1612 .source = .generated, 1613 }; 1614 const tok = tmp_tokenizer.next(); 1615 return tmp_tokenizer.buf[tok.start..tok.end]; 1616 } 1617 1618 pub const CharUnitSize = enum(u32) { 1619 @"1" = 1, 1620 @"2" = 2, 1621 @"4" = 4, 1622 1623 pub fn Type(comptime self: CharUnitSize) type { 1624 return switch (self) { 1625 .@"1" => u8, 1626 .@"2" => u16, 1627 .@"4" => u32, 1628 }; 1629 } 1630 }; 1631 1632 pub const addDiagnostic = Diagnostics.add; 1633 1634 test "addSourceFromReader" { 1635 const Test = struct { 1636 fn addSourceFromReader(str: []const u8, expected: []const u8, warning_count: u32, splices: []const u32) !void { 1637 var comp = Compilation.init(std.testing.allocator, std.fs.cwd()); 1638 defer comp.deinit(); 1639 1640 var buf_reader = std.io.fixedBufferStream(str); 1641 const source = try comp.addSourceFromReader(buf_reader.reader(), "path", .user); 1642 1643 try std.testing.expectEqualStrings(expected, source.buf); 1644 try std.testing.expectEqual(warning_count, @as(u32, @intCast(comp.diagnostics.list.items.len))); 1645 try std.testing.expectEqualSlices(u32, splices, source.splice_locs); 1646 } 1647 1648 fn withAllocationFailures(allocator: std.mem.Allocator) !void { 1649 var comp = Compilation.init(allocator, std.fs.cwd()); 1650 defer comp.deinit(); 1651 1652 _ = try comp.addSourceFromBuffer("path", "spliced\\\nbuffer\n"); 1653 _ = try comp.addSourceFromBuffer("path", "non-spliced buffer\n"); 1654 } 1655 }; 1656 try Test.addSourceFromReader("ab\\\nc", "abc", 0, &.{2}); 1657 try Test.addSourceFromReader("ab\\\rc", "abc", 0, &.{2}); 1658 try Test.addSourceFromReader("ab\\\r\nc", "abc", 0, &.{2}); 1659 try Test.addSourceFromReader("ab\\ \nc", "abc", 1, &.{2}); 1660 try Test.addSourceFromReader("ab\\\t\nc", "abc", 1, &.{2}); 1661 try Test.addSourceFromReader("ab\\ \t\nc", "abc", 1, &.{2}); 1662 try Test.addSourceFromReader("ab\\\r \nc", "ab \nc", 0, &.{2}); 1663 try Test.addSourceFromReader("ab\\\\\nc", "ab\\c", 0, &.{3}); 1664 try Test.addSourceFromReader("ab\\ \r\nc", "abc", 1, &.{2}); 1665 try Test.addSourceFromReader("ab\\ \\\nc", "ab\\ c", 0, &.{4}); 1666 try Test.addSourceFromReader("ab\\\r\\\nc", "abc", 0, &.{ 2, 2 }); 1667 try Test.addSourceFromReader("ab\\ \rc", "abc", 1, &.{2}); 1668 try Test.addSourceFromReader("ab\\", "ab\\", 0, &.{}); 1669 try Test.addSourceFromReader("ab\\\\", "ab\\\\", 0, &.{}); 1670 try Test.addSourceFromReader("ab\\ ", "ab\\ ", 0, &.{}); 1671 try Test.addSourceFromReader("ab\\\n", "ab", 0, &.{2}); 1672 try Test.addSourceFromReader("ab\\\r\n", "ab", 0, &.{2}); 1673 try Test.addSourceFromReader("ab\\\r", "ab", 0, &.{2}); 1674 1675 // carriage return normalization 1676 try Test.addSourceFromReader("ab\r", "ab\n", 0, &.{}); 1677 try Test.addSourceFromReader("ab\r\r", "ab\n\n", 0, &.{}); 1678 try Test.addSourceFromReader("ab\r\r\n", "ab\n\n", 0, &.{}); 1679 try Test.addSourceFromReader("ab\r\r\n\r", "ab\n\n\n", 0, &.{}); 1680 try Test.addSourceFromReader("\r\\", "\n\\", 0, &.{}); 1681 try Test.addSourceFromReader("\\\r\\", "\\", 0, &.{0}); 1682 1683 try std.testing.checkAllAllocationFailures(std.testing.allocator, Test.withAllocationFailures, .{}); 1684 } 1685 1686 test "addSourceFromReader - exhaustive check for carriage return elimination" { 1687 const alphabet = [_]u8{ '\r', '\n', ' ', '\\', 'a' }; 1688 const alen = alphabet.len; 1689 var buf: [alphabet.len]u8 = [1]u8{alphabet[0]} ** alen; 1690 1691 var comp = Compilation.init(std.testing.allocator, std.fs.cwd()); 1692 defer comp.deinit(); 1693 1694 var source_count: u32 = 0; 1695 1696 while (true) { 1697 const source = try comp.addSourceFromBuffer(&buf, &buf); 1698 source_count += 1; 1699 try std.testing.expect(std.mem.indexOfScalar(u8, source.buf, '\r') == null); 1700 1701 if (std.mem.allEqual(u8, &buf, alphabet[alen - 1])) break; 1702 1703 var idx = std.mem.indexOfScalar(u8, &alphabet, buf[buf.len - 1]).?; 1704 buf[buf.len - 1] = alphabet[(idx + 1) % alen]; 1705 var j = buf.len - 1; 1706 while (j > 0) : (j -= 1) { 1707 idx = std.mem.indexOfScalar(u8, &alphabet, buf[j - 1]).?; 1708 if (buf[j] == alphabet[0]) buf[j - 1] = alphabet[(idx + 1) % alen] else break; 1709 } 1710 } 1711 try std.testing.expect(source_count == std.math.powi(usize, alen, alen) catch unreachable); 1712 } 1713 1714 test "ignore BOM at beginning of file" { 1715 const BOM = "\xEF\xBB\xBF"; 1716 1717 const Test = struct { 1718 fn run(buf: []const u8) !void { 1719 var comp = Compilation.init(std.testing.allocator, std.fs.cwd()); 1720 defer comp.deinit(); 1721 1722 var buf_reader = std.io.fixedBufferStream(buf); 1723 const source = try comp.addSourceFromReader(buf_reader.reader(), "file.c", .user); 1724 const expected_output = if (mem.startsWith(u8, buf, BOM)) buf[BOM.len..] else buf; 1725 try std.testing.expectEqualStrings(expected_output, source.buf); 1726 } 1727 }; 1728 1729 try Test.run(BOM); 1730 try Test.run(BOM ++ "x"); 1731 try Test.run("x" ++ BOM); 1732 try Test.run(BOM ++ " "); 1733 try Test.run(BOM ++ "\n"); 1734 try Test.run(BOM ++ "\\"); 1735 1736 try Test.run(BOM[0..1] ++ "x"); 1737 try Test.run(BOM[0..2] ++ "x"); 1738 try Test.run(BOM[1..] ++ "x"); 1739 try Test.run(BOM[2..] ++ "x"); 1740 }