Skip to content

Commit 63d02e8

Browse files
committed
Add integer div-by-zero check
Closes #1184 Only when the numerator and denominator are both integral types Use -no-div-zero-checks to disable Includes documentation
1 parent daf9eec commit 63d02e8

File tree

12 files changed

+118
-28
lines changed

12 files changed

+118
-28
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,3 +38,4 @@ venv/*
3838
.vscode/
3939
buildh2.bat
4040
gen_version.bat
41+
mkdocs_serve.sh

docs/cppfront/options.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,12 +51,16 @@ This option also sets `-import-std`.
5151
Print version, build, copyright, and license information.
5252

5353

54-
## Additional dynamic safety checks and contract information
54+
## Additional dynamic safety check controls
5555

5656
### `-no-comparison-checks`, `-no-c`
5757

5858
Disable mixed-sign comparison safety checks. If not disabled, mixed-sign comparisons are diagnosed by default.
5959

60+
### `-no-div-zero-checks`, `-no-d`
61+
62+
Disable integer division by zero checks. If not disabled, integer division by zero checks are performed by default.
63+
6064
### `-no-null-checks`, `-no-n`
6165

6266
Disable null safety checks. If not disabled, null dereference checks are performed by default.

include/cpp2util.h

Lines changed: 55 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -798,7 +798,7 @@ namespace impl {
798798

799799
//-----------------------------------------------------------------------
800800
//
801-
// Check for invalid dereference or indirection which would result in undefined behavior.
801+
// Invalid/null dereference checking - cases that would result in UB.
802802
//
803803
// - Null pointer
804804
// - std::unique_ptr that owns nothing
@@ -861,11 +861,60 @@ auto assert_not_null(auto&& arg CPP2_SOURCE_LOCATION_PARAM_WITH_DEFAULT) -> decl
861861
return CPP2_FORWARD(arg);
862862
}
863863

864-
// Subscript bounds checking
864+
865+
//-----------------------------------------------------------------------
866+
//
867+
// Integer divide-by-zero checking - cases that would result in UB.
868+
//
869+
// Notes:
870+
// NumType is the Numerator type
871+
// arg is the denominator value
872+
// Both must be integral to enable the check
873+
//
874+
875+
#define CPP2_ASSERT_NOT_ZERO_IMPL \
876+
requires (std::is_integral_v<CPP2_TYPEOF(arg)> && \
877+
std::is_integral_v<NumType>) \
878+
{ \
879+
if (0 == arg) { \
880+
type_safety.report_violation("integer division by zero attempt detected" CPP2_SOURCE_LOCATION_ARG); \
881+
} \
882+
return arg; \
883+
}
884+
885+
template<typename NumType, auto arg>
886+
auto assert_not_zero([[maybe_unused]] char _ CPP2_SOURCE_LOCATION_PARAM_WITH_DEFAULT) -> auto
887+
CPP2_ASSERT_NOT_ZERO_IMPL
888+
889+
template<typename NumType, auto arg>
890+
auto assert_not_zero([[maybe_unused]] char _ CPP2_SOURCE_LOCATION_PARAM_WITH_DEFAULT) -> auto
891+
{
892+
return arg;
893+
}
894+
895+
template<typename NumType>
896+
auto assert_not_zero(auto arg CPP2_SOURCE_LOCATION_PARAM_WITH_DEFAULT) -> auto
897+
CPP2_ASSERT_NOT_ZERO_IMPL
898+
899+
template<typename NumType>
900+
auto assert_not_zero(auto&& arg CPP2_SOURCE_LOCATION_PARAM_WITH_DEFAULT) -> decltype(auto)
901+
requires (!std::is_integral_v<CPP2_TYPEOF(arg)>
902+
|| !std::is_integral_v<NumType>)
903+
{
904+
return CPP2_FORWARD(arg);
905+
}
906+
907+
#define CPP2_ASSERT_NOT_ZERO(NumType, arg) (cpp2::impl::assert_not_zero<NumType>((arg)))
908+
#define CPP2_ASSERT_NOT_ZERO_LITERAL(NumType, arg) (cpp2::impl::assert_not_zero<NumType, (arg)>('_'))
909+
910+
911+
//-----------------------------------------------------------------------
912+
//
913+
// Subscript bounds checking - cases that would result in UB.
865914
//
866915
#define CPP2_ASSERT_IN_BOUNDS_IMPL \
867916
requires (std::is_integral_v<CPP2_TYPEOF(arg)> && \
868-
requires { std::size(x); std::ssize(x); x[arg]; std::begin(x) + 2; }) \
917+
requires { std::size(x); std::ssize(x); x[arg]; std::begin(x) + 2; }) \
869918
{ \
870919
auto max = [&]() -> auto { \
871920
if constexpr (std::is_signed_v<CPP2_TYPEOF(arg)>) { return std::ssize(x); } \
@@ -888,15 +937,15 @@ template<auto arg>
888937
auto assert_in_bounds(auto&& x CPP2_SOURCE_LOCATION_PARAM_WITH_DEFAULT) -> decltype(auto)
889938
CPP2_ASSERT_IN_BOUNDS_IMPL
890939

891-
auto assert_in_bounds(auto&& x, auto&& arg CPP2_SOURCE_LOCATION_PARAM_WITH_DEFAULT) -> decltype(auto)
892-
CPP2_ASSERT_IN_BOUNDS_IMPL
893-
894940
template<auto arg>
895941
auto assert_in_bounds(auto&& x CPP2_SOURCE_LOCATION_PARAM_WITH_DEFAULT) -> decltype(auto)
896942
{
897943
return CPP2_FORWARD(x) [ arg ];
898944
}
899945

946+
auto assert_in_bounds(auto&& x, auto&& arg CPP2_SOURCE_LOCATION_PARAM_WITH_DEFAULT) -> decltype(auto)
947+
CPP2_ASSERT_IN_BOUNDS_IMPL
948+
900949
auto assert_in_bounds(auto&& x, auto&& arg CPP2_SOURCE_LOCATION_PARAM_WITH_DEFAULT) -> decltype(auto)
901950
{
902951
return CPP2_FORWARD(x) [ CPP2_FORWARD(arg) ];

regression-tests/test-results/gcc-10-c++20/pure2-bugfix-for-requires-clause-in-forward-declaration.cpp.output

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,12 @@ pure2-bugfix-for-requires-clause-in-forward-declaration.cpp2:3:46: error: expect
66
In file included from pure2-bugfix-for-requires-clause-in-forward-declaration.cpp:7:
77
../../../include/cpp2util.h:10005:47: error: static assertion failed: GCC 11 or higher is required to support variables and type-scope functions that have a 'requires' clause. This includes a type-scope 'forward' parameter of non-wildcard type, such as 'func: (this, forward s: std::string)', which relies on being able to add a 'requires' clause - in that case, use 'forward s: _' instead if you need the result to compile with GCC 10.
88
pure2-bugfix-for-requires-clause-in-forward-declaration.cpp2:4:1: note: in expansion of macro ‘CPP2_REQUIRES_’
9-
pure2-bugfix-for-requires-clause-in-forward-declaration.cpp2:3:3: error: no declaration matches ‘element::element(auto:259&&) requires is_same_v<std::__cxx11::string, typename std::remove_cv<typename std::remove_reference<decltype(element::__ct ::n)>::type>::type>’
9+
pure2-bugfix-for-requires-clause-in-forward-declaration.cpp2:3:3: error: no declaration matches ‘element::element(auto:261&&) requires is_same_v<std::__cxx11::string, typename std::remove_cv<typename std::remove_reference<decltype(element::__ct ::n)>::type>::type>’
1010
pure2-bugfix-for-requires-clause-in-forward-declaration.cpp2:5:11: note: candidates are: ‘element::element(const element&)’
11-
pure2-bugfix-for-requires-clause-in-forward-declaration.cpp2:3:20: note: ‘template<class auto:257> element::element(auto:257&&)’
11+
pure2-bugfix-for-requires-clause-in-forward-declaration.cpp2:3:20: note: ‘template<class auto:259> element::element(auto:259&&)’
1212
pure2-bugfix-for-requires-clause-in-forward-declaration.cpp2:1:7: note: ‘class element’ defined here
1313
pure2-bugfix-for-requires-clause-in-forward-declaration.cpp2:5:78: error: expected unqualified-id before ‘{’ token
14-
pure2-bugfix-for-requires-clause-in-forward-declaration.cpp2:3:8: error: no declaration matches ‘element& element::operator=(auto:260&&) requires is_same_v<std::__cxx11::string, typename std::remove_cv<typename std::remove_reference<decltype(element::operator=::n)>::type>::type>’
14+
pure2-bugfix-for-requires-clause-in-forward-declaration.cpp2:3:8: error: no declaration matches ‘element& element::operator=(auto:262&&) requires is_same_v<std::__cxx11::string, typename std::remove_cv<typename std::remove_reference<decltype(element::operator=::n)>::type>::type>’
1515
pure2-bugfix-for-requires-clause-in-forward-declaration.cpp2:6:16: note: candidates are: ‘void element::operator=(const element&)’
16-
pure2-bugfix-for-requires-clause-in-forward-declaration.cpp2:3:16: note: ‘template<class auto:258> element& element::operator=(auto:258&&)’
16+
pure2-bugfix-for-requires-clause-in-forward-declaration.cpp2:3:16: note: ‘template<class auto:260> element& element::operator=(auto:260&&)’
1717
pure2-bugfix-for-requires-clause-in-forward-declaration.cpp2:1:7: note: ‘class element’ defined here

regression-tests/test-results/gcc-10-c++20/pure2-print.cpp.output

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@ pure2-print.cpp2:68:1: note: in expansion of macro ‘CPP2_REQUIRES_’
99
pure2-print.cpp2:97:1: note: in expansion of macro ‘CPP2_REQUIRES_’
1010
pure2-print.cpp2:9:41: error: ‘constexpr const T outer::object_alias’ is not a static data member of ‘class outer’
1111
pure2-print.cpp2:9:48: error: template definition of non-template ‘constexpr const T outer::object_alias’
12-
pure2-print.cpp2:67:14: error: no declaration matches ‘void outer::mytype::variadic(const auto:258& ...) requires (is_convertible_v<typename std::remove_cv<typename std::remove_reference<decltype(outer::mytype::variadic::x)>::type>::type, int> && ...)’
13-
pure2-print.cpp2:67:29: note: candidate is: ‘template<class ... auto:257> static void outer::mytype::variadic(const auto:257& ...)’
12+
pure2-print.cpp2:67:14: error: no declaration matches ‘void outer::mytype::variadic(const auto:260& ...) requires (is_convertible_v<typename std::remove_cv<typename std::remove_reference<decltype(outer::mytype::variadic::x)>::type>::type, int> && ...)’
13+
pure2-print.cpp2:67:29: note: candidate is: ‘template<class ... auto:259> static void outer::mytype::variadic(const auto:259& ...)’
1414
pure2-print.cpp2:10:19: note: ‘class outer::mytype’ defined here
1515
pure2-print.cpp2:96:37: error: no declaration matches ‘void outer::print(std::ostream&, const Args& ...) requires cpp2::impl::cmp_greater_eq(sizeof ... (Args ...), 0)’
1616
pure2-print.cpp2:96:37: note: no functions named ‘void outer::print(std::ostream&, const Args& ...) requires cpp2::impl::cmp_greater_eq(sizeof ... (Args ...), 0)’
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
mixed-bounds-safety-with-assert.cpp2(11) void print_subrange(const auto:255&, cpp2::impl::in<int>, cpp2::impl::in<int>) [with auto:255 = std::vector<int>; cpp2::impl::in<int> = const int]: Bounds safety violation
1+
mixed-bounds-safety-with-assert.cpp2(11) void print_subrange(const auto:257&, cpp2::impl::in<int>, cpp2::impl::in<int>) [with auto:257 = std::vector<int>; cpp2::impl::in<int> = const int]: Bounds safety violation
Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,41 +1,41 @@
11
In file included from mixed-bugfix-for-ufcs-non-local.cpp:6:
22
../../../include/cpp2util.h:2100:1: error: lambda-expression in template parameter type
3-
2100 |
3+
2100 | constexpr auto is( std::optional<U> const& x ) -> bool
44
| ^
55
../../../include/cpp2util.h:2137:59: note: in expansion of macro ‘CPP2_UFCS_’
6-
2137 | ~finally() noexcept { f(); }
6+
2137 | //
77
| ^
88
mixed-bugfix-for-ufcs-non-local.cpp2:13:12: note: in expansion of macro ‘CPP2_UFCS_NONLOCAL’
99
mixed-bugfix-for-ufcs-non-local.cpp2:13:36: error: template argument 1 is invalid
1010
../../../include/cpp2util.h:2100:1: error: lambda-expression in template parameter type
11-
2100 |
11+
2100 | constexpr auto is( std::optional<U> const& x ) -> bool
1212
| ^
1313
../../../include/cpp2util.h:2137:59: note: in expansion of macro ‘CPP2_UFCS_’
14-
2137 | ~finally() noexcept { f(); }
14+
2137 | //
1515
| ^
1616
mixed-bugfix-for-ufcs-non-local.cpp2:21:12: note: in expansion of macro ‘CPP2_UFCS_NONLOCAL’
1717
mixed-bugfix-for-ufcs-non-local.cpp2:21:36: error: template argument 1 is invalid
1818
../../../include/cpp2util.h:2100:1: error: lambda-expression in template parameter type
19-
2100 |
19+
2100 | constexpr auto is( std::optional<U> const& x ) -> bool
2020
| ^
2121
../../../include/cpp2util.h:2137:59: note: in expansion of macro ‘CPP2_UFCS_’
22-
2137 | ~finally() noexcept { f(); }
22+
2137 | //
2323
| ^
2424
mixed-bugfix-for-ufcs-non-local.cpp2:31:12: note: in expansion of macro ‘CPP2_UFCS_NONLOCAL’
2525
mixed-bugfix-for-ufcs-non-local.cpp2:31:36: error: template argument 1 is invalid
2626
../../../include/cpp2util.h:2100:1: error: lambda-expression in template parameter type
27-
2100 |
27+
2100 | constexpr auto is( std::optional<U> const& x ) -> bool
2828
| ^
2929
../../../include/cpp2util.h:2137:59: note: in expansion of macro ‘CPP2_UFCS_’
30-
2137 | ~finally() noexcept { f(); }
30+
2137 | //
3131
| ^
3232
mixed-bugfix-for-ufcs-non-local.cpp2:33:12: note: in expansion of macro ‘CPP2_UFCS_NONLOCAL’
3333
mixed-bugfix-for-ufcs-non-local.cpp2:33:36: error: template argument 1 is invalid
3434
../../../include/cpp2util.h:2100:1: error: lambda-expression in template parameter type
35-
2100 |
35+
2100 | constexpr auto is( std::optional<U> const& x ) -> bool
3636
| ^
3737
../../../include/cpp2util.h:2137:59: note: in expansion of macro ‘CPP2_UFCS_’
38-
2137 | ~finally() noexcept { f(); }
38+
2137 | //
3939
| ^
4040
mixed-bugfix-for-ufcs-non-local.cpp2:21:12: note: in expansion of macro ‘CPP2_UFCS_NONLOCAL’
4141
mixed-bugfix-for-ufcs-non-local.cpp2:21:36: error: template argument 1 is invalid

regression-tests/test-results/pure2-print.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ requires (true) inline CPP2_CONSTEXPR T outer::object_alias{ 42 };
105105
if (cpp2::impl::cmp_less(*cpp2::impl::assert_not_null(p),0)) {
106106
ret = -*cpp2::impl::assert_not_null(cpp2::move(p));
107107
}
108-
ret += strlen(s) - 10 + CPP2_UFCS(strlen)(s) * (16 / (3 & 2)) % 3;
108+
ret += strlen(s) - 10 + CPP2_UFCS(strlen)(s) * (16 / CPP2_ASSERT_NOT_ZERO(CPP2_TYPEOF(16),(3 & 2))) % 3;
109109

110110
map<int const,string> m {};
111111
CPP2_ASSERT_IN_BOUNDS_LITERAL(m, 0) = cpp2::impl::as_<string>("har");

regression-tests/test-results/version

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11

2-
cppfront compiler v0.7.2 Build 9727:1056
2+
cppfront compiler v0.7.2 Build 9729:1513
33
Copyright(c) Herb Sutter All rights reserved
44

55
SPDX-License-Identifier: CC-BY-NC-ND-4.0

source/build.info

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
"9727:1056"
1+
"9729:1513"

0 commit comments

Comments
 (0)