From 24b1d7ad4b3b5ddc079c71f3b0b48fac88bd6e0d Mon Sep 17 00:00:00 2001 From: Evan Haas Date: Tue, 5 Jul 2022 20:15:16 -0700 Subject: [PATCH 1/3] Parser: don't perform bounds check for trailing one-element array access --- src/Parser.zig | 31 +++++++++++++----- test/cases/old style flexible array.c | 45 +++++++++++++++++++++++++++ 2 files changed, 69 insertions(+), 7 deletions(-) create mode 100644 test/cases/old style flexible array.c diff --git a/src/Parser.zig b/src/Parser.zig index ad608883..b3509169 100644 --- a/src/Parser.zig +++ b/src/Parser.zig @@ -5565,7 +5565,7 @@ fn offsetofMemberDesignator(p: *Parser, base_ty: Type) Error!Result { try index.lvalConversion(p); if (!index.ty.isInt()) try p.errTok(.invalid_index, l_bracket_tok); - try p.checkArrayBounds(index, lhs.ty, l_bracket_tok); + try p.checkArrayBounds(index, lhs, l_bracket_tok); try index.saveValue(p); try ptr.bin(p, .array_access_expr, index); @@ -5928,19 +5928,19 @@ fn suffixExpr(p: *Parser, lhs: Result) Error!Result { try index.expect(p); try p.expectClosing(l_bracket, .r_bracket); - const l_ty = lhs.ty; - const r_ty = index.ty; + const array_before_conversion = lhs; + const index_before_conversion = index; var ptr = lhs; try ptr.lvalConversion(p); 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, l_ty, l_bracket); + try p.checkArrayBounds(index_before_conversion, array_before_conversion, 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(ptr, r_ty, l_bracket); + try p.checkArrayBounds(array_before_conversion, index_before_conversion, l_bracket); std.mem.swap(Result, &ptr, &index); } else { try p.errTok(.invalid_subscript, l_bracket); @@ -6162,9 +6162,26 @@ fn callExpr(p: *Parser, lhs: Result) Error!Result { return Result{ .node = try p.addNode(call_node), .ty = call_node.ty }; } -fn checkArrayBounds(p: *Parser, index: Result, arr_ty: Type, tok: TokenIndex) !void { +fn checkArrayBounds(p: *Parser, index: Result, array: Result, tok: TokenIndex) !void { if (index.val.tag == .unavailable) return; - const len = Value.int(arr_ty.arrayLen() orelse return); + + const array_len = array.ty.arrayLen() orelse return; + if (array_len == 0) return; + + if (array_len == 1) { + if (p.getNode(array.node, .member_access_expr) orelse p.getNode(array.node, .member_access_ptr_expr)) |node| { + const data = p.nodes.items(.data)[@enumToInt(node)]; + var lhs = p.nodes.items(.ty)[@enumToInt(data.member.lhs)]; + if (lhs.get(.pointer)) |ptr| { + lhs = ptr.data.sub_type.*; + } + if (lhs.is(.@"struct")) { + const record = lhs.getRecord().?; + if (data.member.index + 1 == record.fields.len) return; + } + } + } + const len = Value.int(array_len); if (index.ty.isUnsignedInt(p.comp)) { if (index.val.compare(.gte, len, p.comp.types.size, p.comp)) diff --git a/test/cases/old style flexible array.c b/test/cases/old style flexible array.c new file mode 100644 index 00000000..dddde561 --- /dev/null +++ b/test/cases/old style flexible array.c @@ -0,0 +1,45 @@ +struct S1 { + int a[1]; + int b[1]; +}; + +struct S2 { + int x; + struct S1 s1[1]; +}; + +void foo(struct S1 *p) { + struct S1 s1; + s1.a[1] = 0; + s1.b[1] = 0; + p->a[1] = 0; + p->b[1] = 0; + + struct S2 s2; + s2.s1[2].a[1] = 0; + s2.s1[2].b[1] = 0; +} + +struct S3 { + int w; + struct { + int x; + int y[1]; + int z[1]; + }; +}; + +void bar(struct S3 *p) { + p->y[1] = 0; + p->z[1] = 0; + struct S3 s3; + s3.y[1] = 0; + s3.z[1] = 0; +} + +#define EXPECTED_ERRORS "old style flexible array.c:13:9: warning: array index 1 is past the end of the array [-Warray-bounds]" \ + "old style flexible array.c:15:9: warning: array index 1 is past the end of the array [-Warray-bounds]" \ + "old style flexible array.c:19:15: warning: array index 1 is past the end of the array [-Warray-bounds]" \ + "old style flexible array.c:33:9: warning: array index 1 is past the end of the array [-Warray-bounds]" \ + "old style flexible array.c:36:9: warning: array index 1 is past the end of the array [-Warray-bounds]" \ + From 969042891c18d657b6dfbf72be518c3b54f5b209 Mon Sep 17 00:00:00 2001 From: Evan Haas Date: Tue, 5 Jul 2022 20:25:56 -0700 Subject: [PATCH 2/3] Parser: issue a warning for zero-length arrays --- src/Diagnostics.zig | 7 +++++++ src/Parser.zig | 4 +++- test/cases/zero length array.c | 17 +++++++++++++++++ 3 files changed, 27 insertions(+), 1 deletion(-) create mode 100644 test/cases/zero length array.c diff --git a/src/Diagnostics.zig b/src/Diagnostics.zig index 984d88fe..e01b8244 100644 --- a/src/Diagnostics.zig +++ b/src/Diagnostics.zig @@ -141,6 +141,7 @@ pub const Options = packed struct { @"designated-init": Kind = .default, @"attribute-warning": Kind = .default, @"invalid-noreturn": Kind = .default, + @"zero-length-array": Kind = .default, }; const messages = struct { @@ -1982,6 +1983,12 @@ const messages = struct { const extra = .str; const kind = .@"error"; }; + const zero_length_array = struct { + const msg = "zero size arrays are an extension"; + const kind = .off; + const pedantic = true; + const opt = "zero-length-array"; + }; }; list: std.ArrayListUnmanaged(Message) = .{}, diff --git a/src/Parser.zig b/src/Parser.zig index b3509169..91e39d9e 100644 --- a/src/Parser.zig +++ b/src/Parser.zig @@ -2510,7 +2510,9 @@ fn directDeclarator(p: *Parser, base_type: Type, d: *Declarator, kind: Declarato } else { var size_val = size.val; const size_t = p.comp.types.size; - if (size_val.compare(.lt, Value.int(0), size_t, p.comp)) { + if (size_val.isZero()) { + try p.errTok(.zero_length_array, l_bracket); + } else if (size_val.compare(.lt, Value.int(0), size_t, p.comp)) { try p.errTok(.negative_array_size, l_bracket); return error.ParsingFailed; } diff --git a/test/cases/zero length array.c b/test/cases/zero length array.c new file mode 100644 index 00000000..2b30d8e7 --- /dev/null +++ b/test/cases/zero length array.c @@ -0,0 +1,17 @@ +//aro-args -Wzero-length-array + +struct S { + int x; + int y[0]; +}; + +void foo(void) { + struct S s; + s.y[2] = 5; + int z[0]; + z[5] = 2; +} + +#define EXPECTED_ERRORS "zero length array.c:5:10: warning: zero size arrays are an extension [-Wzero-length-array]" \ + "zero length array.c:11:10: warning: zero size arrays are an extension [-Wzero-length-array]" \ + From e655bbae6ecf3896ba3a104a116f9f1762db66cf Mon Sep 17 00:00:00 2001 From: Evan Haas Date: Tue, 5 Jul 2022 20:51:39 -0700 Subject: [PATCH 3/3] Parser: add warning for old-style flexible struct usage --- src/Diagnostics.zig | 8 ++++++++ src/Parser.zig | 9 ++++++++- test/cases/old style flexible array.c | 8 ++++++++ 3 files changed, 24 insertions(+), 1 deletion(-) diff --git a/src/Diagnostics.zig b/src/Diagnostics.zig index e01b8244..ae92f908 100644 --- a/src/Diagnostics.zig +++ b/src/Diagnostics.zig @@ -142,6 +142,7 @@ pub const Options = packed struct { @"attribute-warning": Kind = .default, @"invalid-noreturn": Kind = .default, @"zero-length-array": Kind = .default, + @"old-style-flexible-struct": Kind = .default, }; const messages = struct { @@ -1989,6 +1990,13 @@ const messages = struct { const pedantic = true; const opt = "zero-length-array"; }; + const old_style_flexible_struct = struct { + const msg = "array index {d} is past the end of the array"; + const extra = .unsigned; + const kind = .off; + const pedantic = true; + const opt = "old-style-flexible-struct"; + }; }; list: std.ArrayListUnmanaged(Message) = .{}, diff --git a/src/Parser.zig b/src/Parser.zig index 91e39d9e..c9578bc9 100644 --- a/src/Parser.zig +++ b/src/Parser.zig @@ -6179,7 +6179,14 @@ fn checkArrayBounds(p: *Parser, index: Result, array: Result, tok: TokenIndex) ! } if (lhs.is(.@"struct")) { const record = lhs.getRecord().?; - if (data.member.index + 1 == record.fields.len) return; + if (data.member.index + 1 == record.fields.len) { + if (!index.val.isZero()) { + try p.errExtra(.old_style_flexible_struct, tok, .{ + .unsigned = index.val.data.int, + }); + } + return; + } } } } diff --git a/test/cases/old style flexible array.c b/test/cases/old style flexible array.c index dddde561..cf45afc7 100644 --- a/test/cases/old style flexible array.c +++ b/test/cases/old style flexible array.c @@ -37,9 +37,17 @@ void bar(struct S3 *p) { s3.z[1] = 0; } +#pragma GCC diagnostic warning "-Wold-style-flexible-struct" +void baz(void) { + struct S1 s1; + s1.b[0] = 1; + s1.b[1] = 1; +} + #define EXPECTED_ERRORS "old style flexible array.c:13:9: warning: array index 1 is past the end of the array [-Warray-bounds]" \ "old style flexible array.c:15:9: warning: array index 1 is past the end of the array [-Warray-bounds]" \ "old style flexible array.c:19:15: warning: array index 1 is past the end of the array [-Warray-bounds]" \ "old style flexible array.c:33:9: warning: array index 1 is past the end of the array [-Warray-bounds]" \ "old style flexible array.c:36:9: warning: array index 1 is past the end of the array [-Warray-bounds]" \ + "old style flexible array.c:44:9: warning: array index 1 is past the end of the array [-Wold-style-flexible-struct]" \