ZijunZhao updated this revision to Diff 553639.
ZijunZhao added a comment.
Add integer type test and update c2x tests.
Repository:
rG LLVM Github Monorepo
CHANGES SINCE LAST ACTION
https://reviews.llvm.org/D157331/new/
https://reviews.llvm.org/D157331
Files:
clang/docs/ReleaseNotes.rst
clang/include/clang/AST/Type.h
clang/include/clang/Basic/DiagnosticSemaKinds.td
clang/lib/Headers/CMakeLists.txt
clang/lib/Headers/stdckdint.h
clang/lib/Lex/ModuleMap.cpp
clang/lib/Lex/PPDirectives.cpp
clang/lib/Sema/SemaChecking.cpp
clang/test/C/C2x/n2359.c
clang/test/C/C2x/n2683.c
clang/test/C/C2x/n2683_2.c
clang/test/Headers/stdckdint.c
clang/test/Modules/Inputs/System/usr/include/module.map
clang/www/c_status.html
Index: clang/www/c_status.html
===================================================================
--- clang/www/c_status.html
+++ clang/www/c_status.html
@@ -887,6 +887,11 @@
<td><a href="https://www.open-std.org/jtc1/sc22/wg14/www/docs/n2672.pdf">N2672</a></td>
<td class="full" align="center">Yes</td>
</tr>
+ <tr>
+ <td>Towards Integer Safety</td>
+ <td><a href="https://www.open-std.org/jtc1/sc22/wg14/www/docs/n2683.pdf">N2683</a></td>
+ <td class="full" align="center">Clang 18</td>
+ </tr>
<tr id="_BitInt">
<td rowspan="5">Adding Fundamental Type for N-bit Integers</td>
</tr>
Index: clang/test/Modules/Inputs/System/usr/include/module.map
===================================================================
--- clang/test/Modules/Inputs/System/usr/include/module.map
+++ clang/test/Modules/Inputs/System/usr/include/module.map
@@ -14,6 +14,11 @@
header "stdbool.h"
}
+ // In both directories (compiler support version wins, does not forward)
+ module stdckdint {
+ header "stdckdint.h"
+ }
+
// In both directories (compiler support version wins, forwards)
module stdint {
header "stdint.h"
Index: clang/test/Headers/stdckdint.c
===================================================================
--- /dev/null
+++ clang/test/Headers/stdckdint.c
@@ -0,0 +1,51 @@
+// RUN: %clang_cc1 -emit-llvm -verify -std=c23 %s -o - | FileCheck %s
+// expected-no-diagnostics
+#include <stdckdint.h>
+
+_Static_assert(__STDC_VERSION_STDCKDINT_H__ == 202311L, "");
+
+// CHECK-LABEL: define dso_local zeroext i1 @test_ckd_add() #0 {
+// CHECK: entry:
+// CHECK: %result = alloca i32, align 4
+// CHECK: %0 = call { i32, i1 } @llvm.sadd.with.overflow.i32(i32 -1073741826, i32 -1073741826)
+// CHECK: %1 = extractvalue { i32, i1 } %0, 1
+// CHECK: %2 = extractvalue { i32, i1 } %0, 0
+// CHECK: store i32 %2, ptr %result, align 4
+// CHECK: ret i1 %1
+// CHECK: }
+// CHECK: ; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none)
+// CHECK: declare { i32, i1 } @llvm.sadd.with.overflow.i32(i32, i32) #1
+bool test_ckd_add() {
+ int result;
+ return ckd_add(&result, -1073741826, -1073741826);
+}
+
+// CHECK-LABEL: define dso_local zeroext i1 @test_ckd_sub() #0 {
+// CHECK: entry:
+// CHECK: %result = alloca i32, align 4
+// CHECK: %0 = call { i32, i1 } @llvm.ssub.with.overflow.i32(i32 -1073741826, i32 1073741826)
+// CHECK: %1 = extractvalue { i32, i1 } %0, 1
+// CHECK: %2 = extractvalue { i32, i1 } %0, 0
+// CHECK: store i32 %2, ptr %result, align 4
+// CHECK: ret i1 %1
+// CHECK: }
+// CHECK: declare { i32, i1 } @llvm.ssub.with.overflow.i32(i32, i32) #1
+bool test_ckd_sub() {
+ int result;
+ return ckd_sub(&result, -1073741826, 1073741826);
+}
+
+// CHECK-LABEL: define dso_local zeroext i1 @test_ckd_mul() #0 {
+// CHECK: entry:
+// CHECK: %result = alloca i32, align 4
+// CHECK: %0 = call { i32, i1 } @llvm.smul.with.overflow.i32(i32 -1073741826, i32 2)
+// CHECK: %1 = extractvalue { i32, i1 } %0, 1
+// CHECK: %2 = extractvalue { i32, i1 } %0, 0
+// CHECK: store i32 %2, ptr %result, align 4
+// CHECK: ret i1 %1
+// CHECK: }
+// CHECK: declare { i32, i1 } @llvm.smul.with.overflow.i32(i32, i32) #1
+bool test_ckd_mul() {
+ int result;
+ return ckd_mul(&result, -1073741826, 2);
+}
Index: clang/test/C/C2x/n2683_2.c
===================================================================
--- /dev/null
+++ clang/test/C/C2x/n2683_2.c
@@ -0,0 +1,61 @@
+// RUN: %clang_cc1 -emit-llvm -o - -std=c23 %s | FileCheck %s
+
+#include <stdckdint.h>
+#include <stdint.h>
+// CHECK-LABEL: define dso_local void @test_add_overflow_to64() #0 {
+// CHECK: entry:
+// CHECK: %result64 = alloca i64, align 8
+// CHECK: %flag_add = alloca i8, align 1
+// CHECK: store i64 0, ptr %result64, align 8
+// CHECK: %0 = call { i64, i1 } @llvm.sadd.with.overflow.i64(i64 2147483647, i64 1)
+// CHECK: %1 = extractvalue { i64, i1 } %0, 1
+// CHECK: %2 = extractvalue { i64, i1 } %0, 0
+// CHECK: store i64 %2, ptr %result64, align 8
+// CHECK: %frombool = zext i1 %1 to i8
+// CHECK: store i8 %frombool, ptr %flag_add, align 1
+// CHECK: ret void
+// CHECK: }
+void test_add_overflow_to64() {
+ int64_t result64 = 0;
+ bool flag_add = ckd_add(&result64, INT32_MAX, 1);
+}
+
+// CHECK-LABEL: define dso_local void @test_sub_overflow() #0 {
+// CHECK: entry:
+// CHECK: %result32 = alloca i32, align 4
+// CHECK: %flag_sub = alloca i8, align 1
+// CHECK: store i32 0, ptr %result32, align 4
+// CHECK: %0 = call { i32, i1 } @llvm.ssub.with.overflow.i32(i32 2147483647, i32 -1)
+// CHECK: %1 = extractvalue { i32, i1 } %0, 1
+// CHECK: %2 = extractvalue { i32, i1 } %0, 0
+// CHECK: store i32 %2, ptr %result32, align 4
+// CHECK: %frombool = zext i1 %1 to i8
+// CHECK: store i8 %frombool, ptr %flag_sub, align 1
+// CHECK: ret void
+// CHECK: }
+void test_sub_overflow() {
+ int32_t result32 = 0;
+ bool flag_sub = ckd_sub(&result32, INT32_MAX, -1);
+}
+
+// CHECK-LABEL: define dso_local void @test_mul_normal() #0 {
+// CHECK: entry:
+// CHECK: %a = alloca i32, align 4
+// CHECK: %result = alloca i32, align 4
+// CHECK: %flag_mul = alloca i8, align 1
+// CHECK: store i32 3, ptr %a, align 4
+// CHECK: store i32 0, ptr %result, align 4
+// CHECK: %0 = load i32, ptr %a, align 4
+// CHECK: %1 = call { i32, i1 } @llvm.smul.with.overflow.i32(i32 %0, i32 2)
+// CHECK: %2 = extractvalue { i32, i1 } %1, 1
+// CHECK: %3 = extractvalue { i32, i1 } %1, 0
+// CHECK: store i32 %3, ptr %result, align 4
+// CHECK: %frombool = zext i1 %2 to i8
+// CHECK: store i8 %frombool, ptr %flag_mul, align 1
+// CHECK: ret void
+// CHECK: }
+void test_mul_normal() {
+ int a = 3;
+ int result = 0;
+ bool flag_mul = ckd_mul(&result, a, 2);
+}
Index: clang/test/C/C2x/n2683.c
===================================================================
--- /dev/null
+++ clang/test/C/C2x/n2683.c
@@ -0,0 +1,47 @@
+// RUN: %clang_cc1 -verify -ffreestanding -std=c23 %s
+
+/* WG14 N2683: Clang 18
+ * Towards Integer Safety
+ */
+#include <stdckdint.h>
+#include <stdint.h>
+
+void test_semantic() {
+ int64_t result64 = 0;
+ int32_t result32 = 0;
+ bool flag_add = ckd_add(&result64, INT32_MAX, 1);
+ bool flag_sub = ckd_sub(&result32, INT32_MAX, -1);
+ bool flag_mul = ckd_mul(&result64, INT64_MAX, 1);
+
+ // FIXME: add static_assert calls to check the resulting values for correctness
+ // once the constant expression interpreter is able to handle the checked arithmetic
+ // builtins in C. Currently, they're only a valid constant expression in C++ due to
+ // looking for an ICE in C. Also all values in the tests of n2683_2.c should be checked.
+}
+
+void test_invalid_input() {
+ _BitInt(33) a33 = 1;
+ char char_var = 'd'; // The ascii value of `d` is 100
+ bool bool_var = 1;
+ const int const_result = 0;
+ enum week{Mon, Tue, Wed};
+ enum week day = Mon;
+ short short_result = 0;
+ int a = 100;
+ int b = 55;
+ int result = 10;
+ char plain_char[] = {U'ç'}; /* expected-warning {{implicit conversion from 'unsigned int' to 'char' changes value from 29275 to 91}} */
+
+ // invalid operand argument
+ bool flag_no_bitint = ckd_add(&result, a33, a); /* expected-error {{operand argument to overflow builtin must be an integer ('_BitInt(33)' invalid)}} */
+ bool flag_no_bool = ckd_sub(&result, bool_var, b); /* expected-error {{operand argument to overflow builtin must be an integer ('bool' invalid)}} */
+ bool flag_no_char = ckd_mul(&result, char_var, a); /* expected-error {{operand argument to overflow builtin must be an integer ('char' invalid)}} */
+ bool flag_no_enum = ckd_mul(&result, day, b); /* expected-error {{operand argument to overflow builtin must be an integer ('enum week' invalid)}} */
+
+ // invalid result type
+ bool flag_nostr = ckd_sub(&plain_char, a, b); /* expected-error {{result argument to overflow builtin must be a pointer to a non-const integer ('char (*)[1]' invalid)}} */
+ bool flag_nobool = ckd_mul(&bool_var, a, b); /* expected-error {{result argument to overflow builtin must be a pointer to a non-const integer ('bool *' invalid)}} */
+ bool flag_noptr = ckd_add(result, a, b); /* expected-error {{result argument to overflow builtin must be a pointer to a non-const integer ('int' invalid)}} */
+ bool flag_noconst = ckd_sub(&const_result, a, b); /* expected-error {{result argument to overflow builtin must be a pointer to a non-const integer ('const int *' invalid)}} */
+ bool flag_noshort = ckd_mul(&short_result, a, b); /* expected-warning {{'short' may not be suitable to hold the result of operating two 'int's}} */
+}
Index: clang/test/C/C2x/n2359.c
===================================================================
--- clang/test/C/C2x/n2359.c
+++ clang/test/C/C2x/n2359.c
@@ -34,3 +34,7 @@
// expected-error@-1 {{"__STDC_VERSION_STDINT_H__ not defined"}}
#endif
+#include <stdckdint.h>
+#ifndef __STDC_VERSION_STDCKDINT_H__
+#error "__STDC_VERSION_STDCKDINT_H__ not defined"
+#endif
Index: clang/lib/Sema/SemaChecking.cpp
===================================================================
--- clang/lib/Sema/SemaChecking.cpp
+++ clang/lib/Sema/SemaChecking.cpp
@@ -370,6 +370,22 @@
if (checkArgCount(S, TheCall, 3))
return true;
+ bool CkdOperation = false;
+ SourceManager &SM = S.getSourceManager();
+ if (BuiltinID == Builtin::BI__builtin_add_overflow &&
+ TheCall->getExprLoc().isMacroID() && Lexer::getImmediateMacroName(
+ TheCall->getExprLoc(), SM, S.getLangOpts()) == "ckd_add") {
+ CkdOperation = true;
+ } else if (BuiltinID == Builtin::BI__builtin_sub_overflow &&
+ TheCall->getExprLoc().isMacroID() && Lexer::getImmediateMacroName(
+ TheCall->getExprLoc(), SM, S.getLangOpts()) == "ckd_sub") {
+ CkdOperation = true;
+ } else if (BuiltinID == Builtin::BI__builtin_mul_overflow &&
+ TheCall->getExprLoc().isMacroID() && Lexer::getImmediateMacroName(
+ TheCall->getExprLoc(), SM, S.getLangOpts()) == "ckd_mul") {
+ CkdOperation = true;
+ }
+
// First two arguments should be integers.
for (unsigned I = 0; I < 2; ++I) {
ExprResult Arg = S.DefaultFunctionArrayLvalueConversion(TheCall->getArg(I));
@@ -377,7 +393,8 @@
TheCall->setArg(I, Arg.get());
QualType Ty = Arg.get()->getType();
- if (!Ty->isIntegerType()) {
+ bool IsInt = CkdOperation ? Ty->isPureIntegerType() : Ty->isIntegerType();
+ if (!IsInt) {
S.Diag(Arg.get()->getBeginLoc(), diag::err_overflow_builtin_must_be_int)
<< Ty << Arg.get()->getSourceRange();
return true;
@@ -396,11 +413,21 @@
const auto *PtrTy = Ty->getAs<PointerType>();
if (!PtrTy ||
!PtrTy->getPointeeType()->isIntegerType() ||
+ (!PtrTy->getPointeeType()->isPureIntegerType() && CkdOperation) ||
PtrTy->getPointeeType().isConstQualified()) {
S.Diag(Arg.get()->getBeginLoc(),
diag::err_overflow_builtin_must_be_ptr_int)
<< Ty << Arg.get()->getSourceRange();
return true;
+ } else if (CkdOperation){
+ // Third argument can't be short because it may be unable to hold the
+ // result of operating two `int`s.
+ auto Pty = PtrTy->getPointeeType();
+ if (S.getASTContext().getIntWidth(Pty) == 16) {
+ S.Diag(Arg.get()->getBeginLoc(),
+ diag::warn_overflow_builtin_can_not_be_short);
+ return true;
+ }
}
}
Index: clang/lib/Lex/PPDirectives.cpp
===================================================================
--- clang/lib/Lex/PPDirectives.cpp
+++ clang/lib/Lex/PPDirectives.cpp
@@ -231,9 +231,10 @@
.Cases("assert.h", "complex.h", "ctype.h", "errno.h", "fenv.h", true)
.Cases("float.h", "inttypes.h", "iso646.h", "limits.h", "locale.h", true)
.Cases("math.h", "setjmp.h", "signal.h", "stdalign.h", "stdarg.h", true)
- .Cases("stdatomic.h", "stdbool.h", "stddef.h", "stdint.h", "stdio.h", true)
- .Cases("stdlib.h", "stdnoreturn.h", "string.h", "tgmath.h", "threads.h", true)
- .Cases("time.h", "uchar.h", "wchar.h", "wctype.h", true)
+ .Cases("stdatomic.h", "stdbool.h", "stdckdint.h", "stddef.h", true)
+ .Cases("stdint.h", "stdio.h", "stdlib.h", "stdnoreturn.h", true)
+ .Cases("string.h", "tgmath.h", "threads.h", "time.h", "uchar.h", true)
+ .Cases("wchar.h", "wctype.h", true)
// C++ headers for C library facilities
.Cases("cassert", "ccomplex", "cctype", "cerrno", "cfenv", true)
Index: clang/lib/Lex/ModuleMap.cpp
===================================================================
--- clang/lib/Lex/ModuleMap.cpp
+++ clang/lib/Lex/ModuleMap.cpp
@@ -385,6 +385,7 @@
.Case("stdarg.h", true)
.Case("stdatomic.h", true)
.Case("stdbool.h", true)
+ .Case("stdckdint.h", true)
.Case("stddef.h", true)
.Case("stdint.h", true)
.Case("tgmath.h", true)
Index: clang/lib/Headers/stdckdint.h
===================================================================
--- /dev/null
+++ clang/lib/Headers/stdckdint.h
@@ -0,0 +1,21 @@
+/*===---- stdckdint.h - Standard header for checking integer----------------===
+ *
+ * Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+ * See https://llvm.org/LICENSE.txt for license information.
+ * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+ *
+ *===-----------------------------------------------------------------------===
+ */
+
+#ifndef __STDCKDINT_H
+#define __STDCKDINT_H
+
+/* C23 7.20.1 Defines several macros for performing checked integer arithmetic*/
+#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 202311L
+#define __STDC_VERSION_STDCKDINT_H__ 202311L
+#define ckd_add(R, A, B) __builtin_add_overflow((A), (B), (R))
+#define ckd_sub(R, A, B) __builtin_sub_overflow((A), (B), (R))
+#define ckd_mul(R, A, B) __builtin_mul_overflow((A), (B), (R))
+#endif
+
+#endif /* __STDCKDINT_H */
Index: clang/lib/Headers/CMakeLists.txt
===================================================================
--- clang/lib/Headers/CMakeLists.txt
+++ clang/lib/Headers/CMakeLists.txt
@@ -12,6 +12,7 @@
stdarg.h
stdatomic.h
stdbool.h
+ stdckdint.h
stddef.h
__stddef_max_align_t.h
stdint.h
Index: clang/include/clang/Basic/DiagnosticSemaKinds.td
===================================================================
--- clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -8621,6 +8621,8 @@
def err_overflow_builtin_bit_int_max_size : Error<
"__builtin_mul_overflow does not support 'signed _BitInt' operands of more "
"than %0 bits">;
+def warn_overflow_builtin_can_not_be_short : Warning<
+ "'short' may not be suitable to hold the result of operating two 'int's ">;
def err_expected_struct_pointer_argument : Error<
"expected pointer to struct as %ordinal0 argument to %1, found %2">;
def err_expected_callable_argument : Error<
Index: clang/include/clang/AST/Type.h
===================================================================
--- clang/include/clang/AST/Type.h
+++ clang/include/clang/AST/Type.h
@@ -2156,6 +2156,7 @@
/// isComplexIntegerType() can be used to test for complex integers.
bool isIntegerType() const; // C99 6.2.5p17 (int, char, bool, enum)
bool isEnumeralType() const;
+ bool isPureIntegerType() const;
/// Determine whether this type is a scoped enumeration type.
bool isScopedEnumeralType() const;
@@ -7346,6 +7347,17 @@
bool IsEnumDeclComplete(EnumDecl *);
bool IsEnumDeclScoped(EnumDecl *);
+inline bool Type::isPureIntegerType() const {
+ // Type shall be any integer type other than "plain" char, bool, a bit-precise
+ // integer type, or an enumerated type
+ if (const auto *BT = dyn_cast<BuiltinType>(CanonicalType))
+ return (BT->getKind() >= BuiltinType::Short &&
+ BT->getKind() <= BuiltinType::Int128) || (
+ BT->getKind() >= BuiltinType::UShort &&
+ BT->getKind() <= BuiltinType::UInt128);
+ return false;
+}
+
inline bool Type::isIntegerType() const {
if (const auto *BT = dyn_cast<BuiltinType>(CanonicalType))
return BT->getKind() >= BuiltinType::Bool &&
Index: clang/docs/ReleaseNotes.rst
===================================================================
--- clang/docs/ReleaseNotes.rst
+++ clang/docs/ReleaseNotes.rst
@@ -110,6 +110,9 @@
previous placeholder value. Clang continues to accept ``-std=c2x`` and
``-std=gnu2x`` as aliases for C23 and GNU C23, respectively.
+- Clang now supports ``<stdckdint.h>`` which defines several macros for performing
+ checked integer arithmetic.
+
Non-comprehensive list of changes in this release
-------------------------------------------------
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits