Skip to content

Commit 8504e86

Browse files
committed
Auto-enable file/line/function info on contracts violations + usability improvements
Thanks @MikeShah for your YouTube video here: https://youtu.be/ZslemeHsOjk?si=qPoNdJLxBoSgFh6b . I'm treating the video as a "looking over your shoulder using Cpp2" mini-UX study to make a better experience for folks coming to the syntax the first time... Diagnose attempt to declare two things at once, e.g., `x,y : int = 5;` Diagnose attempt to for-loop over two ranges, e.g., `for x,y do ..` Automatically use <source_location>, including for file/line/function information on contracts checks so when using a capable compiler you get nice bounds/other violation messages out of the box that point directly to the offending source line Remove the -add-source-info flag as no longer needed, now <source_location> is now reliable and automated (modulo a workaround in cpp2util.h for an MSVC bug where importing source_location via a module doesn't work correctly)
1 parent a252d50 commit 8504e86

26 files changed

+92
-59
lines changed

include/cpp2util.h

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,10 @@
4343

4444
// If this implementation doesn't support source_location yet, disable it
4545
#include <version>
46-
#if !defined(_MSC_VER) && !defined(__cpp_lib_source_location)
47-
#undef CPP2_USE_SOURCE_LOCATION
46+
47+
#undef CPP2_USE_SOURCE_LOCATION
48+
#if defined(__cpp_lib_source_location)
49+
#define CPP2_USE_SOURCE_LOCATION Yes
4850
#endif
4951

5052
// If the user requested making the entire C++ standard library available
@@ -553,15 +555,17 @@ auto pointer_eq(T const* a, T const* b) {
553555
//
554556

555557
#ifdef CPP2_USE_SOURCE_LOCATION
556-
#define CPP2_SOURCE_LOCATION_PARAM , std::source_location where
557-
#define CPP2_SOURCE_LOCATION_PARAM_WITH_DEFAULT , std::source_location where = std::source_location::current()
558-
#define CPP2_SOURCE_LOCATION_PARAM_SOLO std::source_location where
558+
#define CPP2_SOURCE_LOCATION_PARAM , [[maybe_unused]] std::source_location where
559+
#define CPP2_SOURCE_LOCATION_PARAM_WITH_DEFAULT , [[maybe_unused]] std::source_location where = std::source_location::current()
560+
#define CPP2_SOURCE_LOCATION_PARAM_SOLO [[maybe_unused]] std::source_location where
559561
#define CPP2_SOURCE_LOCATION_ARG , where
562+
#define CPP2_SOURCE_LOCATION_VALUE (cpp2::to_string(where.file_name()) + "(" + cpp2::to_string(where.line()) + ") " + where.function_name())
560563
#else
561564
#define CPP2_SOURCE_LOCATION_PARAM
562565
#define CPP2_SOURCE_LOCATION_PARAM_WITH_DEFAULT
563566
#define CPP2_SOURCE_LOCATION_PARAM_SOLO
564567
#define CPP2_SOURCE_LOCATION_ARG
568+
#define CPP2_SOURCE_LOCATION_VALUE std::string("")
565569
#endif
566570

567571
// For C++23: make this std::string_view and drop the macro
@@ -1520,8 +1524,16 @@ inline constexpr auto as() -> auto
15201524
return cpp2::to_string(CPP2_FORWARD(x));
15211525
}
15221526

1527+
// Work around MSVC modules bugs: source_location doesn't work correctly if imported via a module
1528+
#if defined(_MSC_VER) && defined(CPP2_IMPORT_STD)
1529+
#define CPP2_SOURCE_LOCATION_PARAM_WITH_DEFAULT_AS
1530+
#define CPP2_SOURCE_LOCATION_ARG_AS
1531+
#else
1532+
#define CPP2_SOURCE_LOCATION_PARAM_WITH_DEFAULT_AS CPP2_SOURCE_LOCATION_PARAM_WITH_DEFAULT
1533+
#define CPP2_SOURCE_LOCATION_ARG_AS CPP2_SOURCE_LOCATION_ARG
1534+
#endif
15231535
template< typename C >
1524-
auto as(auto&& x CPP2_SOURCE_LOCATION_PARAM_WITH_DEFAULT) -> decltype(auto)
1536+
auto as(auto&& x CPP2_SOURCE_LOCATION_PARAM_WITH_DEFAULT_AS) -> decltype(auto)
15251537
// This "requires" list may need to be tweaked further. The idea is to have
15261538
// this function used for all the cases it's supposed to cover, but not
15271539
// hide user-supplied extensions (such as the ones later in this file for
@@ -1555,7 +1567,7 @@ auto as(auto&& x CPP2_SOURCE_LOCATION_PARAM_WITH_DEFAULT) -> decltype(auto)
15551567
const C c = static_cast<C>(CPP2_FORWARD(x));
15561568
type_safety.enforce( // precondition check: must be round-trippable => not lossy
15571569
static_cast<CPP2_TYPEOF(x)>(c) == x && (c < C{}) == (x < CPP2_TYPEOF(x){}),
1558-
"dynamic lossy narrowing conversion attempt detected" CPP2_SOURCE_LOCATION_ARG
1570+
"dynamic lossy narrowing conversion attempt detected" CPP2_SOURCE_LOCATION_ARG_AS
15591571
);
15601572
return CPP2_COPY(c);
15611573
}

regression-tests/mixed-lifetime-safety-and-null-contracts.cpp2

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,17 @@ try_pointer_stuff: () = {
1818
// to show -n
1919
}
2020

21-
call_my_framework: (msg: * const char) = {
21+
auto call_my_framework(const char* msg CPP2_SOURCE_LOCATION_PARAM) {
2222
std::cout
2323
<< "sending error to my framework... ["
24-
<< msg << "]\n";
24+
<< msg
25+
<< "]\n";
26+
auto loc = CPP2_SOURCE_LOCATION_VALUE;
27+
if (!loc.empty()) {
28+
std::cout
29+
<< "from source location: "
30+
<< loc
31+
<< "]\n";
32+
}
2533
exit(0);
2634
}
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
Bounds safety violation: out of bounds access attempt detected - attempted access at index 5, [min,max] range is [0,4]
1+
mixed-bounds-check.cpp2(9) int main(): Bounds safety violation: out of bounds access attempt detected - attempted access at index 5, [min,max] range is [0,4]
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
Bounds safety violation
1+
mixed-bounds-safety-with-assert.cpp2(11) void print_subrange(const auto:89&, cpp2::impl::in<int>, cpp2::impl::in<int>) [with auto:89 = 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 | template<typename T>
3+
2100 | {
44
| ^
55
../../../include/cpp2util.h:2137:59: note: in expansion of macro ‘CPP2_UFCS_’
6-
2137 | class c_raii {
6+
2137 | // Speculative: RAII wrapping for the C standard library
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 | template<typename T>
11+
2100 | {
1212
| ^
1313
../../../include/cpp2util.h:2137:59: note: in expansion of macro ‘CPP2_UFCS_’
14-
2137 | class c_raii {
14+
2137 | // Speculative: RAII wrapping for the C standard library
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 | template<typename T>
19+
2100 | {
2020
| ^
2121
../../../include/cpp2util.h:2137:59: note: in expansion of macro ‘CPP2_UFCS_’
22-
2137 | class c_raii {
22+
2137 | // Speculative: RAII wrapping for the C standard library
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 | template<typename T>
27+
2100 | {
2828
| ^
2929
../../../include/cpp2util.h:2137:59: note: in expansion of macro ‘CPP2_UFCS_’
30-
2137 | class c_raii {
30+
2137 | // Speculative: RAII wrapping for the C standard library
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 | template<typename T>
35+
2100 | {
3636
| ^
3737
../../../include/cpp2util.h:2137:59: note: in expansion of macro ‘CPP2_UFCS_’
38-
2137 | class c_raii {
38+
2137 | // Speculative: RAII wrapping for the C standard library
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
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
Contract violation: fill: value must contain at least count elements
1+
mixed-initialization-safety-3-contract-violation.cpp2(25) void fill(cpp2::impl::out<std::__cxx11::basic_string<char> >, cpp2::impl::in<std::__cxx11::basic_string<char> >, cpp2::impl::in<int>): Contract violation: fill: value must contain at least count elements
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
11
sending error to my framework... [dynamic null dereference attempt detected]
2+
from source location: mixed-lifetime-safety-and-null-contracts.cpp2(17) void try_pointer_stuff()]
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
Null safety violation: std::expected has an unexpected value
1+
pure2-assert-expected-not-null.cpp2(15) int bad_expected_access(): Null safety violation: std::expected has an unexpected value
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
Null safety violation: std::optional does not contain a value
1+
pure2-assert-optional-not-null.cpp2(14) int bad_optional_access(): Null safety violation: std::optional does not contain a value
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
Null safety violation: std::shared_ptr is empty
1+
pure2-assert-shared-ptr-not-null.cpp2(15) int bad_shared_ptr_access(): Null safety violation: std::shared_ptr is empty
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
Null safety violation: std::unique_ptr is empty
1+
pure2-assert-unique-ptr-not-null.cpp2(15) int bad_unique_ptr_access(): Null safety violation: std::unique_ptr is empty

regression-tests/test-results/mixed-lifetime-safety-and-null-contracts.cpp

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,23 @@
2424
auto null_from_cpp1() -> int* { return nullptr; }
2525

2626
auto try_pointer_stuff() -> void;
27+
#line 20 "mixed-lifetime-safety-and-null-contracts.cpp2"
28+
29+
auto call_my_framework(const char* msg CPP2_SOURCE_LOCATION_PARAM) {
30+
std::cout
31+
<< "sending error to my framework... ["
32+
<< msg
33+
<< "]\n";
34+
auto loc = CPP2_SOURCE_LOCATION_VALUE;
35+
if (!loc.empty()) {
36+
std::cout
37+
<< "from source location: "
38+
<< loc
39+
<< "]\n";
40+
}
41+
exit(0);
42+
}
2743

28-
#line 21 "mixed-lifetime-safety-and-null-contracts.cpp2"
29-
auto call_my_framework(char const* msg) -> void;
3044

3145
//=== Cpp2 function definitions =================================================
3246

@@ -46,11 +60,3 @@ auto try_pointer_stuff() -> void{
4660
// to show -n
4761
}
4862

49-
#line 21 "mixed-lifetime-safety-and-null-contracts.cpp2"
50-
auto call_my_framework(char const* msg) -> void{
51-
std::cout
52-
<< "sending error to my framework... ["
53-
<< msg << "]\n";
54-
exit(0);
55-
}
56-
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
Bounds safety violation: out of bounds access attempt detected - attempted access at index 5, [min,max] range is [0,4]
1+
mixed-bounds-check.cpp2(9) int __cdecl main(void): Bounds safety violation: out of bounds access attempt detected - attempted access at index 5, [min,max] range is [0,4]
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
Bounds safety violation
1+
mixed-bounds-safety-with-assert.cpp2(11) void __cdecl print_subrange<class std::vector<int,class std::allocator<int> >>(const class std::vector<int,class std::allocator<int> > &,const int,const int): Bounds safety violation
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
Contract violation: fill: value must contain at least count elements
1+
mixed-initialization-safety-3-contract-violation.cpp2(25) void __cdecl fill(class cpp2::impl::out<class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > >,const class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > &,const int): Contract violation: fill: value must contain at least count elements
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
11
sending error to my framework... [dynamic null dereference attempt detected]
2+
from source location: mixed-lifetime-safety-and-null-contracts.cpp2(17) void __cdecl try_pointer_stuff(void)]
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
Null safety violation: std::expected has an unexpected value
1+
pure2-assert-expected-not-null.cpp2(15) int __cdecl bad_expected_access(void): Null safety violation: std::expected has an unexpected value
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
Null safety violation: std::optional does not contain a value
1+
pure2-assert-optional-not-null.cpp2(14) int __cdecl bad_optional_access(void): Null safety violation: std::optional does not contain a value
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
Null safety violation: std::shared_ptr is empty
1+
pure2-assert-shared-ptr-not-null.cpp2(15) int __cdecl bad_shared_ptr_access(void): Null safety violation: std::shared_ptr is empty
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
Null safety violation: std::unique_ptr is empty
1+
pure2-assert-unique-ptr-not-null.cpp2(15) int __cdecl bad_unique_ptr_access(void): Null safety violation: std::unique_ptr is empty
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
pure2-statement-parse-error.cpp2...
2-
pure2-statement-parse-error.cpp2(3,9): error: invalid statement encountered inside a compound-statement (at 'b')
2+
pure2-statement-parse-error.cpp2(3,5): error: invalid statement encountered inside a compound-statement (at 'int')
33

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.0 Build 9712:1020
2+
cppfront compiler v0.7.1 Build 9713:1156
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-
"9712:1020"
1+
"9713:1156"

source/parse.h

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6960,6 +6960,9 @@ class parser
69606960
expression_statement_node::current_expression_statements.push_back(n.get());
69616961
auto guard = finally([&]{ expression_statement_node::current_expression_statements.pop_back(); });
69626962

6963+
// Remember current position, in case this isn't a valid expression-statement
6964+
auto start_pos = pos;
6965+
69636966
if (!(n->expr = expression(true, true))) {
69646967
return {};
69656968
}
@@ -6978,6 +6981,7 @@ class parser
69786981
// it doesn't destabilize any regression tests
69796982
)
69806983
{
6984+
pos = start_pos; // backtrack
69816985
return {};
69826986
}
69836987
if (
@@ -7248,6 +7252,10 @@ class parser
72487252
return {};
72497253
}
72507254

7255+
if (curr().type() == lexeme::Comma) {
7256+
error("iterating over multiple ranges at once is not currently supported");
7257+
}
7258+
72517259
if (!handle_optional_next_clause()) { return {}; }
72527260

72537261
if (
@@ -7671,6 +7679,15 @@ class parser
76717679
}
76727680

76737681
else {
7682+
if (
7683+
curr().type() == lexeme::Identifier
7684+
&& peek(1)
7685+
&& peek(1)->type() == lexeme::Comma
7686+
)
7687+
{
7688+
next();
7689+
error("declaring multiple names at once is not currently supported");
7690+
}
76747691
return {};
76757692
}
76767693
}

source/to_cpp1.h

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -147,14 +147,6 @@ static cmdline_processor::register_flag cmd_safe_comparisons(
147147
[]{ flag_safe_comparisons = false; }
148148
);
149149

150-
static auto flag_use_source_location = false;
151-
static cmdline_processor::register_flag cmd_enable_source_info(
152-
2,
153-
"add-source-info",
154-
"Enable source_location information for contract checks",
155-
[]{ flag_use_source_location = true; }
156-
);
157-
158150
static auto flag_cpp1_filename = std::string{};
159151
static cmdline_processor::register_flag cmd_cpp1_filename(
160152
8,
@@ -1309,10 +1301,6 @@ class cppfront
13091301
printer.print_extra( "#define " + cpp1_FILENAME+"_CPP2" + "\n\n" );
13101302
}
13111303

1312-
if (flag_use_source_location) {
1313-
printer.print_extra( "#define CPP2_USE_SOURCE_LOCATION Yes\n" );
1314-
}
1315-
13161304
if (flag_include_std) {
13171305
printer.print_extra( "#define CPP2_INCLUDE_STD Yes\n" );
13181306
}

source/version.info

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
"v0.7.0"
1+
"v0.7.1"

0 commit comments

Comments
 (0)