From 87840d55f9405c8ddaccf5e630b83c92f6cb86de Mon Sep 17 00:00:00 2001 From: Evan Haas Date: Fri, 3 May 2024 23:14:27 -0700 Subject: [PATCH 1/8] Parser: ignore invalid types inside typeof Fixes #704 --- src/aro/Parser.zig | 4 ++++ test/cases/typeof invalid pointer.c | 2 ++ 2 files changed, 6 insertions(+) create mode 100644 test/cases/typeof invalid pointer.c diff --git a/src/aro/Parser.zig b/src/aro/Parser.zig index 31fa9c17..ab64465f 100644 --- a/src/aro/Parser.zig +++ b/src/aro/Parser.zig @@ -1421,6 +1421,8 @@ fn typeof(p: *Parser) Error!?Type { const l_paren = try p.expectToken(.l_paren); if (try p.typeName()) |ty| { try p.expectClosing(l_paren, .r_paren); + if (ty.is(.invalid)) return null; + const typeof_ty = try p.arena.create(Type); typeof_ty.* = .{ .data = ty.data, @@ -1442,6 +1444,8 @@ fn typeof(p: *Parser) Error!?Type { .specifier = .nullptr_t, .qual = if (unqual) .{} else typeof_expr.ty.qual.inheritFromTypeof(), }; + } else if (typeof_expr.ty.is(.invalid)) { + return null; } const inner = try p.arena.create(Type.Expr); diff --git a/test/cases/typeof invalid pointer.c b/test/cases/typeof invalid pointer.c new file mode 100644 index 00000000..10798fbe --- /dev/null +++ b/test/cases/typeof invalid pointer.c @@ -0,0 +1,2 @@ +#define NO_ERROR_VALIDATION +typeof((void)0 + 0) *a = 2; From dd5acedfcf97d600b6ee47fc7917c261d9bbaff7 Mon Sep 17 00:00:00 2001 From: Evan Haas Date: Fri, 3 May 2024 23:31:42 -0700 Subject: [PATCH 2/8] Parser: only check array bounds if subscript is an integer Fixes #703 --- src/aro/Parser.zig | 21 +++++++++++++++------ test/cases/array subscript with string.c | 3 +++ 2 files changed, 18 insertions(+), 6 deletions(-) create mode 100644 test/cases/array subscript with string.c diff --git a/src/aro/Parser.zig b/src/aro/Parser.zig index ab64465f..fd1f7c23 100644 --- a/src/aro/Parser.zig +++ b/src/aro/Parser.zig @@ -6936,8 +6936,11 @@ fn offsetofMemberDesignator(p: *Parser, base_ty: Type, want_bits: bool) Error!Re try ptr.lvalConversion(p); try index.lvalConversion(p); - if (!index.ty.isInt()) try p.errTok(.invalid_index, l_bracket_tok); - try p.checkArrayBounds(index, lhs, l_bracket_tok); + if (index.ty.isInt()) { + try p.checkArrayBounds(index, lhs, l_bracket_tok); + } else { + try p.errTok(.invalid_index, l_bracket_tok); + } try index.saveValue(p); try ptr.bin(p, .array_access_expr, index); @@ -7439,12 +7442,18 @@ fn suffixExpr(p: *Parser, lhs: Result) Error!Result { try index.lvalConversion(p); if (ptr.ty.isPtr()) { ptr.ty = ptr.ty.elemType(); - if (!index.ty.isInt()) try p.errTok(.invalid_index, l_bracket); - try p.checkArrayBounds(index_before_conversion, array_before_conversion, l_bracket); + if (index.ty.isInt()) { + try p.checkArrayBounds(index_before_conversion, array_before_conversion, l_bracket); + } else { + try p.errTok(.invalid_index, l_bracket); + } } else if (index.ty.isPtr()) { index.ty = index.ty.elemType(); - if (!ptr.ty.isInt()) try p.errTok(.invalid_index, l_bracket); - try p.checkArrayBounds(array_before_conversion, index_before_conversion, l_bracket); + if (ptr.ty.isInt()) { + try p.checkArrayBounds(array_before_conversion, index_before_conversion, l_bracket); + } else { + try p.errTok(.invalid_index, l_bracket); + } std.mem.swap(Result, &ptr, &index); } else { try p.errTok(.invalid_subscript, l_bracket); diff --git a/test/cases/array subscript with string.c b/test/cases/array subscript with string.c new file mode 100644 index 00000000..3c4d3e1a --- /dev/null +++ b/test/cases/array subscript with string.c @@ -0,0 +1,3 @@ +#define NO_ERROR_VALIDATION +int x[2]; +x[""[""] From 4e40f6f90828e3d213aa49e00e7562885017cfa4 Mon Sep 17 00:00:00 2001 From: Evan Haas Date: Fri, 3 May 2024 23:32:16 -0700 Subject: [PATCH 3/8] tests: move spliced newline test into cases directory This was accidentally sitting in the tests directory --- test/{ => cases}/spliced newline in char literal.c | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename test/{ => cases}/spliced newline in char literal.c (100%) diff --git a/test/spliced newline in char literal.c b/test/cases/spliced newline in char literal.c similarity index 100% rename from test/spliced newline in char literal.c rename to test/cases/spliced newline in char literal.c From ce6ce9862abe3219e153e3c968ef24aea8fc3c97 Mon Sep 17 00:00:00 2001 From: Evan Haas Date: Sat, 4 May 2024 16:07:49 -0700 Subject: [PATCH 4/8] Parser: do not allow __auto_type variables to be self-initialized Fixes #702 --- src/aro/Diagnostics/messages.def | 5 +++++ src/aro/Parser.zig | 18 ++++++++++++++++-- test/cases/__auto_type self init.c | 14 ++++++++++++++ 3 files changed, 35 insertions(+), 2 deletions(-) create mode 100644 test/cases/__auto_type self init.c diff --git a/src/aro/Diagnostics/messages.def b/src/aro/Diagnostics/messages.def index 5d242d32..e76b2ec6 100644 --- a/src/aro/Diagnostics/messages.def +++ b/src/aro/Diagnostics/messages.def @@ -2487,3 +2487,8 @@ invalid_type_underlying_enum .msg = "non-integral type '{s}' is an invalid underlying type" .extra = .str .kind = .@"error" + +auto_type_self_initialized + .msg = "variable '{s}' declared with deduced type '__auto_type' cannot appear in its own initializer" + .extra = .str + .kind = .@"error" diff --git a/src/aro/Parser.zig b/src/aro/Parser.zig index fd1f7c23..70a29656 100644 --- a/src/aro/Parser.zig +++ b/src/aro/Parser.zig @@ -130,6 +130,10 @@ const_decl_folding: ConstDeclFoldingMode = .fold_const_decls, /// address-of-label expression (tracked with contains_address_of_label) computed_goto_tok: ?TokenIndex = null, +/// __auto_type may only be used with a single declarator. Keep track of the name +/// so that it is not used in its own initializer. +auto_type_decl_name: StringId = .empty, + /// Various variables that are different for each function. func: struct { /// null if not in function, will always be plain func, var_args_func or old_style_func @@ -1792,6 +1796,8 @@ fn initDeclarator(p: *Parser, decl_spec: *DeclSpec, attr_buf_top: usize) Error!? } else { apply_var_attributes = true; } + const c23_auto = init_d.d.ty.is(.c23_auto); + const auto_type = init_d.d.ty.is(.auto_type); if (p.eatToken(.equal)) |eq| init: { if (decl_spec.storage_class == .typedef or @@ -1819,6 +1825,11 @@ fn initDeclarator(p: *Parser, decl_spec: *DeclSpec, attr_buf_top: usize) Error!? const interned_name = try StrInt.intern(p.comp, p.tokSlice(init_d.d.name)); try p.syms.declareSymbol(p, interned_name, init_d.d.ty, init_d.d.name, .none); + if (c23_auto or auto_type) { + p.auto_type_decl_name = interned_name; + } + defer p.auto_type_decl_name = .empty; + var init_list_expr = try p.initializer(init_d.d.ty); init_d.initializer = init_list_expr; if (!init_list_expr.ty.isArray()) break :init; @@ -1828,8 +1839,7 @@ fn initDeclarator(p: *Parser, decl_spec: *DeclSpec, attr_buf_top: usize) Error!? } const name = init_d.d.name; - const c23_auto = init_d.d.ty.is(.c23_auto); - if (init_d.d.ty.is(.auto_type) or c23_auto) { + if (auto_type or c23_auto) { if (init_d.initializer.node == .none) { init_d.d.ty = Type.invalid; if (c23_auto) { @@ -7755,6 +7765,10 @@ fn primaryExpr(p: *Parser) Error!Result { const name_tok = try p.expectIdentifier(); const name = p.tokSlice(name_tok); const interned_name = try StrInt.intern(p.comp, name); + if (interned_name == p.auto_type_decl_name) { + try p.errStr(.auto_type_self_initialized, name_tok, name); + return error.ParsingFailed; + } if (p.syms.findSymbol(interned_name)) |sym| { try p.checkDeprecatedUnavailable(sym.ty, name_tok, sym.tok); if (sym.kind == .constexpr) { diff --git a/test/cases/__auto_type self init.c b/test/cases/__auto_type self init.c new file mode 100644 index 00000000..bfcb1dbc --- /dev/null +++ b/test/cases/__auto_type self init.c @@ -0,0 +1,14 @@ +//aro-args -std=c23 +void foo(void) { + int x; + __auto_type x = x; + + __auto_type y = (int []){1, y}; + + auto z = z + 1; +} + +#define EXPECTED_ERRORS "__auto_type self init.c:4:21: error: variable 'x' declared with deduced type '__auto_type' cannot appear in its own initializer" \ + "__auto_type self init.c:6:33: error: variable 'y' declared with deduced type '__auto_type' cannot appear in its own initializer" \ + "__auto_type self init.c:8:14: error: variable 'z' declared with deduced type '__auto_type' cannot appear in its own initializer" \ + From 760ae06ecd6bb0862caf6a2402b2b22fbfa37a26 Mon Sep 17 00:00:00 2001 From: Evan Haas Date: Sat, 4 May 2024 23:10:31 -0700 Subject: [PATCH 5/8] Parser: don't create pointers to invalid types --- src/aro/Parser.zig | 18 ++++++++++++------ test/cases/invalid _BitInt pointer.c | 3 +++ 2 files changed, 15 insertions(+), 6 deletions(-) create mode 100644 test/cases/invalid _BitInt pointer.c diff --git a/src/aro/Parser.zig b/src/aro/Parser.zig index 70a29656..9b6b249f 100644 --- a/src/aro/Parser.zig +++ b/src/aro/Parser.zig @@ -3154,12 +3154,14 @@ fn directDeclarator(p: *Parser, base_type: Type, d: *Declarator, kind: Declarato fn pointer(p: *Parser, base_ty: Type) Error!Type { var ty = base_ty; while (p.eatToken(.asterisk)) |_| { - const elem_ty = try p.arena.create(Type); - elem_ty.* = ty; - ty = Type{ - .specifier = .pointer, - .data = .{ .sub_type = elem_ty }, - }; + if (!ty.is(.invalid)) { + const elem_ty = try p.arena.create(Type); + elem_ty.* = ty; + ty = Type{ + .specifier = .pointer, + .data = .{ .sub_type = elem_ty }, + }; + } var quals = Type.Qualifiers.Builder{}; _ = try p.typeQual(&quals); try quals.finish(p, &ty); @@ -5128,6 +5130,8 @@ pub const Result = struct { ty: Type = .{ .specifier = .int }, val: Value = .{}, + const invalid: Result = .{ .ty = Type.invalid }; + pub fn str(res: Result, p: *Parser) ![]const u8 { switch (res.val.opt_ref) { .none => return "(none)", @@ -7277,6 +7281,7 @@ fn unExpr(p: *Parser) Error!Result { var operand = try p.castExpr(); try operand.expect(p); try operand.lvalConversion(p); + if (operand.ty.is(.invalid)) return Result.invalid; if (!operand.ty.isInt() and !operand.ty.isFloat()) { try p.errStr(.invalid_imag, imag_tok, try p.typeStr(operand.ty)); } @@ -7307,6 +7312,7 @@ fn unExpr(p: *Parser) Error!Result { var operand = try p.castExpr(); try operand.expect(p); try operand.lvalConversion(p); + if (operand.ty.is(.invalid)) return Result.invalid; if (!operand.ty.isInt() and !operand.ty.isFloat()) { try p.errStr(.invalid_real, real_tok, try p.typeStr(operand.ty)); } diff --git a/test/cases/invalid _BitInt pointer.c b/test/cases/invalid _BitInt pointer.c new file mode 100644 index 00000000..9d650201 --- /dev/null +++ b/test/cases/invalid _BitInt pointer.c @@ -0,0 +1,3 @@ +#define NO_ERROR_VALIDATION + +_BitInt(1) *e= __real__ e; From 7a3674d0b6d5e642e993a168829ed45e6005301c Mon Sep 17 00:00:00 2001 From: Evan Haas Date: Sun, 5 May 2024 13:36:50 -0700 Subject: [PATCH 6/8] Type: perform integer promotion on underlying type of fixed enums --- src/aro/Type.zig | 2 ++ test/cases/enum fixed _BitInt.c | 8 ++++++++ 2 files changed, 10 insertions(+) create mode 100644 test/cases/enum fixed _BitInt.c diff --git a/src/aro/Type.zig b/src/aro/Type.zig index 1ca932a8..b6490dc4 100644 --- a/src/aro/Type.zig +++ b/src/aro/Type.zig @@ -857,6 +857,8 @@ pub fn integerPromotion(ty: Type, comp: *Compilation) Type { switch (specifier) { .@"enum" => { if (ty.hasIncompleteSize()) return .{ .specifier = .int }; + if (ty.data.@"enum".fixed) return ty.data.@"enum".tag_ty.integerPromotion(comp); + specifier = ty.data.@"enum".tag_ty.specifier; }, .bit_int, .complex_bit_int => return .{ .specifier = specifier, .data = ty.data }, diff --git a/test/cases/enum fixed _BitInt.c b/test/cases/enum fixed _BitInt.c new file mode 100644 index 00000000..77b15785 --- /dev/null +++ b/test/cases/enum fixed _BitInt.c @@ -0,0 +1,8 @@ +enum E: _BitInt(20) { + A, +}; + +void foo(void) { + enum E e = 1; + int x = -e; +} \ No newline at end of file From e95bb470cd60acc446d9f69692e2f546a66e26c1 Mon Sep 17 00:00:00 2001 From: Evan Haas Date: Sun, 5 May 2024 21:59:24 -0700 Subject: [PATCH 7/8] Parser: handle array designators which produce too-large arrays --- src/aro/Compilation.zig | 6 ++++++ src/aro/Parser.zig | 11 +++++++---- test/cases/array designator too large.c | 7 +++++++ 3 files changed, 20 insertions(+), 4 deletions(-) create mode 100644 test/cases/array designator too large.c diff --git a/src/aro/Compilation.zig b/src/aro/Compilation.zig index f1401676..647ced61 100644 --- a/src/aro/Compilation.zig +++ b/src/aro/Compilation.zig @@ -1060,6 +1060,12 @@ pub fn nextLargestIntSameSign(comp: *const Compilation, ty: Type) ?Type { return null; } +/// Maximum size of an array, in bytes +pub fn maxArrayBytes(comp: *const Compilation) u64 { + const max_bits = @min(61, comp.target.ptrBitWidth()); + return (@as(u64, 1) << @truncate(max_bits)) - 1; +} + /// If `enum E { ... }` syntax has a fixed underlying integer type regardless of the presence of /// __attribute__((packed)) or the range of values of the corresponding enumerator constants, /// specify it here. diff --git a/src/aro/Parser.zig b/src/aro/Parser.zig index 9b6b249f..fe49f3fa 100644 --- a/src/aro/Parser.zig +++ b/src/aro/Parser.zig @@ -3025,9 +3025,6 @@ fn directDeclarator(p: *Parser, base_type: Type, d: *Declarator, kind: Declarato } const outer = try p.directDeclarator(base_type, d, kind); - var max_bits = p.comp.target.ptrBitWidth(); - if (max_bits > 61) max_bits = 61; - const max_bytes = (@as(u64, 1) << @truncate(max_bits)) - 1; if (!size.ty.isInt()) { try p.errStr(.array_size_non_int, size_tok, try p.typeStr(size.ty)); @@ -3064,7 +3061,7 @@ fn directDeclarator(p: *Parser, base_type: Type, d: *Declarator, kind: Declarato } else { // `outer` is validated later so it may be invalid here const outer_size = outer.sizeof(p.comp); - const max_elems = max_bytes / @max(1, outer_size orelse 1); + const max_elems = p.comp.maxArrayBytes() / @max(1, outer_size orelse 1); var size_val = size.val; if (size_val.isZero(p.comp)) { @@ -3864,6 +3861,12 @@ fn convertInitList(p: *Parser, il: InitList, init_ty: Type) Error!NodeIndex { .data = .{ .bin = .{ .lhs = .none, .rhs = .none } }, }; + const max_elems = p.comp.maxArrayBytes() / (elem_ty.sizeof(p.comp) orelse 1); + if (start > max_elems) { + try p.errTok(.array_too_large, il.tok); + start = max_elems; + } + if (init_ty.specifier == .incomplete_array) { arr_init_node.ty.specifier = .array; arr_init_node.ty.data.array.len = start; diff --git a/test/cases/array designator too large.c b/test/cases/array designator too large.c new file mode 100644 index 00000000..050fdb73 --- /dev/null +++ b/test/cases/array designator too large.c @@ -0,0 +1,7 @@ +int arr1[] = { [0x800000000000000 - 1] = 0 }; +int arr2[] = { [0x800000000000000 - 2] = 0 }; +_Static_assert(sizeof(arr1), ""); +_Static_assert(sizeof(arr2), ""); + +#define EXPECTED_ERRORS "array designator too large.c:1:14: error: array is too large" \ + From f5db7050d99e00bf129583e4e4466a54e055de54 Mon Sep 17 00:00:00 2001 From: Evan Haas Date: Sun, 5 May 2024 22:18:15 -0700 Subject: [PATCH 8/8] Tokenizer: render invalid tokens as "invalid bytes" --- src/aro/Tokenizer.zig | 3 ++- test/cases/assembly invalid token.c | Bin 0 -> 140 bytes 2 files changed, 2 insertions(+), 1 deletion(-) create mode 100644 test/cases/assembly invalid token.c diff --git a/src/aro/Tokenizer.zig b/src/aro/Tokenizer.zig index 8dfadb37..8ee38126 100644 --- a/src/aro/Tokenizer.zig +++ b/src/aro/Tokenizer.zig @@ -735,7 +735,8 @@ pub const Token = struct { pub fn symbol(id: Id) []const u8 { return switch (id) { - .macro_string, .invalid => unreachable, + .macro_string => unreachable, + .invalid => "invalid bytes", .identifier, .extended_identifier, .macro_func, diff --git a/test/cases/assembly invalid token.c b/test/cases/assembly invalid token.c new file mode 100644 index 0000000000000000000000000000000000000000..7ffa0faae258659afb8deea355a776c9fd77e0ad GIT binary patch literal 140 zcmXTR&rDHB%g@&+12Qxfs<{+^AU-~^I5$3CLrIB2ldG1COF1PqEi*4w!8Ibl)j7n~ zCEhhC$Ui7pK`F7gI5jsZr&1v^uPiYKXi`ajc50qpvXzmQp{bQZYEe;s5s<1VNKGzD eO;J$SRM!DoR+Rx4}( literal 0 HcmV?d00001