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/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 31fa9c17..fe49f3fa 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 @@ -1421,6 +1425,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 +1448,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); @@ -1788,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 @@ -1815,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; @@ -1824,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) { @@ -3011,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)); @@ -3050,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)) { @@ -3140,12 +3151,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); @@ -3848,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; @@ -5114,6 +5133,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)", @@ -6932,8 +6953,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); @@ -7260,6 +7284,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)); } @@ -7290,6 +7315,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)); } @@ -7435,12 +7461,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); @@ -7742,6 +7774,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/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/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/__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" \ + 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" \ + 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[""[""] diff --git a/test/cases/assembly invalid token.c b/test/cases/assembly invalid token.c new file mode 100644 index 00000000..7ffa0faa Binary files /dev/null and b/test/cases/assembly invalid token.c differ 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 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; 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 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;