From 53907e5d99625fbd33fc6e4fe413e258643426cb Mon Sep 17 00:00:00 2001 From: Evan Haas Date: Sat, 10 Feb 2024 11:25:23 -0800 Subject: [PATCH] Parser: implement stdckdint.h Closes #578 --- include/stdckdint.h | 9 ++++ src/aro/Diagnostics/messages.def | 10 ++++ src/aro/Parser.zig | 29 +++++++++++- test/cases/ast/stdckdint_ast.c | 81 ++++++++++++++++++++++++++++++++ test/cases/stdckdint.c | 48 +++++++++++++++++++ test/cases/stdckdint_ast.c | 12 +++++ 6 files changed, 188 insertions(+), 1 deletion(-) create mode 100644 include/stdckdint.h create mode 100644 test/cases/ast/stdckdint_ast.c create mode 100644 test/cases/stdckdint.c create mode 100644 test/cases/stdckdint_ast.c diff --git a/include/stdckdint.h b/include/stdckdint.h new file mode 100644 index 00000000..44d954cf --- /dev/null +++ b/include/stdckdint.h @@ -0,0 +1,9 @@ +/* for the Aro C compiler */ + +#pragma once + +#define __STDC_VERSION_STDCKDINT_H__ 202311L + +#define ckd_add(result, a, b) __builtin_add_overflow(a, b, result) +#define ckd_sub(result, a, b) __builtin_sub_overflow(a, b, result) +#define ckd_mul(result, a, b) __builtin_mul_overflow(a, b, result) diff --git a/src/aro/Diagnostics/messages.def b/src/aro/Diagnostics/messages.def index 8e5f53ea..71568ca8 100644 --- a/src/aro/Diagnostics/messages.def +++ b/src/aro/Diagnostics/messages.def @@ -2462,3 +2462,13 @@ complex_conj .opt = W("pedantic") .extra = .str .kind = .off + +overflow_builtin_requires_int + .msg = "operand argument to overflow builtin must be an integer ('{s}' invalid)" + .extra = .str + .kind = .@"error" + +overflow_result_requires_ptr + .msg = "result argument to overflow builtin must be a pointer to a non-const integer ('{s}' invalid)" + .extra = .str + .kind = .@"error" diff --git a/src/aro/Parser.zig b/src/aro/Parser.zig index 4c5a23c8..5bcc7e01 100644 --- a/src/aro/Parser.zig +++ b/src/aro/Parser.zig @@ -4794,7 +4794,11 @@ const CallExpr = union(enum) { Builtin.tagFromName("__va_start").?, Builtin.tagFromName("va_start").?, => arg_idx != 1, - Builtin.tagFromName("__builtin_complex").? => false, + Builtin.tagFromName("__builtin_complex").?, + Builtin.tagFromName("__builtin_add_overflow").?, + Builtin.tagFromName("__builtin_sub_overflow").?, + Builtin.tagFromName("__builtin_mul_overflow").?, + => false, else => true, }, }; @@ -4807,6 +4811,7 @@ const CallExpr = union(enum) { } fn checkVarArg(self: CallExpr, p: *Parser, first_after: TokenIndex, param_tok: TokenIndex, arg: *Result, arg_idx: u32) !void { + @setEvalBranchQuota(10_000); if (self == .standard) return; const builtin_tok = p.nodes.items(.data)[@intFromEnum(self.builtin.node)].decl.name; @@ -4816,6 +4821,11 @@ const CallExpr = union(enum) { Builtin.tagFromName("va_start").?, => return p.checkVaStartArg(builtin_tok, first_after, param_tok, arg, arg_idx), Builtin.tagFromName("__builtin_complex").? => return p.checkComplexArg(builtin_tok, first_after, param_tok, arg, arg_idx), + Builtin.tagFromName("__builtin_add_overflow").?, + Builtin.tagFromName("__builtin_sub_overflow").?, + Builtin.tagFromName("__builtin_mul_overflow").?, + => return p.checkArithOverflowArg(builtin_tok, first_after, param_tok, arg, arg_idx), + else => {}, } } @@ -4859,6 +4869,9 @@ const CallExpr = union(enum) { Builtin.tagFromName("__atomic_xor_fetch").?, Builtin.tagFromName("__atomic_or_fetch").?, Builtin.tagFromName("__atomic_nand_fetch").?, + Builtin.tagFromName("__builtin_add_overflow").?, + Builtin.tagFromName("__builtin_sub_overflow").?, + Builtin.tagFromName("__builtin_mul_overflow").?, => 3, Builtin.tagFromName("__c11_atomic_compare_exchange_strong").?, @@ -7422,6 +7435,20 @@ fn checkVaStartArg(p: *Parser, builtin_tok: TokenIndex, first_after: TokenIndex, } } +fn checkArithOverflowArg(p: *Parser, builtin_tok: TokenIndex, first_after: TokenIndex, param_tok: TokenIndex, arg: *Result, idx: u32) !void { + _ = builtin_tok; + _ = first_after; + if (idx <= 1) { + if (!arg.ty.isInt()) { + return p.errStr(.overflow_builtin_requires_int, param_tok, try p.typeStr(arg.ty)); + } + } else if (idx == 2) { + if (!arg.ty.isPtr()) return p.errStr(.overflow_result_requires_ptr, param_tok, try p.typeStr(arg.ty)); + const child = arg.ty.elemType(); + if (!child.isInt() or child.is(.bool) or child.is(.@"enum") or child.qual.@"const") return p.errStr(.overflow_result_requires_ptr, param_tok, try p.typeStr(arg.ty)); + } +} + fn checkComplexArg(p: *Parser, builtin_tok: TokenIndex, first_after: TokenIndex, param_tok: TokenIndex, arg: *Result, idx: u32) !void { _ = builtin_tok; _ = first_after; diff --git a/test/cases/ast/stdckdint_ast.c b/test/cases/ast/stdckdint_ast.c new file mode 100644 index 00000000..496e288f --- /dev/null +++ b/test/cases/ast/stdckdint_ast.c @@ -0,0 +1,81 @@ +fn_def: 'fn () void' + name: foo + body: + compound_stmt: 'void' + var: 'char' + name: x + init: + implicit_cast: (int_cast) 'char' + int_literal: 'int' (value: 0) + + var: 'unsigned int' + name: y + init: + implicit_cast: (int_cast) 'unsigned int' + int_literal: 'int' (value: 2) + + var: '_Bool' + name: overflowed + + var: 'long' + name: res + + assign_expr: '_Bool' + lhs: + decl_ref_expr: '_Bool' lvalue + name: overflowed + rhs: + builtin_call_expr: '_Bool' + name: __builtin_add_overflow + args: + implicit_cast: (lval_to_rval) 'char' + decl_ref_expr: 'char' lvalue + name: x + implicit_cast: (lval_to_rval) 'unsigned int' + decl_ref_expr: 'unsigned int' lvalue + name: y + addr_of_expr: '*long' + operand: + decl_ref_expr: 'long' lvalue + name: res + + assign_expr: '_Bool' + lhs: + decl_ref_expr: '_Bool' lvalue + name: overflowed + rhs: + builtin_call_expr: '_Bool' + name: __builtin_sub_overflow + args: + implicit_cast: (lval_to_rval) 'char' + decl_ref_expr: 'char' lvalue + name: x + implicit_cast: (lval_to_rval) 'unsigned int' + decl_ref_expr: 'unsigned int' lvalue + name: y + addr_of_expr: '*long' + operand: + decl_ref_expr: 'long' lvalue + name: res + + assign_expr: '_Bool' + lhs: + decl_ref_expr: '_Bool' lvalue + name: overflowed + rhs: + builtin_call_expr: '_Bool' + name: __builtin_mul_overflow + args: + implicit_cast: (lval_to_rval) 'char' + decl_ref_expr: 'char' lvalue + name: x + implicit_cast: (lval_to_rval) 'unsigned int' + decl_ref_expr: 'unsigned int' lvalue + name: y + addr_of_expr: '*long' + operand: + decl_ref_expr: 'long' lvalue + name: res + + implicit_return: 'void' + diff --git a/test/cases/stdckdint.c b/test/cases/stdckdint.c new file mode 100644 index 00000000..797d09ee --- /dev/null +++ b/test/cases/stdckdint.c @@ -0,0 +1,48 @@ +#include + +enum E { + A, +}; + +void foo(void) { + int x = 0; + unsigned y = 2; + _Bool b, overflowed; + enum E e; + unsigned res; + const unsigned const_res; + unsigned arr[2]; + + overflowed = ckd_add(&res, x, y); + overflowed = ckd_sub(&res, x, y); + overflowed = ckd_mul(&res, x, y); + overflowed = ckd_add(arr, x, y); + + overflowed = ckd_add(res, x, y); // non-pointer result + overflowed = ckd_sub(&res, 1.2, y); // non-int argument + overflowed = ckd_mul(&b, x, y); // pointer to boolean result + overflowed = ckd_add(&const_res, x, y); // pointer to const result + overflowed = ckd_sub(&e, x, y); // pointer to enumerated result + overflowed = ckd_mul((void *)&res, x, y); // pointer to non-int result + +} + +#define EXPECTED_ERRORS "stdckdint.c:21:18: error: result argument to overflow builtin must be a pointer to a non-const integer ('unsigned int' invalid)" \ + "stdckdint.h:7:60: note: expanded from here" \ + "stdckdint.c:21:26: note: expanded from here" \ + "stdckdint.c:22:18: error: operand argument to overflow builtin must be an integer ('double' invalid)" \ + "stdckdint.h:8:54: note: expanded from here" \ + "stdckdint.c:22:32: note: expanded from here" \ + "stdckdint.c:23:18: error: result argument to overflow builtin must be a pointer to a non-const integer ('_Bool *' invalid)" \ + "stdckdint.h:9:60: note: expanded from here" \ + "stdckdint.c:23:26: note: expanded from here" \ + "stdckdint.c:24:18: error: result argument to overflow builtin must be a pointer to a non-const integer ('const unsigned int *' invalid)" \ + "stdckdint.h:7:60: note: expanded from here" \ + "stdckdint.c:24:26: note: expanded from here" \ + "stdckdint.c:25:18: error: result argument to overflow builtin must be a pointer to a non-const integer ('enum E *' invalid)" \ + "stdckdint.h:8:60: note: expanded from here" \ + "stdckdint.c:25:26: note: expanded from here" \ + "stdckdint.c:26:18: error: result argument to overflow builtin must be a pointer to a non-const integer ('void *' invalid)" \ + "stdckdint.h:9:60: note: expanded from here" \ + "stdckdint.c:26:26: note: expanded from here" \ + diff --git a/test/cases/stdckdint_ast.c b/test/cases/stdckdint_ast.c new file mode 100644 index 00000000..fb0160e6 --- /dev/null +++ b/test/cases/stdckdint_ast.c @@ -0,0 +1,12 @@ +#include + +void foo(void) { + char x = 0; + unsigned y = 2; + _Bool overflowed; + long res; + + overflowed = ckd_add(&res, x, y); + overflowed = ckd_sub(&res, x, y); + overflowed = ckd_mul(&res, x, y); +}