diff --git a/build.zig b/build.zig index 60f6fe4d..bf7c9024 100644 --- a/build.zig +++ b/build.zig @@ -1,7 +1,6 @@ const std = @import("std"); const Build = std.Build; const GenerateDef = @import("build/GenerateDef.zig"); -const ZigLibDirStep = @import("build/ZigLibDir.zig"); const aro_version = std.SemanticVersion{ .major = 0, @@ -11,44 +10,20 @@ const aro_version = std.SemanticVersion{ fn addFuzzStep(b: *Build, target: std.Build.ResolvedTarget, afl_clang_lto_path: []const u8, aro_module: *std.Build.Module) !void { const fuzz_step = b.step("fuzz", "Build executable for fuzz testing."); - const fuzz_target = blk: { - var query = target.query; - query.ofmt = .c; - break :blk b.resolveTargetQuery(query); - }; - - const lib_dir_step = try ZigLibDirStep.create(b); - - const compiler_rt = b.createModule(.{ - .root_source_file = lib_dir_step.getCompilerRTPath(), - }); const fuzz_lib = b.addStaticLibrary(.{ .name = "fuzz-lib", .root_source_file = .{ .path = "test/fuzz/fuzz_lib.zig" }, .optimize = .Debug, - .target = fuzz_target, + .target = target, .single_threaded = true, }); - fuzz_lib.root_module.addImport("compiler_rt", compiler_rt); + fuzz_lib.want_lto = true; + fuzz_lib.bundle_compiler_rt = true; + fuzz_lib.pie = true; fuzz_lib.root_module.addImport("aro", aro_module); - const fuzz_compile = b.addSystemCommand(&.{ - afl_clang_lto_path, - "-Wno-incompatible-pointer-types", - "-nostdinc", - "-isystem", - }); - fuzz_compile.addDirectoryArg(lib_dir_step.getIncludePath()); - fuzz_compile.addArgs(&.{ - "-isystem", - "/usr/include", - "-isystem", - "/usr/local/include", - "-std=c99", - }); + const fuzz_compile = b.addSystemCommand(&.{afl_clang_lto_path}); fuzz_compile.addFileArg(.{ .path = "test/fuzz/main.c" }); - fuzz_compile.addArg("-I"); - fuzz_compile.addDirectoryArg(lib_dir_step.getLibPath()); fuzz_compile.addArg("-o"); const fuzz_exe = fuzz_compile.addOutputFileArg("arofuzz"); const fuzz_install = b.addInstallBinFile(fuzz_exe, "arofuzz"); diff --git a/build/ZigLibDir.zig b/build/ZigLibDir.zig deleted file mode 100644 index cc0aa268..00000000 --- a/build/ZigLibDir.zig +++ /dev/null @@ -1,65 +0,0 @@ -//! Step for determining the zig lib dir and various subdirectories via `zig env` - -const std = @import("std"); -const Step = std.Build.Step; -const ZigLibDir = @This(); -const LazyPath = std.Build.LazyPath; - -step: Step, -/// path to zig's lib directory -lib_dir: std.Build.GeneratedFile, -/// path to compiler-rt directory -compiler_rt: std.Build.GeneratedFile, -/// path to directory which has zig.h -include: std.Build.GeneratedFile, - -pub fn create(owner: *std.Build) !*ZigLibDir { - const self = try owner.allocator.create(ZigLibDir); - const name = "zig lib dir"; - self.* = .{ - .step = Step.init(.{ - .id = .custom, - .name = name, - .owner = owner, - .makeFn = make, - }), - .lib_dir = .{ .step = &self.step }, - .compiler_rt = .{ .step = &self.step }, - .include = .{ .step = &self.step }, - }; - return self; -} - -const ZigEnv = struct { - lib_dir: []const u8, -}; - -fn make(step: *Step, prog_node: *std.Progress.Node) !void { - _ = prog_node; - - const b = step.owner; - const self: *ZigLibDir = @fieldParentPtr("step", step); - - const zig_env_args: [2][]const u8 = .{ b.graph.zig_exe, "env" }; - var out_code: u8 = undefined; - const zig_env = try b.runAllowFail(&zig_env_args, &out_code, .Ignore); - - const parsed_str = try std.json.parseFromSlice(ZigEnv, b.allocator, zig_env, .{ .ignore_unknown_fields = true }); - defer parsed_str.deinit(); - - self.lib_dir.path = parsed_str.value.lib_dir; - self.compiler_rt.path = try std.fs.path.join(b.allocator, &[_][]const u8{ parsed_str.value.lib_dir, "compiler_rt.zig" }); - self.include.path = try std.fs.path.join(b.allocator, &[_][]const u8{ parsed_str.value.lib_dir, "include" }); -} - -pub fn getLibPath(self: *ZigLibDir) std.Build.LazyPath { - return .{ .generated = &self.lib_dir }; -} - -pub fn getCompilerRTPath(self: *ZigLibDir) std.Build.LazyPath { - return .{ .generated = &self.compiler_rt }; -} - -pub fn getIncludePath(self: *ZigLibDir) std.Build.LazyPath { - return .{ .generated = &self.include }; -} diff --git a/src/aro/Parser.zig b/src/aro/Parser.zig index 9a956b0e..8c971358 100644 --- a/src/aro/Parser.zig +++ b/src/aro/Parser.zig @@ -100,7 +100,7 @@ value_map: Tree.ValueMap, // buffers used during compilation syms: SymbolStack = .{}, -strings: std.ArrayList(u8), +strings: std.ArrayListAligned(u8, 4), labels: std.ArrayList(Label), list_buf: NodeList, decl_buf: NodeList, @@ -160,7 +160,7 @@ record: struct { } fn addFieldsFromAnonymous(r: @This(), p: *Parser, ty: Type) Error!void { - for (ty.data.record.fields) |f| { + for (ty.getRecord().?.fields) |f| { if (f.isAnonymousRecord()) { try r.addFieldsFromAnonymous(p, f.ty.canonicalize(.standard)); } else if (f.name_tok != 0) { @@ -688,7 +688,7 @@ pub fn parse(pp: *Preprocessor) Compilation.Error!Tree { .gpa = pp.comp.gpa, .arena = arena.allocator(), .tok_ids = pp.tokens.items(.id), - .strings = std.ArrayList(u8).init(pp.comp.gpa), + .strings = std.ArrayListAligned(u8, 4).init(pp.comp.gpa), .value_map = Tree.ValueMap.init(pp.comp.gpa), .data = NodeList.init(pp.comp.gpa), .labels = std.ArrayList(Label).init(pp.comp.gpa), @@ -5466,10 +5466,14 @@ pub const Result = struct { fn lvalConversion(res: *Result, p: *Parser) Error!void { if (res.ty.isFunc()) { - const elem_ty = try p.arena.create(Type); - elem_ty.* = res.ty; - res.ty.specifier = .pointer; - res.ty.data = .{ .sub_type = elem_ty }; + if (res.ty.isInvalidFunc()) { + res.ty = .{ .specifier = .invalid }; + } else { + const elem_ty = try p.arena.create(Type); + elem_ty.* = res.ty; + res.ty.specifier = .pointer; + res.ty.data = .{ .sub_type = elem_ty }; + } try res.implicitCast(p, .function_to_pointer); } else if (res.ty.isArray()) { res.val = .{}; @@ -8018,6 +8022,9 @@ fn stringLiteral(p: *Parser) Error!Result { const strings_top = p.strings.items.len; defer p.strings.items.len = strings_top; + const literal_start = mem.alignForward(usize, strings_top, @intFromEnum(char_width)); + try p.strings.resize(literal_start); + while (p.tok_i < string_end) : (p.tok_i += 1) { const this_kind = text_literal.Kind.classify(p.tok_ids[p.tok_i], .string_literal).?; const slice = this_kind.contentSlice(p.tokSlice(p.tok_i)); @@ -8068,7 +8075,7 @@ fn stringLiteral(p: *Parser) Error!Result { switch (char_width) { .@"1" => p.strings.appendSliceAssumeCapacity(view.bytes), .@"2" => { - const capacity_slice: []align(@alignOf(u16)) u8 = @alignCast(p.strings.unusedCapacitySlice()); + const capacity_slice: []align(@alignOf(u16)) u8 = @alignCast(p.strings.allocatedSlice()[literal_start..]); const dest_len = std.mem.alignBackward(usize, capacity_slice.len, 2); const dest = std.mem.bytesAsSlice(u16, capacity_slice[0..dest_len]); const words_written = std.unicode.utf8ToUtf16Le(dest, view.bytes) catch unreachable; @@ -8089,7 +8096,7 @@ fn stringLiteral(p: *Parser) Error!Result { } } p.strings.appendNTimesAssumeCapacity(0, @intFromEnum(char_width)); - const slice = p.strings.items[strings_top..]; + const slice = p.strings.items[literal_start..]; // TODO this won't do anything if there is a cache hit const interned_align = mem.alignForward( diff --git a/src/aro/Preprocessor.zig b/src/aro/Preprocessor.zig index 0266051d..6749fdea 100644 --- a/src/aro/Preprocessor.zig +++ b/src/aro/Preprocessor.zig @@ -976,7 +976,7 @@ fn expr(pp: *Preprocessor, tokenizer: *Tokenizer) MacroError!bool { .tok_i = @intCast(token_state.tokens_len), .arena = pp.arena.allocator(), .in_macro = true, - .strings = std.ArrayList(u8).init(pp.comp.gpa), + .strings = std.ArrayListAligned(u8, 4).init(pp.comp.gpa), .data = undefined, .value_map = undefined, diff --git a/src/aro/Type.zig b/src/aro/Type.zig index 2364adcb..dd983c8d 100644 --- a/src/aro/Type.zig +++ b/src/aro/Type.zig @@ -696,6 +696,16 @@ pub fn params(ty: Type) []Func.Param { }; } +/// Returns true if the return value or any param of `ty` is `.invalid` +/// Asserts that ty is a function type +pub fn isInvalidFunc(ty: Type) bool { + if (ty.returnType().is(.invalid)) return true; + for (ty.params()) |param| { + if (param.ty.is(.invalid)) return true; + } + return false; +} + pub fn arrayLen(ty: Type) ?u64 { return switch (ty.specifier) { .array, .static_array => ty.data.array.len, @@ -1106,7 +1116,7 @@ pub fn alignof(ty: Type, comp: *const Compilation) u29 { .ulong_long => comp.target.c_type_alignment(.ulonglong), .bit_int => @min( - std.math.ceilPowerOfTwoPromote(u16, (ty.data.int.bits + 7) / 8), + std.math.ceilPowerOfTwoPromote(u16, (ty.data.int.bits +| 7) / 8), comp.target.maxIntAlignment(), ), @@ -1220,9 +1230,9 @@ pub fn eql(a_param: Type, b_param: Type, comp: *const Compilation, check_qualifi if (!b.isFunc()) return false; } else if (a.isArray()) { if (!b.isArray()) return false; - } else if (a.specifier == .@"enum" and a.data.@"enum".fixed and b.specifier != .@"enum") { + } else if (a.specifier == .@"enum" and b.specifier != .@"enum") { return a.data.@"enum".tag_ty.eql(b, comp, check_qualifiers); - } else if (b.specifier == .@"enum" and b.data.@"enum".fixed and a.specifier != .@"enum") { + } else if (b.specifier == .@"enum" and a.specifier != .@"enum") { return a.eql(b.data.@"enum".tag_ty, comp, check_qualifiers); } else if (a.specifier != b.specifier) return false; @@ -1318,10 +1328,8 @@ pub fn integerRank(ty: Type, comp: *const Compilation) usize { .typeof_expr => ty.data.expr.ty.integerRank(comp), .attributed => ty.data.attributed.base.integerRank(comp), - .@"enum" => { - std.debug.assert(real.data.@"enum".fixed); - return real.data.@"enum".tag_ty.integerRank(comp); - }, + .@"enum" => real.data.@"enum".tag_ty.integerRank(comp), + else => unreachable, }); } diff --git a/src/aro/Value.zig b/src/aro/Value.zig index fddb37be..d703eb49 100644 --- a/src/aro/Value.zig +++ b/src/aro/Value.zig @@ -757,10 +757,16 @@ pub fn bitAnd(lhs: Value, rhs: Value, comp: *Compilation) !Value { const lhs_bigint = lhs.toBigInt(&lhs_space, comp); const rhs_bigint = rhs.toBigInt(&rhs_space, comp); - const limbs = try comp.gpa.alloc( - std.math.big.Limb, - @max(lhs_bigint.limbs.len, rhs_bigint.limbs.len), - ); + const limb_count = if (lhs_bigint.positive and rhs_bigint.positive) + @min(lhs_bigint.limbs.len, rhs_bigint.limbs.len) + else if (lhs_bigint.positive) + lhs_bigint.limbs.len + else if (rhs_bigint.positive) + rhs_bigint.limbs.len + else + @max(lhs_bigint.limbs.len, rhs_bigint.limbs.len) + 1; + + const limbs = try comp.gpa.alloc(std.math.big.Limb, limb_count); defer comp.gpa.free(limbs); var result_bigint = std.math.big.int.Mutable{ .limbs = limbs, .positive = undefined, .len = undefined }; diff --git a/test/cases/alignment.c b/test/cases/alignment.c index b8df5686..b7ae4226 100644 --- a/test/cases/alignment.c +++ b/test/cases/alignment.c @@ -34,6 +34,7 @@ _Static_assert(_Alignof(n) == 32, "incorrect alignment"); __attribute__((aligned("foo"))) o; _Alignas(1.2) p; +_Static_assert(_Alignof(_BitInt(65535)) > 0, ""); #define EXPECTED_ERRORS "alignment.c:1:1: error: '_Alignas' attribute only applies to variables and fields" \ "alignment.c:3:3: error: '_Alignas' attribute only applies to variables and fields" \ diff --git a/test/cases/attributed anonymous record.c b/test/cases/attributed anonymous record.c new file mode 100644 index 00000000..a0a43d69 --- /dev/null +++ b/test/cases/attributed anonymous record.c @@ -0,0 +1,5 @@ +struct A{ + union { + char a; + } __attribute__((packed)); +}; diff --git a/test/cases/binary expressions.c b/test/cases/binary expressions.c index 2b008d3f..a1ec9630 100644 --- a/test/cases/binary expressions.c +++ b/test/cases/binary expressions.c @@ -87,6 +87,7 @@ _Static_assert(2.0||(2.0 == 2.0), ""); _Static_assert(2.0||(3.0 > 2.0), ""); _Static_assert(2.0||(2.0 && 2.0), ""); +_Static_assert((-10 & -1) == -10, ""); #define EXPECTED_ERRORS "binary expressions.c:3:7: error: invalid operands to binary expression ('long' and 'float')" \ "binary expressions.c:6:13: error: invalid operands to binary expression ('char' and 'int *')" \ "binary expressions.c:8:9: error: invalid operands to binary expression ('void (*)(void)' and 'void')" \ diff --git a/test/cases/enum pointer.c b/test/cases/enum pointer.c new file mode 100644 index 00000000..4b9a4e66 --- /dev/null +++ b/test/cases/enum pointer.c @@ -0,0 +1,18 @@ +enum E { + A, +}; + +void foo(void) { + int x; + unsigned y; + enum E *p1 = &x; + enum E *p2 = &y; +} + +#if __WIN32__ +#define EXPECTED_ERRORS "enum pointer.c:9:18: warning: incompatible pointer types initializing 'enum E *' from incompatible type 'unsigned int *' converts between pointers to integer types with different sign [-Wpointer-sign]" \ + +#else +#define EXPECTED_ERRORS "enum pointer.c:8:18: warning: incompatible pointer types initializing 'enum E *' from incompatible type 'int *' [-Wincompatible-pointer-types]" \ + +#endif diff --git a/test/cases/functions.c b/test/cases/functions.c index b4cf7d73..91d8ff24 100644 --- a/test/cases/functions.c +++ b/test/cases/functions.c @@ -78,6 +78,9 @@ int (*return_array_ptr(void))[2] { void no_params(void); void no_params(int x){} +void invalid_func(__auto_type); +int invalid_int = invalid_func; + #define EXPECTED_ERRORS "functions.c:10:12: error: parameter named 'quux' is missing" \ "functions.c:20:14: error: illegal initializer (only variables can be initialized)" \ "functions.c:18:2: warning: non-void function 'foooo' does not return a value [-Wreturn-type]" \ @@ -90,4 +93,5 @@ void no_params(int x){} "functions.c:55:9: error: parameter has incomplete type 'enum EE'" \ "functions.c:79:6: error: redefinition of 'no_params' with a different type" \ "functions.c:78:6: note: previous definition is here" \ + "functions.c:81:19: error: '__auto_type' not allowed in function prototype" \ diff --git a/test/cases/unaligned u16 string literal.c b/test/cases/unaligned u16 string literal.c new file mode 100644 index 00000000..1a25cd06 --- /dev/null +++ b/test/cases/unaligned u16 string literal.c @@ -0,0 +1,12 @@ +struct S { + int x; +}; + +void foo(void) { + struct S s; + s.y; +} + +_Static_assert(u"A"); + +#define NO_ERROR_VALIDATION