From daf9eec7de39d9d3d259d388fbcf2da248abfc16 Mon Sep 17 00:00:00 2001 From: Herb Sutter Date: Mon, 29 Jul 2024 09:19:44 -0700 Subject: [PATCH 01/22] Catch up feature documentation and C++26 new headers Add documentation for: - tersest syntax (e.g., `:(a,b) a+b;`) - `unevaluated` contract group - for `operator++` and `operator--`, that writing the single operator in Cpp2 generates both forms when lowering to Cpp1 - `-cwd` and `-quiet` command line options Remove documentation for: - `-add-source-info` switch which was removed Add support for new C++26 headers approved in St Louis: - `inplace_vector` --- docs/cpp2/common.md | 2 +- docs/cpp2/contracts.md | 6 ++-- docs/cpp2/functions.md | 18 +++++++++-- docs/cppfront/options.md | 70 ++++++++++++++++++++-------------------- include/cpp2util.h | 3 ++ source/common.h | 2 +- source/to_cpp1.h | 2 +- 7 files changed, 60 insertions(+), 43 deletions(-) diff --git a/docs/cpp2/common.md b/docs/cpp2/common.md index a85d55a5a..6d7de446b 100644 --- a/docs/cpp2/common.md +++ b/docs/cpp2/common.md @@ -220,7 +220,7 @@ Postfix notation lets the code read fluidly left-to-right, in the same order in > Note: The `...` pack expansion syntax is also supported. The above `...` and `..=` are the Cpp2 range operators, which overlap in syntax. -> Note: Because `++` and `--` always have in-place update semantics, we never need to remember "use prefix `++`/`--` unless you need a copy of the old value." If you do need a copy of the old value, just take the copy before calling `++`/`--`. +> Note: Because `++` and `--` always have in-place update semantics, we never need to remember "use prefix `++`/`--` unless you need a copy of the old value." If you do need a copy of the old value, just take the copy before calling `++`/`--`. When you write a type that overloads `operator++` or `operator--`, cppfront generates both Cpp1 overloads (in-place-update and copy-old-value) for that function to support natural use of the type from Cpp1 code. ### Binary operators diff --git a/docs/cpp2/contracts.md b/docs/cpp2/contracts.md index fcdc21a0d..47caa9a3f 100644 --- a/docs/cpp2/contracts.md +++ b/docs/cpp2/contracts.md @@ -15,11 +15,13 @@ Notes: - Optionally, `condition` may be followed by `, "message"`, a message to include if a violation occurs. For example, `pre(condition, "message")`. -- Optionally, a `` can be written inside `<` `>` angle brackets immediately before the `(`, to designate that this test is part of the [contract group](#groups) named `group` and (also optionally) [contract predicates](#predicates) `pred1` and `pred2`. If a violation occurs, `Group.report_violation()` will be called. For example, `pre(condition)`. If no contract group is specified, the contract defaults to being part of the `cpp2_default` group. +- Optionally, a `` can be written inside `<` `>` angle brackets immediately before the `(`, to designate that this test is part of the [contract group](#groups) named `group` and (also optionally) [contract predicates](#predicates) `pred1` and `pred2`. If a violation occurs, `Group.report_violation()` will be called. For example, `pre(condition)`. If no contract group is specified, the contract defaults to being part of the `default` group (spelled `cpp2_default` when used from Cpp1 code). The order of evaluation is: -- First, predicates are evaluated in order. If any predicte evaluates to `#!cpp false`, stop. +- First, if the contract group is `unevaluated` then the contract is ignored; `condition` is never evaluated. This special group is designates conditions intended for use by static analyzers only, and the only requirement is that the condition be grammatically valid. + +- Next, predicates are evaluated in order. If any predicate evaluates to `#!cpp false`, stop. - Next, `group.is_active()` is evaluated. If that evaluates to `#!cpp false`, stop. diff --git a/docs/cpp2/functions.md b/docs/cpp2/functions.md index 4771f7b13..ae5c248de 100644 --- a/docs/cpp2/functions.md +++ b/docs/cpp2/functions.md @@ -78,9 +78,13 @@ wrap_f: ( A function can return either of the following. The default is `#!cpp -> void`. -(1) **`#!cpp -> X`** to return a single unnamed value of type `X`, which can be `#!cpp void` to signify the function has no return value. If `X` is not `#!cpp void`, the function body must have a `#!cpp return /*value*/;` statement that returns a value of type `X` on every path that exits the function. For example: +(1) **`#!cpp -> X`** to return a single unnamed value of type `X`, which can be `#!cpp void` to signify the function has no return value. If `X` is not `#!cpp void`, the function body must have a `#!cpp return /*value*/;` statement that returns a value of type `X` on every path that exits the function. -``` cpp title="Functions with an unnamed return value" hl_lines="2 4 7 9 12 14" +To deduce the return type, write `-> _`. A function whose body returns a single expression `expr` can deduce the return type and omit writing `-> _ = { return /*expr*/ ; }`. + +For example: + +``` cpp title="Functions with an unnamed return value" hl_lines="2 4 7 9 12 14 15 18 20 22" // A function returning no value (void) increment_in_place: (inout a: i32) -> void = { a++; } // Or, using syntactic defaults, the following has identical meaning: @@ -93,8 +97,16 @@ add_one: (a: i32) -> i32 = a+1; // A generic function returning a single value of deduced type add: (a:T, b:U) -> decltype(a+b) = { return a+b; } -// Or, using syntactic defaults, the following has identical meaning: +// Or, using syntactic defaults, the following have identical meaning: add: (a, b) -> _ = a+b; +add: (a, b) a+b; + +// A generic function expression returning a single value of deduced type +vec.std::ranges::sort( :(x:_, y:_) -> _ = { return y (x:T, y:U) -> _ = { return y ( /* parameter list */ )`** to return a list of named return parameters using the same [parameters](#parameters) syntax, but where the only passing styles are `out` (the default, which moves where possible) or `forward`. The function body must [initialize](objects.md#init) the value of each return-parameter `ret` in its body the same way as any other local variable. An explicit return statement is written just `#!cpp return;` and returns the named values; the function has an implicit `#!cpp return;` at the end. If only a single return parameter is in the list, it is emitted in the lowered Cpp1 code the same way as (1) above, so its name is only available inside the function body. diff --git a/docs/cppfront/options.md b/docs/cppfront/options.md index d3cd967ae..ce96c7f0f 100644 --- a/docs/cppfront/options.md +++ b/docs/cppfront/options.md @@ -18,13 +18,13 @@ For convenience, you can shorten the name to any unique prefix not shared with a - `-import-std` and `-include-std` can be shortened to `-im` and `-in` respectively, but not `-i` which would be ambiguous with each other. -# Basic command line options +## Basic command line options -## `-help`, `-h`, `-?` +### `-help`, `-h`, `-?` Prints an abbreviated version of this documentation page. -## `-import-std`, `-im` +### `-import-std`, `-im` Makes the entire C++ standard library (namespace `std::`) available via a module `import std.compat;` (which implies `import std;`). @@ -34,89 +34,89 @@ This option is implicitly set if `-pure-cpp2` is selected. This option is ignored if `-include-std` is selected. If your Cpp1 compiler does not yet support standard library modules `std` and `std.compat`, this option automatically uses `-include-std` instead as a fallback. -## `-include-std`, `-in` +### `-include-std`, `-in` Makes the entire C++ standard library (namespace `std::`) available via an '#include" of every standard header. This option should always work with all standard headers, including draft-standard C++26 headers that are not yet in a published standard, because it tracks new headers as they are added and uses feature tests to not include headers that are not yet available on your Cpp1 implementation. -## `-pure-cpp2`, `-p` +### `-pure-cpp2`, `-p` Allow Cpp2 syntax only. This option also sets `-import-std`. -## `-version`, `-vers` +### `-version`, `-vers` Print version, build, copyright, and license information. -# Additional dynamic safety checks and contract information +## Additional dynamic safety checks and contract information -## `-add-source-info`, `-a` - -Enable `source_location` information for contract checks. If this is supported by your Cpp1 compiler, the default contract failure messages will include exact file/line/function information. For example, if the default `Bounds` violation handler would print this without `-a`: - - Bounds safety violation: out of bounds access attempt detected - attempted access at index 2, [min,max] range is [0,1] - -then it would print something like this with `-a` (the exact text will vary with the Cpp1 standard library vendor's `source_location` implementation): - - demo.cpp2(4) int __cdecl main(void): Bounds safety violation: out of bounds access attempt detected - attempted access at index 2, [min,max] range is [0,1] - -## `-no-comparison-checks`, `-no-c` +### `-no-comparison-checks`, `-no-c` Disable mixed-sign comparison safety checks. If not disabled, mixed-sign comparisons are diagnosed by default. -## `-no-null-checks`, `-no-n` +### `-no-null-checks`, `-no-n` Disable null safety checks. If not disabled, null dereference checks are performed by default. -## `-no-subscript-checks`, `-no-s` +### `-no-subscript-checks`, `-no-s` Disable subscript bounds safety checks. If not disabled, subscript bounds safety checks are performed by default. -# Support for constrained target environments +## Support for constrained target environments -## `-fno-exceptions`, `-fno-e` +### `-fno-exceptions`, `-fno-e` Disable C++ exception handling. This should be used only if you must run in an environment that bans C++ exception handling, and so you are already using a similar command line option for your Cpp1 compiler. If this option is selected, a failed `as` for `std::variant` will assert. -## `-fno-rtti`, `-fno-r` +### `-fno-rtti`, `-fno-r` Disable C++ run-time type information (RTTI). This should be used only if you must run in an environment that bans C++ RTTI, and so you are already using a similar command line option for your Cpp1 compiler. If this option is selected, trying to using `as` for `*` (raw pointers) or `std::any` will assert. -# Other options +## Cpp1 file content options -## `-clean-cpp1`, `-c` +### `-clean-cpp1`, `-cl` Emit clean `.cpp` files without `#line` directives and other extra information that cppfront normally emits in the `.cpp` to light up C++ tools (e.g., to let IDEs integrate cppfront error message output, debuggers step to the right lines in Cpp2 source code, and so forth). In normal use, you won't need `-c`. -## `-debug`, `-d` +### `-emit-cppfront-info`, `-e` -Emit compiler debug output. This is only useful when debugging cppfront itself. +Emit cppfront version and build in the `.cpp` file. -## `-emit-cppfront-info`, `-e` +### `-line-paths`, `-l` -Emit cppfront version and build in the `.cpp` file. +Emit absolute paths in `#line` directives. -## `-format-colon-errors`, `-fo` +## Cppfront output options -Emit cppfront diagnostics using `:line:col:` format for line and column numbers, if that is the format better recognized by your IDE, so that it will pick up cppfront messages and integrate them in its normal error message output location. If not set, by default cppfront diagnostics use `(line,col)` format. +### `-cwd` _path_, `-cw` _path_ -## `-line-paths`, `-l` +Changes the current working directory to 'path'. Can be useful in build scripts to control where generated Cpp1 files are places; see also `-output`. -Emit absolute paths in `#line` directives. +### `-debug`, `-d` -## `-output` _filename_, `-o` _filename_ +Emit compiler debug output. This is only useful when debugging cppfront itself. + +### `-format-colon-errors`, `-fo` + +Emit cppfront diagnostics using `:line:col:` format for line and column numbers, if that is the format better recognized by your IDE, so that it will pick up cppfront messages and integrate them in its normal error message output location. If not set, by default cppfront diagnostics use `(line,col)` format. + +### `-output` _filename_, `-o` _filename_ Output to 'filename' (can be 'stdout'). If not set, the default output filename for is the same as the input filename without the `2` (e.g., compiling `hello.cpp2` by default writes its output to `hello.cpp`, and `header.h2` to `header.h`). -## `-verbose`, `-verb` +### `-quiet`, `-q` + +Print no console output unless there are errors to report. + +### `-verbose`, `-verb` Print verbose statistics and `-debug` output. diff --git a/include/cpp2util.h b/include/cpp2util.h index 62665049d..45b6638fc 100644 --- a/include/cpp2util.h +++ b/include/cpp2util.h @@ -153,6 +153,9 @@ #include #endif #include + #ifdef __cpp_lib_inplace_vector + #include + #endif #include #include #include diff --git a/source/common.h b/source/common.h index b9b41c1bf..41137a22f 100644 --- a/source/common.h +++ b/source/common.h @@ -655,7 +655,7 @@ class cmdline_processor std::unordered_map labels = { { 2, "Additional dynamic safety checks and contract information" }, { 4, "Support for constrained target environments" }, - { 8, "Cpp1 file emission options" }, + { 8, "Cpp1 file content options" }, { 9, "Cppfront output options" } }; diff --git a/source/to_cpp1.h b/source/to_cpp1.h index 765b4e2be..696f8e6fa 100644 --- a/source/to_cpp1.h +++ b/source/to_cpp1.h @@ -149,7 +149,7 @@ static cmdline_processor::register_flag cmd_safe_comparisons( static auto flag_cpp1_filename = std::string{}; static cmdline_processor::register_flag cmd_cpp1_filename( - 8, + 9, "output filename", "Output to 'filename' (can be 'stdout') - default is *.cpp/*.h", nullptr, From 63d02e8f6602dca1bc5d1cfff764805005626392 Mon Sep 17 00:00:00 2001 From: Herb Sutter Date: Mon, 29 Jul 2024 17:31:36 -0700 Subject: [PATCH 02/22] 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 --- .gitignore | 1 + docs/cppfront/options.md | 6 +- include/cpp2util.h | 61 +++++++++++++++++-- ...s-clause-in-forward-declaration.cpp.output | 8 +-- .../gcc-10-c++20/pure2-print.cpp.output | 4 +- ...ed-bounds-safety-with-assert.cpp.execution | 2 +- ...mixed-bugfix-for-ufcs-non-local.cpp.output | 20 +++--- regression-tests/test-results/pure2-print.cpp | 2 +- regression-tests/test-results/version | 2 +- source/build.info | 2 +- source/common.h | 2 +- source/to_cpp1.h | 36 +++++++++++ 12 files changed, 118 insertions(+), 28 deletions(-) diff --git a/.gitignore b/.gitignore index 990c6a75b..a802eea9f 100644 --- a/.gitignore +++ b/.gitignore @@ -38,3 +38,4 @@ venv/* .vscode/ buildh2.bat gen_version.bat +mkdocs_serve.sh diff --git a/docs/cppfront/options.md b/docs/cppfront/options.md index ce96c7f0f..a841cf837 100644 --- a/docs/cppfront/options.md +++ b/docs/cppfront/options.md @@ -51,12 +51,16 @@ This option also sets `-import-std`. Print version, build, copyright, and license information. -## Additional dynamic safety checks and contract information +## Additional dynamic safety check controls ### `-no-comparison-checks`, `-no-c` Disable mixed-sign comparison safety checks. If not disabled, mixed-sign comparisons are diagnosed by default. +### `-no-div-zero-checks`, `-no-d` + +Disable integer division by zero checks. If not disabled, integer division by zero checks are performed by default. + ### `-no-null-checks`, `-no-n` Disable null safety checks. If not disabled, null dereference checks are performed by default. diff --git a/include/cpp2util.h b/include/cpp2util.h index 45b6638fc..91125fbb8 100644 --- a/include/cpp2util.h +++ b/include/cpp2util.h @@ -798,7 +798,7 @@ namespace impl { //----------------------------------------------------------------------- // -// Check for invalid dereference or indirection which would result in undefined behavior. +// Invalid/null dereference checking - cases that would result in UB. // // - Null pointer // - std::unique_ptr that owns nothing @@ -861,11 +861,60 @@ auto assert_not_null(auto&& arg CPP2_SOURCE_LOCATION_PARAM_WITH_DEFAULT) -> decl return CPP2_FORWARD(arg); } -// Subscript bounds checking + +//----------------------------------------------------------------------- +// +// Integer divide-by-zero checking - cases that would result in UB. +// +// Notes: +// NumType is the Numerator type +// arg is the denominator value +// Both must be integral to enable the check +// + +#define CPP2_ASSERT_NOT_ZERO_IMPL \ + requires (std::is_integral_v && \ + std::is_integral_v) \ +{ \ + if (0 == arg) { \ + type_safety.report_violation("integer division by zero attempt detected" CPP2_SOURCE_LOCATION_ARG); \ + } \ + return arg; \ +} + +template +auto assert_not_zero([[maybe_unused]] char _ CPP2_SOURCE_LOCATION_PARAM_WITH_DEFAULT) -> auto + CPP2_ASSERT_NOT_ZERO_IMPL + +template +auto assert_not_zero([[maybe_unused]] char _ CPP2_SOURCE_LOCATION_PARAM_WITH_DEFAULT) -> auto +{ + return arg; +} + +template +auto assert_not_zero(auto arg CPP2_SOURCE_LOCATION_PARAM_WITH_DEFAULT) -> auto + CPP2_ASSERT_NOT_ZERO_IMPL + +template +auto assert_not_zero(auto&& arg CPP2_SOURCE_LOCATION_PARAM_WITH_DEFAULT) -> decltype(auto) + requires (!std::is_integral_v + || !std::is_integral_v) +{ + return CPP2_FORWARD(arg); +} + +#define CPP2_ASSERT_NOT_ZERO(NumType, arg) (cpp2::impl::assert_not_zero((arg))) +#define CPP2_ASSERT_NOT_ZERO_LITERAL(NumType, arg) (cpp2::impl::assert_not_zero('_')) + + +//----------------------------------------------------------------------- +// +// Subscript bounds checking - cases that would result in UB. // #define CPP2_ASSERT_IN_BOUNDS_IMPL \ requires (std::is_integral_v && \ - requires { std::size(x); std::ssize(x); x[arg]; std::begin(x) + 2; }) \ + requires { std::size(x); std::ssize(x); x[arg]; std::begin(x) + 2; }) \ { \ auto max = [&]() -> auto { \ if constexpr (std::is_signed_v) { return std::ssize(x); } \ @@ -888,15 +937,15 @@ template auto assert_in_bounds(auto&& x CPP2_SOURCE_LOCATION_PARAM_WITH_DEFAULT) -> decltype(auto) CPP2_ASSERT_IN_BOUNDS_IMPL -auto assert_in_bounds(auto&& x, auto&& arg CPP2_SOURCE_LOCATION_PARAM_WITH_DEFAULT) -> decltype(auto) - CPP2_ASSERT_IN_BOUNDS_IMPL - template auto assert_in_bounds(auto&& x CPP2_SOURCE_LOCATION_PARAM_WITH_DEFAULT) -> decltype(auto) { return CPP2_FORWARD(x) [ arg ]; } +auto assert_in_bounds(auto&& x, auto&& arg CPP2_SOURCE_LOCATION_PARAM_WITH_DEFAULT) -> decltype(auto) + CPP2_ASSERT_IN_BOUNDS_IMPL + auto assert_in_bounds(auto&& x, auto&& arg CPP2_SOURCE_LOCATION_PARAM_WITH_DEFAULT) -> decltype(auto) { return CPP2_FORWARD(x) [ CPP2_FORWARD(arg) ]; diff --git a/regression-tests/test-results/gcc-10-c++20/pure2-bugfix-for-requires-clause-in-forward-declaration.cpp.output b/regression-tests/test-results/gcc-10-c++20/pure2-bugfix-for-requires-clause-in-forward-declaration.cpp.output index 605533b8b..231ecfcb2 100644 --- a/regression-tests/test-results/gcc-10-c++20/pure2-bugfix-for-requires-clause-in-forward-declaration.cpp.output +++ b/regression-tests/test-results/gcc-10-c++20/pure2-bugfix-for-requires-clause-in-forward-declaration.cpp.output @@ -6,12 +6,12 @@ pure2-bugfix-for-requires-clause-in-forward-declaration.cpp2:3:46: error: expect In file included from pure2-bugfix-for-requires-clause-in-forward-declaration.cpp:7: ../../../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. pure2-bugfix-for-requires-clause-in-forward-declaration.cpp2:4:1: note: in expansion of macro ‘CPP2_REQUIRES_’ -pure2-bugfix-for-requires-clause-in-forward-declaration.cpp2:3:3: error: no declaration matches ‘element::element(auto:259&&) requires is_same_v::type>::type>’ +pure2-bugfix-for-requires-clause-in-forward-declaration.cpp2:3:3: error: no declaration matches ‘element::element(auto:261&&) requires is_same_v::type>::type>’ pure2-bugfix-for-requires-clause-in-forward-declaration.cpp2:5:11: note: candidates are: ‘element::element(const element&)’ -pure2-bugfix-for-requires-clause-in-forward-declaration.cpp2:3:20: note: ‘template element::element(auto:257&&)’ +pure2-bugfix-for-requires-clause-in-forward-declaration.cpp2:3:20: note: ‘template element::element(auto:259&&)’ pure2-bugfix-for-requires-clause-in-forward-declaration.cpp2:1:7: note: ‘class element’ defined here pure2-bugfix-for-requires-clause-in-forward-declaration.cpp2:5:78: error: expected unqualified-id before ‘{’ token -pure2-bugfix-for-requires-clause-in-forward-declaration.cpp2:3:8: error: no declaration matches ‘element& element::operator=(auto:260&&) requires is_same_v::type>::type>’ +pure2-bugfix-for-requires-clause-in-forward-declaration.cpp2:3:8: error: no declaration matches ‘element& element::operator=(auto:262&&) requires is_same_v::type>::type>’ pure2-bugfix-for-requires-clause-in-forward-declaration.cpp2:6:16: note: candidates are: ‘void element::operator=(const element&)’ -pure2-bugfix-for-requires-clause-in-forward-declaration.cpp2:3:16: note: ‘template element& element::operator=(auto:258&&)’ +pure2-bugfix-for-requires-clause-in-forward-declaration.cpp2:3:16: note: ‘template element& element::operator=(auto:260&&)’ pure2-bugfix-for-requires-clause-in-forward-declaration.cpp2:1:7: note: ‘class element’ defined here diff --git a/regression-tests/test-results/gcc-10-c++20/pure2-print.cpp.output b/regression-tests/test-results/gcc-10-c++20/pure2-print.cpp.output index 72475aa8b..2a6d481ca 100644 --- a/regression-tests/test-results/gcc-10-c++20/pure2-print.cpp.output +++ b/regression-tests/test-results/gcc-10-c++20/pure2-print.cpp.output @@ -9,8 +9,8 @@ pure2-print.cpp2:68:1: note: in expansion of macro ‘CPP2_REQUIRES_’ pure2-print.cpp2:97:1: note: in expansion of macro ‘CPP2_REQUIRES_’ pure2-print.cpp2:9:41: error: ‘constexpr const T outer::object_alias’ is not a static data member of ‘class outer’ pure2-print.cpp2:9:48: error: template definition of non-template ‘constexpr const T outer::object_alias’ -pure2-print.cpp2:67:14: error: no declaration matches ‘void outer::mytype::variadic(const auto:258& ...) requires (is_convertible_v::type>::type, int> && ...)’ -pure2-print.cpp2:67:29: note: candidate is: ‘template static void outer::mytype::variadic(const auto:257& ...)’ +pure2-print.cpp2:67:14: error: no declaration matches ‘void outer::mytype::variadic(const auto:260& ...) requires (is_convertible_v::type>::type, int> && ...)’ +pure2-print.cpp2:67:29: note: candidate is: ‘template static void outer::mytype::variadic(const auto:259& ...)’ pure2-print.cpp2:10:19: note: ‘class outer::mytype’ defined here 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)’ 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)’ diff --git a/regression-tests/test-results/gcc-14-c++2b/mixed-bounds-safety-with-assert.cpp.execution b/regression-tests/test-results/gcc-14-c++2b/mixed-bounds-safety-with-assert.cpp.execution index e74faf8ba..b2780a74b 100644 --- a/regression-tests/test-results/gcc-14-c++2b/mixed-bounds-safety-with-assert.cpp.execution +++ b/regression-tests/test-results/gcc-14-c++2b/mixed-bounds-safety-with-assert.cpp.execution @@ -1 +1 @@ -mixed-bounds-safety-with-assert.cpp2(11) void print_subrange(const auto:255&, cpp2::impl::in, cpp2::impl::in) [with auto:255 = std::vector; cpp2::impl::in = const int]: Bounds safety violation +mixed-bounds-safety-with-assert.cpp2(11) void print_subrange(const auto:257&, cpp2::impl::in, cpp2::impl::in) [with auto:257 = std::vector; cpp2::impl::in = const int]: Bounds safety violation diff --git a/regression-tests/test-results/gcc-14-c++2b/mixed-bugfix-for-ufcs-non-local.cpp.output b/regression-tests/test-results/gcc-14-c++2b/mixed-bugfix-for-ufcs-non-local.cpp.output index 7fc683d2c..2f0a3d38f 100644 --- a/regression-tests/test-results/gcc-14-c++2b/mixed-bugfix-for-ufcs-non-local.cpp.output +++ b/regression-tests/test-results/gcc-14-c++2b/mixed-bugfix-for-ufcs-non-local.cpp.output @@ -1,41 +1,41 @@ In file included from mixed-bugfix-for-ufcs-non-local.cpp:6: ../../../include/cpp2util.h:2100:1: error: lambda-expression in template parameter type - 2100 | + 2100 | constexpr auto is( std::optional const& x ) -> bool | ^ ../../../include/cpp2util.h:2137:59: note: in expansion of macro ‘CPP2_UFCS_’ - 2137 | ~finally() noexcept { f(); } + 2137 | // | ^ mixed-bugfix-for-ufcs-non-local.cpp2:13:12: note: in expansion of macro ‘CPP2_UFCS_NONLOCAL’ mixed-bugfix-for-ufcs-non-local.cpp2:13:36: error: template argument 1 is invalid ../../../include/cpp2util.h:2100:1: error: lambda-expression in template parameter type - 2100 | + 2100 | constexpr auto is( std::optional const& x ) -> bool | ^ ../../../include/cpp2util.h:2137:59: note: in expansion of macro ‘CPP2_UFCS_’ - 2137 | ~finally() noexcept { f(); } + 2137 | // | ^ mixed-bugfix-for-ufcs-non-local.cpp2:21:12: note: in expansion of macro ‘CPP2_UFCS_NONLOCAL’ mixed-bugfix-for-ufcs-non-local.cpp2:21:36: error: template argument 1 is invalid ../../../include/cpp2util.h:2100:1: error: lambda-expression in template parameter type - 2100 | + 2100 | constexpr auto is( std::optional const& x ) -> bool | ^ ../../../include/cpp2util.h:2137:59: note: in expansion of macro ‘CPP2_UFCS_’ - 2137 | ~finally() noexcept { f(); } + 2137 | // | ^ mixed-bugfix-for-ufcs-non-local.cpp2:31:12: note: in expansion of macro ‘CPP2_UFCS_NONLOCAL’ mixed-bugfix-for-ufcs-non-local.cpp2:31:36: error: template argument 1 is invalid ../../../include/cpp2util.h:2100:1: error: lambda-expression in template parameter type - 2100 | + 2100 | constexpr auto is( std::optional const& x ) -> bool | ^ ../../../include/cpp2util.h:2137:59: note: in expansion of macro ‘CPP2_UFCS_’ - 2137 | ~finally() noexcept { f(); } + 2137 | // | ^ mixed-bugfix-for-ufcs-non-local.cpp2:33:12: note: in expansion of macro ‘CPP2_UFCS_NONLOCAL’ mixed-bugfix-for-ufcs-non-local.cpp2:33:36: error: template argument 1 is invalid ../../../include/cpp2util.h:2100:1: error: lambda-expression in template parameter type - 2100 | + 2100 | constexpr auto is( std::optional const& x ) -> bool | ^ ../../../include/cpp2util.h:2137:59: note: in expansion of macro ‘CPP2_UFCS_’ - 2137 | ~finally() noexcept { f(); } + 2137 | // | ^ mixed-bugfix-for-ufcs-non-local.cpp2:21:12: note: in expansion of macro ‘CPP2_UFCS_NONLOCAL’ mixed-bugfix-for-ufcs-non-local.cpp2:21:36: error: template argument 1 is invalid diff --git a/regression-tests/test-results/pure2-print.cpp b/regression-tests/test-results/pure2-print.cpp index a244f77e2..a131b4991 100644 --- a/regression-tests/test-results/pure2-print.cpp +++ b/regression-tests/test-results/pure2-print.cpp @@ -105,7 +105,7 @@ requires (true) inline CPP2_CONSTEXPR T outer::object_alias{ 42 }; if (cpp2::impl::cmp_less(*cpp2::impl::assert_not_null(p),0)) { ret = -*cpp2::impl::assert_not_null(cpp2::move(p)); } - ret += strlen(s) - 10 + CPP2_UFCS(strlen)(s) * (16 / (3 & 2)) % 3; + ret += strlen(s) - 10 + CPP2_UFCS(strlen)(s) * (16 / CPP2_ASSERT_NOT_ZERO(CPP2_TYPEOF(16),(3 & 2))) % 3; map m {}; CPP2_ASSERT_IN_BOUNDS_LITERAL(m, 0) = cpp2::impl::as_("har"); diff --git a/regression-tests/test-results/version b/regression-tests/test-results/version index 949bd2a76..bd29747ad 100644 --- a/regression-tests/test-results/version +++ b/regression-tests/test-results/version @@ -1,5 +1,5 @@ -cppfront compiler v0.7.2 Build 9727:1056 +cppfront compiler v0.7.2 Build 9729:1513 Copyright(c) Herb Sutter All rights reserved SPDX-License-Identifier: CC-BY-NC-ND-4.0 diff --git a/source/build.info b/source/build.info index 185f85719..d64456b83 100644 --- a/source/build.info +++ b/source/build.info @@ -1 +1 @@ -"9727:1056" \ No newline at end of file +"9729:1513" \ No newline at end of file diff --git a/source/common.h b/source/common.h index 41137a22f..cf1d6e488 100644 --- a/source/common.h +++ b/source/common.h @@ -653,7 +653,7 @@ class cmdline_processor int max_flag_length = 0; std::unordered_map labels = { - { 2, "Additional dynamic safety checks and contract information" }, + { 2, "Additional dynamic safety check controls" }, { 4, "Support for constrained target environments" }, { 8, "Cpp1 file content options" }, { 9, "Cppfront output options" } diff --git a/source/to_cpp1.h b/source/to_cpp1.h index 696f8e6fa..954456827 100644 --- a/source/to_cpp1.h +++ b/source/to_cpp1.h @@ -131,6 +131,14 @@ static cmdline_processor::register_flag cmd_safe_null_pointers( []{ flag_safe_null_pointers = false; } ); +static auto flag_safe_zero_division = true; +static cmdline_processor::register_flag cmd_safe_zero_division( + 2, + "no-div-zero-checks", + "Disable integer division by zero checks", + []{ flag_safe_zero_division = false; } +); + static auto flag_safe_subscripts = true; static cmdline_processor::register_flag cmd_safe_subscripts( 2, @@ -3868,6 +3876,7 @@ class cppfront } // If it's "_ =" then emit static_cast() bool emit_discard = false; + auto last_expr = decltype(n.expr.get()){}; if ( !n.terms.empty() && n.terms.front().op->type() == lexeme::Assignment @@ -3881,6 +3890,7 @@ class cppfront } else { + last_expr = n.expr.get(); emit(*n.expr); } suppress_move_from_last_use = false; @@ -3972,7 +3982,33 @@ class cppfront } // Otherwise, just emit the general expression as usual else { + // If this is a division, wrap the denominator in a not-zero check + auto suffix = std::string{}; + if ( + flag_safe_zero_division + && ( + x.op->type() == lexeme::Slash + || x.op->type() == lexeme::SlashEq + ) + ) + { + assert(last_expr && "ICE: shouldn't get here without having captured a pointer to the numerator expression"); + if (auto lit = x.expr->get_literal(); + lit + && lit->get_token()->type() == lexeme::DecimalLiteral + ) + { + printer.print_cpp2( "CPP2_ASSERT_NOT_ZERO_LITERAL(CPP2_TYPEOF(" + print_to_string(*last_expr) + "),", x.op->position() ); + } + else + { + printer.print_cpp2( "CPP2_ASSERT_NOT_ZERO(CPP2_TYPEOF(" + print_to_string(*last_expr) + "),", x.op->position() ); + } + suffix = ")"; + } + last_expr = x.expr.get(); emit(*x.expr); + printer.print_cpp2( suffix, x.expr->position() ); } } From e7046c7d0aefce14c8a9eeab48bbdd9ccd7c2a5b Mon Sep 17 00:00:00 2001 From: Neil Henderson <2060747+bluetarpmedia@users.noreply.github.com> Date: Tue, 30 Jul 2024 16:25:09 +1000 Subject: [PATCH 03/22] Update regression tests after recent changes (#1188) Co-authored-by: Neil Henderson --- .../pure2-function-typeids.cpp.execution | 17 ++++++++++++++++ .../pure2-function-typeids.cpp.execution | 17 ++++++++++++++++ .../mixed-bounds-check.cpp.execution | 2 +- ...ed-bounds-safety-with-assert.cpp.execution | 2 +- ...-safety-3-contract-violation.cpp.execution | 2 +- ...me-safety-and-null-contracts.cpp.execution | 2 +- ...re2-assert-optional-not-null.cpp.execution | 2 +- ...2-assert-shared-ptr-not-null.cpp.execution | 2 +- ...2-assert-unique-ptr-not-null.cpp.execution | 2 +- .../pure2-function-typeids.cpp.execution | 17 ++++++++++++++++ .../pure2-function-typeids.cpp.execution | 17 ++++++++++++++++ ...ed-bounds-safety-with-assert.cpp.execution | 2 +- ...mixed-bugfix-for-ufcs-non-local.cpp.output | 20 +++++++++---------- .../pure2-function-typeids.cpp.execution | 17 ++++++++++++++++ .../pure2-assert-expected-not-null.cpp.output | 4 ++-- .../pure2-function-typeids.cpp.execution | 17 ++++++++++++++++ .../pure2-function-typeids.cpp.output | 1 + 17 files changed, 123 insertions(+), 20 deletions(-) create mode 100644 regression-tests/test-results/apple-clang-14-c++2b/pure2-function-typeids.cpp.execution create mode 100644 regression-tests/test-results/clang-15-c++20-libcpp/pure2-function-typeids.cpp.execution create mode 100644 regression-tests/test-results/clang-15-c++20/pure2-function-typeids.cpp.execution create mode 100644 regression-tests/test-results/clang-18-c++20/pure2-function-typeids.cpp.execution create mode 100644 regression-tests/test-results/gcc-13-c++2b/pure2-function-typeids.cpp.execution create mode 100644 regression-tests/test-results/msvc-2022-c++20/pure2-function-typeids.cpp.execution create mode 100644 regression-tests/test-results/msvc-2022-c++20/pure2-function-typeids.cpp.output diff --git a/regression-tests/test-results/apple-clang-14-c++2b/pure2-function-typeids.cpp.execution b/regression-tests/test-results/apple-clang-14-c++2b/pure2-function-typeids.cpp.execution new file mode 100644 index 000000000..08f16f663 --- /dev/null +++ b/regression-tests/test-results/apple-clang-14-c++2b/pure2-function-typeids.cpp.execution @@ -0,0 +1,17 @@ +hello world! +hello world! +Come in, Frodo +Come in, Sam +Come in awhile, but take some biscuits on your way out, Frodo! +Come in awhile, but take some biscuits on your way out, Sam! +fg_out initialized gandalf to: A Powerful Mage +pg_out initialized galadriel to: A Powerful Mage +I hear you've moving, Frodo? +I hear you've moving, Sam? +Inout Gandalf ... fh_forward returned: Gandalf +Inout Galadriel ... ph_forward returned: Galadriel +Inout Galadriel ... ph_forward2 returned: Galadriel +In Gandalf ... fh_out returned: yohoho +In Galadriel ... ph_out returned: yohoho +43 +44 diff --git a/regression-tests/test-results/clang-15-c++20-libcpp/pure2-function-typeids.cpp.execution b/regression-tests/test-results/clang-15-c++20-libcpp/pure2-function-typeids.cpp.execution new file mode 100644 index 000000000..08f16f663 --- /dev/null +++ b/regression-tests/test-results/clang-15-c++20-libcpp/pure2-function-typeids.cpp.execution @@ -0,0 +1,17 @@ +hello world! +hello world! +Come in, Frodo +Come in, Sam +Come in awhile, but take some biscuits on your way out, Frodo! +Come in awhile, but take some biscuits on your way out, Sam! +fg_out initialized gandalf to: A Powerful Mage +pg_out initialized galadriel to: A Powerful Mage +I hear you've moving, Frodo? +I hear you've moving, Sam? +Inout Gandalf ... fh_forward returned: Gandalf +Inout Galadriel ... ph_forward returned: Galadriel +Inout Galadriel ... ph_forward2 returned: Galadriel +In Gandalf ... fh_out returned: yohoho +In Galadriel ... ph_out returned: yohoho +43 +44 diff --git a/regression-tests/test-results/clang-15-c++20/mixed-bounds-check.cpp.execution b/regression-tests/test-results/clang-15-c++20/mixed-bounds-check.cpp.execution index fa8252b3c..48b014ff8 100644 --- a/regression-tests/test-results/clang-15-c++20/mixed-bounds-check.cpp.execution +++ b/regression-tests/test-results/clang-15-c++20/mixed-bounds-check.cpp.execution @@ -1 +1 @@ -../../../include/cpp2util.h(883) decltype(auto) cpp2::impl::assert_in_bounds(auto &&, std::source_location) [arg = 5, x:auto = std::vector]: Bounds safety violation: out of bounds access attempt detected - attempted access at index 5, [min,max] range is [0,4] +../../../include/cpp2util.h(937) decltype(auto) cpp2::impl::assert_in_bounds(auto &&, std::source_location) [arg = 5, x:auto = std::vector]: Bounds safety violation: out of bounds access attempt detected - attempted access at index 5, [min,max] range is [0,4] diff --git a/regression-tests/test-results/clang-15-c++20/mixed-bounds-safety-with-assert.cpp.execution b/regression-tests/test-results/clang-15-c++20/mixed-bounds-safety-with-assert.cpp.execution index 312fa7694..e4b3e0b61 100644 --- a/regression-tests/test-results/clang-15-c++20/mixed-bounds-safety-with-assert.cpp.execution +++ b/regression-tests/test-results/clang-15-c++20/mixed-bounds-safety-with-assert.cpp.execution @@ -1 +1 @@ -../../../include/cpp2util.h(744) : Bounds safety violation +../../../include/cpp2util.h(749) : Bounds safety violation diff --git a/regression-tests/test-results/clang-15-c++20/mixed-initialization-safety-3-contract-violation.cpp.execution b/regression-tests/test-results/clang-15-c++20/mixed-initialization-safety-3-contract-violation.cpp.execution index e58245c78..9c4799bce 100644 --- a/regression-tests/test-results/clang-15-c++20/mixed-initialization-safety-3-contract-violation.cpp.execution +++ b/regression-tests/test-results/clang-15-c++20/mixed-initialization-safety-3-contract-violation.cpp.execution @@ -1 +1 @@ -../../../include/cpp2util.h(744) : Contract violation: fill: value must contain at least count elements +../../../include/cpp2util.h(749) : Contract violation: fill: value must contain at least count elements diff --git a/regression-tests/test-results/clang-15-c++20/mixed-lifetime-safety-and-null-contracts.cpp.execution b/regression-tests/test-results/clang-15-c++20/mixed-lifetime-safety-and-null-contracts.cpp.execution index fb8eb511b..b5f3ff105 100644 --- a/regression-tests/test-results/clang-15-c++20/mixed-lifetime-safety-and-null-contracts.cpp.execution +++ b/regression-tests/test-results/clang-15-c++20/mixed-lifetime-safety-and-null-contracts.cpp.execution @@ -1,2 +1,2 @@ sending error to my framework... [dynamic null dereference attempt detected] -from source location: ../../../include/cpp2util.h(823) decltype(auto) cpp2::impl::assert_not_null(auto &&, std::source_location) [arg:auto = int *&] +from source location: ../../../include/cpp2util.h(828) decltype(auto) cpp2::impl::assert_not_null(auto &&, std::source_location) [arg:auto = int *&] diff --git a/regression-tests/test-results/clang-15-c++20/pure2-assert-optional-not-null.cpp.execution b/regression-tests/test-results/clang-15-c++20/pure2-assert-optional-not-null.cpp.execution index bb106dd63..dedfd5463 100644 --- a/regression-tests/test-results/clang-15-c++20/pure2-assert-optional-not-null.cpp.execution +++ b/regression-tests/test-results/clang-15-c++20/pure2-assert-optional-not-null.cpp.execution @@ -1 +1 @@ -../../../include/cpp2util.h(823) decltype(auto) cpp2::impl::assert_not_null(auto &&, std::source_location) [arg:auto = std::optional]: Null safety violation: std::optional does not contain a value +../../../include/cpp2util.h(828) decltype(auto) cpp2::impl::assert_not_null(auto &&, std::source_location) [arg:auto = std::optional]: Null safety violation: std::optional does not contain a value diff --git a/regression-tests/test-results/clang-15-c++20/pure2-assert-shared-ptr-not-null.cpp.execution b/regression-tests/test-results/clang-15-c++20/pure2-assert-shared-ptr-not-null.cpp.execution index 375d8a3bf..beacf8d96 100644 --- a/regression-tests/test-results/clang-15-c++20/pure2-assert-shared-ptr-not-null.cpp.execution +++ b/regression-tests/test-results/clang-15-c++20/pure2-assert-shared-ptr-not-null.cpp.execution @@ -1 +1 @@ -../../../include/cpp2util.h(823) decltype(auto) cpp2::impl::assert_not_null(auto &&, std::source_location) [arg:auto = std::shared_ptr]: Null safety violation: std::shared_ptr is empty +../../../include/cpp2util.h(828) decltype(auto) cpp2::impl::assert_not_null(auto &&, std::source_location) [arg:auto = std::shared_ptr]: Null safety violation: std::shared_ptr is empty diff --git a/regression-tests/test-results/clang-15-c++20/pure2-assert-unique-ptr-not-null.cpp.execution b/regression-tests/test-results/clang-15-c++20/pure2-assert-unique-ptr-not-null.cpp.execution index 21996fccd..e24a6337a 100644 --- a/regression-tests/test-results/clang-15-c++20/pure2-assert-unique-ptr-not-null.cpp.execution +++ b/regression-tests/test-results/clang-15-c++20/pure2-assert-unique-ptr-not-null.cpp.execution @@ -1 +1 @@ -../../../include/cpp2util.h(823) decltype(auto) cpp2::impl::assert_not_null(auto &&, std::source_location) [arg:auto = std::unique_ptr &]: Null safety violation: std::unique_ptr is empty +../../../include/cpp2util.h(828) decltype(auto) cpp2::impl::assert_not_null(auto &&, std::source_location) [arg:auto = std::unique_ptr &]: Null safety violation: std::unique_ptr is empty diff --git a/regression-tests/test-results/clang-15-c++20/pure2-function-typeids.cpp.execution b/regression-tests/test-results/clang-15-c++20/pure2-function-typeids.cpp.execution new file mode 100644 index 000000000..08f16f663 --- /dev/null +++ b/regression-tests/test-results/clang-15-c++20/pure2-function-typeids.cpp.execution @@ -0,0 +1,17 @@ +hello world! +hello world! +Come in, Frodo +Come in, Sam +Come in awhile, but take some biscuits on your way out, Frodo! +Come in awhile, but take some biscuits on your way out, Sam! +fg_out initialized gandalf to: A Powerful Mage +pg_out initialized galadriel to: A Powerful Mage +I hear you've moving, Frodo? +I hear you've moving, Sam? +Inout Gandalf ... fh_forward returned: Gandalf +Inout Galadriel ... ph_forward returned: Galadriel +Inout Galadriel ... ph_forward2 returned: Galadriel +In Gandalf ... fh_out returned: yohoho +In Galadriel ... ph_out returned: yohoho +43 +44 diff --git a/regression-tests/test-results/clang-18-c++20/pure2-function-typeids.cpp.execution b/regression-tests/test-results/clang-18-c++20/pure2-function-typeids.cpp.execution new file mode 100644 index 000000000..08f16f663 --- /dev/null +++ b/regression-tests/test-results/clang-18-c++20/pure2-function-typeids.cpp.execution @@ -0,0 +1,17 @@ +hello world! +hello world! +Come in, Frodo +Come in, Sam +Come in awhile, but take some biscuits on your way out, Frodo! +Come in awhile, but take some biscuits on your way out, Sam! +fg_out initialized gandalf to: A Powerful Mage +pg_out initialized galadriel to: A Powerful Mage +I hear you've moving, Frodo? +I hear you've moving, Sam? +Inout Gandalf ... fh_forward returned: Gandalf +Inout Galadriel ... ph_forward returned: Galadriel +Inout Galadriel ... ph_forward2 returned: Galadriel +In Gandalf ... fh_out returned: yohoho +In Galadriel ... ph_out returned: yohoho +43 +44 diff --git a/regression-tests/test-results/gcc-13-c++2b/mixed-bounds-safety-with-assert.cpp.execution b/regression-tests/test-results/gcc-13-c++2b/mixed-bounds-safety-with-assert.cpp.execution index d7550af32..e6ee874fc 100644 --- a/regression-tests/test-results/gcc-13-c++2b/mixed-bounds-safety-with-assert.cpp.execution +++ b/regression-tests/test-results/gcc-13-c++2b/mixed-bounds-safety-with-assert.cpp.execution @@ -1 +1 @@ -mixed-bounds-safety-with-assert.cpp2(11) void print_subrange(const auto:261&, cpp2::impl::in, cpp2::impl::in) [with auto:261 = std::vector; cpp2::impl::in = const int]: Bounds safety violation +mixed-bounds-safety-with-assert.cpp2(11) void print_subrange(const auto:263&, cpp2::impl::in, cpp2::impl::in) [with auto:263 = std::vector; cpp2::impl::in = const int]: Bounds safety violation diff --git a/regression-tests/test-results/gcc-13-c++2b/mixed-bugfix-for-ufcs-non-local.cpp.output b/regression-tests/test-results/gcc-13-c++2b/mixed-bugfix-for-ufcs-non-local.cpp.output index 4ba9bb6e1..2f0a3d38f 100644 --- a/regression-tests/test-results/gcc-13-c++2b/mixed-bugfix-for-ufcs-non-local.cpp.output +++ b/regression-tests/test-results/gcc-13-c++2b/mixed-bugfix-for-ufcs-non-local.cpp.output @@ -1,41 +1,41 @@ In file included from mixed-bugfix-for-ufcs-non-local.cpp:6: ../../../include/cpp2util.h:2100:1: error: lambda-expression in template parameter type - 2100 | class finally_success + 2100 | constexpr auto is( std::optional const& x ) -> bool | ^ ../../../include/cpp2util.h:2137:59: note: in expansion of macro ‘CPP2_UFCS_’ - 2137 | finally(finally&& that) noexcept + 2137 | // | ^ mixed-bugfix-for-ufcs-non-local.cpp2:13:12: note: in expansion of macro ‘CPP2_UFCS_NONLOCAL’ mixed-bugfix-for-ufcs-non-local.cpp2:13:36: error: template argument 1 is invalid ../../../include/cpp2util.h:2100:1: error: lambda-expression in template parameter type - 2100 | class finally_success + 2100 | constexpr auto is( std::optional const& x ) -> bool | ^ ../../../include/cpp2util.h:2137:59: note: in expansion of macro ‘CPP2_UFCS_’ - 2137 | finally(finally&& that) noexcept + 2137 | // | ^ mixed-bugfix-for-ufcs-non-local.cpp2:21:12: note: in expansion of macro ‘CPP2_UFCS_NONLOCAL’ mixed-bugfix-for-ufcs-non-local.cpp2:21:36: error: template argument 1 is invalid ../../../include/cpp2util.h:2100:1: error: lambda-expression in template parameter type - 2100 | class finally_success + 2100 | constexpr auto is( std::optional const& x ) -> bool | ^ ../../../include/cpp2util.h:2137:59: note: in expansion of macro ‘CPP2_UFCS_’ - 2137 | finally(finally&& that) noexcept + 2137 | // | ^ mixed-bugfix-for-ufcs-non-local.cpp2:31:12: note: in expansion of macro ‘CPP2_UFCS_NONLOCAL’ mixed-bugfix-for-ufcs-non-local.cpp2:31:36: error: template argument 1 is invalid ../../../include/cpp2util.h:2100:1: error: lambda-expression in template parameter type - 2100 | class finally_success + 2100 | constexpr auto is( std::optional const& x ) -> bool | ^ ../../../include/cpp2util.h:2137:59: note: in expansion of macro ‘CPP2_UFCS_’ - 2137 | finally(finally&& that) noexcept + 2137 | // | ^ mixed-bugfix-for-ufcs-non-local.cpp2:33:12: note: in expansion of macro ‘CPP2_UFCS_NONLOCAL’ mixed-bugfix-for-ufcs-non-local.cpp2:33:36: error: template argument 1 is invalid ../../../include/cpp2util.h:2100:1: error: lambda-expression in template parameter type - 2100 | class finally_success + 2100 | constexpr auto is( std::optional const& x ) -> bool | ^ ../../../include/cpp2util.h:2137:59: note: in expansion of macro ‘CPP2_UFCS_’ - 2137 | finally(finally&& that) noexcept + 2137 | // | ^ mixed-bugfix-for-ufcs-non-local.cpp2:21:12: note: in expansion of macro ‘CPP2_UFCS_NONLOCAL’ mixed-bugfix-for-ufcs-non-local.cpp2:21:36: error: template argument 1 is invalid diff --git a/regression-tests/test-results/gcc-13-c++2b/pure2-function-typeids.cpp.execution b/regression-tests/test-results/gcc-13-c++2b/pure2-function-typeids.cpp.execution new file mode 100644 index 000000000..08f16f663 --- /dev/null +++ b/regression-tests/test-results/gcc-13-c++2b/pure2-function-typeids.cpp.execution @@ -0,0 +1,17 @@ +hello world! +hello world! +Come in, Frodo +Come in, Sam +Come in awhile, but take some biscuits on your way out, Frodo! +Come in awhile, but take some biscuits on your way out, Sam! +fg_out initialized gandalf to: A Powerful Mage +pg_out initialized galadriel to: A Powerful Mage +I hear you've moving, Frodo? +I hear you've moving, Sam? +Inout Gandalf ... fh_forward returned: Gandalf +Inout Galadriel ... ph_forward returned: Galadriel +Inout Galadriel ... ph_forward2 returned: Galadriel +In Gandalf ... fh_out returned: yohoho +In Galadriel ... ph_out returned: yohoho +43 +44 diff --git a/regression-tests/test-results/msvc-2022-c++20/pure2-assert-expected-not-null.cpp.output b/regression-tests/test-results/msvc-2022-c++20/pure2-assert-expected-not-null.cpp.output index cd8f31a81..272a4fc8d 100644 --- a/regression-tests/test-results/msvc-2022-c++20/pure2-assert-expected-not-null.cpp.output +++ b/regression-tests/test-results/msvc-2022-c++20/pure2-assert-expected-not-null.cpp.output @@ -6,7 +6,7 @@ pure2-assert-expected-not-null.cpp2(7): error C2143: syntax error: missing ';' b pure2-assert-expected-not-null.cpp2(7): error C2143: syntax error: missing ';' before '}' pure2-assert-expected-not-null.cpp2(9): error C2065: 'ex': undeclared identifier pure2-assert-expected-not-null.cpp2(9): error C2672: 'cpp2::impl::assert_not_null': no matching overloaded function found -D:\a\cppfront\cppfront\include\cpp2util.h(823): note: could be 'decltype(auto) cpp2::impl::assert_not_null(_T0 &&,std::source_location)' +D:\a\cppfront\cppfront\include\cpp2util.h(828): note: could be 'decltype(auto) cpp2::impl::assert_not_null(_T0 &&,std::source_location)' pure2-assert-expected-not-null.cpp2(14): error C2039: 'expected': is not a member of 'std' predefined C++ types (compiler internal)(347): note: see declaration of 'std' pure2-assert-expected-not-null.cpp2(14): error C2062: type 'int' unexpected @@ -19,4 +19,4 @@ pure2-assert-expected-not-null.cpp2(14): note: while trying to match the argumen pure2-assert-expected-not-null.cpp2(14): error C2143: syntax error: missing ';' before '}' pure2-assert-expected-not-null.cpp2(15): error C2065: 'ex': undeclared identifier pure2-assert-expected-not-null.cpp2(15): error C2672: 'cpp2::impl::assert_not_null': no matching overloaded function found -D:\a\cppfront\cppfront\include\cpp2util.h(823): note: could be 'decltype(auto) cpp2::impl::assert_not_null(_T0 &&,std::source_location)' +D:\a\cppfront\cppfront\include\cpp2util.h(828): note: could be 'decltype(auto) cpp2::impl::assert_not_null(_T0 &&,std::source_location)' diff --git a/regression-tests/test-results/msvc-2022-c++20/pure2-function-typeids.cpp.execution b/regression-tests/test-results/msvc-2022-c++20/pure2-function-typeids.cpp.execution new file mode 100644 index 000000000..08f16f663 --- /dev/null +++ b/regression-tests/test-results/msvc-2022-c++20/pure2-function-typeids.cpp.execution @@ -0,0 +1,17 @@ +hello world! +hello world! +Come in, Frodo +Come in, Sam +Come in awhile, but take some biscuits on your way out, Frodo! +Come in awhile, but take some biscuits on your way out, Sam! +fg_out initialized gandalf to: A Powerful Mage +pg_out initialized galadriel to: A Powerful Mage +I hear you've moving, Frodo? +I hear you've moving, Sam? +Inout Gandalf ... fh_forward returned: Gandalf +Inout Galadriel ... ph_forward returned: Galadriel +Inout Galadriel ... ph_forward2 returned: Galadriel +In Gandalf ... fh_out returned: yohoho +In Galadriel ... ph_out returned: yohoho +43 +44 diff --git a/regression-tests/test-results/msvc-2022-c++20/pure2-function-typeids.cpp.output b/regression-tests/test-results/msvc-2022-c++20/pure2-function-typeids.cpp.output new file mode 100644 index 000000000..962933802 --- /dev/null +++ b/regression-tests/test-results/msvc-2022-c++20/pure2-function-typeids.cpp.output @@ -0,0 +1 @@ +pure2-function-typeids.cpp From 458957f113736ebdc7e33ee56e389c3e6ab3d5a6 Mon Sep 17 00:00:00 2001 From: Herb Sutter Date: Mon, 29 Jul 2024 23:38:14 -0700 Subject: [PATCH 04/22] Made range operators explicit about whether the last value is included The original design was to support `...` (implicitly half-open, last value is excluded) and `..=` (explicitly closed, last value is included). This changes that to `..<` (explicitly half-open, last value is excluded) and `..=` (explicitly closed, last value is included). Rationale: Originally I made `...` the default because it's the safe default, for common cases like iterator ranges where including the last element is a bounds violation error. It would be creating a terrible pitfall to make the "default" syntax be an out-of-bounds error on every use with iterators. However, feedback on Reddit and elsewhere quickly pointed out that for numeric ranges `...` not including the last value is also surprising. What to do? One way out is to not support iterators. However, that would be a needless loss of functionality if there was a better answer. And there is: A better way out is to simply embrace that there should not be a default, but make it convenient for programmers to be explicit every time about whether the last element is included or not. Supporting `..<` and `..=` as the range operators achieves that goal. I appreciate the feedback, and I think it led to a superior design for a C++-compatible ecosystem that heavily uses iterators today. Thanks to everyone who gave feedback! --- docs/cpp2/expressions.md | 8 ++++---- regression-tests/pure2-range-operators.cpp2 | 6 +++--- regression-tests/test-results/version | 2 +- source/build.info | 2 +- source/lex.h | 11 +++++++---- source/parse.h | 11 ++++++----- source/to_cpp1.h | 5 ++++- 7 files changed, 26 insertions(+), 19 deletions(-) diff --git a/docs/cpp2/expressions.md b/docs/cpp2/expressions.md index 8881ce5cc..24980cbd8 100644 --- a/docs/cpp2/expressions.md +++ b/docs/cpp2/expressions.md @@ -223,18 +223,18 @@ test(42); For more examples, see also the examples in the previous two sections on `is` and `as`, many of which use `inspect`. -## `...` and `..=` — range operators +## `..<` and `..=` — range operators -`...` and `..=` designate a range of things. In addition to using `...` for variadic parameters, variadic pack expansion, and fold expressions as in Cpp1, Cpp2 also supports using `begin...end` for a half-open range (that does not include `end`) and `first..=last` for a closed range (that does include `last`). +`..<` and `..=` designate a range of things. Use `begin ..< end` for a half-open range (that does not include `end`) and `first ..= last` for a closed range (that does include `last`). These operators work for any type that supports `++`; they start with the first value, and use `++` to increment until they reach the last value (which is included by `..=`, and not included by `..<`). For example: -``` cpp title="Using ... and ..= for ranges" hl_lines="5 11" +``` cpp title="Using ..< and ..= for ranges" hl_lines="5 11" test: (v: std::vector) = { // Print strings from "Nonesuch" (if present) onward i1 := v.std::ranges::find("Nonesuch"); - for i1 ... v.end() do (e) { + for i1 ..< v.end() do (e) { std::cout << " (e*)$\n"; } diff --git a/regression-tests/pure2-range-operators.cpp2 b/regression-tests/pure2-range-operators.cpp2 index 12384c871..b98e61435 100644 --- a/regression-tests/pure2-range-operators.cpp2 +++ b/regression-tests/pure2-range-operators.cpp2 @@ -4,12 +4,12 @@ main: () = { ( "Aardvark", "Baboon", "Cat", "Dolphin", "Elephant", "Flicker", "Grue", "Wumpus" ); std::cout << "We have some alpabetical animals:\n"; - for v.begin()...v.end() do (e) { + for v.begin() ..< v.end() do (e) { std::cout << " (e*)$\n"; } std::cout << "\nAnd from indexes 1..=5 they are:\n"; - for 1..=5 do (e) { + for 1 ..= 5 do (e) { std::cout << " (e)$ (v[e])$\n"; } @@ -17,7 +17,7 @@ main: () = { ( "Hokey", "Pokey" ); std::cout << "\nMake sure non-random-access iterators work:\n"; - for all_about.begin()...all_about.end() do (e) { + for all_about.begin() ..< all_about.end() do (e) { std::cout << " (e*)$\n"; } diff --git a/regression-tests/test-results/version b/regression-tests/test-results/version index bd29747ad..4c7f242cb 100644 --- a/regression-tests/test-results/version +++ b/regression-tests/test-results/version @@ -1,5 +1,5 @@ -cppfront compiler v0.7.2 Build 9729:1513 +cppfront compiler v0.7.2 Build 9729:2320 Copyright(c) Herb Sutter All rights reserved SPDX-License-Identifier: CC-BY-NC-ND-4.0 diff --git a/source/build.info b/source/build.info index d64456b83..8399e3fe3 100644 --- a/source/build.info +++ b/source/build.info @@ -1 +1 @@ -"9729:1513" \ No newline at end of file +"9729:2320" \ No newline at end of file diff --git a/source/lex.h b/source/lex.h index bf48449cf..2e2393341 100644 --- a/source/lex.h +++ b/source/lex.h @@ -85,7 +85,8 @@ enum class lexeme : std::int8_t { Dot, DotDot, Ellipsis, - EllipsisEq, + EllipsisLess, + EllipsisEqual, QuestionMark, At, Dollar, @@ -184,7 +185,8 @@ auto _as(lexeme l) break;case lexeme::Dot: return "Dot"; break;case lexeme::DotDot: return "DotDot"; break;case lexeme::Ellipsis: return "Ellipsis"; - break;case lexeme::EllipsisEq: return "EllipsisEq"; + break;case lexeme::EllipsisLess: return "EllipsisLess"; + break;case lexeme::EllipsisEqual: return "EllipsisEqual"; break;case lexeme::QuestionMark: return "QuestionMark"; break;case lexeme::At: return "At"; break;case lexeme::Dollar: return "Dollar"; @@ -1440,10 +1442,11 @@ auto lex_line( //G //G punctuator: one of - //G '.' '..' '...' '..=' + //G '.' '..' '...' '..<' '..=' break;case '.': if (peek1 == '.' && peek2 == '.') { store(3, lexeme::Ellipsis); } - else if (peek1 == '.' && peek2 == '=') { store(3, lexeme::EllipsisEq); } + else if (peek1 == '.' && peek2 == '<') { store(3, lexeme::EllipsisLess); } + else if (peek1 == '.' && peek2 == '=') { store(3, lexeme::EllipsisEqual); } else if (peek1 == '.') { store(2, lexeme::DotDot); } else { store(1, lexeme::Dot); } diff --git a/source/parse.h b/source/parse.h index 287747b0d..6f47d2908 100644 --- a/source/parse.h +++ b/source/parse.h @@ -61,7 +61,8 @@ auto is_postfix_operator(lexeme l) case lexeme::Tilde: case lexeme::Dollar: case lexeme::Ellipsis: - case lexeme::EllipsisEq: + case lexeme::EllipsisLess: + case lexeme::EllipsisEqual: return true; break;default: return false; @@ -6031,8 +6032,8 @@ class parser || curr().type() == lexeme::LeftParen || curr().type() == lexeme::Dot || curr().type() == lexeme::DotDot - || curr().type() == lexeme::Ellipsis - || curr().type() == lexeme::EllipsisEq + || curr().type() == lexeme::EllipsisLess + || curr().type() == lexeme::EllipsisEqual ) ) { @@ -6127,8 +6128,8 @@ class parser } else if ( ( - term.op->type() == lexeme::Ellipsis - || term.op->type() == lexeme::EllipsisEq + term.op->type() == lexeme::EllipsisLess + || term.op->type() == lexeme::EllipsisEqual ) && n->expr->to_string() != "sizeof" ) diff --git a/source/to_cpp1.h b/source/to_cpp1.h index 954456827..f2514259f 100644 --- a/source/to_cpp1.h +++ b/source/to_cpp1.h @@ -3472,9 +3472,12 @@ class cppfront { prefix.emplace_back( "cpp2::range(", i->op->position() ); auto print = print_to_string( *i->last_expr ); - if (i->op->type() == lexeme::EllipsisEq) { + if (i->op->type() == lexeme::EllipsisEqual) { print += ",true"; } + else { + assert(i->op->type() == lexeme::EllipsisLess); + } suffix.emplace_back( "," + print + ")", i->last_expr->position()); } From a8d70c1d44a11d6872fb506931d7aaf5c898f940 Mon Sep 17 00:00:00 2001 From: Neil Henderson <2060747+bluetarpmedia@users.noreply.github.com> Date: Wed, 31 Jul 2024 09:01:15 +1000 Subject: [PATCH 05/22] [CI] Fix clang-18 C++23 build of cppfront (#1187) * Move the ctor to out-of-line for `parameter_declaration_node` The `declaration_node` definition for the `unique_ptr` member is not yet visible if the ctor is written inline. * Add out-of-line dtor for `declaration_node` * Add out-of-line dtor for `parameter_declaration_node` --------- Co-authored-by: Neil Henderson --- source/parse.h | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/source/parse.h b/source/parse.h index 6f47d2908..a740fcc62 100644 --- a/source/parse.h +++ b/source/parse.h @@ -2274,7 +2274,13 @@ struct parameter_declaration_node std::unique_ptr declaration; - parameter_declaration_node(parameter_declaration_list_node const* my) : my_list{my} { } + // Out-of-line definition of the ctor is necessary due to the forward-declared + // type(s) used in a std::unique_ptr as a member + parameter_declaration_node(parameter_declaration_list_node const* my); + + // Out-of-line definition of the dtor is necessary due to the forward-declared + // type(s) used in a std::unique_ptr as a member + ~parameter_declaration_node(); // API // @@ -2860,6 +2866,10 @@ struct declaration_node : parent_declaration{parent} { } + // Out-of-line definition of the dtor is necessary due to the forward-declared + // type(s) used in a std::unique_ptr as a member + ~declaration_node(); + // API // @@ -4513,7 +4523,13 @@ struct translation_unit_node } }; -// Definitions of out-of-line dtors for nodes with unique_ptr members of forward-declared types +// Definitions of out-of-line ctors & dtors for nodes with unique_ptr members of forward-declared types + +parameter_declaration_node::parameter_declaration_node(parameter_declaration_list_node const* my) + : my_list{my} +{ } + +parameter_declaration_node::~parameter_declaration_node() = default; type_id_node::~type_id_node() = default; @@ -4537,6 +4553,8 @@ inspect_expression_node::~inspect_expression_node() = default; statement_node::~statement_node() = default; +declaration_node::~declaration_node() = default; + //----------------------------------------------------------------------- // From f5363cc8cb6d0f792d5e08043dce404dd09b390b Mon Sep 17 00:00:00 2001 From: Neil Henderson <2060747+bluetarpmedia@users.noreply.github.com> Date: Wed, 31 Jul 2024 10:03:38 +1000 Subject: [PATCH 06/22] Update regression tests for clang-15 on macOS and clang-18 on ubuntu (#1192) --- .../mixed-bounds-check.cpp.execution | 2 +- ...ixed-bounds-safety-with-assert.cpp.execution | 2 +- ...on-safety-3-contract-violation.cpp.execution | 2 +- ...time-safety-and-null-contracts.cpp.execution | 2 +- ...pure2-assert-expected-not-null.cpp.execution | 2 +- ...pure2-assert-optional-not-null.cpp.execution | 2 +- ...re2-assert-shared-ptr-not-null.cpp.execution | 2 +- ...re2-assert-unique-ptr-not-null.cpp.execution | 2 +- .../pure2-function-typeids.cpp.execution | 17 +++++++++++++++++ .../pure2-function-typeids.cpp.execution | 17 +++++++++++++++++ 10 files changed, 42 insertions(+), 8 deletions(-) create mode 100644 regression-tests/test-results/apple-clang-15-c++2b/pure2-function-typeids.cpp.execution create mode 100644 regression-tests/test-results/clang-18-c++23-libcpp/pure2-function-typeids.cpp.execution diff --git a/regression-tests/test-results/apple-clang-15-c++2b/mixed-bounds-check.cpp.execution b/regression-tests/test-results/apple-clang-15-c++2b/mixed-bounds-check.cpp.execution index fa8252b3c..48b014ff8 100644 --- a/regression-tests/test-results/apple-clang-15-c++2b/mixed-bounds-check.cpp.execution +++ b/regression-tests/test-results/apple-clang-15-c++2b/mixed-bounds-check.cpp.execution @@ -1 +1 @@ -../../../include/cpp2util.h(883) decltype(auto) cpp2::impl::assert_in_bounds(auto &&, std::source_location) [arg = 5, x:auto = std::vector]: Bounds safety violation: out of bounds access attempt detected - attempted access at index 5, [min,max] range is [0,4] +../../../include/cpp2util.h(937) decltype(auto) cpp2::impl::assert_in_bounds(auto &&, std::source_location) [arg = 5, x:auto = std::vector]: Bounds safety violation: out of bounds access attempt detected - attempted access at index 5, [min,max] range is [0,4] diff --git a/regression-tests/test-results/apple-clang-15-c++2b/mixed-bounds-safety-with-assert.cpp.execution b/regression-tests/test-results/apple-clang-15-c++2b/mixed-bounds-safety-with-assert.cpp.execution index 312fa7694..e4b3e0b61 100644 --- a/regression-tests/test-results/apple-clang-15-c++2b/mixed-bounds-safety-with-assert.cpp.execution +++ b/regression-tests/test-results/apple-clang-15-c++2b/mixed-bounds-safety-with-assert.cpp.execution @@ -1 +1 @@ -../../../include/cpp2util.h(744) : Bounds safety violation +../../../include/cpp2util.h(749) : Bounds safety violation diff --git a/regression-tests/test-results/apple-clang-15-c++2b/mixed-initialization-safety-3-contract-violation.cpp.execution b/regression-tests/test-results/apple-clang-15-c++2b/mixed-initialization-safety-3-contract-violation.cpp.execution index e58245c78..9c4799bce 100644 --- a/regression-tests/test-results/apple-clang-15-c++2b/mixed-initialization-safety-3-contract-violation.cpp.execution +++ b/regression-tests/test-results/apple-clang-15-c++2b/mixed-initialization-safety-3-contract-violation.cpp.execution @@ -1 +1 @@ -../../../include/cpp2util.h(744) : Contract violation: fill: value must contain at least count elements +../../../include/cpp2util.h(749) : Contract violation: fill: value must contain at least count elements diff --git a/regression-tests/test-results/apple-clang-15-c++2b/mixed-lifetime-safety-and-null-contracts.cpp.execution b/regression-tests/test-results/apple-clang-15-c++2b/mixed-lifetime-safety-and-null-contracts.cpp.execution index fb8eb511b..b5f3ff105 100644 --- a/regression-tests/test-results/apple-clang-15-c++2b/mixed-lifetime-safety-and-null-contracts.cpp.execution +++ b/regression-tests/test-results/apple-clang-15-c++2b/mixed-lifetime-safety-and-null-contracts.cpp.execution @@ -1,2 +1,2 @@ sending error to my framework... [dynamic null dereference attempt detected] -from source location: ../../../include/cpp2util.h(823) decltype(auto) cpp2::impl::assert_not_null(auto &&, std::source_location) [arg:auto = int *&] +from source location: ../../../include/cpp2util.h(828) decltype(auto) cpp2::impl::assert_not_null(auto &&, std::source_location) [arg:auto = int *&] diff --git a/regression-tests/test-results/apple-clang-15-c++2b/pure2-assert-expected-not-null.cpp.execution b/regression-tests/test-results/apple-clang-15-c++2b/pure2-assert-expected-not-null.cpp.execution index d4f4704ce..97aef3c59 100644 --- a/regression-tests/test-results/apple-clang-15-c++2b/pure2-assert-expected-not-null.cpp.execution +++ b/regression-tests/test-results/apple-clang-15-c++2b/pure2-assert-expected-not-null.cpp.execution @@ -1 +1 @@ -../../../include/cpp2util.h(823) decltype(auto) cpp2::impl::assert_not_null(auto &&, std::source_location) [arg:auto = std::expected]: Null safety violation: std::expected has an unexpected value +../../../include/cpp2util.h(828) decltype(auto) cpp2::impl::assert_not_null(auto &&, std::source_location) [arg:auto = std::expected]: Null safety violation: std::expected has an unexpected value diff --git a/regression-tests/test-results/apple-clang-15-c++2b/pure2-assert-optional-not-null.cpp.execution b/regression-tests/test-results/apple-clang-15-c++2b/pure2-assert-optional-not-null.cpp.execution index bb106dd63..dedfd5463 100644 --- a/regression-tests/test-results/apple-clang-15-c++2b/pure2-assert-optional-not-null.cpp.execution +++ b/regression-tests/test-results/apple-clang-15-c++2b/pure2-assert-optional-not-null.cpp.execution @@ -1 +1 @@ -../../../include/cpp2util.h(823) decltype(auto) cpp2::impl::assert_not_null(auto &&, std::source_location) [arg:auto = std::optional]: Null safety violation: std::optional does not contain a value +../../../include/cpp2util.h(828) decltype(auto) cpp2::impl::assert_not_null(auto &&, std::source_location) [arg:auto = std::optional]: Null safety violation: std::optional does not contain a value diff --git a/regression-tests/test-results/apple-clang-15-c++2b/pure2-assert-shared-ptr-not-null.cpp.execution b/regression-tests/test-results/apple-clang-15-c++2b/pure2-assert-shared-ptr-not-null.cpp.execution index 375d8a3bf..beacf8d96 100644 --- a/regression-tests/test-results/apple-clang-15-c++2b/pure2-assert-shared-ptr-not-null.cpp.execution +++ b/regression-tests/test-results/apple-clang-15-c++2b/pure2-assert-shared-ptr-not-null.cpp.execution @@ -1 +1 @@ -../../../include/cpp2util.h(823) decltype(auto) cpp2::impl::assert_not_null(auto &&, std::source_location) [arg:auto = std::shared_ptr]: Null safety violation: std::shared_ptr is empty +../../../include/cpp2util.h(828) decltype(auto) cpp2::impl::assert_not_null(auto &&, std::source_location) [arg:auto = std::shared_ptr]: Null safety violation: std::shared_ptr is empty diff --git a/regression-tests/test-results/apple-clang-15-c++2b/pure2-assert-unique-ptr-not-null.cpp.execution b/regression-tests/test-results/apple-clang-15-c++2b/pure2-assert-unique-ptr-not-null.cpp.execution index 21996fccd..e24a6337a 100644 --- a/regression-tests/test-results/apple-clang-15-c++2b/pure2-assert-unique-ptr-not-null.cpp.execution +++ b/regression-tests/test-results/apple-clang-15-c++2b/pure2-assert-unique-ptr-not-null.cpp.execution @@ -1 +1 @@ -../../../include/cpp2util.h(823) decltype(auto) cpp2::impl::assert_not_null(auto &&, std::source_location) [arg:auto = std::unique_ptr &]: Null safety violation: std::unique_ptr is empty +../../../include/cpp2util.h(828) decltype(auto) cpp2::impl::assert_not_null(auto &&, std::source_location) [arg:auto = std::unique_ptr &]: Null safety violation: std::unique_ptr is empty diff --git a/regression-tests/test-results/apple-clang-15-c++2b/pure2-function-typeids.cpp.execution b/regression-tests/test-results/apple-clang-15-c++2b/pure2-function-typeids.cpp.execution new file mode 100644 index 000000000..08f16f663 --- /dev/null +++ b/regression-tests/test-results/apple-clang-15-c++2b/pure2-function-typeids.cpp.execution @@ -0,0 +1,17 @@ +hello world! +hello world! +Come in, Frodo +Come in, Sam +Come in awhile, but take some biscuits on your way out, Frodo! +Come in awhile, but take some biscuits on your way out, Sam! +fg_out initialized gandalf to: A Powerful Mage +pg_out initialized galadriel to: A Powerful Mage +I hear you've moving, Frodo? +I hear you've moving, Sam? +Inout Gandalf ... fh_forward returned: Gandalf +Inout Galadriel ... ph_forward returned: Galadriel +Inout Galadriel ... ph_forward2 returned: Galadriel +In Gandalf ... fh_out returned: yohoho +In Galadriel ... ph_out returned: yohoho +43 +44 diff --git a/regression-tests/test-results/clang-18-c++23-libcpp/pure2-function-typeids.cpp.execution b/regression-tests/test-results/clang-18-c++23-libcpp/pure2-function-typeids.cpp.execution new file mode 100644 index 000000000..08f16f663 --- /dev/null +++ b/regression-tests/test-results/clang-18-c++23-libcpp/pure2-function-typeids.cpp.execution @@ -0,0 +1,17 @@ +hello world! +hello world! +Come in, Frodo +Come in, Sam +Come in awhile, but take some biscuits on your way out, Frodo! +Come in awhile, but take some biscuits on your way out, Sam! +fg_out initialized gandalf to: A Powerful Mage +pg_out initialized galadriel to: A Powerful Mage +I hear you've moving, Frodo? +I hear you've moving, Sam? +Inout Gandalf ... fh_forward returned: Gandalf +Inout Galadriel ... ph_forward returned: Galadriel +Inout Galadriel ... ph_forward2 returned: Galadriel +In Gandalf ... fh_out returned: yohoho +In Galadriel ... ph_out returned: yohoho +43 +44 From 0898f4126aca5da112922f5b039a39d4f6d0c3c9 Mon Sep 17 00:00:00 2001 From: Herb Sutter Date: Wed, 31 Jul 2024 10:27:33 -0700 Subject: [PATCH 07/22] Restrict `unsafe_narrow`, add `unsafe_cast` Closes #1191 Restrict `unsafe_narrow` to narrowing cases and arithmetic types Make `as` diagnose unsafe pointer casts, with a message to use `unsafe_cast` instead Allow both `unsafe_narrow` and `unsafe_cast` to be used without `cpp2::` qualification in Cpp2 code Add a new "unsafe casts" doc section Remove some uses of `unsafe_narrow` in cppfront's own code that weren't actually narrowing Example: f: (i: i32, inout s: std::string) = { // j := i as i16; // error, maybe-lossy narrowing j := unsafe_narrow(i); // ok, 'unsafe' is explicit pv: *void = s&; // pi := pv as *std::string; // error, unsafe cast ps := unsafe_cast<*std::string>(pv); // ok, 'unsafe' is explicit ps* = "plugh"; } main: () = { str: std::string = "xyzzy"; f( 42, str ); std::cout << str; // prints: plush } --- docs/cpp2/expressions.md | 25 ++++++- include/cpp2regex.h | 8 +-- include/cpp2util.h | 69 +++++++++++++++---- regression-tests/pure2-unsafe.cpp2 | 16 +++++ .../clang-12-c++20/pure2-unsafe.cpp.execution | 1 + .../gcc-10-c++20/pure2-unsafe.cpp.execution | 1 + ...mixed-bugfix-for-ufcs-non-local.cpp.output | 20 +++--- .../gcc-14-c++2b/pure2-unsafe.cpp.execution | 1 + .../pure2-unsafe.cpp.execution | 1 + .../pure2-unsafe.cpp.output | 1 + .../test-results/pure2-unsafe.cpp | 43 ++++++++++++ .../test-results/pure2-unsafe.cpp2.output | 2 + regression-tests/test-results/version | 2 +- source/build.info | 2 +- source/lex.h | 6 +- source/to_cpp1.h | 6 +- 16 files changed, 168 insertions(+), 36 deletions(-) create mode 100644 regression-tests/pure2-unsafe.cpp2 create mode 100644 regression-tests/test-results/clang-12-c++20/pure2-unsafe.cpp.execution create mode 100644 regression-tests/test-results/gcc-10-c++20/pure2-unsafe.cpp.execution create mode 100644 regression-tests/test-results/gcc-14-c++2b/pure2-unsafe.cpp.execution create mode 100644 regression-tests/test-results/msvc-2022-c++latest/pure2-unsafe.cpp.execution create mode 100644 regression-tests/test-results/msvc-2022-c++latest/pure2-unsafe.cpp.output create mode 100644 regression-tests/test-results/pure2-unsafe.cpp create mode 100644 regression-tests/test-results/pure2-unsafe.cpp2.output diff --git a/docs/cpp2/expressions.md b/docs/cpp2/expressions.md index 24980cbd8..90d74a118 100644 --- a/docs/cpp2/expressions.md +++ b/docs/cpp2/expressions.md @@ -99,7 +99,9 @@ _ = vec.emplace_back(1,2,3); For details, see [Design note: Explicit discard](https://github.com/hsutter/cppfront/wiki/Design-note%3A-Explicit-discard). In Cpp2, data is always initialized, data is never silently lost, data flow is always visible. Data is precious, and it's always safe. -## `is` — safe type/value queries +## Type/value queries and casts + +### `is` — safe type/value queries An `x is C` expression allows safe type and value queries, and evaluates to `#!cpp true` if `x` matches constraint `C`. It supports both static and dynamic queries, including customization, with support for standard library dynamic types like `std::variant`, `std::optional`, `std::expected`, and `std::any` provided out of the box. @@ -147,7 +149,7 @@ Here are some `is` queries with their Cpp1 equivalents. In this table, uppercase > Note: `is` unifies a variety of differently-named Cpp1 language and library queries under one syntax, and supports only the type-safe ones. -## `as` — safe casts and conversions +### `as` — safe casts and conversions An `x as T` expression allows safe type casts. `x` must be an object or expression, and `T` must be a type. Like `is`, `as` supports both static and dynamic typing, including customization, with support for standard library dynamic types like `std::variant`, `std::optional`, `std::expected`, and `std::any` provided out of the box. For example: @@ -184,6 +186,24 @@ Here are some `as` casts with their Cpp1 equivalents. In this table, uppercase n > Note: `as` unifies a variety of differently-named Cpp1 language and library casts and conversions under one syntax, and supports only the type-safe ones. +### Unsafe casts + +Unsafe casts must always be explicit. + +To perform a numeric narrowing cast, such as `i32` to `i16` or `u32`, use `unsafe_narrow(from)`. For example: + +``` cpp title="Unsafe narrowing and casts must be explicit" hl_lines="2 3 6 7" +f: (i: i32, inout s: std::string) = { + // j := i as i16; // error, maybe-lossy narrowing + j := unsafe_narrow(i); // ok, 'unsafe' is explicit + + pv: *void = s&; + // pi := pv as *std::string; // error, unsafe cast + pi := unsafe_cast<*std::string>(pv); // ok, 'unsafe' is explicit +} +``` + + ## `inspect` — pattern matching An `inspect expr -> Type = { /* alternatives */ }` expression allows pattern matching using `is`. @@ -353,3 +373,4 @@ std::cout << "now x+2 is (x+2)$\n"; ``` A string literal capture can include a `:suffix` where the suffix is a [standard C++ format specification](https://en.cppreference.com/w/cpp/utility/format/spec). For example, `#!cpp (x.price(): <10.2f)$` evaluates `x.price()` and converts the result to a string with 10-character width, 2 digits of precision, and left-justified. + diff --git a/include/cpp2regex.h b/include/cpp2regex.h index 12867bed2..fcc1363e4 100644 --- a/include/cpp2regex.h +++ b/include/cpp2regex.h @@ -3111,7 +3111,7 @@ size_t i{0}; auto number {0}; if (!(string_util::string_to_int(group, number, 8))) {return ctx.error("Could not convert octal to int."); } - char number_as_char {unsafe_narrow(cpp2::move(number))}; + char number_as_char {cpp2::unsafe_narrow(cpp2::move(number))}; auto token {CPP2_UFCS_TEMPLATE(cpp2_new)(cpp2::shared, number_as_char, ctx.get_modifiers().has(expression_flags::case_insensitive))}; (*cpp2::impl::assert_not_null(token)).set_string("\\" + cpp2::to_string(string_util::int_to_string<8>(cpp2::impl::as_(cpp2::move(number_as_char)))) + ""); @@ -3462,7 +3462,7 @@ template [[nodiscard]] auto gr if (!(string_util::string_to_int(cpp2::move(number_str), number, 16))) {return ctx.error("Could not convert hexadecimal to int."); } // TODO: Change for unicode. - char number_as_char {unsafe_narrow(cpp2::move(number))}; + char number_as_char {cpp2::unsafe_narrow(cpp2::move(number))}; std::string syntax {string_util::int_to_string<16>(cpp2::impl::as_(number_as_char))}; if (cpp2::move(has_brackets)) { @@ -3605,7 +3605,7 @@ template [[nodiscard]] auto lookahead_token_match if (!(string_util::string_to_int(cpp2::move(number_str), number, 8))) {return ctx.error("Could not convert octal to int."); } // TODO: Change for unicode. - char number_as_char {unsafe_narrow(cpp2::move(number))}; + char number_as_char {cpp2::unsafe_narrow(cpp2::move(number))}; std::string syntax {"\\o{" + cpp2::to_string(string_util::int_to_string<8>(cpp2::impl::as_(number_as_char))) + "}"}; auto r {CPP2_UFCS_TEMPLATE(cpp2_new)(cpp2::shared, cpp2::move(number_as_char), ctx.get_modifiers().has(expression_flags::case_insensitive))}; @@ -3981,7 +3981,7 @@ template [[nodiscard]] auto word_boundary_token_mat template template regular_expression::search_return::search_return(cpp2::impl::in matched_, context const& ctx_, Iter const& pos_) : matched{ matched_ } , ctx{ ctx_ } - , pos{ unsafe_narrow(std::distance(ctx_.begin, pos_)) }{ + , pos{ cpp2::unsafe_narrow(std::distance(ctx_.begin, pos_)) }{ #line 2633 "cpp2regex.h2" } diff --git a/include/cpp2util.h b/include/cpp2util.h index 91125fbb8..d61024e96 100644 --- a/include/cpp2util.h +++ b/include/cpp2util.h @@ -1681,6 +1681,7 @@ inline constexpr auto is( X const& x, bool (*value)(X const&) ) -> bool { // The 'as' cast functions are so use that order here // If it's confusing, we can switch this to + template< typename To, typename From > inline constexpr auto is_narrowing_v = // [dcl.init.list] 7.1 @@ -1694,7 +1695,20 @@ inline constexpr auto is_narrowing_v = (std::is_integral_v && std::is_integral_v && sizeof(From) > sizeof(To)) || (std::is_enum_v && std::is_integral_v && sizeof(From) > sizeof(To)) || // [dcl.init.list] 7.5 - (std::is_pointer_v && std::is_same_v); + (std::is_pointer_v && std::is_same_v) + ; + +template< typename To, typename From > +inline constexpr auto is_unsafe_pointer_conversion_v = + std::is_pointer_v + && std::is_pointer_v +// Work around Clang <= 15 C++20 mode not conforming to C++20 P0929 +#if (defined(__clang_major__) && __clang_major__ <= 15) + && !std::is_same_v, void*> +#else + && !requires (To t, From f) { t = f; } +#endif + ; template inline constexpr auto program_violates_type_safety_guarantee = sizeof...(Ts) < 0; @@ -1807,6 +1821,12 @@ auto as(auto&& x CPP2_SOURCE_LOCATION_PARAM_WITH_DEFAULT_AS) -> decltype(auto) { return Dynamic_cast(CPP2_FORWARD(x)); } + else if constexpr ( + is_unsafe_pointer_conversion_v + ) + { + return nonesuch; + } else if constexpr (requires { C{CPP2_FORWARD(x)}; }) { // Experiment: Recognize the nested `::value_type` pattern for some dynamic library types // like std::optional, and try to prevent accidental narrowing conversions even when @@ -2242,7 +2262,23 @@ class finally_presuccess //----------------------------------------------------------------------- // template -constexpr auto unsafe_narrow( X&& x ) noexcept -> decltype(auto) +constexpr auto unsafe_narrow( X x ) noexcept + -> decltype(auto) + requires ( + impl::is_narrowing_v + || ( + std::is_arithmetic_v + && std::is_arithmetic_v + ) + ) +{ + return static_cast(x); +} + + +template +constexpr auto unsafe_cast( X&& x ) noexcept + -> decltype(auto) { return static_cast(CPP2_FORWARD(x)); } @@ -2291,16 +2327,16 @@ struct args int curr; }; - auto begin() const -> iterator { return iterator{ argc, argv, 0 }; } - auto end() const -> iterator { return iterator{ argc, argv, argc }; } - auto cbegin() const -> iterator { return begin(); } - auto cend() const -> iterator { return end(); } - auto size() const -> std::size_t { return cpp2::unsafe_narrow(ssize()); } - auto ssize() const -> int { return argc; } + auto begin() const -> iterator { return iterator{ argc, argv, 0 }; } + auto end() const -> iterator { return iterator{ argc, argv, argc }; } + auto cbegin() const -> iterator { return begin(); } + auto cend() const -> iterator { return end(); } + auto size() const -> std::size_t { return cpp2::unsafe_narrow(ssize()); } + auto ssize() const -> std::ptrdiff_t { return argc; } auto operator[](int i) const { - if (0 <= i && i < ssize()) { return std::string_view{ argv[i] }; } - else { return std::string_view{}; } + if (0 <= i && i < ssize()) { return std::string_view{ argv[i] }; } + else { return std::string_view{}; } } mutable int argc = 0; // mutable for compatibility with frameworks that take 'int& argc' @@ -2704,9 +2740,16 @@ inline constexpr auto as_( auto&& x ) -> decltype(auto) if constexpr (is_narrowing_v) { static_assert( program_violates_type_safety_guarantee, - "'as' does not allow unsafe narrowing conversions - if you're sure you want this, use `unsafe_narrow()` to force the conversion" + "'as' does not allow unsafe possibly-lossy narrowing conversions - if you're sure you want this, use 'unsafe_narrow' to explicitly force the conversion and possibly lose information" ); } + else if constexpr (is_unsafe_pointer_conversion_v) + { + static_assert( + program_violates_type_safety_guarantee, + "'as' does not allow unsafe pointer conversions - if you're sure you want this, use `unsafe_cast()` to explicitly force the unsafe cast" + ); + } else if constexpr( std::is_same_v< CPP2_TYPEOF(as(CPP2_FORWARD(x))), nonesuch_ > ) { static_assert( program_violates_type_safety_guarantee, @@ -2724,8 +2767,8 @@ inline constexpr auto as_() -> decltype(auto) if constexpr( std::is_same_v< CPP2_TYPEOF((as())), nonesuch_ > ) { static_assert( program_violates_type_safety_guarantee, - "Literal cannot be narrowed using 'as' - if you're sure you want this, use 'unsafe_narrow()' to force the conversion" - ); + "'as' does not allow unsafe possibly-lossy narrowing conversions - if you're sure you want this, use `unsafe_narrow()` to explicitly force the conversion and possibly lose information" + ); } } else { diff --git a/regression-tests/pure2-unsafe.cpp2 b/regression-tests/pure2-unsafe.cpp2 new file mode 100644 index 000000000..b05ec6389 --- /dev/null +++ b/regression-tests/pure2-unsafe.cpp2 @@ -0,0 +1,16 @@ + +f: (i: i32, inout s: std::string) = { + // j := i as i16; // error, maybe-lossy narrowing + j := unsafe_narrow(i); // ok, 'unsafe' is explicit + + pv: *void = s&; + // pi := pv as *std::string; // error, unsafe cast + ps := unsafe_cast<*std::string>(pv); // ok, 'unsafe' is explicit + ps* = "plugh"; +} + +main: () = { + str: std::string = "xyzzy"; + f( 42, str ); + std::cout << str; +} diff --git a/regression-tests/test-results/clang-12-c++20/pure2-unsafe.cpp.execution b/regression-tests/test-results/clang-12-c++20/pure2-unsafe.cpp.execution new file mode 100644 index 000000000..705506a60 --- /dev/null +++ b/regression-tests/test-results/clang-12-c++20/pure2-unsafe.cpp.execution @@ -0,0 +1 @@ +plugh \ No newline at end of file diff --git a/regression-tests/test-results/gcc-10-c++20/pure2-unsafe.cpp.execution b/regression-tests/test-results/gcc-10-c++20/pure2-unsafe.cpp.execution new file mode 100644 index 000000000..705506a60 --- /dev/null +++ b/regression-tests/test-results/gcc-10-c++20/pure2-unsafe.cpp.execution @@ -0,0 +1 @@ +plugh \ No newline at end of file diff --git a/regression-tests/test-results/gcc-14-c++2b/mixed-bugfix-for-ufcs-non-local.cpp.output b/regression-tests/test-results/gcc-14-c++2b/mixed-bugfix-for-ufcs-non-local.cpp.output index 2f0a3d38f..459a8add9 100644 --- a/regression-tests/test-results/gcc-14-c++2b/mixed-bugfix-for-ufcs-non-local.cpp.output +++ b/regression-tests/test-results/gcc-14-c++2b/mixed-bugfix-for-ufcs-non-local.cpp.output @@ -1,41 +1,41 @@ In file included from mixed-bugfix-for-ufcs-non-local.cpp:6: ../../../include/cpp2util.h:2100:1: error: lambda-expression in template parameter type - 2100 | constexpr auto is( std::optional const& x ) -> bool + 2100 | // | ^ ../../../include/cpp2util.h:2137:59: note: in expansion of macro ‘CPP2_UFCS_’ - 2137 | // + 2137 | // Value case | ^ mixed-bugfix-for-ufcs-non-local.cpp2:13:12: note: in expansion of macro ‘CPP2_UFCS_NONLOCAL’ mixed-bugfix-for-ufcs-non-local.cpp2:13:36: error: template argument 1 is invalid ../../../include/cpp2util.h:2100:1: error: lambda-expression in template parameter type - 2100 | constexpr auto is( std::optional const& x ) -> bool + 2100 | // | ^ ../../../include/cpp2util.h:2137:59: note: in expansion of macro ‘CPP2_UFCS_’ - 2137 | // + 2137 | // Value case | ^ mixed-bugfix-for-ufcs-non-local.cpp2:21:12: note: in expansion of macro ‘CPP2_UFCS_NONLOCAL’ mixed-bugfix-for-ufcs-non-local.cpp2:21:36: error: template argument 1 is invalid ../../../include/cpp2util.h:2100:1: error: lambda-expression in template parameter type - 2100 | constexpr auto is( std::optional const& x ) -> bool + 2100 | // | ^ ../../../include/cpp2util.h:2137:59: note: in expansion of macro ‘CPP2_UFCS_’ - 2137 | // + 2137 | // Value case | ^ mixed-bugfix-for-ufcs-non-local.cpp2:31:12: note: in expansion of macro ‘CPP2_UFCS_NONLOCAL’ mixed-bugfix-for-ufcs-non-local.cpp2:31:36: error: template argument 1 is invalid ../../../include/cpp2util.h:2100:1: error: lambda-expression in template parameter type - 2100 | constexpr auto is( std::optional const& x ) -> bool + 2100 | // | ^ ../../../include/cpp2util.h:2137:59: note: in expansion of macro ‘CPP2_UFCS_’ - 2137 | // + 2137 | // Value case | ^ mixed-bugfix-for-ufcs-non-local.cpp2:33:12: note: in expansion of macro ‘CPP2_UFCS_NONLOCAL’ mixed-bugfix-for-ufcs-non-local.cpp2:33:36: error: template argument 1 is invalid ../../../include/cpp2util.h:2100:1: error: lambda-expression in template parameter type - 2100 | constexpr auto is( std::optional const& x ) -> bool + 2100 | // | ^ ../../../include/cpp2util.h:2137:59: note: in expansion of macro ‘CPP2_UFCS_’ - 2137 | // + 2137 | // Value case | ^ mixed-bugfix-for-ufcs-non-local.cpp2:21:12: note: in expansion of macro ‘CPP2_UFCS_NONLOCAL’ mixed-bugfix-for-ufcs-non-local.cpp2:21:36: error: template argument 1 is invalid diff --git a/regression-tests/test-results/gcc-14-c++2b/pure2-unsafe.cpp.execution b/regression-tests/test-results/gcc-14-c++2b/pure2-unsafe.cpp.execution new file mode 100644 index 000000000..705506a60 --- /dev/null +++ b/regression-tests/test-results/gcc-14-c++2b/pure2-unsafe.cpp.execution @@ -0,0 +1 @@ +plugh \ No newline at end of file diff --git a/regression-tests/test-results/msvc-2022-c++latest/pure2-unsafe.cpp.execution b/regression-tests/test-results/msvc-2022-c++latest/pure2-unsafe.cpp.execution new file mode 100644 index 000000000..705506a60 --- /dev/null +++ b/regression-tests/test-results/msvc-2022-c++latest/pure2-unsafe.cpp.execution @@ -0,0 +1 @@ +plugh \ No newline at end of file diff --git a/regression-tests/test-results/msvc-2022-c++latest/pure2-unsafe.cpp.output b/regression-tests/test-results/msvc-2022-c++latest/pure2-unsafe.cpp.output new file mode 100644 index 000000000..4b92375e0 --- /dev/null +++ b/regression-tests/test-results/msvc-2022-c++latest/pure2-unsafe.cpp.output @@ -0,0 +1 @@ +pure2-unsafe.cpp diff --git a/regression-tests/test-results/pure2-unsafe.cpp b/regression-tests/test-results/pure2-unsafe.cpp new file mode 100644 index 000000000..7069e8084 --- /dev/null +++ b/regression-tests/test-results/pure2-unsafe.cpp @@ -0,0 +1,43 @@ + +#define CPP2_IMPORT_STD Yes + +//=== Cpp2 type declarations ==================================================== + + +#include "cpp2util.h" + +#line 1 "pure2-unsafe.cpp2" + + +//=== Cpp2 type definitions and function declarations =========================== + +#line 1 "pure2-unsafe.cpp2" + +#line 2 "pure2-unsafe.cpp2" +auto f(cpp2::impl::in i, std::string& s) -> void; + +#line 12 "pure2-unsafe.cpp2" +auto main() -> int; + +//=== Cpp2 function definitions ================================================= + +#line 1 "pure2-unsafe.cpp2" + +#line 2 "pure2-unsafe.cpp2" +auto f(cpp2::impl::in i, std::string& s) -> void{ + // j := i as i16; // error, maybe-lossy narrowing + auto j {cpp2::unsafe_narrow(i)}; // ok, 'unsafe' is explicit + + void* pv {&s}; + // pi := pv as *std::string; // error, unsafe cast + auto ps {cpp2::unsafe_cast(cpp2::move(pv))}; // ok, 'unsafe' is explicit + *cpp2::impl::assert_not_null(ps) = "plugh"; +} + +#line 12 "pure2-unsafe.cpp2" +auto main() -> int{ + std::string str {"xyzzy"}; + f(42, str); + std::cout << cpp2::move(str); +} + diff --git a/regression-tests/test-results/pure2-unsafe.cpp2.output b/regression-tests/test-results/pure2-unsafe.cpp2.output new file mode 100644 index 000000000..2a3ca4d58 --- /dev/null +++ b/regression-tests/test-results/pure2-unsafe.cpp2.output @@ -0,0 +1,2 @@ +pure2-unsafe.cpp2... ok (all Cpp2, passes safety checks) + diff --git a/regression-tests/test-results/version b/regression-tests/test-results/version index 4c7f242cb..c1cbb4036 100644 --- a/regression-tests/test-results/version +++ b/regression-tests/test-results/version @@ -1,5 +1,5 @@ -cppfront compiler v0.7.2 Build 9729:2320 +cppfront compiler v0.7.2 Build 9731:1008 Copyright(c) Herb Sutter All rights reserved SPDX-License-Identifier: CC-BY-NC-ND-4.0 diff --git a/source/build.info b/source/build.info index 8399e3fe3..ea9968ce5 100644 --- a/source/build.info +++ b/source/build.info @@ -1 +1 @@ -"9729:2320" \ No newline at end of file +"9731:1008" \ No newline at end of file diff --git a/source/lex.h b/source/lex.h index 2e2393341..5d5cd04e0 100644 --- a/source/lex.h +++ b/source/lex.h @@ -1116,15 +1116,15 @@ auto lex_line( //G one of: 'import' 'module' 'export' 'is' 'as' //G auto do_is_keyword = [&](std::vector const& r) { - auto remaining_line = std::string_view(line).substr(unsafe_narrow(i)); + auto remaining_line = std::string_view(line).substr(i); auto m = std::find_if(r.begin(), r.end(), [&](std::string_view s) { return remaining_line.starts_with(s); }); if (m != r.end()) { // If we matched and what's next is EOL or a non-identifier char, we matched! if ( - i+std::ssize(*m) == std::ssize(line) // EOL - || !is_identifier_continue(line[unsafe_narrow(i)+std::size(*m)]) // non-identifier char + i+std::ssize(*m) == std::ssize(line) // EOL + || !is_identifier_continue(line[i+std::size(*m)]) // non-identifier char ) { return static_cast(std::ssize(*m)); diff --git a/source/to_cpp1.h b/source/to_cpp1.h index f2514259f..d248f0529 100644 --- a/source/to_cpp1.h +++ b/source/to_cpp1.h @@ -1635,8 +1635,8 @@ class cppfront pos = n.position(); } - // Implicit "cpp2::" qualification of Cpp2 fixed-width type aliases - // and cpp2::finally + // Implicit "cpp2::" qualification of utilities we want to + // make usable without cpp2:: qualification if ( !is_qualified && ( @@ -1644,6 +1644,8 @@ class cppfront || n == "finally" || n == "cpp1_ref" || n == "cpp1_rvalue_ref" + || n == "unsafe_narrow" + || n == "unsafe_cast" ) ) { From 890388309231711cf4b8c9fd0a4c12ab2ddac504 Mon Sep 17 00:00:00 2001 From: jarzec Date: Wed, 31 Jul 2024 23:08:51 +0200 Subject: [PATCH 08/22] CI Update test files after recent changes (#1196) --- .../pure2-unsafe.cpp.execution | 1 + .../pure2-unsafe.cpp.execution | 1 + .../pure2-unsafe.cpp.execution | 1 + .../clang-15-c++20/pure2-unsafe.cpp.execution | 1 + .../clang-18-c++20/pure2-unsafe.cpp.execution | 1 + .../pure2-unsafe.cpp.execution | 1 + ...mixed-bugfix-for-ufcs-non-local.cpp.output | 20 +++++++++---------- .../gcc-13-c++2b/pure2-unsafe.cpp.execution | 1 + .../pure2-unsafe.cpp.execution | 1 + .../msvc-2022-c++20/pure2-unsafe.cpp.output | 1 + 10 files changed, 19 insertions(+), 10 deletions(-) create mode 100644 regression-tests/test-results/apple-clang-14-c++2b/pure2-unsafe.cpp.execution create mode 100644 regression-tests/test-results/apple-clang-15-c++2b/pure2-unsafe.cpp.execution create mode 100644 regression-tests/test-results/clang-15-c++20-libcpp/pure2-unsafe.cpp.execution create mode 100644 regression-tests/test-results/clang-15-c++20/pure2-unsafe.cpp.execution create mode 100644 regression-tests/test-results/clang-18-c++20/pure2-unsafe.cpp.execution create mode 100644 regression-tests/test-results/clang-18-c++23-libcpp/pure2-unsafe.cpp.execution create mode 100644 regression-tests/test-results/gcc-13-c++2b/pure2-unsafe.cpp.execution create mode 100644 regression-tests/test-results/msvc-2022-c++20/pure2-unsafe.cpp.execution create mode 100644 regression-tests/test-results/msvc-2022-c++20/pure2-unsafe.cpp.output diff --git a/regression-tests/test-results/apple-clang-14-c++2b/pure2-unsafe.cpp.execution b/regression-tests/test-results/apple-clang-14-c++2b/pure2-unsafe.cpp.execution new file mode 100644 index 000000000..705506a60 --- /dev/null +++ b/regression-tests/test-results/apple-clang-14-c++2b/pure2-unsafe.cpp.execution @@ -0,0 +1 @@ +plugh \ No newline at end of file diff --git a/regression-tests/test-results/apple-clang-15-c++2b/pure2-unsafe.cpp.execution b/regression-tests/test-results/apple-clang-15-c++2b/pure2-unsafe.cpp.execution new file mode 100644 index 000000000..705506a60 --- /dev/null +++ b/regression-tests/test-results/apple-clang-15-c++2b/pure2-unsafe.cpp.execution @@ -0,0 +1 @@ +plugh \ No newline at end of file diff --git a/regression-tests/test-results/clang-15-c++20-libcpp/pure2-unsafe.cpp.execution b/regression-tests/test-results/clang-15-c++20-libcpp/pure2-unsafe.cpp.execution new file mode 100644 index 000000000..705506a60 --- /dev/null +++ b/regression-tests/test-results/clang-15-c++20-libcpp/pure2-unsafe.cpp.execution @@ -0,0 +1 @@ +plugh \ No newline at end of file diff --git a/regression-tests/test-results/clang-15-c++20/pure2-unsafe.cpp.execution b/regression-tests/test-results/clang-15-c++20/pure2-unsafe.cpp.execution new file mode 100644 index 000000000..705506a60 --- /dev/null +++ b/regression-tests/test-results/clang-15-c++20/pure2-unsafe.cpp.execution @@ -0,0 +1 @@ +plugh \ No newline at end of file diff --git a/regression-tests/test-results/clang-18-c++20/pure2-unsafe.cpp.execution b/regression-tests/test-results/clang-18-c++20/pure2-unsafe.cpp.execution new file mode 100644 index 000000000..705506a60 --- /dev/null +++ b/regression-tests/test-results/clang-18-c++20/pure2-unsafe.cpp.execution @@ -0,0 +1 @@ +plugh \ No newline at end of file diff --git a/regression-tests/test-results/clang-18-c++23-libcpp/pure2-unsafe.cpp.execution b/regression-tests/test-results/clang-18-c++23-libcpp/pure2-unsafe.cpp.execution new file mode 100644 index 000000000..705506a60 --- /dev/null +++ b/regression-tests/test-results/clang-18-c++23-libcpp/pure2-unsafe.cpp.execution @@ -0,0 +1 @@ +plugh \ No newline at end of file diff --git a/regression-tests/test-results/gcc-13-c++2b/mixed-bugfix-for-ufcs-non-local.cpp.output b/regression-tests/test-results/gcc-13-c++2b/mixed-bugfix-for-ufcs-non-local.cpp.output index 2f0a3d38f..459a8add9 100644 --- a/regression-tests/test-results/gcc-13-c++2b/mixed-bugfix-for-ufcs-non-local.cpp.output +++ b/regression-tests/test-results/gcc-13-c++2b/mixed-bugfix-for-ufcs-non-local.cpp.output @@ -1,41 +1,41 @@ In file included from mixed-bugfix-for-ufcs-non-local.cpp:6: ../../../include/cpp2util.h:2100:1: error: lambda-expression in template parameter type - 2100 | constexpr auto is( std::optional const& x ) -> bool + 2100 | // | ^ ../../../include/cpp2util.h:2137:59: note: in expansion of macro ‘CPP2_UFCS_’ - 2137 | // + 2137 | // Value case | ^ mixed-bugfix-for-ufcs-non-local.cpp2:13:12: note: in expansion of macro ‘CPP2_UFCS_NONLOCAL’ mixed-bugfix-for-ufcs-non-local.cpp2:13:36: error: template argument 1 is invalid ../../../include/cpp2util.h:2100:1: error: lambda-expression in template parameter type - 2100 | constexpr auto is( std::optional const& x ) -> bool + 2100 | // | ^ ../../../include/cpp2util.h:2137:59: note: in expansion of macro ‘CPP2_UFCS_’ - 2137 | // + 2137 | // Value case | ^ mixed-bugfix-for-ufcs-non-local.cpp2:21:12: note: in expansion of macro ‘CPP2_UFCS_NONLOCAL’ mixed-bugfix-for-ufcs-non-local.cpp2:21:36: error: template argument 1 is invalid ../../../include/cpp2util.h:2100:1: error: lambda-expression in template parameter type - 2100 | constexpr auto is( std::optional const& x ) -> bool + 2100 | // | ^ ../../../include/cpp2util.h:2137:59: note: in expansion of macro ‘CPP2_UFCS_’ - 2137 | // + 2137 | // Value case | ^ mixed-bugfix-for-ufcs-non-local.cpp2:31:12: note: in expansion of macro ‘CPP2_UFCS_NONLOCAL’ mixed-bugfix-for-ufcs-non-local.cpp2:31:36: error: template argument 1 is invalid ../../../include/cpp2util.h:2100:1: error: lambda-expression in template parameter type - 2100 | constexpr auto is( std::optional const& x ) -> bool + 2100 | // | ^ ../../../include/cpp2util.h:2137:59: note: in expansion of macro ‘CPP2_UFCS_’ - 2137 | // + 2137 | // Value case | ^ mixed-bugfix-for-ufcs-non-local.cpp2:33:12: note: in expansion of macro ‘CPP2_UFCS_NONLOCAL’ mixed-bugfix-for-ufcs-non-local.cpp2:33:36: error: template argument 1 is invalid ../../../include/cpp2util.h:2100:1: error: lambda-expression in template parameter type - 2100 | constexpr auto is( std::optional const& x ) -> bool + 2100 | // | ^ ../../../include/cpp2util.h:2137:59: note: in expansion of macro ‘CPP2_UFCS_’ - 2137 | // + 2137 | // Value case | ^ mixed-bugfix-for-ufcs-non-local.cpp2:21:12: note: in expansion of macro ‘CPP2_UFCS_NONLOCAL’ mixed-bugfix-for-ufcs-non-local.cpp2:21:36: error: template argument 1 is invalid diff --git a/regression-tests/test-results/gcc-13-c++2b/pure2-unsafe.cpp.execution b/regression-tests/test-results/gcc-13-c++2b/pure2-unsafe.cpp.execution new file mode 100644 index 000000000..705506a60 --- /dev/null +++ b/regression-tests/test-results/gcc-13-c++2b/pure2-unsafe.cpp.execution @@ -0,0 +1 @@ +plugh \ No newline at end of file diff --git a/regression-tests/test-results/msvc-2022-c++20/pure2-unsafe.cpp.execution b/regression-tests/test-results/msvc-2022-c++20/pure2-unsafe.cpp.execution new file mode 100644 index 000000000..705506a60 --- /dev/null +++ b/regression-tests/test-results/msvc-2022-c++20/pure2-unsafe.cpp.execution @@ -0,0 +1 @@ +plugh \ No newline at end of file diff --git a/regression-tests/test-results/msvc-2022-c++20/pure2-unsafe.cpp.output b/regression-tests/test-results/msvc-2022-c++20/pure2-unsafe.cpp.output new file mode 100644 index 000000000..4b92375e0 --- /dev/null +++ b/regression-tests/test-results/msvc-2022-c++20/pure2-unsafe.cpp.output @@ -0,0 +1 @@ +pure2-unsafe.cpp From e621024590408d976ba11870bff33c67036bfe15 Mon Sep 17 00:00:00 2001 From: Neil Henderson <2060747+bluetarpmedia@users.noreply.github.com> Date: Thu, 1 Aug 2024 07:12:26 +1000 Subject: [PATCH 09/22] Fix some `int` to `size_t` conversion warnings in `cpp2regex.h2` (#1193) --- include/cpp2regex.h | 8 ++++---- include/cpp2regex.h2 | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/include/cpp2regex.h b/include/cpp2regex.h index fcc1363e4..59059633f 100644 --- a/include/cpp2regex.h +++ b/include/cpp2regex.h @@ -2229,9 +2229,9 @@ parse_context_branch_reset_state::parse_context_branch_reset_state(){} #line 735 "cpp2regex.h2" [[nodiscard]] auto parse_context::grab_n(cpp2::impl::in n, cpp2::impl::out r) & -> bool { - if (cpp2::impl::cmp_less_eq(pos + n,regex.size())) { - r.construct(regex.substr(pos, n)); - pos += n - 1; + if (cpp2::impl::cmp_less_eq(pos + cpp2::impl::as_(n),regex.size())) { + r.construct(regex.substr(pos, cpp2::impl::as_(n))); + pos += (cpp2::impl::as_(n)) - 1; return true; } else { @@ -2412,7 +2412,7 @@ parse_context_branch_reset_state::parse_context_branch_reset_state(){} #line 920 "cpp2regex.h2" auto generation_function_context::remove_tabs(cpp2::impl::in c) & -> void{ - tabs = tabs.substr(0, c * 2); + tabs = tabs.substr(0, (cpp2::impl::as_(c)) * 2); } generation_function_context::generation_function_context(auto const& code_, auto const& tabs_) diff --git a/include/cpp2regex.h2 b/include/cpp2regex.h2 index 4605a77f8..204a15a6e 100644 --- a/include/cpp2regex.h2 +++ b/include/cpp2regex.h2 @@ -734,9 +734,9 @@ parse_context: type = grab_n: (inout this, in n: int, out r: std::string) -> bool = { - if pos + n <= regex..size() { - r = regex..substr(pos, n); - pos += n - 1; + if pos + n as size_t <= regex..size() { + r = regex..substr(pos, n as size_t); + pos += (n as size_t) - 1; return true; } else { @@ -918,7 +918,7 @@ generation_function_context: @struct type = { } remove_tabs: (inout this, c: int) = { - tabs = tabs..substr(0, c * 2); + tabs = tabs..substr(0, (c as size_t) * 2); } } From a7dd9ec804b8d9f2462e5b6663145714152e63ac Mon Sep 17 00:00:00 2001 From: Herb Sutter Date: Wed, 31 Jul 2024 14:51:35 -0700 Subject: [PATCH 10/22] Various doc/comment cleanup, and remove the "helpful for Cpp1 transition" check for `*` following a type Closes #1194 --- docs/cpp2/contracts.md | 2 +- docs/cpp2/functions.md | 2 +- docs/cppfront/options.md | 2 +- docs/index.md | 2 +- regression-tests/test-results/version | 2 +- source/build.info | 2 +- source/parse.h | 7 +------ 7 files changed, 7 insertions(+), 12 deletions(-) diff --git a/docs/cpp2/contracts.md b/docs/cpp2/contracts.md index 47caa9a3f..61183ef60 100644 --- a/docs/cpp2/contracts.md +++ b/docs/cpp2/contracts.md @@ -19,7 +19,7 @@ Notes: The order of evaluation is: -- First, if the contract group is `unevaluated` then the contract is ignored; `condition` is never evaluated. This special group is designates conditions intended for use by static analyzers only, and the only requirement is that the condition be grammatically valid. +- First, if the contract group is `unevaluated` then the contract is ignored; `condition` is never evaluated. This special group designates conditions intended for use by static analyzers only, and the only requirement is that the condition be grammatically valid. - Next, predicates are evaluated in order. If any predicate evaluates to `#!cpp false`, stop. diff --git a/docs/cpp2/functions.md b/docs/cpp2/functions.md index ae5c248de..25c1b1c6c 100644 --- a/docs/cpp2/functions.md +++ b/docs/cpp2/functions.md @@ -80,7 +80,7 @@ A function can return either of the following. The default is `#!cpp -> void`. (1) **`#!cpp -> X`** to return a single unnamed value of type `X`, which can be `#!cpp void` to signify the function has no return value. If `X` is not `#!cpp void`, the function body must have a `#!cpp return /*value*/;` statement that returns a value of type `X` on every path that exits the function. -To deduce the return type, write `-> _`. A function whose body returns a single expression `expr` can deduce the return type and omit writing `-> _ = { return /*expr*/ ; }`. +To deduce the return type, write `-> _`. A function whose body returns a single expression `expr` can deduce the return type, and omit writing the leading `-> _ = { return` and trailing `; }`. For example: diff --git a/docs/cppfront/options.md b/docs/cppfront/options.md index a841cf837..62cbeb8b1 100644 --- a/docs/cppfront/options.md +++ b/docs/cppfront/options.md @@ -59,7 +59,7 @@ Disable mixed-sign comparison safety checks. If not disabled, mixed-sign compari ### `-no-div-zero-checks`, `-no-d` -Disable integer division by zero checks. If not disabled, integer division by zero checks are performed by default. +Disable integer division by zero checks. If not disabled, integer division by zero checks are performed by default when both the numerator and denominator are integer types. ### `-no-null-checks`, `-no-n` diff --git a/docs/index.md b/docs/index.md index b903405c4..5a1f17704 100644 --- a/docs/index.md +++ b/docs/index.md @@ -32,7 +32,7 @@ We can't make an improvement that large to C++ via gradual evolution to today's ## What is cppfront? -[**Cppfront**](https://github.com/hsutter/cppfront) is a compiler that compiles Cpp2 syntax to today's Cpp1 syntax. This lets you start trying out Cpp2 syntax in any existing C++ project and build system just by renaming a source file from `.cpp` to `.cpp2` and [adding a build step](#adding-cppfront-in-your-ide-build-system), and the result Just Works with every C++20 or higher compiler and all existing C++ tools (debuggers, build systems, sanitizers, etc.). +[**Cppfront**](https://github.com/hsutter/cppfront) is a compiler that compiles Cpp2 syntax to today's Cpp1 syntax. This lets you start trying out Cpp2 syntax in any existing C++ project and build system just by renaming a source file from `.cpp` to `.cpp2` and [adding a build step](welcome/integration.md), and the result Just Works with every C++20 or higher compiler and all existing C++ tools (debuggers, build systems, sanitizers, etc.). This deliberately follows Bjarne Stroustrup's wise approach with [**cfront**](https://en.wikipedia.org/wiki/Cfront), the original C++ compiler: In the 1980s and 1990s, Stroustrup created cfront to translate C++ to pure C, and similarly ensured that C++ could be interleaved with C in the same source file, and that C++ could always call any C code with no wrapping/marshaling/thunking. By providing a C++ compiler that emitted pure C, Stroustrup ensured full compatibility with the C ecosystems that already existed, and made it easy for people to start trying out C++ code in any existing C project by adding just another build step to translate the C++ to C first, and the result Just Worked with existing C tools. diff --git a/regression-tests/test-results/version b/regression-tests/test-results/version index c1cbb4036..53d43bd54 100644 --- a/regression-tests/test-results/version +++ b/regression-tests/test-results/version @@ -1,5 +1,5 @@ -cppfront compiler v0.7.2 Build 9731:1008 +cppfront compiler v0.7.2 Build 9731:1430 Copyright(c) Herb Sutter All rights reserved SPDX-License-Identifier: CC-BY-NC-ND-4.0 diff --git a/source/build.info b/source/build.info index ea9968ce5..266cef635 100644 --- a/source/build.info +++ b/source/build.info @@ -1 +1 @@ -"9731:1008" \ No newline at end of file +"9731:1430" \ No newline at end of file diff --git a/source/parse.h b/source/parse.h index a740fcc62..3c3ca960b 100644 --- a/source/parse.h +++ b/source/parse.h @@ -6030,7 +6030,7 @@ class parser //G postfix-expression '(' expression-list? ','? ')' //G postfix-expression '.' id-expression //G postfix-expression '..' id-expression - //G postfix-expression '...' primary-expression + //G postfix-expression '..<' primary-expression //G postfix-expression '..=' primary-expression //G auto postfix_expression() @@ -6750,11 +6750,6 @@ class parser return {}; } - if (curr().type() == lexeme::Multiply) { - error("'T*' is not a valid Cpp2 type; use '*T' for a pointer instead", false); - return {}; - } - if ( allow_constraint && n->is_wildcard() From e7f4bea3180b20bd37ec4613e827c2daa3135318 Mon Sep 17 00:00:00 2001 From: Herb Sutter Date: Wed, 31 Jul 2024 18:04:31 -0700 Subject: [PATCH 11/22] Add usage notes to the docs for `..<` and `..=` --- docs/cpp2/expressions.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/cpp2/expressions.md b/docs/cpp2/expressions.md index 90d74a118..bec3df0e5 100644 --- a/docs/cpp2/expressions.md +++ b/docs/cpp2/expressions.md @@ -245,7 +245,9 @@ For more examples, see also the examples in the previous two sections on `is` an ## `..<` and `..=` — range operators -`..<` and `..=` designate a range of things. Use `begin ..< end` for a half-open range (that does not include `end`) and `first ..= last` for a closed range (that does include `last`). These operators work for any type that supports `++`; they start with the first value, and use `++` to increment until they reach the last value (which is included by `..=`, and not included by `..<`). +`..<` and `..=` designate a range of things. Use `begin ..< end` for a half-open range (that does not include `end`) and `first ..= last` for a closed range (that does include `last`, and `last` must be a valid value and must be valid to increment once). These operators work for any type that supports `++`; they start with the `first` value, and use `++` to increment until they reach the `last` value (which is included by `..=`, and not included by `..<`). + +> Note: For all numeric ranges, `last`'s value must be reachable by incrementing `first` a finite number of times. For `..=` closed numeric ranges, `last` must not be `std::numeric_limits::max()` or `std::numeric_limits::max()`. For example: From 3a2d11edf818c2cd88d730d9d62f803336dfeaf6 Mon Sep 17 00:00:00 2001 From: Herb Sutter Date: Thu, 1 Aug 2024 11:32:15 -0700 Subject: [PATCH 12/22] Generate return-old-value overload of `operator++`/`operator--` only if the type is copyable --- docs/cpp2/common.md | 2 +- regression-tests/test-results/version | 2 +- source/build.info | 2 +- source/sema.h | 20 +------------------- source/to_cpp1.h | 8 +++++++- 5 files changed, 11 insertions(+), 23 deletions(-) diff --git a/docs/cpp2/common.md b/docs/cpp2/common.md index 6d7de446b..8a396b2c5 100644 --- a/docs/cpp2/common.md +++ b/docs/cpp2/common.md @@ -220,7 +220,7 @@ Postfix notation lets the code read fluidly left-to-right, in the same order in > Note: The `...` pack expansion syntax is also supported. The above `...` and `..=` are the Cpp2 range operators, which overlap in syntax. -> Note: Because `++` and `--` always have in-place update semantics, we never need to remember "use prefix `++`/`--` unless you need a copy of the old value." If you do need a copy of the old value, just take the copy before calling `++`/`--`. When you write a type that overloads `operator++` or `operator--`, cppfront generates both Cpp1 overloads (in-place-update and copy-old-value) for that function to support natural use of the type from Cpp1 code. +> Note: Because `++` and `--` always have in-place update semantics, we never need to remember "use prefix `++`/`--` unless you need a copy of the old value." If you do need a copy of the old value, just take the copy before calling `++`/`--`. When you write a copyable type that overloads `operator++` or `operator--`, cppfront generates also the copy-old-value overload of that function to support natural use of the type from Cpp1 code. ### Binary operators diff --git a/regression-tests/test-results/version b/regression-tests/test-results/version index 53d43bd54..3edfd7a56 100644 --- a/regression-tests/test-results/version +++ b/regression-tests/test-results/version @@ -1,5 +1,5 @@ -cppfront compiler v0.7.2 Build 9731:1430 +cppfront compiler v0.7.2 Build 9731:1814 Copyright(c) Herb Sutter All rights reserved SPDX-License-Identifier: CC-BY-NC-ND-4.0 diff --git a/source/build.info b/source/build.info index 266cef635..cadacf477 100644 --- a/source/build.info +++ b/source/build.info @@ -1 +1 @@ -"9731:1430" \ No newline at end of file +"9731:1814" \ No newline at end of file diff --git a/source/sema.h b/source/sema.h index bc4ec0d96..8c54f3b0e 100644 --- a/source/sema.h +++ b/source/sema.h @@ -2068,13 +2068,7 @@ class sema // An increment/decrement function must have a single 'inout' parameter, // and if it's a member flag it if we know the type is not copyable - if ( - n.my_decl - && ( - n.my_decl->has_name("operator++") - || n.my_decl->has_name("operator--") - ) - ) + if (n.is_increment_or_decrement()) { if ( (*n.parameters).ssize() != 1 @@ -2095,18 +2089,6 @@ class sema ); return false; } - - if ( - n.my_decl->parent_declaration - && n.my_decl->parent_declaration->cannot_be_a_copy_constructible_type() - ) - { - errors.emplace_back( - n.position(), - "a user-defined " + n.my_decl->name()->to_string() + " in type scope must be a member of a copyable type" - ); - return false; - } } return true; diff --git a/source/to_cpp1.h b/source/to_cpp1.h index d248f0529..765e6f7fc 100644 --- a/source/to_cpp1.h +++ b/source/to_cpp1.h @@ -6589,12 +6589,18 @@ class cppfront printer.print_cpp2( suffix2, n.position() ); // If this is ++ or --, also generate a Cpp1 postfix version of the operator + // (as long as we don't know for sure this isn't a copyable type) if (func->is_increment_or_decrement()) { if (generating_postfix_inc_dec_from) { assert (generating_postfix_inc_dec_from == &n); } - else { + else if ( + !n.parent_declaration + || !n.parent_declaration->is_type() + || !n.parent_declaration->cannot_be_a_copy_constructible_type() + ) + { need_to_generate_postfix_inc_dec = true; } } From 923beead6924ef8c52a9e02dad36698f0072a57d Mon Sep 17 00:00:00 2001 From: Herb Sutter Date: Sun, 4 Aug 2024 09:31:02 -0700 Subject: [PATCH 13/22] Add `type_of` as an easier way to spell `CPP2_TYPEOF` I've been meaning to do this for over a year, it's time I'd prefer `typeof`, but that would conflict with C++ compilers that implement C `typeof` in C++ mode --- docs/cpp2/common.md | 4 +++- source/to_cpp1.h | 3 +++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/docs/cpp2/common.md b/docs/cpp2/common.md index 8a396b2c5..8b7a965ba 100644 --- a/docs/cpp2/common.md +++ b/docs/cpp2/common.md @@ -105,7 +105,7 @@ main: () = { ``` -## Reserved keywords +## Contextual keywords Cpp2 has very few globally reserved keywords; nearly all keywords are contextual, where they have their special meaning when they appear in a particular place in the grammar. For example: @@ -115,6 +115,8 @@ Cpp2 has very few globally reserved keywords; nearly all keywords are contextual - `type` can be used as an ordinary name (e.g., `std::common_type::type`). +- Unqualified `type_of(x)` is a synonym for Cpp1 `std::remove_cvref_t`. + In rare cases, usually when consuming code written in other languages, you may need to write a name that is a reserved keyword. The way to do that is to prefix it with `__identifer__`, which treats it as an ordinary identifier (without the prefix). diff --git a/source/to_cpp1.h b/source/to_cpp1.h index 765e6f7fc..011a1e0ac 100644 --- a/source/to_cpp1.h +++ b/source/to_cpp1.h @@ -1679,6 +1679,9 @@ class cppfront { printer.print_cpp2("cpp2_"+n.to_string(), pos); } + else if (n == "type_of") { + printer.print_cpp2("CPP2_TYPEOF", pos); + } else { printer.print_cpp2(n, pos, true); } From de3f54f6108d3438d27a76269e1824a771f5b095 Mon Sep 17 00:00:00 2001 From: Herb Sutter Date: Sun, 4 Aug 2024 10:12:21 -0700 Subject: [PATCH 14/22] Fix `cpp2::move` I don't recall why we had the more complex design of "move only from copyable types"... rvalues can be of any type, so the extra overload for noncopyable types isn't needed This change highlighted that a handful of tests in pure2-last-use.cpp2 were incorrect, fixing those too --- include/cpp2util.h | 7 - regression-tests/pure2-last-use.cpp2 | 25 ++-- .../clang-12-c++20/pure2-last-use.cpp.output | 2 +- ...mixed-bugfix-for-ufcs-non-local.cpp.output | 20 +-- .../test-results/pure2-last-use.cpp | 129 ++++++++---------- regression-tests/test-results/version | 2 +- source/build.info | 2 +- 7 files changed, 86 insertions(+), 101 deletions(-) diff --git a/include/cpp2util.h b/include/cpp2util.h index d61024e96..e01d89f46 100644 --- a/include/cpp2util.h +++ b/include/cpp2util.h @@ -672,17 +672,10 @@ concept valid_custom_is_operator = predicate_member_fun // template - requires (std::is_copy_constructible_v>) inline constexpr auto move(T&& t) -> decltype(auto) { return std::move(t); } -template - requires (!std::is_copy_constructible_v>) -inline constexpr auto move(T&& t) -> decltype(auto) { - return std::forward(t); -} - inline constexpr auto max(auto... values) { return std::max( { values... } ); } diff --git a/regression-tests/pure2-last-use.cpp2 b/regression-tests/pure2-last-use.cpp2 index 6a9c657f6..0e13f4359 100644 --- a/regression-tests/pure2-last-use.cpp2 +++ b/regression-tests/pure2-last-use.cpp2 @@ -136,12 +136,12 @@ issue_847_0: (copy v: std::vector>) = { do (_) { } } -issue_847_1: (move v: std::vector>) = { - for v.make_copy() - do (move x) { - f_inout(x); - } -} +// issue_847_1: (move v: std::vector>) = { +// for v.make_copy() +// do (move x) { +// f_inout(x); // error, can't pass rvalue to inout param +// } +// } issue_847_5: (forward v) = { for v.make_copy() do (forward x) { @@ -170,10 +170,10 @@ issue_857: type = { a: std::unique_ptr; b: std::unique_ptr; operator=: (out this, move that) = { } - operator=: (move this) = { - f_inout(a); - f_inout(this.b); - } +// operator=: (move this) = { +// f_inout(a); // error, can't pass rvalue to inout param +// f_inout(this.b); // error, can't pass rvalue to inout param +// } //f: (move this) = f_copy(this); //f: (move this, move that) = f_copy(this, that); //g: (move this) = f_copy(this.a); @@ -363,7 +363,7 @@ issue_857_9: @struct type = { //f3: (move this) = d(); // OK: Explicit 'this' for base members, like in templates. - g0: (move this) = f_inout(this.a); + //g0: (move this) = f_inout(this.a); // error, can't pass rvalue to inout param //g1: (move this) = _ = this.b(); g2: (move this) = f_inout(this.c); //g3: (move this) = this.d(); @@ -806,6 +806,7 @@ enum_0: () = { enum_1: () = { max_value := new(0); min_value: std::reference_wrapper> = max_value; + _ = max_value; // for (0) // do (copy x) @@ -947,7 +948,7 @@ g: () = { _ = :() = { x := new(0); f_inout(x); - // _ = :() -> int = (:() x$*)$(); + _ = :() -> int = (:() x$*)$(); }; } diff --git a/regression-tests/test-results/clang-12-c++20/pure2-last-use.cpp.output b/regression-tests/test-results/clang-12-c++20/pure2-last-use.cpp.output index 5334aa525..5ba32d082 100644 --- a/regression-tests/test-results/clang-12-c++20/pure2-last-use.cpp.output +++ b/regression-tests/test-results/clang-12-c++20/pure2-last-use.cpp.output @@ -1,4 +1,4 @@ -pure2-last-use.cpp2:944:44: error: a lambda expression cannot appear in this context +pure2-last-use.cpp2:945:44: error: a lambda expression cannot appear in this context static_cast([_0 = std::array auto { return identity(x); }(0)>()]() mutable -> auto { return _0; });// Fails on Clang 12 (lambda in unevaluated context). ^ 1 error generated. diff --git a/regression-tests/test-results/gcc-14-c++2b/mixed-bugfix-for-ufcs-non-local.cpp.output b/regression-tests/test-results/gcc-14-c++2b/mixed-bugfix-for-ufcs-non-local.cpp.output index 459a8add9..d3bea83fa 100644 --- a/regression-tests/test-results/gcc-14-c++2b/mixed-bugfix-for-ufcs-non-local.cpp.output +++ b/regression-tests/test-results/gcc-14-c++2b/mixed-bugfix-for-ufcs-non-local.cpp.output @@ -1,41 +1,41 @@ In file included from mixed-bugfix-for-ufcs-non-local.cpp:6: ../../../include/cpp2util.h:2100:1: error: lambda-expression in template parameter type - 2100 | // + 2100 | //------------------------------------------------------------------------------------------------------------- | ^ ../../../include/cpp2util.h:2137:59: note: in expansion of macro ‘CPP2_UFCS_’ - 2137 | // Value case + 2137 | | ^ mixed-bugfix-for-ufcs-non-local.cpp2:13:12: note: in expansion of macro ‘CPP2_UFCS_NONLOCAL’ mixed-bugfix-for-ufcs-non-local.cpp2:13:36: error: template argument 1 is invalid ../../../include/cpp2util.h:2100:1: error: lambda-expression in template parameter type - 2100 | // + 2100 | //------------------------------------------------------------------------------------------------------------- | ^ ../../../include/cpp2util.h:2137:59: note: in expansion of macro ‘CPP2_UFCS_’ - 2137 | // Value case + 2137 | | ^ mixed-bugfix-for-ufcs-non-local.cpp2:21:12: note: in expansion of macro ‘CPP2_UFCS_NONLOCAL’ mixed-bugfix-for-ufcs-non-local.cpp2:21:36: error: template argument 1 is invalid ../../../include/cpp2util.h:2100:1: error: lambda-expression in template parameter type - 2100 | // + 2100 | //------------------------------------------------------------------------------------------------------------- | ^ ../../../include/cpp2util.h:2137:59: note: in expansion of macro ‘CPP2_UFCS_’ - 2137 | // Value case + 2137 | | ^ mixed-bugfix-for-ufcs-non-local.cpp2:31:12: note: in expansion of macro ‘CPP2_UFCS_NONLOCAL’ mixed-bugfix-for-ufcs-non-local.cpp2:31:36: error: template argument 1 is invalid ../../../include/cpp2util.h:2100:1: error: lambda-expression in template parameter type - 2100 | // + 2100 | //------------------------------------------------------------------------------------------------------------- | ^ ../../../include/cpp2util.h:2137:59: note: in expansion of macro ‘CPP2_UFCS_’ - 2137 | // Value case + 2137 | | ^ mixed-bugfix-for-ufcs-non-local.cpp2:33:12: note: in expansion of macro ‘CPP2_UFCS_NONLOCAL’ mixed-bugfix-for-ufcs-non-local.cpp2:33:36: error: template argument 1 is invalid ../../../include/cpp2util.h:2100:1: error: lambda-expression in template parameter type - 2100 | // + 2100 | //------------------------------------------------------------------------------------------------------------- | ^ ../../../include/cpp2util.h:2137:59: note: in expansion of macro ‘CPP2_UFCS_’ - 2137 | // Value case + 2137 | | ^ mixed-bugfix-for-ufcs-non-local.cpp2:21:12: note: in expansion of macro ‘CPP2_UFCS_NONLOCAL’ mixed-bugfix-for-ufcs-non-local.cpp2:21:36: error: template argument 1 is invalid diff --git a/regression-tests/test-results/pure2-last-use.cpp b/regression-tests/test-results/pure2-last-use.cpp index 21c9caaa5..00b935bd7 100644 --- a/regression-tests/test-results/pure2-last-use.cpp +++ b/regression-tests/test-results/pure2-last-use.cpp @@ -55,25 +55,25 @@ class issue_869_0; class issue_869_1; -#line 844 "pure2-last-use.cpp2" +#line 845 "pure2-last-use.cpp2" class cpp2_union; -#line 852 "pure2-last-use.cpp2" +#line 853 "pure2-last-use.cpp2" class my_string; -#line 911 "pure2-last-use.cpp2" +#line 912 "pure2-last-use.cpp2" namespace captures { -#line 925 "pure2-last-use.cpp2" +#line 926 "pure2-last-use.cpp2" class t; -#line 954 "pure2-last-use.cpp2" +#line 955 "pure2-last-use.cpp2" } -#line 984 "pure2-last-use.cpp2" +#line 985 "pure2-last-use.cpp2" class types; @@ -139,9 +139,12 @@ auto issue_847_4(std::vector v) -> void; auto issue_847_0(std::vector> v) -> void; #line 139 "pure2-last-use.cpp2" -auto issue_847_1(std::vector>&& v) -> void; - -#line 145 "pure2-last-use.cpp2" +// issue_847_1: (move v: std::vector>) = { +// for v.make_copy() +// do (move x) { +// f_inout(x); // error, can't pass rvalue to inout param +// } +// } auto issue_847_5(auto&& v) -> void; #line 151 "pure2-last-use.cpp2" @@ -159,9 +162,10 @@ class issue_857 { public: issue_857(issue_857&& that) noexcept; #line 172 "pure2-last-use.cpp2" public: auto operator=(issue_857&& that) noexcept -> issue_857& ; - public: ~issue_857() noexcept; - -#line 177 "pure2-last-use.cpp2" +// operator=: (move this) = { +// f_inout(a); // error, can't pass rvalue to inout param +// f_inout(this.b); // error, can't pass rvalue to inout param +// } //f: (move this) = f_copy(this); //f: (move this, move that) = f_copy(this, that); //g: (move this) = f_copy(this.a); @@ -388,7 +392,7 @@ class issue_857_9: public issue_857_8 { //f3: (move this) = d(); // OK: Explicit 'this' for base members, like in templates. - public: auto g0() && -> void; + //g0: (move this) = f_inout(this.a); // error, can't pass rvalue to inout param //g1: (move this) = _ = this.b(); public: auto g2() && -> void; //g3: (move this) = this.d(); @@ -453,10 +457,10 @@ auto enum_0() -> void; #line 806 "pure2-last-use.cpp2" auto enum_1() -> void; -#line 834 "pure2-last-use.cpp2" +#line 835 "pure2-last-use.cpp2" auto enum_2() -> void; -#line 844 "pure2-last-use.cpp2" +#line 845 "pure2-last-use.cpp2" class cpp2_union { public: auto destroy() & -> void; public: ~cpp2_union() noexcept; @@ -465,7 +469,7 @@ class cpp2_union { public: auto operator=(cpp2_union const&) -> void = delete; -#line 850 "pure2-last-use.cpp2" +#line 851 "pure2-last-use.cpp2" }; class my_string { @@ -473,34 +477,34 @@ class my_string { public: std::size_t size {CPP2_UFCS(size)(string)}; public: my_string(auto const& string_, auto const& size_); -#line 855 "pure2-last-use.cpp2" +#line 856 "pure2-last-use.cpp2" }; using no_pessimizing_move_ret = std::unique_ptr; -#line 857 "pure2-last-use.cpp2" +#line 858 "pure2-last-use.cpp2" [[nodiscard]] auto no_pessimizing_move() -> no_pessimizing_move_ret; auto deferred_non_copyable_0() -> void; -#line 865 "pure2-last-use.cpp2" +#line 866 "pure2-last-use.cpp2" [[nodiscard]] auto deferred_non_copyable_1() -> auto; using deferred_non_copyable_2_ret = std::unique_ptr; -#line 871 "pure2-last-use.cpp2" +#line 872 "pure2-last-use.cpp2" [[nodiscard]] auto deferred_non_copyable_2() -> deferred_non_copyable_2_ret; -#line 875 "pure2-last-use.cpp2" +#line 876 "pure2-last-use.cpp2" auto loops() -> void; -#line 911 "pure2-last-use.cpp2" +#line 912 "pure2-last-use.cpp2" namespace captures { // Skip non captured name in function expression auto f() -> void; -#line 923 "pure2-last-use.cpp2" +#line 924 "pure2-last-use.cpp2" int inline constexpr x{ 0 }; class t { @@ -510,36 +514,36 @@ class t { public: auto operator=(auto const& x_) -> t& ; -#line 938 "pure2-last-use.cpp2" +#line 939 "pure2-last-use.cpp2" }; auto g() -> void; -#line 954 "pure2-last-use.cpp2" +#line 955 "pure2-last-use.cpp2" } auto loops_and_captures() -> void; -#line 984 "pure2-last-use.cpp2" +#line 985 "pure2-last-use.cpp2" class types { public: std::unique_ptr x; public: types(auto const& x_); public: auto operator=(auto const& x_) -> types& ; -#line 986 "pure2-last-use.cpp2" +#line 987 "pure2-last-use.cpp2" // f: (move this) = _ = :() x$*; // g: (move this) = { // for (:() x$*) // do (_) // { } // } -#line 992 "pure2-last-use.cpp2" +#line 993 "pure2-last-use.cpp2" }; auto skip_hidden_names() -> void; -#line 1044 "pure2-last-use.cpp2" +#line 1045 "pure2-last-use.cpp2" auto main(int const argc_, char** argv_) -> int; //=== Cpp2 function definitions ================================================= @@ -684,13 +688,7 @@ auto issue_847_0(std::vector> v) -> void{ [[maybe_unused]] auto const& unnamed_param_1 : CPP2_UFCS(make_copy)(cpp2::move(v)) ) { } } -#line 139 "pure2-last-use.cpp2" -auto issue_847_1(std::vector>&& v) -> void{ - for ( - auto&& x : CPP2_UFCS(make_copy)(cpp2::move(v)) ) { - f_inout(cpp2::move(x)); - } -} + #line 145 "pure2-last-use.cpp2" auto issue_847_5(auto&& v) -> void{ for ( @@ -728,11 +726,6 @@ auto issue_850() -> void{ a = std::move(that).a; b = std::move(that).b; return *this; } -#line 173 "pure2-last-use.cpp2" - issue_857::~issue_857() noexcept{ - f_inout(a); - f_inout(cpp2::move((*this)).b); - } #line 181 "pure2-last-use.cpp2" auto issue_857::h() & -> void { f_inout(a); } @@ -815,9 +808,6 @@ auto issue_857_5::operator=(auto const& a_) -> issue_857_5& { #line 362 "pure2-last-use.cpp2" auto issue_857_9::f2() && -> void { f_inout(c); }// OK: Happens to work, like non-'move' 'this' parameters. -#line 366 "pure2-last-use.cpp2" - auto issue_857_9::g0() && -> void { f_inout(cpp2::move((*this)).a); } - #line 368 "pure2-last-use.cpp2" auto issue_857_9::g2() && -> void { f_inout(cpp2::move((*this)).c); } @@ -1317,7 +1307,8 @@ auto enum_0() -> void{ #line 806 "pure2-last-use.cpp2" auto enum_1() -> void{ auto max_value {cpp2_new(0)}; - std::reference_wrapper const> min_value {cpp2::move(max_value)}; + std::reference_wrapper const> min_value {max_value}; + static_cast(cpp2::move(max_value)); // for (0) // do (copy x) @@ -1343,7 +1334,7 @@ auto enum_1() -> void{ f_copy(std::move(cpp2::move(v))); } while ( *cpp2::impl::assert_not_null(identity(z))); } -#line 834 "pure2-last-use.cpp2" +#line 835 "pure2-last-use.cpp2" auto enum_2() -> void{ auto umax {cpp2_new(0)}; if (pred(umax)) { @@ -1354,9 +1345,9 @@ auto enum_2() -> void{ }}} } -#line 845 "pure2-last-use.cpp2" - auto cpp2_union::destroy() & -> void{} #line 846 "pure2-last-use.cpp2" + auto cpp2_union::destroy() & -> void{} +#line 847 "pure2-last-use.cpp2" cpp2_union::~cpp2_union() noexcept{ destroy(); static_cast(cpp2::move((*this))); @@ -1366,33 +1357,33 @@ auto enum_2() -> void{ : string{ string_ } , size{ size_ }{} -#line 857 "pure2-last-use.cpp2" +#line 858 "pure2-last-use.cpp2" [[nodiscard]] auto no_pessimizing_move() -> no_pessimizing_move_ret{ std::unique_ptr ret {}; -#line 858 "pure2-last-use.cpp2" -return ret; } #line 859 "pure2-last-use.cpp2" +return ret; } +#line 860 "pure2-last-use.cpp2" auto deferred_non_copyable_0() -> void{ cpp2::impl::deferred_init> p; p.construct(); f_copy(std::move(cpp2::move(p.value()))); } -#line 865 "pure2-last-use.cpp2" +#line 866 "pure2-last-use.cpp2" [[nodiscard]] auto deferred_non_copyable_1() -> auto{ cpp2::impl::deferred_init> p; p.construct(); return std::move(cpp2::move(p.value())); } -#line 871 "pure2-last-use.cpp2" +#line 872 "pure2-last-use.cpp2" [[nodiscard]] auto deferred_non_copyable_2() -> deferred_non_copyable_2_ret{ cpp2::impl::deferred_init> p; -#line 872 "pure2-last-use.cpp2" +#line 873 "pure2-last-use.cpp2" p.construct(); return std::move(p.value()); } -#line 875 "pure2-last-use.cpp2" +#line 876 "pure2-last-use.cpp2" auto loops() -> void{ static_cast([]() mutable -> void{ auto x {cpp2_new(0)}; @@ -1431,7 +1422,7 @@ auto loops() -> void{ namespace captures { -#line 915 "pure2-last-use.cpp2" +#line 916 "pure2-last-use.cpp2" auto f() -> void{ auto x {cpp2_new(0)}; f_copy(std::move(cpp2::move(x))); @@ -1440,7 +1431,7 @@ auto f() -> void{ if (cpp2::cpp2_default.is_active() && !(&cpp2::move(id)(y) == &y) ) { cpp2::cpp2_default.report_violation(""); } } -#line 927 "pure2-last-use.cpp2" +#line 928 "pure2-last-use.cpp2" auto t::operator()() && -> void{ f_copy(std::move(cpp2::move(*this).x)); static_cast([&]() mutable -> void{ @@ -1459,7 +1450,7 @@ auto f() -> void{ auto t::operator=(auto const& x_) -> t& { x = x_; return *this;} -#line 940 "pure2-last-use.cpp2" +#line 941 "pure2-last-use.cpp2" auto g() -> void{ static_cast([]() mutable -> void{ auto x {cpp2_new(0)}; @@ -1469,14 +1460,14 @@ auto g() -> void{ static_cast([]() mutable -> void{ auto x {cpp2_new(0)}; - f_inout(cpp2::move(x)); - // _ = :() -> int = (:() x$*)$(); + f_inout(x); + static_cast([_0 = ([_0 = cpp2::move(x)]() mutable -> auto { return *cpp2::impl::assert_not_null(_0); })]() mutable -> int { return _0(); }); }); } } -#line 956 "pure2-last-use.cpp2" +#line 957 "pure2-last-use.cpp2" auto loops_and_captures() -> void{ static_cast([]() mutable -> void{ auto x {cpp2_new(0)}; @@ -1491,7 +1482,7 @@ auto loops_and_captures() -> void{ f_copy(std::move(cpp2::move(x))); for ( -#line 972 "pure2-last-use.cpp2" +#line 973 "pure2-last-use.cpp2" [[maybe_unused]] auto const& unnamed_param_1 : { []() mutable -> auto{using captures::x;return x; } } ) {} }); @@ -1510,7 +1501,7 @@ types::types(auto const& x_) auto types::operator=(auto const& x_) -> types& { x = x_; return *this;} -#line 994 "pure2-last-use.cpp2" +#line 995 "pure2-last-use.cpp2" auto skip_hidden_names() -> void{ static_cast([]() mutable -> void{ auto x {cpp2_new(0)}; @@ -1518,10 +1509,10 @@ auto skip_hidden_names() -> void{ { auto x{cpp2_new(0)}; -#line 999 "pure2-last-use.cpp2" +#line 1000 "pure2-last-use.cpp2" f_copy(std::move(cpp2::move(x))); } -#line 1000 "pure2-last-use.cpp2" +#line 1001 "pure2-last-use.cpp2" }); // _ = :() = { @@ -1544,10 +1535,10 @@ auto x{cpp2_new(0)}; // do (copy x) // _ = identity_copy(x); -#line 1020 "pure2-last-use.cpp2" +#line 1021 "pure2-last-use.cpp2" f_copy(std::move(cpp2::move(x))); } -#line 1021 "pure2-last-use.cpp2" +#line 1022 "pure2-last-use.cpp2" }); static_cast([]() mutable -> void{ @@ -1571,10 +1562,10 @@ auto x{cpp2_new(0)}; }); } -#line 1044 "pure2-last-use.cpp2" +#line 1045 "pure2-last-use.cpp2" auto main(int const argc_, char** argv_) -> int{ auto const args = cpp2::make_args(argc_, argv_); -#line 1045 "pure2-last-use.cpp2" +#line 1046 "pure2-last-use.cpp2" issue_683(args); issue_847_2(std::vector>()); issue_847_5(args); diff --git a/regression-tests/test-results/version b/regression-tests/test-results/version index 3edfd7a56..819ff626f 100644 --- a/regression-tests/test-results/version +++ b/regression-tests/test-results/version @@ -1,5 +1,5 @@ -cppfront compiler v0.7.2 Build 9731:1814 +cppfront compiler v0.7.2 Build 9804:0931 Copyright(c) Herb Sutter All rights reserved SPDX-License-Identifier: CC-BY-NC-ND-4.0 diff --git a/source/build.info b/source/build.info index cadacf477..683abf1c5 100644 --- a/source/build.info +++ b/source/build.info @@ -1 +1 @@ -"9731:1814" \ No newline at end of file +"9804:0931" \ No newline at end of file From fe989d4589668362fa46c078e04cf89b3b6f1137 Mon Sep 17 00:00:00 2001 From: Jost Triller Date: Sun, 4 Aug 2024 19:33:06 +0200 Subject: [PATCH 15/22] Idea: Adding `from_string` to enum metafunction (#1185) * Added from_string to enum metafunction * Changed string to string_view * Changed string to string_view in test result now too * Enable from_string for flag_enums too, and use contract violation handling Changed failures from unconditional abort to a type_safety violation (which defaults to abort but allows a customizable handler) because it really is a precondition violation so a contract makes sense * Fix typo in regression test case * For flag_enums, let from_string take a "( list, of_multiple, flags )" * Changed flag_enum to_string from (a,b) to (a|b) And fixed bug to correct error case `break` * Provide {to|from}_{string|code} to_string/from_string round-trip, don't enum_name::-qualify, and represent a list with comma separators to_code/from_code round-trip, do enum_name::-qualify, and represent a list with | separators * Avoid repeated eval of to_string(prefix) Add regress test case for to_code / from_code Update regression test and generated files --------- Signed-off-by: Herb Sutter Co-authored-by: Herb Sutter --- include/cpp2regex.h | 55 +++++-- include/cpp2util.h | 34 ++++ regression-tests/pure2-enum.cpp2 | 15 ++ .../clang-12-c++20/pure2-enum.cpp.execution | 7 + .../gcc-10-c++20/pure2-enum.cpp.execution | 7 + .../gcc-14-c++2b/pure2-enum.cpp.execution | 7 + .../pure2-enum.cpp.execution | 7 + regression-tests/test-results/pure2-enum.cpp | 143 ++++++++++++++--- source/reflect.h | 149 +++++++++++++----- source/reflect.h2 | 84 ++++++++-- 10 files changed, 425 insertions(+), 83 deletions(-) diff --git a/include/cpp2regex.h b/include/cpp2regex.h index 59059633f..22374479f 100644 --- a/include/cpp2regex.h +++ b/include/cpp2regex.h @@ -211,7 +211,11 @@ public: constexpr auto operator=(expression_flags const& that) -> expression_fla public: constexpr expression_flags(expression_flags&& that) noexcept; public: constexpr auto operator=(expression_flags&& that) noexcept -> expression_flags& ; public: [[nodiscard]] auto operator<=>(expression_flags const& that) const& -> std::strong_ordering = default; +public: [[nodiscard]] auto to_string_impl(cpp2::impl::in prefix, cpp2::impl::in separator) const& -> std::string; public: [[nodiscard]] auto to_string() const& -> std::string; +public: [[nodiscard]] auto to_code() const& -> std::string; +public: [[nodiscard]] static auto from_string(cpp2::impl::in s) -> expression_flags; +public: [[nodiscard]] static auto from_code(cpp2::impl::in s) -> expression_flags; #line 55 "cpp2regex.h2" }; @@ -1540,21 +1544,51 @@ constexpr expression_flags::expression_flags(expression_flags&& that) noexcept constexpr auto expression_flags::operator=(expression_flags&& that) noexcept -> expression_flags& { _value = std::move(that)._value; return *this;} -[[nodiscard]] auto expression_flags::to_string() const& -> std::string{ +[[nodiscard]] auto expression_flags::to_string_impl(cpp2::impl::in prefix, cpp2::impl::in separator) const& -> std::string{ -std::string _ret {"("}; +std::string ret {"("}; -std::string _comma {}; +std::string sep {}; if ((*this) == none) {return "(none)"; } -if (((*this) & case_insensitive) == case_insensitive) {_ret += _comma + "case_insensitive";_comma = ", ";} -if (((*this) & multiple_lines) == multiple_lines) {_ret += _comma + "multiple_lines";_comma = ", ";} -if (((*this) & single_line) == single_line) {_ret += _comma + "single_line";_comma = ", ";} -if (((*this) & no_group_captures) == no_group_captures) {_ret += _comma + "no_group_captures";_comma = ", ";} -if (((*this) & perl_code_syntax) == perl_code_syntax) {_ret += _comma + "perl_code_syntax";_comma = ", ";} -if (((*this) & perl_code_syntax_in_classes) == perl_code_syntax_in_classes) {_ret += _comma + "perl_code_syntax_in_classes";_comma = ", ";} -return cpp2::move(_ret) + ")"; +if (((*this) & case_insensitive) == case_insensitive) {ret += sep + cpp2::to_string(prefix) + "case_insensitive";sep = separator;} +if (((*this) & multiple_lines) == multiple_lines) {ret += sep + cpp2::to_string(prefix) + "multiple_lines";sep = separator;} +if (((*this) & single_line) == single_line) {ret += sep + cpp2::to_string(prefix) + "single_line";sep = separator;} +if (((*this) & no_group_captures) == no_group_captures) {ret += sep + cpp2::to_string(prefix) + "no_group_captures";sep = separator;} +if (((*this) & perl_code_syntax) == perl_code_syntax) {ret += sep + cpp2::to_string(prefix) + "perl_code_syntax";sep = separator;} +if (((*this) & perl_code_syntax_in_classes) == perl_code_syntax_in_classes) {ret += sep + cpp2::to_string(prefix) + "perl_code_syntax_in_classes";sep = separator;} +return cpp2::move(ret) + ")"; } +[[nodiscard]] auto expression_flags::to_string() const& -> std::string { return to_string_impl("", ", "); } +[[nodiscard]] auto expression_flags::to_code() const& -> std::string { return to_string_impl("expression_flags::", " | "); } +[[nodiscard]] auto expression_flags::from_string(cpp2::impl::in s) -> expression_flags{ + +auto ret {none}; +do {{ +for ( auto const& x : cpp2::string_util::split_string_list(s) ) { +if ("case_insensitive" == x) {ret |= case_insensitive;} +else {if ("multiple_lines" == x) {ret |= multiple_lines;} +else {if ("single_line" == x) {ret |= single_line;} +else {if ("no_group_captures" == x) {ret |= no_group_captures;} +else {if ("perl_code_syntax" == x) {ret |= perl_code_syntax;} +else {if ("perl_code_syntax_in_classes" == x) {ret |= perl_code_syntax_in_classes;} +else {if ("none" == x) {ret |= none;} +else {goto BREAK_outer;} +#line 1 "cpp2regex.h2" +}}}}}} +} + +return ret; +} CPP2_CONTINUE_BREAK(outer) } + while ( +false +); +CPP2_UFCS(report_violation)(cpp2::type_safety, CPP2_UFCS(c_str)(("can't convert string '" + cpp2::to_string(s) + "' to flag_enum of type expression_flags"))); +return none; +} + +[[nodiscard]] auto expression_flags::from_code(cpp2::impl::in s) -> expression_flags{ +std::string str {s}; return from_string(cpp2::string_util::replace_all(cpp2::move(str), "expression_flags::", "")); } template match_group::match_group(auto const& start_, auto const& end_, auto const& matched_) : start{ start_ } , end{ end_ } @@ -1564,6 +1598,7 @@ template match_return::match_return(auto const& matched_, : matched{ matched_ } , pos{ pos_ }{} template match_return::match_return(){} + #line 38 "cpp2regex.h2" //----------------------------------------------------------------------- // diff --git a/include/cpp2util.h b/include/cpp2util.h index e01d89f46..f35914ef2 100644 --- a/include/cpp2util.h +++ b/include/cpp2util.h @@ -378,6 +378,40 @@ using _uchar = unsigned char; // normally use u8 instead namespace string_util { +// Break a string_view into a vector of views of simple qidentifier +// substrings separated by other characters +auto split_string_list(std::string_view str) + -> std::vector +{ + std::vector ret; + + auto is_id_char = [](char c) { + return std::isalnum(c) || c == '_'; + }; + + auto pos = 0; + while( pos < std::ssize(str) ) { + // Skip non-alnum + while (pos < std::ssize(str) && !is_id_char(str[pos])) { + ++pos; + } + auto start = pos; + + // Find the end of the current component + while (pos < std::ssize(str) && is_id_char(str[pos])) { + ++pos; + } + + // Add nonempty substring to the vector + if (start < pos) { + ret.emplace_back(str.substr(start, pos - start)); + } + } + + return ret; +} + + // From https://stackoverflow.com/questions/216823/how-to-trim-a-stdstring // Trim from start (in place) diff --git a/regression-tests/pure2-enum.cpp2 b/regression-tests/pure2-enum.cpp2 index 4f1f886f2..ea5b09f7e 100644 --- a/regression-tests/pure2-enum.cpp2 +++ b/regression-tests/pure2-enum.cpp2 @@ -35,12 +35,17 @@ main: () = { x: skat_game = skat_game::clubs; x2 := skat_game::diamonds; x2 = x; + x3 := skat_game::from_string("hearts"); + x4 := skat_game::from_code("skat_game::hearts"); // if x == 9 { } // error, can't compare skat_game and integer // if x == rgb::red { } // error, can't compare skat_game and rgb color std::cout << "x.to_string() is (x.to_string())$\n"; std::cout << "x2.to_string() is (x2.to_string())$\n"; + std::cout << "x3.to_string() is (x3.to_string())$\n"; + std::cout << "x3.to_code() is (x3.to_code())$\n"; + std::cout << "x4.to_string() is (x3.to_string())$\n"; std::cout << "with if else: "; if x == skat_game::diamonds { // ok, can compare two skat_games @@ -117,4 +122,14 @@ main: () = { is (cpp2::has_flags(f2)) = "includes all f2's flags ('cached' and 'current')"; is _ = "something else"; } << "\n"; + + f_from_string := file_attributes::from_string("cached_and_current"); + std::cout << "f_from_string is " << f_from_string.to_string() << "\n"; + + f_from_string = file_attributes::from_string("(current, obsolete)"); + std::cout << "f_from_string is " << f_from_string.to_string() << "\n"; + std::cout << "f_from_string.to_code() is " << f_from_string.to_code() << "\n"; + + f_from_string = file_attributes::from_code("(file_attributes::cached | file_attributes::obsolete)"); + std::cout << "f_from_string is " << f_from_string.to_string() << "\n"; } diff --git a/regression-tests/test-results/clang-12-c++20/pure2-enum.cpp.execution b/regression-tests/test-results/clang-12-c++20/pure2-enum.cpp.execution index 6d9a33c28..7e6611f6d 100644 --- a/regression-tests/test-results/clang-12-c++20/pure2-enum.cpp.execution +++ b/regression-tests/test-results/clang-12-c++20/pure2-enum.cpp.execution @@ -1,5 +1,8 @@ x.to_string() is clubs x2.to_string() is clubs +x3.to_string() is hearts +x3.to_code() is skat_game::hearts +x4.to_string() is hearts with if else: clubs with inspect: clubs @@ -27,3 +30,7 @@ f is (f2) is false f2 is (f ) is false (f & f2) == f2 is true inspecting f: includes all f2's flags ('cached' and 'current') +f_from_string is (cached, current, cached_and_current) +f_from_string is (current, obsolete) +f_from_string.to_code() is (file_attributes::current | file_attributes::obsolete) +f_from_string is (cached, obsolete) diff --git a/regression-tests/test-results/gcc-10-c++20/pure2-enum.cpp.execution b/regression-tests/test-results/gcc-10-c++20/pure2-enum.cpp.execution index 6d9a33c28..7e6611f6d 100644 --- a/regression-tests/test-results/gcc-10-c++20/pure2-enum.cpp.execution +++ b/regression-tests/test-results/gcc-10-c++20/pure2-enum.cpp.execution @@ -1,5 +1,8 @@ x.to_string() is clubs x2.to_string() is clubs +x3.to_string() is hearts +x3.to_code() is skat_game::hearts +x4.to_string() is hearts with if else: clubs with inspect: clubs @@ -27,3 +30,7 @@ f is (f2) is false f2 is (f ) is false (f & f2) == f2 is true inspecting f: includes all f2's flags ('cached' and 'current') +f_from_string is (cached, current, cached_and_current) +f_from_string is (current, obsolete) +f_from_string.to_code() is (file_attributes::current | file_attributes::obsolete) +f_from_string is (cached, obsolete) diff --git a/regression-tests/test-results/gcc-14-c++2b/pure2-enum.cpp.execution b/regression-tests/test-results/gcc-14-c++2b/pure2-enum.cpp.execution index 6d9a33c28..7e6611f6d 100644 --- a/regression-tests/test-results/gcc-14-c++2b/pure2-enum.cpp.execution +++ b/regression-tests/test-results/gcc-14-c++2b/pure2-enum.cpp.execution @@ -1,5 +1,8 @@ x.to_string() is clubs x2.to_string() is clubs +x3.to_string() is hearts +x3.to_code() is skat_game::hearts +x4.to_string() is hearts with if else: clubs with inspect: clubs @@ -27,3 +30,7 @@ f is (f2) is false f2 is (f ) is false (f & f2) == f2 is true inspecting f: includes all f2's flags ('cached' and 'current') +f_from_string is (cached, current, cached_and_current) +f_from_string is (current, obsolete) +f_from_string.to_code() is (file_attributes::current | file_attributes::obsolete) +f_from_string is (cached, obsolete) diff --git a/regression-tests/test-results/msvc-2022-c++latest/pure2-enum.cpp.execution b/regression-tests/test-results/msvc-2022-c++latest/pure2-enum.cpp.execution index 6d9a33c28..7e6611f6d 100644 --- a/regression-tests/test-results/msvc-2022-c++latest/pure2-enum.cpp.execution +++ b/regression-tests/test-results/msvc-2022-c++latest/pure2-enum.cpp.execution @@ -1,5 +1,8 @@ x.to_string() is clubs x2.to_string() is clubs +x3.to_string() is hearts +x3.to_code() is skat_game::hearts +x4.to_string() is hearts with if else: clubs with inspect: clubs @@ -27,3 +30,7 @@ f is (f2) is false f2 is (f ) is false (f & f2) == f2 is true inspecting f: includes all f2's flags ('cached' and 'current') +f_from_string is (cached, current, cached_and_current) +f_from_string is (current, obsolete) +f_from_string.to_code() is (file_attributes::current | file_attributes::obsolete) +f_from_string is (cached, obsolete) diff --git a/regression-tests/test-results/pure2-enum.cpp b/regression-tests/test-results/pure2-enum.cpp index d88d18f79..08c557c68 100644 --- a/regression-tests/test-results/pure2-enum.cpp +++ b/regression-tests/test-results/pure2-enum.cpp @@ -42,7 +42,11 @@ public: constexpr auto operator=(skat_game const& that) -> skat_game& ; public: constexpr skat_game(skat_game&& that) noexcept; public: constexpr auto operator=(skat_game&& that) noexcept -> skat_game& ; public: [[nodiscard]] auto operator<=>(skat_game const& that) const& -> std::strong_ordering = default; +public: [[nodiscard]] auto to_string_impl(cpp2::impl::in prefix) const& -> std::string; public: [[nodiscard]] auto to_string() const& -> std::string; +public: [[nodiscard]] auto to_code() const& -> std::string; +public: [[nodiscard]] static auto from_string(cpp2::impl::in s) -> skat_game; +public: [[nodiscard]] static auto from_code(cpp2::impl::in s) -> skat_game; #line 4 "pure2-enum.cpp2" // 10 @@ -68,7 +72,11 @@ public: constexpr auto operator=(janus const& that) -> janus& ; public: constexpr janus(janus&& that) noexcept; public: constexpr auto operator=(janus&& that) noexcept -> janus& ; public: [[nodiscard]] auto operator<=>(janus const& that) const& -> std::strong_ordering = default; +public: [[nodiscard]] auto to_string_impl(cpp2::impl::in prefix) const& -> std::string; public: [[nodiscard]] auto to_string() const& -> std::string; +public: [[nodiscard]] auto to_code() const& -> std::string; +public: [[nodiscard]] static auto from_string(cpp2::impl::in s) -> janus; +public: [[nodiscard]] static auto from_code(cpp2::impl::in s) -> janus; #line 19 "pure2-enum.cpp2" }; @@ -98,7 +106,11 @@ public: constexpr auto operator=(file_attributes const& that) -> file_attributes public: constexpr file_attributes(file_attributes&& that) noexcept; public: constexpr auto operator=(file_attributes&& that) noexcept -> file_attributes& ; public: [[nodiscard]] auto operator<=>(file_attributes const& that) const& -> std::strong_ordering = default; +public: [[nodiscard]] auto to_string_impl(cpp2::impl::in prefix, cpp2::impl::in separator) const& -> std::string; public: [[nodiscard]] auto to_string() const& -> std::string; +public: [[nodiscard]] auto to_code() const& -> std::string; +public: [[nodiscard]] static auto from_string(cpp2::impl::in s) -> file_attributes; +public: [[nodiscard]] static auto from_code(cpp2::impl::in s) -> file_attributes; #line 22 "pure2-enum.cpp2" // 1 @@ -146,15 +158,38 @@ constexpr skat_game::skat_game(skat_game&& that) noexcept constexpr auto skat_game::operator=(skat_game&& that) noexcept -> skat_game& { _value = std::move(that)._value; return *this;} -[[nodiscard]] auto skat_game::to_string() const& -> std::string{ -if ((*this) == diamonds) {return "diamonds"; } -if ((*this) == hearts) {return "hearts"; } -if ((*this) == spades) {return "spades"; } -if ((*this) == clubs) {return "clubs"; } -if ((*this) == grand) {return "grand"; } -if ((*this) == null) {return "null"; } +[[nodiscard]] auto skat_game::to_string_impl(cpp2::impl::in prefix) const& -> std::string{ + +auto pref {cpp2::to_string(prefix)}; +if ((*this) == diamonds) {return pref + "diamonds"; } +if ((*this) == hearts) {return pref + "hearts"; } +if ((*this) == spades) {return pref + "spades"; } +if ((*this) == clubs) {return pref + "clubs"; } +if ((*this) == grand) {return pref + "grand"; } +if ((*this) == null) {return cpp2::move(pref) + "null"; } return "invalid skat_game value"; } + +[[nodiscard]] auto skat_game::to_string() const& -> std::string { return to_string_impl(""); } +[[nodiscard]] auto skat_game::to_code() const& -> std::string { return to_string_impl("skat_game::"); } +[[nodiscard]] auto skat_game::from_string(cpp2::impl::in s) -> skat_game{ + +auto x {s}; +if ("diamonds" == x) {return diamonds; } +else {if ("hearts" == x) {return hearts; } +else {if ("spades" == x) {return spades; } +else {if ("clubs" == x) {return clubs; } +else {if ("grand" == x) {return grand; } +else {if ("null" == cpp2::move(x)) {return null; } +#line 1 "pure2-enum.cpp2" +}}}}} +CPP2_UFCS(report_violation)(cpp2::type_safety, CPP2_UFCS(c_str)(("can't convert string '" + cpp2::to_string(s) + "' to enum of type skat_game"))); +return diamonds; +} + +[[nodiscard]] auto skat_game::from_code(cpp2::impl::in s) -> skat_game{ +std::string str {s}; return from_string(cpp2::string_util::replace_all(cpp2::move(str), "skat_game::", "")); } + #line 15 "pure2-enum.cpp2" constexpr auto janus::flip() & -> void{ if ((*this) == past) {(*this) = future; } @@ -185,14 +220,33 @@ constexpr janus::janus(janus&& that) noexcept constexpr auto janus::operator=(janus&& that) noexcept -> janus& { _value = std::move(that)._value; return *this;} -[[nodiscard]] auto janus::to_string() const& -> std::string{ - if ((*this) == past) {return "past"; } - if ((*this) == future) {return "future"; } +[[nodiscard]] auto janus::to_string_impl(cpp2::impl::in prefix) const& -> std::string{ + + auto pref {cpp2::to_string(prefix)}; + if ((*this) == past) {return pref + "past"; } + if ((*this) == future) {return cpp2::move(pref) + "future"; } return "invalid janus value"; } - constexpr file_attributes::file_attributes(cpp2::impl::in _val) + [[nodiscard]] auto janus::to_string() const& -> std::string { return to_string_impl(""); } +[[nodiscard]] auto janus::to_code() const& -> std::string { return to_string_impl("janus::"); } +[[nodiscard]] auto janus::from_string(cpp2::impl::in s) -> janus{ + + auto x {s}; + if ("past" == x) {return past; } + else {if ("future" == cpp2::move(x)) {return future; } +#line 1 "pure2-enum.cpp2" +} +CPP2_UFCS(report_violation)(cpp2::type_safety, CPP2_UFCS(c_str)(("can't convert string '" + cpp2::to_string(s) + "' to enum of type janus"))); +return past; +} + +[[nodiscard]] auto janus::from_code(cpp2::impl::in s) -> janus{ +std::string str {s}; return from_string(cpp2::string_util::replace_all(cpp2::move(str), "janus::", "")); } + +constexpr file_attributes::file_attributes(cpp2::impl::in _val) : _value{ cpp2::unsafe_narrow(_val) } { } + constexpr auto file_attributes::operator=(cpp2::impl::in _val) -> file_attributes& { _value = cpp2::unsafe_narrow(_val); return *this; } @@ -228,18 +282,50 @@ constexpr file_attributes::file_attributes(file_attributes&& that) noexcept constexpr auto file_attributes::operator=(file_attributes&& that) noexcept -> file_attributes& { _value = std::move(that)._value; return *this;} -[[nodiscard]] auto file_attributes::to_string() const& -> std::string{ +[[nodiscard]] auto file_attributes::to_string_impl(cpp2::impl::in prefix, cpp2::impl::in separator) const& -> std::string{ - std::string _ret {"("}; +std::string ret {"("}; + +std::string sep {}; +if ((*this) == none) {return "(none)"; } + +auto pref {cpp2::to_string(prefix)}; +if (((*this) & cached) == cached) {ret += sep + pref + "cached";sep = separator;} +if (((*this) & current) == current) {ret += sep + pref + "current";sep = separator;} +if (((*this) & obsolete) == obsolete) {ret += sep + pref + "obsolete";sep = separator;} +if (((*this) & cached_and_current) == cached_and_current) {ret += sep + cpp2::move(pref) + "cached_and_current";sep = separator;} +return cpp2::move(ret) + ")"; +} + +[[nodiscard]] auto file_attributes::to_string() const& -> std::string { return to_string_impl("", ", "); } +[[nodiscard]] auto file_attributes::to_code() const& -> std::string { return to_string_impl("file_attributes::", " | "); } +[[nodiscard]] auto file_attributes::from_string(cpp2::impl::in s) -> file_attributes{ + +auto ret {none}; +do {{ +for ( auto const& x : cpp2::string_util::split_string_list(s) ) { +if ("cached" == x) {ret |= cached;} +else {if ("current" == x) {ret |= current;} +else {if ("obsolete" == x) {ret |= obsolete;} +else {if ("cached_and_current" == x) {ret |= cached_and_current;} +else {if ("none" == x) {ret |= none;} +else {goto BREAK_outer;} +#line 1 "pure2-enum.cpp2" +}}}} +} + +return ret; +} CPP2_CONTINUE_BREAK(outer) } + while ( +false +); +CPP2_UFCS(report_violation)(cpp2::type_safety, CPP2_UFCS(c_str)(("can't convert string '" + cpp2::to_string(s) + "' to flag_enum of type file_attributes"))); +return none; +} + +[[nodiscard]] auto file_attributes::from_code(cpp2::impl::in s) -> file_attributes{ +std::string str {s}; return from_string(cpp2::string_util::replace_all(cpp2::move(str), "file_attributes::", "")); } - std::string _comma {}; - if ((*this) == none) {return "(none)"; } - if (((*this) & cached) == cached) {_ret += _comma + "cached";_comma = ", ";} - if (((*this) & current) == current) {_ret += _comma + "current";_comma = ", ";} - if (((*this) & obsolete) == obsolete) {_ret += _comma + "obsolete";_comma = ", ";} - if (((*this) & cached_and_current) == cached_and_current) {_ret += _comma + "cached_and_current";_comma = ", ";} - return cpp2::move(_ret) + ")"; - } #line 28 "pure2-enum.cpp2" auto main() -> int{ auto j {janus::past}; @@ -251,12 +337,17 @@ auto main() -> int{ skat_game x {skat_game::clubs}; auto x2 {skat_game::diamonds}; x2 = x; + auto x3 {skat_game::from_string("hearts")}; + auto x4 {skat_game::from_code("skat_game::hearts")}; // if x == 9 { } // error, can't compare skat_game and integer // if x == rgb::red { } // error, can't compare skat_game and rgb color std::cout << "x.to_string() is " + cpp2::to_string(CPP2_UFCS(to_string)(x)) + "\n"; std::cout << "x2.to_string() is " + cpp2::to_string(CPP2_UFCS(to_string)(cpp2::move(x2))) + "\n"; + std::cout << "x3.to_string() is " + cpp2::to_string(CPP2_UFCS(to_string)(x3)) + "\n"; + std::cout << "x3.to_code() is " + cpp2::to_string(CPP2_UFCS(to_code)(x3)) + "\n"; + std::cout << "x4.to_string() is " + cpp2::to_string(CPP2_UFCS(to_string)(cpp2::move(x3))) + "\n"; std::cout << "with if else: "; if (x == skat_game::diamonds) { // ok, can compare two skat_games @@ -333,5 +424,15 @@ auto main() -> int{ else if (cpp2::impl::is(_expr, cpp2::has_flags(cpp2::move(f2)))) { if constexpr( requires{"includes all f2's flags ('cached' and 'current')";} ) if constexpr( std::is_convertible_v ) return "includes all f2's flags ('cached' and 'current')"; else return std::string{}; else return std::string{}; } else return "something else"; } () << "\n"; + + auto f_from_string {file_attributes::from_string("cached_and_current")}; + std::cout << "f_from_string is " << CPP2_UFCS(to_string)(f_from_string) << "\n"; + + f_from_string = file_attributes::from_string("(current, obsolete)"); + std::cout << "f_from_string is " << CPP2_UFCS(to_string)(f_from_string) << "\n"; + std::cout << "f_from_string.to_code() is " << CPP2_UFCS(to_code)(f_from_string) << "\n"; + + f_from_string = file_attributes::from_code("(file_attributes::cached | file_attributes::obsolete)"); + std::cout << "f_from_string is " << CPP2_UFCS(to_string)(cpp2::move(f_from_string)) << "\n"; } diff --git a/source/reflect.h b/source/reflect.h index 7b89be4d5..cc84c0dd4 100644 --- a/source/reflect.h +++ b/source/reflect.h @@ -39,7 +39,7 @@ class alias_declaration; #line 1006 "reflect.h2" class value_member_info; -#line 1632 "reflect.h2" +#line 1692 "reflect.h2" } } @@ -708,7 +708,7 @@ auto basic_enum( cpp2::impl::in bitwise ) -> void; -#line 1207 "reflect.h2" +#line 1267 "reflect.h2" //----------------------------------------------------------------------- // // "An enum[...] is a totally ordered value type that stores a @@ -720,7 +720,7 @@ auto basic_enum( // auto cpp2_enum(meta::type_declaration& t) -> void; -#line 1233 "reflect.h2" +#line 1293 "reflect.h2" //----------------------------------------------------------------------- // // "flag_enum expresses an enumeration that stores values @@ -733,7 +733,7 @@ auto cpp2_enum(meta::type_declaration& t) -> void; // auto flag_enum(meta::type_declaration& t) -> void; -#line 1265 "reflect.h2" +#line 1325 "reflect.h2" //----------------------------------------------------------------------- // // "As with void*, programmers should know that unions [...] are @@ -760,14 +760,14 @@ auto flag_enum(meta::type_declaration& t) -> void; auto cpp2_union(meta::type_declaration& t) -> void; -#line 1436 "reflect.h2" +#line 1496 "reflect.h2" //----------------------------------------------------------------------- // // print - output a pretty-printed visualization of t // auto print(cpp2::impl::in t) -> void; -#line 1446 "reflect.h2" +#line 1506 "reflect.h2" //----------------------------------------------------------------------- // // regex - creates regular expressions from members @@ -784,7 +784,7 @@ auto print(cpp2::impl::in t) -> void; // auto regex_gen(meta::type_declaration& t) -> void; -#line 1513 "reflect.h2" +#line 1573 "reflect.h2" //----------------------------------------------------------------------- // // apply_metafunctions @@ -795,7 +795,7 @@ auto regex_gen(meta::type_declaration& t) -> void; auto const& error ) -> bool; -#line 1632 "reflect.h2" +#line 1692 "reflect.h2" } } @@ -1837,52 +1837,121 @@ std::string value{"-1"}; // Generate the common functions CPP2_UFCS(add_member)(t, " get_raw_value : (this) -> " + cpp2::to_string(cpp2::move(underlying_type.value())) + " == _value;"); - CPP2_UFCS(add_member)(t, " operator= : (out this) == { _value = " + cpp2::to_string(cpp2::move(default_value)) + "._value; }"); + CPP2_UFCS(add_member)(t, " operator= : (out this) == { _value = " + cpp2::to_string(default_value) + "._value; }"); CPP2_UFCS(add_member)(t, " operator= : (out this, that) == { }"); CPP2_UFCS(add_member)(t, " operator<=> : (this, that) -> std::strong_ordering;"); { -std::string to_string{" to_string: (this) -> std::string = { \n"}; +std::string to_string_impl{" to_string_impl: (this, prefix: std::string_view"}; - // Provide a 'to_string' function to print enumerator name(s) + // Provide 'to_string' and 'to_code' functions to print enumerator + // name(s) as human-readable strings or as code expressions -#line 1172 "reflect.h2" +#line 1173 "reflect.h2" { if (bitwise) { - to_string += " _ret : std::string = \"(\";\n" - " _comma : std::string = ();\n" - " if this == none { return \"(none)\"; }\n"; + to_string_impl += ", separator: std::string_view ) -> std::string = { \n" + " ret : std::string = \"(\";\n" + " sep : std::string = ();\n" + " if this == none { return \"(none)\"; }\n"; } + else { + to_string_impl += ") -> std::string = { \n"; + } + + to_string_impl += " pref := cpp2::to_string(prefix);\n"; for ( - auto const& e : cpp2::move(enumerators) ) { + auto const& e : enumerators ) { if (e.name != "_") {// ignore unnamed values if (bitwise) { if (e.name != "none") { - to_string += " if (this & " + cpp2::to_string(e.name) + ") == " + cpp2::to_string(e.name) + " { " - "_ret += _comma + \"" + cpp2::to_string(e.name) + "\"; _comma = \", \"; " - "}\n"; + to_string_impl += " if (this & " + cpp2::to_string(e.name) + ") == " + cpp2::to_string(e.name) + " { " + "ret += sep + pref + \"" + cpp2::to_string(e.name) + "\"; sep = separator; " + "}\n"; } } else { - to_string += " if this == " + cpp2::to_string(e.name) + " { return \"" + cpp2::to_string(e.name) + "\"; }\n"; + to_string_impl += " if this == " + cpp2::to_string(e.name) + " { return pref + \"" + cpp2::to_string(e.name) + "\"; }\n"; } } } if (bitwise) { - to_string += " return _ret+\")\";\n}\n"; + to_string_impl += " return ret+\")\";\n}\n"; } else { - to_string += " return \"invalid " + cpp2::to_string(CPP2_UFCS(name)(t)) + " value\";\n}\n"; + to_string_impl += " return \"invalid " + cpp2::to_string(CPP2_UFCS(name)(t)) + " value\";\n}\n"; } - CPP2_UFCS(add_member)(t, cpp2::move(to_string)); + CPP2_UFCS(add_member)(t, cpp2::move(to_string_impl)); } } -#line 1204 "reflect.h2" + +#line 1212 "reflect.h2" + if (bitwise) { + CPP2_UFCS(add_member)(t, " to_string: (this) -> std::string = to_string_impl( \"\", \", \" );"); + CPP2_UFCS(add_member)(t, " to_code : (this) -> std::string = to_string_impl( \"" + cpp2::to_string(CPP2_UFCS(name)(t)) + "::\", \" | \" );"); + } + else { + CPP2_UFCS(add_member)(t, " to_string: (this) -> std::string = to_string_impl( \"\" );"); + CPP2_UFCS(add_member)(t, " to_code : (this) -> std::string = to_string_impl( \"" + cpp2::to_string(CPP2_UFCS(name)(t)) + "::\" );"); + } +{ +std::string from_string{" from_string: (s: std::string_view) -> " + cpp2::to_string(CPP2_UFCS(name)(t)) + " = { \n"}; + + // Provide a 'from_string' function to parse strings into enumerators + +#line 1223 "reflect.h2" + { + std::string_view prefix {""}; + std::string_view combine_op {"return"}; + + // For flags, accept a list that we break apart and then |= together + if (bitwise) + { + prefix = "flag_"; + combine_op = "ret |="; + + from_string += " ret := none;\n" + " outer: do {\n" + " for cpp2::string_util::split_string_list(s) do (x) {\n"; + } + // Otherwise, accept just a single string + else { + from_string += " x := s;\n"; + } +{ +std::string_view else_{""}; + +#line 1243 "reflect.h2" + for ( + auto const& e : cpp2::move(enumerators) ) { + from_string += " " + cpp2::to_string(else_) + "if \"" + cpp2::to_string(e.name) + "\" == x { " + cpp2::to_string(combine_op) + " " + cpp2::to_string(e.name) + "; }\n"; + else_ = "else "; + } } -#line 1216 "reflect.h2" +#line 1249 "reflect.h2" + if (bitwise) { + from_string += " else { break outer; }\n" + " }\n" + " return ret;\n" + " } while false;\n"; + } + + from_string += " cpp2::type_safety.report_violation( (\"can't convert string '\" + cpp2::to_string(s) + \"' to " + cpp2::to_string(cpp2::move(prefix)) + "enum of type " + cpp2::to_string(CPP2_UFCS(name)(t)) + "\").c_str() );\n" + " return " + cpp2::to_string(cpp2::move(default_value)) + ";\n" + " }\n\n"; + + CPP2_UFCS(add_member)(t, cpp2::move(from_string)); + } +} + +#line 1263 "reflect.h2" + CPP2_UFCS(add_member)(t, " from_code: (s: std::string_view) -> " + cpp2::to_string(CPP2_UFCS(name)(t)) + " = { str: std::string = s; return from_string( cpp2::string_util::replace_all(str, \"" + cpp2::to_string(CPP2_UFCS(name)(t)) + "::\", \"\" ) ); }"); +} + +#line 1276 "reflect.h2" auto cpp2_enum(meta::type_declaration& t) -> void { // Let basic_enum do its thing, with an incrementing value generator @@ -1899,7 +1968,7 @@ auto cpp2_enum(meta::type_declaration& t) -> void ); } -#line 1243 "reflect.h2" +#line 1303 "reflect.h2" auto flag_enum(meta::type_declaration& t) -> void { // Let basic_enum do its thing, with a power-of-two value generator @@ -1921,7 +1990,7 @@ auto flag_enum(meta::type_declaration& t) -> void ); } -#line 1289 "reflect.h2" +#line 1349 "reflect.h2" auto cpp2_union(meta::type_declaration& t) -> void { std::vector alternatives {}; @@ -1930,7 +1999,7 @@ auto value{0}; // 1. Gather: All the user-written members, and find/compute the max size -#line 1296 "reflect.h2" +#line 1356 "reflect.h2" for ( auto const& m : CPP2_UFCS(get_members)(t) ) { do @@ -1960,7 +2029,7 @@ auto value{0}; } while (false); ++value; } } -#line 1324 "reflect.h2" +#line 1384 "reflect.h2" std::string discriminator_type {}; if (cpp2::impl::cmp_less(CPP2_UFCS(ssize)(alternatives),std::numeric_limits::max())) { discriminator_type = "i8"; @@ -1975,7 +2044,7 @@ auto value{0}; discriminator_type = "i64"; }}} -#line 1339 "reflect.h2" +#line 1399 "reflect.h2" // 2. Replace: Erase the contents and replace with modified contents CPP2_UFCS(remove_marked_members)(t); @@ -1984,7 +2053,7 @@ std::string storage{" _storage: cpp2::aligned_storage t) -> void { std::cout << CPP2_UFCS(print)(t) << "\n"; } -#line 1460 "reflect.h2" +#line 1520 "reflect.h2" auto regex_gen(meta::type_declaration& t) -> void { auto has_default {false}; @@ -2146,7 +2215,7 @@ auto regex_gen(meta::type_declaration& t) -> void } } -#line 1517 "reflect.h2" +#line 1577 "reflect.h2" [[nodiscard]] auto apply_metafunctions( declaration_node& n, type_declaration& rtype, @@ -2261,7 +2330,7 @@ auto regex_gen(meta::type_declaration& t) -> void return true; } -#line 1632 "reflect.h2" +#line 1692 "reflect.h2" } } diff --git a/source/reflect.h2 b/source/reflect.h2 index 9f82035a0..08a6ac6fc 100644 --- a/source/reflect.h2 +++ b/source/reflect.h2 @@ -1167,40 +1167,100 @@ basic_enum: ( t.add_member( " operator= : (out this, that) == { }"); t.add_member( " operator<=> : (this, that) -> std::strong_ordering;"); - // Provide a 'to_string' function to print enumerator name(s) - (copy to_string: std::string = " to_string: (this) -> std::string = { \n") + // Provide 'to_string' and 'to_code' functions to print enumerator + // name(s) as human-readable strings or as code expressions + (copy to_string_impl: std::string = " to_string_impl: (this, prefix: std::string_view") { if bitwise { - to_string += " _ret : std::string = \"(\";\n" - " _comma : std::string = ();\n" - " if this == none { return \"(none)\"; }\n"; + to_string_impl += ", separator: std::string_view ) -> std::string = { \n" + " ret : std::string = \"(\";\n" + " sep : std::string = ();\n" + " if this == none { return \"(none)\"; }\n"; + } + else { + to_string_impl += ") -> std::string = { \n"; } + to_string_impl += " pref := cpp2::to_string(prefix);\n"; + for enumerators do (e) { if e.name != "_" { // ignore unnamed values if bitwise { if e.name != "none" { - to_string += " if (this & (e.name)$) == (e.name)$ { " - "_ret += _comma + \"(e.name)$\"; _comma = \", \"; " - "}\n"; + to_string_impl += " if (this & (e.name)$) == (e.name)$ { " + "ret += sep + pref + \"(e.name)$\"; sep = separator; " + "}\n"; } } else { - to_string += " if this == (e.name)$ { return \"(e.name)$\"; }\n"; + to_string_impl += " if this == (e.name)$ { return pref + \"(e.name)$\"; }\n"; } } } if bitwise { - to_string += " return _ret+\")\";\n}\n"; + to_string_impl += " return ret+\")\";\n}\n"; } else { - to_string += " return \"invalid (t.name())$ value\";\n}\n"; + to_string_impl += " return \"invalid (t.name())$ value\";\n}\n"; } - t.add_member( to_string ); + t.add_member( to_string_impl ); + } + + if bitwise { + t.add_member( " to_string: (this) -> std::string = to_string_impl( \"\", \", \" );" ); + t.add_member( " to_code : (this) -> std::string = to_string_impl( \"(t.name())$::\", \" | \" );" ); + } + else { + t.add_member( " to_string: (this) -> std::string = to_string_impl( \"\" );" ); + t.add_member( " to_code : (this) -> std::string = to_string_impl( \"(t.name())$::\" );" ); } + + // Provide a 'from_string' function to parse strings into enumerators + (copy from_string: std::string = " from_string: (s: std::string_view) -> (t.name())$ = { \n") + { + prefix : std::string_view = ""; + combine_op: std::string_view = "return"; + + // For flags, accept a list that we break apart and then |= together + if bitwise + { + prefix = "flag_"; + combine_op = "ret |="; + + from_string += " ret := none;\n" + " outer: do {\n" + " for cpp2::string_util::split_string_list(s) do (x) {\n"; + } + // Otherwise, accept just a single string + else { + from_string += " x := s;\n"; + } + + (copy else_: std::string_view = "") + for enumerators + do (e) { + from_string += " (else_)$if \"(e.name)$\" == x { (combine_op)$ (e.name)$; }\n"; + else_ = "else "; + } + + if bitwise { + from_string += " else { break outer; }\n" + " }\n" + " return ret;\n" + " } while false;\n"; + } + + from_string += " cpp2::type_safety.report_violation( (\"can't convert string '\" + cpp2::to_string(s) + \"' to (prefix)$enum of type (t.name())$\").c_str() );\n" + " return (default_value)$;\n" + " }\n\n"; + + t.add_member( from_string ); + } + + t.add_member( " from_code: (s: std::string_view) -> (t.name())$ = { str: std::string = s; return from_string( cpp2::string_util::replace_all(str, \"(t.name())$::\", \"\" ) ); }" ); } From 494208301d1c6daa03a67654e8b884dc4f44fdb6 Mon Sep 17 00:00:00 2001 From: Herb Sutter Date: Sun, 4 Aug 2024 12:12:17 -0700 Subject: [PATCH 16/22] Update version and catch up files for last merges --- include/cpp2regex.h | 14 ++++++++------ regression-tests/test-results/version | 2 +- source/build.info | 2 +- 3 files changed, 10 insertions(+), 8 deletions(-) diff --git a/include/cpp2regex.h b/include/cpp2regex.h index 22374479f..01fc3440e 100644 --- a/include/cpp2regex.h +++ b/include/cpp2regex.h @@ -1550,12 +1550,14 @@ std::string ret {"("}; std::string sep {}; if ((*this) == none) {return "(none)"; } -if (((*this) & case_insensitive) == case_insensitive) {ret += sep + cpp2::to_string(prefix) + "case_insensitive";sep = separator;} -if (((*this) & multiple_lines) == multiple_lines) {ret += sep + cpp2::to_string(prefix) + "multiple_lines";sep = separator;} -if (((*this) & single_line) == single_line) {ret += sep + cpp2::to_string(prefix) + "single_line";sep = separator;} -if (((*this) & no_group_captures) == no_group_captures) {ret += sep + cpp2::to_string(prefix) + "no_group_captures";sep = separator;} -if (((*this) & perl_code_syntax) == perl_code_syntax) {ret += sep + cpp2::to_string(prefix) + "perl_code_syntax";sep = separator;} -if (((*this) & perl_code_syntax_in_classes) == perl_code_syntax_in_classes) {ret += sep + cpp2::to_string(prefix) + "perl_code_syntax_in_classes";sep = separator;} + +auto pref {cpp2::to_string(prefix)}; +if (((*this) & case_insensitive) == case_insensitive) {ret += sep + pref + "case_insensitive";sep = separator;} +if (((*this) & multiple_lines) == multiple_lines) {ret += sep + pref + "multiple_lines";sep = separator;} +if (((*this) & single_line) == single_line) {ret += sep + pref + "single_line";sep = separator;} +if (((*this) & no_group_captures) == no_group_captures) {ret += sep + pref + "no_group_captures";sep = separator;} +if (((*this) & perl_code_syntax) == perl_code_syntax) {ret += sep + pref + "perl_code_syntax";sep = separator;} +if (((*this) & perl_code_syntax_in_classes) == perl_code_syntax_in_classes) {ret += sep + cpp2::move(pref) + "perl_code_syntax_in_classes";sep = separator;} return cpp2::move(ret) + ")"; } diff --git a/regression-tests/test-results/version b/regression-tests/test-results/version index 819ff626f..8c82ed8e3 100644 --- a/regression-tests/test-results/version +++ b/regression-tests/test-results/version @@ -1,5 +1,5 @@ -cppfront compiler v0.7.2 Build 9804:0931 +cppfront compiler v0.7.2 Build 9804:1033 Copyright(c) Herb Sutter All rights reserved SPDX-License-Identifier: CC-BY-NC-ND-4.0 diff --git a/source/build.info b/source/build.info index 683abf1c5..f7c98cb74 100644 --- a/source/build.info +++ b/source/build.info @@ -1 +1 @@ -"9804:0931" \ No newline at end of file +"9804:1033" \ No newline at end of file From 0975248e557c50c4550ece558fc1596ba209c0b9 Mon Sep 17 00:00:00 2001 From: Herb Sutter Date: Mon, 5 Aug 2024 15:45:47 -0700 Subject: [PATCH 17/22] Update pure2-regex_10_escapes.cpp.execution --- .../pure2-regex_10_escapes.cpp.execution | 38 +++++++++++-------- 1 file changed, 22 insertions(+), 16 deletions(-) diff --git a/regression-tests/test-results/msvc-2022-c++latest/pure2-regex_10_escapes.cpp.execution b/regression-tests/test-results/msvc-2022-c++latest/pure2-regex_10_escapes.cpp.execution index 14d06c270..41bd8a972 100644 --- a/regression-tests/test-results/msvc-2022-c++latest/pure2-regex_10_escapes.cpp.execution +++ b/regression-tests/test-results/msvc-2022-c++latest/pure2-regex_10_escapes.cpp.execution @@ -9,30 +9,36 @@ Running tests_10_escapes: 08_y: OK regex: foo(\h)bar parsed_regex: foo(\h)bar str: foo bar result_expr: $1 expected_results 09_y: OK regex: (\H)(\h) parsed_regex: (\H)(\h) str: foo bar result_expr: $1-$2 expected_results o- 10_y: OK regex: (\h)(\H) parsed_regex: (\h)(\H) str: foo bar result_expr: $1-$2 expected_results -b -11_y: OK regex: foo(\v+)bar parsed_regex: foo(\v+)bar str: foo - +11_y: OK regex: foo(\v+)bar parsed_regex: foo(\v+)bar str: foo -bar result_expr: $1 expected_results - +bar result_expr: $1 expected_results -12_y: OK regex: (\V+)(\v) parsed_regex: (\V+)(\v) str: foo - -bar result_expr: $1-$2 expected_results foo- -13_y: OK regex: (\v+)(\V) parsed_regex: (\v+)(\V) str: foo - -bar result_expr: $1-$2 expected_results - +12_y: OK regex: (\V+)(\v) parsed_regex: (\V+)(\v) str: foo + +bar result_expr: $1-$2 expected_results foo- +13_y: OK regex: (\v+)(\V) parsed_regex: (\v+)(\V) str: foo + + +bar result_expr: $1-$2 expected_results + + +-b +14_y: OK regex: foo(\v)bar parsed_regex: foo(\v)bar str: foo +bar result_expr: $1 expected_results +15_y: OK regex: (\V)(\v) parsed_regex: (\V)(\v) str: foo +bar result_expr: $1-$2 expected_results o- +16_y: OK regex: (\v)(\V) parsed_regex: (\v)(\V) str: foo +bar result_expr: $1-$2 expected_results -b -14_y: OK regex: foo(\v)bar parsed_regex: foo(\v)bar str: foo bar result_expr: $1 expected_results -15_y: OK regex: (\V)(\v) parsed_regex: (\V)(\v) str: foo bar result_expr: $1-$2 expected_results o- -16_y: OK regex: (\v)(\V) parsed_regex: (\v)(\V) str: foo bar result_expr: $1-$2 expected_results -b 17_y: OK regex: foo\t\n\r\f\a\ebar parsed_regex: foo\t\n\r\f\a\ebar str: foo - bar result_expr: $& expected_results foo - bar + + bar result_expr: $& expected_results foo + + bar 18_y: OK regex: foo\Kbar parsed_regex: foo\Kbar str: foobar result_expr: $& expected_results bar 19_y: OK regex: \x41\x42 parsed_regex: \x41\x42 str: AB result_expr: $& expected_results AB 20_y: OK regex: \101\o{102} parsed_regex: \101\o{102} str: AB result_expr: $& expected_results AB From df3f4ff6a2f6853a3162eccae159202c5f264c54 Mon Sep 17 00:00:00 2001 From: Neil Henderson <2060747+bluetarpmedia@users.noreply.github.com> Date: Tue, 6 Aug 2024 08:46:47 +1000 Subject: [PATCH 18/22] Update regression tests after recent changes (#1208) Signed-off-by: Herb Sutter Co-authored-by: Herb Sutter --- .../pure2-enum.cpp.execution | 7 +++++++ .../mixed-bounds-check.cpp.execution | 2 +- ...ed-bounds-safety-with-assert.cpp.execution | 2 +- ...-safety-3-contract-violation.cpp.execution | 2 +- ...me-safety-and-null-contracts.cpp.execution | 2 +- ...re2-assert-expected-not-null.cpp.execution | 2 +- ...re2-assert-optional-not-null.cpp.execution | 2 +- ...2-assert-shared-ptr-not-null.cpp.execution | 2 +- ...2-assert-unique-ptr-not-null.cpp.execution | 2 +- .../pure2-enum.cpp.execution | 7 +++++++ .../pure2-enum.cpp.execution | 7 +++++++ .../mixed-bounds-check.cpp.execution | 2 +- ...ed-bounds-safety-with-assert.cpp.execution | 2 +- ...-safety-3-contract-violation.cpp.execution | 2 +- ...me-safety-and-null-contracts.cpp.execution | 2 +- ...re2-assert-optional-not-null.cpp.execution | 2 +- ...2-assert-shared-ptr-not-null.cpp.execution | 2 +- ...2-assert-unique-ptr-not-null.cpp.execution | 2 +- .../clang-15-c++20/pure2-enum.cpp.execution | 7 +++++++ .../clang-18-c++20/pure2-enum.cpp.execution | 7 +++++++ .../pure2-enum.cpp.execution | 7 +++++++ ...mixed-bugfix-for-ufcs-non-local.cpp.output | 20 +++++++++---------- .../gcc-13-c++2b/pure2-enum.cpp.execution | 7 +++++++ ...mixed-bugfix-for-ufcs-non-local.cpp.output | 10 +++++----- .../pure2-assert-expected-not-null.cpp.output | 4 ++-- .../msvc-2022-c++20/pure2-enum.cpp.execution | 7 +++++++ .../pure2-regex_10_escapes.cpp.execution | 15 ++++++++++++++ 27 files changed, 103 insertions(+), 32 deletions(-) diff --git a/regression-tests/test-results/apple-clang-14-c++2b/pure2-enum.cpp.execution b/regression-tests/test-results/apple-clang-14-c++2b/pure2-enum.cpp.execution index 6d9a33c28..7e6611f6d 100644 --- a/regression-tests/test-results/apple-clang-14-c++2b/pure2-enum.cpp.execution +++ b/regression-tests/test-results/apple-clang-14-c++2b/pure2-enum.cpp.execution @@ -1,5 +1,8 @@ x.to_string() is clubs x2.to_string() is clubs +x3.to_string() is hearts +x3.to_code() is skat_game::hearts +x4.to_string() is hearts with if else: clubs with inspect: clubs @@ -27,3 +30,7 @@ f is (f2) is false f2 is (f ) is false (f & f2) == f2 is true inspecting f: includes all f2's flags ('cached' and 'current') +f_from_string is (cached, current, cached_and_current) +f_from_string is (current, obsolete) +f_from_string.to_code() is (file_attributes::current | file_attributes::obsolete) +f_from_string is (cached, obsolete) diff --git a/regression-tests/test-results/apple-clang-15-c++2b/mixed-bounds-check.cpp.execution b/regression-tests/test-results/apple-clang-15-c++2b/mixed-bounds-check.cpp.execution index 48b014ff8..1d524e6e1 100644 --- a/regression-tests/test-results/apple-clang-15-c++2b/mixed-bounds-check.cpp.execution +++ b/regression-tests/test-results/apple-clang-15-c++2b/mixed-bounds-check.cpp.execution @@ -1 +1 @@ -../../../include/cpp2util.h(937) decltype(auto) cpp2::impl::assert_in_bounds(auto &&, std::source_location) [arg = 5, x:auto = std::vector]: Bounds safety violation: out of bounds access attempt detected - attempted access at index 5, [min,max] range is [0,4] +../../../include/cpp2util.h(964) decltype(auto) cpp2::impl::assert_in_bounds(auto &&, std::source_location) [arg = 5, x:auto = std::vector]: Bounds safety violation: out of bounds access attempt detected - attempted access at index 5, [min,max] range is [0,4] diff --git a/regression-tests/test-results/apple-clang-15-c++2b/mixed-bounds-safety-with-assert.cpp.execution b/regression-tests/test-results/apple-clang-15-c++2b/mixed-bounds-safety-with-assert.cpp.execution index e4b3e0b61..0f1269838 100644 --- a/regression-tests/test-results/apple-clang-15-c++2b/mixed-bounds-safety-with-assert.cpp.execution +++ b/regression-tests/test-results/apple-clang-15-c++2b/mixed-bounds-safety-with-assert.cpp.execution @@ -1 +1 @@ -../../../include/cpp2util.h(749) : Bounds safety violation +../../../include/cpp2util.h(776) : Bounds safety violation diff --git a/regression-tests/test-results/apple-clang-15-c++2b/mixed-initialization-safety-3-contract-violation.cpp.execution b/regression-tests/test-results/apple-clang-15-c++2b/mixed-initialization-safety-3-contract-violation.cpp.execution index 9c4799bce..d4f3fe0ad 100644 --- a/regression-tests/test-results/apple-clang-15-c++2b/mixed-initialization-safety-3-contract-violation.cpp.execution +++ b/regression-tests/test-results/apple-clang-15-c++2b/mixed-initialization-safety-3-contract-violation.cpp.execution @@ -1 +1 @@ -../../../include/cpp2util.h(749) : Contract violation: fill: value must contain at least count elements +../../../include/cpp2util.h(776) : Contract violation: fill: value must contain at least count elements diff --git a/regression-tests/test-results/apple-clang-15-c++2b/mixed-lifetime-safety-and-null-contracts.cpp.execution b/regression-tests/test-results/apple-clang-15-c++2b/mixed-lifetime-safety-and-null-contracts.cpp.execution index b5f3ff105..ec5bef9c8 100644 --- a/regression-tests/test-results/apple-clang-15-c++2b/mixed-lifetime-safety-and-null-contracts.cpp.execution +++ b/regression-tests/test-results/apple-clang-15-c++2b/mixed-lifetime-safety-and-null-contracts.cpp.execution @@ -1,2 +1,2 @@ sending error to my framework... [dynamic null dereference attempt detected] -from source location: ../../../include/cpp2util.h(828) decltype(auto) cpp2::impl::assert_not_null(auto &&, std::source_location) [arg:auto = int *&] +from source location: ../../../include/cpp2util.h(855) decltype(auto) cpp2::impl::assert_not_null(auto &&, std::source_location) [arg:auto = int *&] diff --git a/regression-tests/test-results/apple-clang-15-c++2b/pure2-assert-expected-not-null.cpp.execution b/regression-tests/test-results/apple-clang-15-c++2b/pure2-assert-expected-not-null.cpp.execution index 97aef3c59..c611b6acf 100644 --- a/regression-tests/test-results/apple-clang-15-c++2b/pure2-assert-expected-not-null.cpp.execution +++ b/regression-tests/test-results/apple-clang-15-c++2b/pure2-assert-expected-not-null.cpp.execution @@ -1 +1 @@ -../../../include/cpp2util.h(828) decltype(auto) cpp2::impl::assert_not_null(auto &&, std::source_location) [arg:auto = std::expected]: Null safety violation: std::expected has an unexpected value +../../../include/cpp2util.h(855) decltype(auto) cpp2::impl::assert_not_null(auto &&, std::source_location) [arg:auto = std::expected]: Null safety violation: std::expected has an unexpected value diff --git a/regression-tests/test-results/apple-clang-15-c++2b/pure2-assert-optional-not-null.cpp.execution b/regression-tests/test-results/apple-clang-15-c++2b/pure2-assert-optional-not-null.cpp.execution index dedfd5463..57dee71f2 100644 --- a/regression-tests/test-results/apple-clang-15-c++2b/pure2-assert-optional-not-null.cpp.execution +++ b/regression-tests/test-results/apple-clang-15-c++2b/pure2-assert-optional-not-null.cpp.execution @@ -1 +1 @@ -../../../include/cpp2util.h(828) decltype(auto) cpp2::impl::assert_not_null(auto &&, std::source_location) [arg:auto = std::optional]: Null safety violation: std::optional does not contain a value +../../../include/cpp2util.h(855) decltype(auto) cpp2::impl::assert_not_null(auto &&, std::source_location) [arg:auto = std::optional]: Null safety violation: std::optional does not contain a value diff --git a/regression-tests/test-results/apple-clang-15-c++2b/pure2-assert-shared-ptr-not-null.cpp.execution b/regression-tests/test-results/apple-clang-15-c++2b/pure2-assert-shared-ptr-not-null.cpp.execution index beacf8d96..60a924d17 100644 --- a/regression-tests/test-results/apple-clang-15-c++2b/pure2-assert-shared-ptr-not-null.cpp.execution +++ b/regression-tests/test-results/apple-clang-15-c++2b/pure2-assert-shared-ptr-not-null.cpp.execution @@ -1 +1 @@ -../../../include/cpp2util.h(828) decltype(auto) cpp2::impl::assert_not_null(auto &&, std::source_location) [arg:auto = std::shared_ptr]: Null safety violation: std::shared_ptr is empty +../../../include/cpp2util.h(855) decltype(auto) cpp2::impl::assert_not_null(auto &&, std::source_location) [arg:auto = std::shared_ptr]: Null safety violation: std::shared_ptr is empty diff --git a/regression-tests/test-results/apple-clang-15-c++2b/pure2-assert-unique-ptr-not-null.cpp.execution b/regression-tests/test-results/apple-clang-15-c++2b/pure2-assert-unique-ptr-not-null.cpp.execution index e24a6337a..f8b437704 100644 --- a/regression-tests/test-results/apple-clang-15-c++2b/pure2-assert-unique-ptr-not-null.cpp.execution +++ b/regression-tests/test-results/apple-clang-15-c++2b/pure2-assert-unique-ptr-not-null.cpp.execution @@ -1 +1 @@ -../../../include/cpp2util.h(828) decltype(auto) cpp2::impl::assert_not_null(auto &&, std::source_location) [arg:auto = std::unique_ptr &]: Null safety violation: std::unique_ptr is empty +../../../include/cpp2util.h(855) decltype(auto) cpp2::impl::assert_not_null(auto &&, std::source_location) [arg:auto = std::unique_ptr]: Null safety violation: std::unique_ptr is empty diff --git a/regression-tests/test-results/apple-clang-15-c++2b/pure2-enum.cpp.execution b/regression-tests/test-results/apple-clang-15-c++2b/pure2-enum.cpp.execution index 6d9a33c28..7e6611f6d 100644 --- a/regression-tests/test-results/apple-clang-15-c++2b/pure2-enum.cpp.execution +++ b/regression-tests/test-results/apple-clang-15-c++2b/pure2-enum.cpp.execution @@ -1,5 +1,8 @@ x.to_string() is clubs x2.to_string() is clubs +x3.to_string() is hearts +x3.to_code() is skat_game::hearts +x4.to_string() is hearts with if else: clubs with inspect: clubs @@ -27,3 +30,7 @@ f is (f2) is false f2 is (f ) is false (f & f2) == f2 is true inspecting f: includes all f2's flags ('cached' and 'current') +f_from_string is (cached, current, cached_and_current) +f_from_string is (current, obsolete) +f_from_string.to_code() is (file_attributes::current | file_attributes::obsolete) +f_from_string is (cached, obsolete) diff --git a/regression-tests/test-results/clang-15-c++20-libcpp/pure2-enum.cpp.execution b/regression-tests/test-results/clang-15-c++20-libcpp/pure2-enum.cpp.execution index 6d9a33c28..7e6611f6d 100644 --- a/regression-tests/test-results/clang-15-c++20-libcpp/pure2-enum.cpp.execution +++ b/regression-tests/test-results/clang-15-c++20-libcpp/pure2-enum.cpp.execution @@ -1,5 +1,8 @@ x.to_string() is clubs x2.to_string() is clubs +x3.to_string() is hearts +x3.to_code() is skat_game::hearts +x4.to_string() is hearts with if else: clubs with inspect: clubs @@ -27,3 +30,7 @@ f is (f2) is false f2 is (f ) is false (f & f2) == f2 is true inspecting f: includes all f2's flags ('cached' and 'current') +f_from_string is (cached, current, cached_and_current) +f_from_string is (current, obsolete) +f_from_string.to_code() is (file_attributes::current | file_attributes::obsolete) +f_from_string is (cached, obsolete) diff --git a/regression-tests/test-results/clang-15-c++20/mixed-bounds-check.cpp.execution b/regression-tests/test-results/clang-15-c++20/mixed-bounds-check.cpp.execution index 48b014ff8..1d524e6e1 100644 --- a/regression-tests/test-results/clang-15-c++20/mixed-bounds-check.cpp.execution +++ b/regression-tests/test-results/clang-15-c++20/mixed-bounds-check.cpp.execution @@ -1 +1 @@ -../../../include/cpp2util.h(937) decltype(auto) cpp2::impl::assert_in_bounds(auto &&, std::source_location) [arg = 5, x:auto = std::vector]: Bounds safety violation: out of bounds access attempt detected - attempted access at index 5, [min,max] range is [0,4] +../../../include/cpp2util.h(964) decltype(auto) cpp2::impl::assert_in_bounds(auto &&, std::source_location) [arg = 5, x:auto = std::vector]: Bounds safety violation: out of bounds access attempt detected - attempted access at index 5, [min,max] range is [0,4] diff --git a/regression-tests/test-results/clang-15-c++20/mixed-bounds-safety-with-assert.cpp.execution b/regression-tests/test-results/clang-15-c++20/mixed-bounds-safety-with-assert.cpp.execution index e4b3e0b61..0f1269838 100644 --- a/regression-tests/test-results/clang-15-c++20/mixed-bounds-safety-with-assert.cpp.execution +++ b/regression-tests/test-results/clang-15-c++20/mixed-bounds-safety-with-assert.cpp.execution @@ -1 +1 @@ -../../../include/cpp2util.h(749) : Bounds safety violation +../../../include/cpp2util.h(776) : Bounds safety violation diff --git a/regression-tests/test-results/clang-15-c++20/mixed-initialization-safety-3-contract-violation.cpp.execution b/regression-tests/test-results/clang-15-c++20/mixed-initialization-safety-3-contract-violation.cpp.execution index 9c4799bce..d4f3fe0ad 100644 --- a/regression-tests/test-results/clang-15-c++20/mixed-initialization-safety-3-contract-violation.cpp.execution +++ b/regression-tests/test-results/clang-15-c++20/mixed-initialization-safety-3-contract-violation.cpp.execution @@ -1 +1 @@ -../../../include/cpp2util.h(749) : Contract violation: fill: value must contain at least count elements +../../../include/cpp2util.h(776) : Contract violation: fill: value must contain at least count elements diff --git a/regression-tests/test-results/clang-15-c++20/mixed-lifetime-safety-and-null-contracts.cpp.execution b/regression-tests/test-results/clang-15-c++20/mixed-lifetime-safety-and-null-contracts.cpp.execution index b5f3ff105..ec5bef9c8 100644 --- a/regression-tests/test-results/clang-15-c++20/mixed-lifetime-safety-and-null-contracts.cpp.execution +++ b/regression-tests/test-results/clang-15-c++20/mixed-lifetime-safety-and-null-contracts.cpp.execution @@ -1,2 +1,2 @@ sending error to my framework... [dynamic null dereference attempt detected] -from source location: ../../../include/cpp2util.h(828) decltype(auto) cpp2::impl::assert_not_null(auto &&, std::source_location) [arg:auto = int *&] +from source location: ../../../include/cpp2util.h(855) decltype(auto) cpp2::impl::assert_not_null(auto &&, std::source_location) [arg:auto = int *&] diff --git a/regression-tests/test-results/clang-15-c++20/pure2-assert-optional-not-null.cpp.execution b/regression-tests/test-results/clang-15-c++20/pure2-assert-optional-not-null.cpp.execution index dedfd5463..57dee71f2 100644 --- a/regression-tests/test-results/clang-15-c++20/pure2-assert-optional-not-null.cpp.execution +++ b/regression-tests/test-results/clang-15-c++20/pure2-assert-optional-not-null.cpp.execution @@ -1 +1 @@ -../../../include/cpp2util.h(828) decltype(auto) cpp2::impl::assert_not_null(auto &&, std::source_location) [arg:auto = std::optional]: Null safety violation: std::optional does not contain a value +../../../include/cpp2util.h(855) decltype(auto) cpp2::impl::assert_not_null(auto &&, std::source_location) [arg:auto = std::optional]: Null safety violation: std::optional does not contain a value diff --git a/regression-tests/test-results/clang-15-c++20/pure2-assert-shared-ptr-not-null.cpp.execution b/regression-tests/test-results/clang-15-c++20/pure2-assert-shared-ptr-not-null.cpp.execution index beacf8d96..60a924d17 100644 --- a/regression-tests/test-results/clang-15-c++20/pure2-assert-shared-ptr-not-null.cpp.execution +++ b/regression-tests/test-results/clang-15-c++20/pure2-assert-shared-ptr-not-null.cpp.execution @@ -1 +1 @@ -../../../include/cpp2util.h(828) decltype(auto) cpp2::impl::assert_not_null(auto &&, std::source_location) [arg:auto = std::shared_ptr]: Null safety violation: std::shared_ptr is empty +../../../include/cpp2util.h(855) decltype(auto) cpp2::impl::assert_not_null(auto &&, std::source_location) [arg:auto = std::shared_ptr]: Null safety violation: std::shared_ptr is empty diff --git a/regression-tests/test-results/clang-15-c++20/pure2-assert-unique-ptr-not-null.cpp.execution b/regression-tests/test-results/clang-15-c++20/pure2-assert-unique-ptr-not-null.cpp.execution index e24a6337a..f8b437704 100644 --- a/regression-tests/test-results/clang-15-c++20/pure2-assert-unique-ptr-not-null.cpp.execution +++ b/regression-tests/test-results/clang-15-c++20/pure2-assert-unique-ptr-not-null.cpp.execution @@ -1 +1 @@ -../../../include/cpp2util.h(828) decltype(auto) cpp2::impl::assert_not_null(auto &&, std::source_location) [arg:auto = std::unique_ptr &]: Null safety violation: std::unique_ptr is empty +../../../include/cpp2util.h(855) decltype(auto) cpp2::impl::assert_not_null(auto &&, std::source_location) [arg:auto = std::unique_ptr]: Null safety violation: std::unique_ptr is empty diff --git a/regression-tests/test-results/clang-15-c++20/pure2-enum.cpp.execution b/regression-tests/test-results/clang-15-c++20/pure2-enum.cpp.execution index 6d9a33c28..7e6611f6d 100644 --- a/regression-tests/test-results/clang-15-c++20/pure2-enum.cpp.execution +++ b/regression-tests/test-results/clang-15-c++20/pure2-enum.cpp.execution @@ -1,5 +1,8 @@ x.to_string() is clubs x2.to_string() is clubs +x3.to_string() is hearts +x3.to_code() is skat_game::hearts +x4.to_string() is hearts with if else: clubs with inspect: clubs @@ -27,3 +30,7 @@ f is (f2) is false f2 is (f ) is false (f & f2) == f2 is true inspecting f: includes all f2's flags ('cached' and 'current') +f_from_string is (cached, current, cached_and_current) +f_from_string is (current, obsolete) +f_from_string.to_code() is (file_attributes::current | file_attributes::obsolete) +f_from_string is (cached, obsolete) diff --git a/regression-tests/test-results/clang-18-c++20/pure2-enum.cpp.execution b/regression-tests/test-results/clang-18-c++20/pure2-enum.cpp.execution index 6d9a33c28..7e6611f6d 100644 --- a/regression-tests/test-results/clang-18-c++20/pure2-enum.cpp.execution +++ b/regression-tests/test-results/clang-18-c++20/pure2-enum.cpp.execution @@ -1,5 +1,8 @@ x.to_string() is clubs x2.to_string() is clubs +x3.to_string() is hearts +x3.to_code() is skat_game::hearts +x4.to_string() is hearts with if else: clubs with inspect: clubs @@ -27,3 +30,7 @@ f is (f2) is false f2 is (f ) is false (f & f2) == f2 is true inspecting f: includes all f2's flags ('cached' and 'current') +f_from_string is (cached, current, cached_and_current) +f_from_string is (current, obsolete) +f_from_string.to_code() is (file_attributes::current | file_attributes::obsolete) +f_from_string is (cached, obsolete) diff --git a/regression-tests/test-results/clang-18-c++23-libcpp/pure2-enum.cpp.execution b/regression-tests/test-results/clang-18-c++23-libcpp/pure2-enum.cpp.execution index 6d9a33c28..7e6611f6d 100644 --- a/regression-tests/test-results/clang-18-c++23-libcpp/pure2-enum.cpp.execution +++ b/regression-tests/test-results/clang-18-c++23-libcpp/pure2-enum.cpp.execution @@ -1,5 +1,8 @@ x.to_string() is clubs x2.to_string() is clubs +x3.to_string() is hearts +x3.to_code() is skat_game::hearts +x4.to_string() is hearts with if else: clubs with inspect: clubs @@ -27,3 +30,7 @@ f is (f2) is false f2 is (f ) is false (f & f2) == f2 is true inspecting f: includes all f2's flags ('cached' and 'current') +f_from_string is (cached, current, cached_and_current) +f_from_string is (current, obsolete) +f_from_string.to_code() is (file_attributes::current | file_attributes::obsolete) +f_from_string is (cached, obsolete) diff --git a/regression-tests/test-results/gcc-13-c++2b/mixed-bugfix-for-ufcs-non-local.cpp.output b/regression-tests/test-results/gcc-13-c++2b/mixed-bugfix-for-ufcs-non-local.cpp.output index 459a8add9..a62e6b086 100644 --- a/regression-tests/test-results/gcc-13-c++2b/mixed-bugfix-for-ufcs-non-local.cpp.output +++ b/regression-tests/test-results/gcc-13-c++2b/mixed-bugfix-for-ufcs-non-local.cpp.output @@ -1,41 +1,41 @@ In file included from mixed-bugfix-for-ufcs-non-local.cpp:6: ../../../include/cpp2util.h:2100:1: error: lambda-expression in template parameter type - 2100 | // + 2100 | constexpr auto is( X const& x ) -> bool | ^ ../../../include/cpp2util.h:2137:59: note: in expansion of macro ‘CPP2_UFCS_’ - 2137 | // Value case + 2137 | | ^ mixed-bugfix-for-ufcs-non-local.cpp2:13:12: note: in expansion of macro ‘CPP2_UFCS_NONLOCAL’ mixed-bugfix-for-ufcs-non-local.cpp2:13:36: error: template argument 1 is invalid ../../../include/cpp2util.h:2100:1: error: lambda-expression in template parameter type - 2100 | // + 2100 | constexpr auto is( X const& x ) -> bool | ^ ../../../include/cpp2util.h:2137:59: note: in expansion of macro ‘CPP2_UFCS_’ - 2137 | // Value case + 2137 | | ^ mixed-bugfix-for-ufcs-non-local.cpp2:21:12: note: in expansion of macro ‘CPP2_UFCS_NONLOCAL’ mixed-bugfix-for-ufcs-non-local.cpp2:21:36: error: template argument 1 is invalid ../../../include/cpp2util.h:2100:1: error: lambda-expression in template parameter type - 2100 | // + 2100 | constexpr auto is( X const& x ) -> bool | ^ ../../../include/cpp2util.h:2137:59: note: in expansion of macro ‘CPP2_UFCS_’ - 2137 | // Value case + 2137 | | ^ mixed-bugfix-for-ufcs-non-local.cpp2:31:12: note: in expansion of macro ‘CPP2_UFCS_NONLOCAL’ mixed-bugfix-for-ufcs-non-local.cpp2:31:36: error: template argument 1 is invalid ../../../include/cpp2util.h:2100:1: error: lambda-expression in template parameter type - 2100 | // + 2100 | constexpr auto is( X const& x ) -> bool | ^ ../../../include/cpp2util.h:2137:59: note: in expansion of macro ‘CPP2_UFCS_’ - 2137 | // Value case + 2137 | | ^ mixed-bugfix-for-ufcs-non-local.cpp2:33:12: note: in expansion of macro ‘CPP2_UFCS_NONLOCAL’ mixed-bugfix-for-ufcs-non-local.cpp2:33:36: error: template argument 1 is invalid ../../../include/cpp2util.h:2100:1: error: lambda-expression in template parameter type - 2100 | // + 2100 | constexpr auto is( X const& x ) -> bool | ^ ../../../include/cpp2util.h:2137:59: note: in expansion of macro ‘CPP2_UFCS_’ - 2137 | // Value case + 2137 | | ^ mixed-bugfix-for-ufcs-non-local.cpp2:21:12: note: in expansion of macro ‘CPP2_UFCS_NONLOCAL’ mixed-bugfix-for-ufcs-non-local.cpp2:21:36: error: template argument 1 is invalid diff --git a/regression-tests/test-results/gcc-13-c++2b/pure2-enum.cpp.execution b/regression-tests/test-results/gcc-13-c++2b/pure2-enum.cpp.execution index 6d9a33c28..7e6611f6d 100644 --- a/regression-tests/test-results/gcc-13-c++2b/pure2-enum.cpp.execution +++ b/regression-tests/test-results/gcc-13-c++2b/pure2-enum.cpp.execution @@ -1,5 +1,8 @@ x.to_string() is clubs x2.to_string() is clubs +x3.to_string() is hearts +x3.to_code() is skat_game::hearts +x4.to_string() is hearts with if else: clubs with inspect: clubs @@ -27,3 +30,7 @@ f is (f2) is false f2 is (f ) is false (f & f2) == f2 is true inspecting f: includes all f2's flags ('cached' and 'current') +f_from_string is (cached, current, cached_and_current) +f_from_string is (current, obsolete) +f_from_string.to_code() is (file_attributes::current | file_attributes::obsolete) +f_from_string is (cached, obsolete) diff --git a/regression-tests/test-results/gcc-14-c++2b/mixed-bugfix-for-ufcs-non-local.cpp.output b/regression-tests/test-results/gcc-14-c++2b/mixed-bugfix-for-ufcs-non-local.cpp.output index d3bea83fa..a62e6b086 100644 --- a/regression-tests/test-results/gcc-14-c++2b/mixed-bugfix-for-ufcs-non-local.cpp.output +++ b/regression-tests/test-results/gcc-14-c++2b/mixed-bugfix-for-ufcs-non-local.cpp.output @@ -1,6 +1,6 @@ In file included from mixed-bugfix-for-ufcs-non-local.cpp:6: ../../../include/cpp2util.h:2100:1: error: lambda-expression in template parameter type - 2100 | //------------------------------------------------------------------------------------------------------------- + 2100 | constexpr auto is( X const& x ) -> bool | ^ ../../../include/cpp2util.h:2137:59: note: in expansion of macro ‘CPP2_UFCS_’ 2137 | @@ -8,7 +8,7 @@ In file included from mixed-bugfix-for-ufcs-non-local.cpp:6: mixed-bugfix-for-ufcs-non-local.cpp2:13:12: note: in expansion of macro ‘CPP2_UFCS_NONLOCAL’ mixed-bugfix-for-ufcs-non-local.cpp2:13:36: error: template argument 1 is invalid ../../../include/cpp2util.h:2100:1: error: lambda-expression in template parameter type - 2100 | //------------------------------------------------------------------------------------------------------------- + 2100 | constexpr auto is( X const& x ) -> bool | ^ ../../../include/cpp2util.h:2137:59: note: in expansion of macro ‘CPP2_UFCS_’ 2137 | @@ -16,7 +16,7 @@ mixed-bugfix-for-ufcs-non-local.cpp2:13:36: error: template argument 1 is invali mixed-bugfix-for-ufcs-non-local.cpp2:21:12: note: in expansion of macro ‘CPP2_UFCS_NONLOCAL’ mixed-bugfix-for-ufcs-non-local.cpp2:21:36: error: template argument 1 is invalid ../../../include/cpp2util.h:2100:1: error: lambda-expression in template parameter type - 2100 | //------------------------------------------------------------------------------------------------------------- + 2100 | constexpr auto is( X const& x ) -> bool | ^ ../../../include/cpp2util.h:2137:59: note: in expansion of macro ‘CPP2_UFCS_’ 2137 | @@ -24,7 +24,7 @@ mixed-bugfix-for-ufcs-non-local.cpp2:21:36: error: template argument 1 is invali mixed-bugfix-for-ufcs-non-local.cpp2:31:12: note: in expansion of macro ‘CPP2_UFCS_NONLOCAL’ mixed-bugfix-for-ufcs-non-local.cpp2:31:36: error: template argument 1 is invalid ../../../include/cpp2util.h:2100:1: error: lambda-expression in template parameter type - 2100 | //------------------------------------------------------------------------------------------------------------- + 2100 | constexpr auto is( X const& x ) -> bool | ^ ../../../include/cpp2util.h:2137:59: note: in expansion of macro ‘CPP2_UFCS_’ 2137 | @@ -32,7 +32,7 @@ mixed-bugfix-for-ufcs-non-local.cpp2:31:36: error: template argument 1 is invali mixed-bugfix-for-ufcs-non-local.cpp2:33:12: note: in expansion of macro ‘CPP2_UFCS_NONLOCAL’ mixed-bugfix-for-ufcs-non-local.cpp2:33:36: error: template argument 1 is invalid ../../../include/cpp2util.h:2100:1: error: lambda-expression in template parameter type - 2100 | //------------------------------------------------------------------------------------------------------------- + 2100 | constexpr auto is( X const& x ) -> bool | ^ ../../../include/cpp2util.h:2137:59: note: in expansion of macro ‘CPP2_UFCS_’ 2137 | diff --git a/regression-tests/test-results/msvc-2022-c++20/pure2-assert-expected-not-null.cpp.output b/regression-tests/test-results/msvc-2022-c++20/pure2-assert-expected-not-null.cpp.output index 272a4fc8d..a05de73b4 100644 --- a/regression-tests/test-results/msvc-2022-c++20/pure2-assert-expected-not-null.cpp.output +++ b/regression-tests/test-results/msvc-2022-c++20/pure2-assert-expected-not-null.cpp.output @@ -6,7 +6,7 @@ pure2-assert-expected-not-null.cpp2(7): error C2143: syntax error: missing ';' b pure2-assert-expected-not-null.cpp2(7): error C2143: syntax error: missing ';' before '}' pure2-assert-expected-not-null.cpp2(9): error C2065: 'ex': undeclared identifier pure2-assert-expected-not-null.cpp2(9): error C2672: 'cpp2::impl::assert_not_null': no matching overloaded function found -D:\a\cppfront\cppfront\include\cpp2util.h(828): note: could be 'decltype(auto) cpp2::impl::assert_not_null(_T0 &&,std::source_location)' +D:\a\cppfront\cppfront\include\cpp2util.h(855): note: could be 'decltype(auto) cpp2::impl::assert_not_null(_T0 &&,std::source_location)' pure2-assert-expected-not-null.cpp2(14): error C2039: 'expected': is not a member of 'std' predefined C++ types (compiler internal)(347): note: see declaration of 'std' pure2-assert-expected-not-null.cpp2(14): error C2062: type 'int' unexpected @@ -19,4 +19,4 @@ pure2-assert-expected-not-null.cpp2(14): note: while trying to match the argumen pure2-assert-expected-not-null.cpp2(14): error C2143: syntax error: missing ';' before '}' pure2-assert-expected-not-null.cpp2(15): error C2065: 'ex': undeclared identifier pure2-assert-expected-not-null.cpp2(15): error C2672: 'cpp2::impl::assert_not_null': no matching overloaded function found -D:\a\cppfront\cppfront\include\cpp2util.h(828): note: could be 'decltype(auto) cpp2::impl::assert_not_null(_T0 &&,std::source_location)' +D:\a\cppfront\cppfront\include\cpp2util.h(855): note: could be 'decltype(auto) cpp2::impl::assert_not_null(_T0 &&,std::source_location)' diff --git a/regression-tests/test-results/msvc-2022-c++20/pure2-enum.cpp.execution b/regression-tests/test-results/msvc-2022-c++20/pure2-enum.cpp.execution index 6d9a33c28..7e6611f6d 100644 --- a/regression-tests/test-results/msvc-2022-c++20/pure2-enum.cpp.execution +++ b/regression-tests/test-results/msvc-2022-c++20/pure2-enum.cpp.execution @@ -1,5 +1,8 @@ x.to_string() is clubs x2.to_string() is clubs +x3.to_string() is hearts +x3.to_code() is skat_game::hearts +x4.to_string() is hearts with if else: clubs with inspect: clubs @@ -27,3 +30,7 @@ f is (f2) is false f2 is (f ) is false (f & f2) == f2 is true inspecting f: includes all f2's flags ('cached' and 'current') +f_from_string is (cached, current, cached_and_current) +f_from_string is (current, obsolete) +f_from_string.to_code() is (file_attributes::current | file_attributes::obsolete) +f_from_string is (cached, obsolete) diff --git a/regression-tests/test-results/msvc-2022-c++latest/pure2-regex_10_escapes.cpp.execution b/regression-tests/test-results/msvc-2022-c++latest/pure2-regex_10_escapes.cpp.execution index 41bd8a972..d973c4161 100644 --- a/regression-tests/test-results/msvc-2022-c++latest/pure2-regex_10_escapes.cpp.execution +++ b/regression-tests/test-results/msvc-2022-c++latest/pure2-regex_10_escapes.cpp.execution @@ -26,6 +26,21 @@ bar result_expr: $1-$2 expected_results foo- bar result_expr: $1-$2 expected_results + +bar result_expr: $1-$2 expected_results foo- +13_y: OK regex: (\v+)(\V) parsed_regex: (\v+)(\V) str: foo + + +bar result_expr: $1-$2 expected_results + + +-b +14_y: OK regex: foo(\v)bar parsed_regex: foo(\v)bar str: foo +bar result_expr: $1 expected_results +15_y: OK regex: (\V)(\v) parsed_regex: (\V)(\v) str: foo +bar result_expr: $1-$2 expected_results o- +16_y: OK regex: (\v)(\V) parsed_regex: (\v)(\V) str: foo +bar result_expr: $1-$2 expected_results -b 14_y: OK regex: foo(\v)bar parsed_regex: foo(\v)bar str: foo bar result_expr: $1 expected_results From 630e61a28031c076c510a0fdf1b4fc15cd64b6b4 Mon Sep 17 00:00:00 2001 From: Neil Henderson <2060747+bluetarpmedia@users.noreply.github.com> Date: Tue, 6 Aug 2024 09:14:37 +1000 Subject: [PATCH 19/22] Fix clang conversion warnings in `string_util::split_string_list` (#1207) * Fix clang conversion warnings in `string_util::split_string_list` * Keeping fix while keeping signed type How does this look? --------- Co-authored-by: Herb Sutter --- include/cpp2util.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/cpp2util.h b/include/cpp2util.h index f35914ef2..0572a9820 100644 --- a/include/cpp2util.h +++ b/include/cpp2util.h @@ -389,7 +389,7 @@ auto split_string_list(std::string_view str) return std::isalnum(c) || c == '_'; }; - auto pos = 0; + auto pos = decltype(std::ssize(str)){ 0 }; while( pos < std::ssize(str) ) { // Skip non-alnum while (pos < std::ssize(str) && !is_id_char(str[pos])) { From c618ed507573e935d351b2918a6f1121a6e5bb5c Mon Sep 17 00:00:00 2001 From: Max Sagebaum Date: Tue, 6 Aug 2024 16:15:48 +0200 Subject: [PATCH 20/22] Documentation for regular expressions. (#1195) * Documentation for regular expressions. * Update for regex documentation and improved matching detection of regex names. * Fixes for line endings in msvc-2022. * Pass through regex docs Mainly turn code lists into tables * Small fixes after update from Herb. * Fix for regression tests. * Quote tweak Signed-off-by: Herb Sutter --------- Signed-off-by: Herb Sutter Co-authored-by: Herb Sutter --- docs/cpp2/metafunctions.md | 82 ++++++ docs/notes/regex_status.md | 233 ++++++++++++++++++ mkdocs.yml | 2 + .../pure2-regex_10_escapes.cpp.execution | 31 +-- source/reflect.h | 8 +- source/reflect.h2 | 8 +- 6 files changed, 330 insertions(+), 34 deletions(-) create mode 100644 docs/notes/regex_status.md diff --git a/docs/cpp2/metafunctions.md b/docs/cpp2/metafunctions.md index f7075e288..b434483d8 100644 --- a/docs/cpp2/metafunctions.md +++ b/docs/cpp2/metafunctions.md @@ -360,6 +360,88 @@ main: () = { ``` +### For computational and functional types + + +#### `regex` + +A `regex` type has data members that are regular expression objects. This metafunction replaces all of the type's data members named `regex` or `regex_*` with regular expression objects of the same type. For example: + +``` cpp title="Regular expression example" hl_lines="1 3 4 16 17 19 27 30 31" +name_matcher: @regex type += { + regex := R"((\w+) (\w+))"; // for example: Margaret Hamilton + regex_no_case := R"(/(ab)+/i)"; // case insensitive match of "ab"+ +} + +main: (args) = { + m: name_matcher = (); + + data: std::string = "Donald Duck"; + if args.ssize() >= 2 { + data = args[1]; + } + + // regex.match requires matches to match the entire string, from start to end + result := m.regex.match(data); + if result.matched { + // We found a match; reverse the order of the substrings + std::cout << "Hello (result.group(2))$, (result.group(1))$!\n"; + } + else { + std::cout << "I only know names of the form: .\n"; + } + + // regex.search finds a match anywhere within the target string + std::cout << "Case insensitive match: " + "(m.regex_no_case.search(\"blubabABblah\").group(0))$\n"; +} +// Prints: +// Hello Duck, Donald! +// Case insensitive match: abAB +``` + +The `@regex` metafunction currently supports most of [Perl regex syntax](https://perldoc.perl.org/perlre), except for Unicode characters and the syntax tokens associated with them. See [Supported regular expression features](../notes/regex_status.md) for a list of regex options. + +Each regex object has the type `cpp2::regex::regular_expression`, which is defined in `include/cpp2regex.h2`. The member functions are: + +``` cpp title="Member functions for regular expressions" +// .match() requires matches to match the entire string, from start to end +// .search() finds a match anywhere within the target string + +match : (this, str: std::string_view) -> search_return; +search: (this, str: std::string_view) -> search_return; + +match : (this, str: std::string_view, start) -> search_return; +search: (this, str: std::string_view, start) -> search_return; + +match : (this, str: std::string_view, start, length) -> search_return; +search: (this, str: std::string_view, start, length) -> search_return; + +match : (this, start: Iter, end: Iter) -> search_return; +search: (this, start: Iter, end: Iter) -> search_return; +``` + +The return type `search_return` is defined in `cpp2::regex::regular_expression`. It has these members: + +``` cpp title="Members of a regular expression result" +matched: bool; +pos: int; + +// Functions to access groups by number +group_number: (this) -> size_t;; +group: (this, g: int) -> std::string; +group_start: (this, g: int) -> int; +group_end: (this, g: int) -> int; + +// Functions to access groups by name +group: (this, g: bstring) -> std::string; +group_start: (this, g: bstring) -> int; +group_end: (this, g: bstring) -> int; +``` + + + ### Helpers and utilities diff --git a/docs/notes/regex_status.md b/docs/notes/regex_status.md new file mode 100644 index 000000000..fa43c928a --- /dev/null +++ b/docs/notes/regex_status.md @@ -0,0 +1,233 @@ +# Supported regular expression features + +The listings are taken from the [Perl regex docs](https://perldoc.perl.org/perlre). Regular expressions are applied via the [`regex` metafunction](../cpp2/metafunctions.md#regex). + + +## Currently supported or planned features + + +### Modifiers + +| Modifier | Notes | Status | +| --- | --- | --- | +| **`i`** | Do case-insensitive pattern matching. For example, "A" will match "a" under `/i`. | Supported | +| **`m`** | Treat the string being matched against as multiple lines. That is, change `^` and `$` from matching the start of the string's first line and the end of its last line to matching the start and end of each line within the string. | Supported | +| **`s`** | Treat the string as single line. That is, change `.` to match any character whatsoever, even a newline, which normally it would not match. | Supported | +| ***`x` and `xx`** | Extend your pattern's legibility by permitting whitespace and comments. For details see: [Perl regex docs: `/x` and `/xx`](https://perldoc.perl.org/perlre#/x-and-/xx). | Supported | +| **`n`** | Prevent the grouping metacharacters `(` and `)` from capturing. This modifier will stop `$1`, `$2`, etc. from being filled in. | Supported | +| **`c`** | Keep the current position during repeated matching. | Planned | + + +### Escape sequences __(Complete)__ + +| Escape sequence | Notes | Status | +| --- | --- | --- | +| **`\t`** | Tab (HT, TAB)X | Supported | +| **`\n`** | Newline (LF, NL) | Supported | +| **`\r`** | Return (CR) | Supported | +| **`\f`** | Form feed (FF) | Supported | +| **`\a`** | Alarm (bell) (BEL) | Supported | +| **`\e`** | Escape (think troff) (ESC) | Supported | +| **`\x{}`, `\x00`** | Character whose ordinal is the given hexadecimal number | Supported | +| **`\o{}`, `\000`** | Character whose ordinal is the given octal number | Supported | + + +### Quantifiers __(Complete)__ + +| Quantifier | Notes | Status | +| --- | --- | --- | +| **`*`** | Match 0 or more times | Supported | +| **`+`** | Match 1 or more times | Supported | +| **`?`** | Match 1 or 0 times | Supported | +| **`{n}`** | Match exactly n times | Supported | +| **`{n,}`** | Match at least n times | Supported | +| **`{,n}`** | Match at most n times | Supported | +| **`{n,m}`** | Match at least n but not more than m times | Supported | +| | | | +| **`*?`** | Match 0 or more times, not greedily | Supported | +| **`+?`** | Match 1 or more times, not greedily | Supported | +| **`??`** | Match 0 or 1 time, not greedily | Supported | +| **`{n}?`** | Match exactly n times, not greedily (redundant) | Supported | +| **`{n,}?`** | Match at least n times, not greedily | Supported | +| **`{,n}?`** | Match at most n times, not greedily | Supported | +| **`{n,m}?`** | Match at least n but not more than m times, not greedily | Supported | +| | | | +| **`*+`** | Match 0 or more times and give nothing back | Supported | +| **`++`** | Match 1 or more times and give nothing back | Supported | +| **`?+`** | Match 0 or 1 time and give nothing back | Supported | +| **`{n}+`** | Match exactly n times and give nothing back (redundant) | Supported | +| **`{n,}+`** | Match at least n times and give nothing back | Supported | +| **`{,n}+`** | Match at most n times and give nothing back | Supported | +| **`{n,m}+`** | Match at least n but not more than m times and give nothing back | Supported | + + +### Character Classes and other Special Escapes __(Complete)__ + +| Feature | Notes | Status | +| --- | --- | --- | +| **`[`...`]`** | Match a character according to the rules of the bracketed character class defined by the "...". Example: `[a-z]` matches "a" or "b" or "c" ... or "z" | Supported | +| **`[[:`...`:]]`** | Match a character according to the rules of the POSIX character class "..." within the outer bracketed character class. Example: `[[:upper:]]` matches any uppercase character. | Supported | +| **`\g1`** or **`\g{-1}`** | Backreference to a specific or previous group. The number may be negative indicating a relative previous group and may optionally be wrapped in curly brackets for safer parsing. | Supported | +| **`\g{name}`** | Named backreference | Supported | +| **`\k`** | Named backreference | Supported | +| **`\k'name'`** | Named backreference | Supported | +| **`\k{name}`** | Named backreference | Supported | +| **`\w`** | Match a "word" character (alphanumeric plus "_", plus other connector punctuation chars plus Unicode marks) | Supported | +| **`\W`** | Match a non-"word" character | Supported | +| **`\s`** | Match a whitespace character | Supported | +| **`\S`** | Match a non-whitespace character | Supported | +| **`\d`** | Match a decimal digit character | Supported | +| **`\D`** | Match a non-digit character | Supported | +| **`\v`** | Vertical whitespace | Supported | +| **`\V`** | Not vertical whitespace | Supported | +| **`\h`** | Horizontal whitespace | Supported | +| **`\H`** | Not horizontal whitespace | Supported | +| **`\1`** | Backreference to a specific capture group or buffer. '1' may actually be any positive integer. | Supported | +| **`\N`** | Any character but \n. Not affected by /s modifier | Supported | +| **`\K`** | Keep the stuff left of the \K, don't include it in $& | Supported | + + +### Assertions + +| Assertion | Notes | Status | +| --- | --- | --- | +| **`\b`** | Match a \w\W or \W\w boundary | Supported | +| **`\B`** | Match except at a \w\W or \W\w boundary | Supported | +| **`\A`** | Match only at beginning of string | Supported | +| **`\Z`** | Match only at end of string, or before newline at the end | Supported | +| **`\z`** | Match only at end of string | Supported | +| **`\G`** | Match only at pos() (e.g. at the end-of-match position of prior m//g) | Planned | + + +### Capture groups __(Complete)__ + +| Feature | Status | +| --- | --- | +| **`(`...`)`** | Supported | + + +### Quoting metacharacters __(Complete)__ + +| Feature | Status | +| --- | --- | +| **For `^.[]$()*{}?+|\`** | Supported | + + +### Extended Patterns + +| Extended pattern | Notes | Status | +| --- | --- | --- | +| **`(?pattern)`** | Named capture group | Supported | +| **`(?#text)`** | Comments | Supported | +| **`(?adlupimnsx-imnsx)`** | Modification for surrounding context | Supported | +| **`(?^alupimnsx)`** | Modification for surrounding context | Supported | +| **`(?:pattern)`** | Clustering, does not generate a group index. | Supported | +| **`(?adluimnsx-imnsx:pattern)`** | Clustering, does not generate a group index and modifications for the cluster. | Supported | +| **`(?^aluimnsx:pattern)`** | Clustering, does not generate a group index and modifications for the cluster. | Supported | +| **`(?`|`pattern)`** | Branch reset | Supported | +| **`(?'NAME'pattern)`** | Named capture group | Supported | +| **`(?(condition)yes-pattern`|`no-pattern)`** | Conditional patterns. | Planned | +| **`(?(condition)yes-pattern)`** | Conditional patterns. | Planned | +| **`(?>pattern)`** | Atomic patterns. (Disable backtrack.) | Planned | +| **`(*atomic:pattern)`** | Atomic patterns. (Disable backtrack.) | Planned | + + +### Lookaround Assertions + +| Lookaround assertion | Notes | Status | +| --- | --- | --- | +| **`(?=pattern)`** | Positive look ahead. | Supported | +| **`(*pla:pattern)`** | Positive look ahead. | Supported | +| **`(*positive_lookahead:pattern)`** | Positive look ahead. | Supported | +| **`(?!pattern)`** | Negative look ahead. | Supported | +| **`(*nla:pattern)`** | Negative look ahead. | Supported | +| **`(*negative_lookahead:pattern)`** | Negative look ahead. | Supported | +| **`(?<=pattern)`** | Positive look behind. | Planned | +| **`(*plb:pattern)`** | Positive look behind. | Planned | +| **`(*positive_lookbehind:pattern)`** | Positive look behind. | Planned | +| **`(?Planned | +| **`(*nlb:pattern)`** | Negative look behind. | Planned | +| **`(*negative_lookbehind:pattern)`** | Negative look behind. | Planned | + + +### Special Backtracking Control Verbs + +| Backtracking control verb | Notes | Status | +| --- | --- | --- | +| **`(*SKIP) (*SKIP:NAME)`** | Start next search here. | Planned | +| **`(*PRUNE) (*PRUNE:NAME)`** | No backtracking over this point. | Planned | +| **`(*MARK:NAME) (*:NAME)`** | Place a named mark. | Planned | +| **`(*THEN) (*THEN:NAME)`** | Like PRUNE. | Planned | +| **`(*COMMIT) (*COMMIT:arg)`** | Stop searching. | Planned | +| **`(*FAIL) (*F) (*FAIL:arg)`** | Fail the pattern/branch. | Planned | +| **`(*ACCEPT) (*ACCEPT:arg)`** | Accept the pattern/subpattern. | Planned | + + +## Not planned (Mainly because of Unicode or perl specifics) + +### Modifiers + +| Modifier | Notes | Status | +| --- | --- | --- | +| `p` | Preserve the string matched such that ${^PREMATCH}, ${^MATCH}, and ${^POSTMATCH} are available for use after matching. | Not planned | +| `a`, `d`, `l`, and `u` | These modifiers affect which character-set rules (Unicode, etc.) are used, as described below in "Character set modifiers". | Not planned | +| `g` | globally match the pattern repeatedly in the string | Not planned | +| `e` | evaluate the right-hand side as an expression | Not planned | +| `ee` | evaluate the right side as a string then eval the result | Not planned | +| `o` | pretend to optimize your code, but actually introduce bugs | Not planned | +| `r` | perform non-destructive substitution and return the new value | Not planned | + + +### Escape sequences + +| Escape sequence | Notes | Status | +| --- | --- | --- | +| `\cK` | control char (example: VT) | Not planned | +| `\N{name}` | named Unicode character or character sequence | Not planned | +| `\N{U+263D}` | Unicode character (example: FIRST QUARTER MOON) | Not planned | +| `\l` | lowercase next char (think vi) | Not planned | +| `\u` | uppercase next char (think vi) | Not planned | +| `\L` | lowercase until \E (think vi) | Not planned | +| `\U` | uppercase until \E (think vi) | Not planned | +| `\Q` | quote (disable) pattern metacharacters until \E | Not planned | +| `\E` | end either case modification or quoted section, think vi | Not planned | + + +### Character Classes and other Special Escapes + +| Character class or escape | Notes | Status | +| --- | --- | --- | +| `(?[...])` | Extended bracketed character class | Not planned | +| `\pP` | Match P, named property. Use \p{Prop} for longer names | Not planned | +| `\PP` | Match non-P | Not planned | +| `\X` | Match Unicode "eXtended grapheme cluster" | Not planned | +| `\R` | Linebreak | Not planned | + + +### Assertions + +| Assertion | Notes | Status | +| --- | --- | --- | +| `\b{}` | Match at Unicode boundary of specified type | Not planned | +| `\B{}` | Match where corresponding \b{} doesn't match | Not planned | + +### Extended Patterns + + +| Extended pattern | Notes | Status | +| --- | --- | --- | +| `(?{ code })` | Perl code execution. | Not planned | +| `(*{ code })` | Perl code execution. | Not planned | +| `(??{ code })` | Perl code execution. | Not planned | +| `(?PARNO)` `(?-PARNO)` `(?+PARNO)` `(?R)` `(?0)` | Recursive subpattern. | Not planned | +| `(?&NAME)` | Recursive subpattern. | Not planned | + + +### Script runs + +| Script runs | Notes | Status | +| --- | --- | --- | +| `(*script_run:pattern)` | All chars in pattern need to be of the same script. | Not planned | +| `(*sr:pattern)` | All chars in pattern need to be of the same script. | Not planned | +| `(*atomic_script_run:pattern)` | Without backtracking. | Not planned | +| `(*asr:pattern)` | Without backtracking. | Not planned | diff --git a/mkdocs.yml b/mkdocs.yml index cac325c0c..8d8ea9494 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -68,6 +68,8 @@ nav: - 'Cppfront reference': - 'Using Cpp1 (today''s syntax) and Cpp2 in the same source file': cppfront/mixed.md - 'Cppfront command line options': cppfront/options.md + - 'Notes and supplemental topics': + - '@regex status: Regular expression features': notes/regex_status.md markdown_extensions: - pymdownx.highlight: diff --git a/regression-tests/test-results/msvc-2022-c++latest/pure2-regex_10_escapes.cpp.execution b/regression-tests/test-results/msvc-2022-c++latest/pure2-regex_10_escapes.cpp.execution index d973c4161..fe6e6efc9 100644 --- a/regression-tests/test-results/msvc-2022-c++latest/pure2-regex_10_escapes.cpp.execution +++ b/regression-tests/test-results/msvc-2022-c++latest/pure2-regex_10_escapes.cpp.execution @@ -19,14 +19,6 @@ bar result_expr: $1 expected_results 12_y: OK regex: (\V+)(\v) parsed_regex: (\V+)(\v) str: foo -bar result_expr: $1-$2 expected_results foo- -13_y: OK regex: (\v+)(\V) parsed_regex: (\v+)(\V) str: foo - - -bar result_expr: $1-$2 expected_results - - - bar result_expr: $1-$2 expected_results foo- 13_y: OK regex: (\v+)(\V) parsed_regex: (\v+)(\V) str: foo @@ -35,25 +27,12 @@ bar result_expr: $1-$2 expected_results -b -14_y: OK regex: foo(\v)bar parsed_regex: foo(\v)bar str: foo -bar result_expr: $1 expected_results -15_y: OK regex: (\V)(\v) parsed_regex: (\V)(\v) str: foo -bar result_expr: $1-$2 expected_results o- -16_y: OK regex: (\v)(\V) parsed_regex: (\v)(\V) str: foo -bar result_expr: $1-$2 expected_results --b -14_y: OK regex: foo(\v)bar parsed_regex: foo(\v)bar str: foo -bar result_expr: $1 expected_results -15_y: OK regex: (\V)(\v) parsed_regex: (\V)(\v) str: foo -bar result_expr: $1-$2 expected_results o- -16_y: OK regex: (\v)(\V) parsed_regex: (\v)(\V) str: foo -bar result_expr: $1-$2 expected_results --b +14_y: OK regex: foo(\v)bar parsed_regex: foo(\v)bar str: foo bar result_expr: $1 expected_results +15_y: OK regex: (\V)(\v) parsed_regex: (\V)(\v) str: foo bar result_expr: $1-$2 expected_results o- +16_y: OK regex: (\v)(\V) parsed_regex: (\v)(\V) str: foo bar result_expr: $1-$2 expected_results -b 17_y: OK regex: foo\t\n\r\f\a\ebar parsed_regex: foo\t\n\r\f\a\ebar str: foo - - bar result_expr: $& expected_results foo - - bar + bar result_expr: $& expected_results foo + bar 18_y: OK regex: foo\Kbar parsed_regex: foo\Kbar str: foobar result_expr: $& expected_results bar 19_y: OK regex: \x41\x42 parsed_regex: \x41\x42 str: AB result_expr: $& expected_results AB 20_y: OK regex: \101\o{102} parsed_regex: \101\o{102} str: AB result_expr: $& expected_results AB diff --git a/source/reflect.h b/source/reflect.h index cc84c0dd4..bd7d97213 100644 --- a/source/reflect.h +++ b/source/reflect.h @@ -2166,22 +2166,22 @@ auto print(cpp2::impl::in t) -> void auto regex_gen(meta::type_declaration& t) -> void { auto has_default {false}; - auto prefix {"regex"}; - std::string postfix {"_mod"}; // TODO: remove mod syntax when 'm.initializer()' can be '("pat", "mod")' + auto exact_name {"regex"}; + auto prefix {"regex_"}; std::map expressions {}; for ( auto& m : CPP2_UFCS(get_member_objects)(t) ) { std::string name {CPP2_UFCS(name)(m)}; - if (CPP2_UFCS(starts_with)(name, prefix)) + if (CPP2_UFCS(starts_with)(name, prefix) || name == exact_name) { if (!(CPP2_UFCS(has_initializer)(m))) { CPP2_UFCS(error)(t, "Regular expression must have an initializer."); } CPP2_UFCS(mark_for_removal_from_enclosing_type)(m); - if (name == prefix) { + if (name == exact_name) { if (has_default) { CPP2_UFCS(error)(t, "Type can only contain one default named regular expression."); } diff --git a/source/reflect.h2 b/source/reflect.h2 index 08a6ac6fc..9989aedbe 100644 --- a/source/reflect.h2 +++ b/source/reflect.h2 @@ -1520,22 +1520,22 @@ print: (t: meta::type_declaration) = regex_gen: (inout t: meta::type_declaration) = { has_default := false; - prefix := "regex"; - postfix : std::string = "_mod"; // TODO: remove mod syntax when 'm.initializer()' can be '("pat", "mod")' + exact_name := "regex"; + prefix := "regex_"; expressions : std::map = (); for t.get_member_objects() do (inout m) { name: std::string = m.name(); - if name.starts_with(prefix) + if name.starts_with(prefix) || name == exact_name { if !m.has_initializer() { t.error("Regular expression must have an initializer."); } m.mark_for_removal_from_enclosing_type(); - if name == prefix { + if name == exact_name { if has_default { t.error("Type can only contain one default named regular expression."); } From 658d3075e2915a36359393f06e09b829c3494808 Mon Sep 17 00:00:00 2001 From: Neil Henderson <2060747+bluetarpmedia@users.noreply.github.com> Date: Sat, 10 Aug 2024 02:27:05 +1000 Subject: [PATCH 21/22] Disable some clang conversion warnings in `cpp2util.h` and cppfront itself (#1212) * Disable clang conversion warnings when compiling with -Wconversion cppfront and cpp2util.h use signed integer types for indices and container sizes so disable signed-to-unsigned conversion warnings. cppfront also uses implicit conversions from string literal to bool for: `assert(!"message")` so disable those warnings too. * Update regression tests caused by line number changes in `cpp2util.h` * Removed dependency on `!"string literal"` * Address `.size()` narrowing warnings Using the answer we recommend for everyone else, so model the right behavior here --------- Co-authored-by: Herb Sutter --- include/cpp2util.h | 11 ++++++++ .../mixed-bounds-check.cpp.execution | 2 +- ...ed-bounds-safety-with-assert.cpp.execution | 2 +- ...-safety-3-contract-violation.cpp.execution | 2 +- ...me-safety-and-null-contracts.cpp.execution | 2 +- ...re2-assert-expected-not-null.cpp.execution | 2 +- ...re2-assert-optional-not-null.cpp.execution | 2 +- ...2-assert-shared-ptr-not-null.cpp.execution | 2 +- ...2-assert-unique-ptr-not-null.cpp.execution | 2 +- .../mixed-bounds-check.cpp.execution | 2 +- ...ed-bounds-safety-with-assert.cpp.execution | 2 +- ...-safety-3-contract-violation.cpp.execution | 2 +- ...me-safety-and-null-contracts.cpp.execution | 2 +- ...re2-assert-optional-not-null.cpp.execution | 2 +- ...2-assert-shared-ptr-not-null.cpp.execution | 2 +- ...2-assert-unique-ptr-not-null.cpp.execution | 2 +- ...mixed-bugfix-for-ufcs-non-local.cpp.output | 20 +++++++------- ...mixed-bugfix-for-ufcs-non-local.cpp.output | 20 +++++++------- .../pure2-assert-expected-not-null.cpp.output | 4 +-- .../pure2-regex_10_escapes.cpp.execution | 26 +++++++++---------- source/common.h | 11 +++++++- source/cppfront.cpp | 6 ++--- source/parse.h | 18 ++++++------- source/sema.h | 6 ++--- source/to_cpp1.h | 6 ++--- 25 files changed, 89 insertions(+), 69 deletions(-) diff --git a/include/cpp2util.h b/include/cpp2util.h index 0572a9820..d9ae3c3dd 100644 --- a/include/cpp2util.h +++ b/include/cpp2util.h @@ -302,6 +302,12 @@ #include #endif +// cpp2util.h uses signed integer types for indices and container sizes +// so disable clang signed-to-unsigned conversion warnings in this header. +#ifdef __clang__ + #pragma clang diagnostic push + #pragma clang diagnostic ignored "-Wsign-conversion" +#endif //----------------------------------------------------------------------- // @@ -2833,4 +2839,9 @@ using cpp2::cpp2_new; #define CPP2_REQUIRES_(...) requires (__VA_ARGS__) #endif +// Restore clang signed-to-unsigned conversion warnings +#ifdef __clang__ + #pragma clang diagnostic pop #endif + +#endif // CPP2_CPP2UTIL_H diff --git a/regression-tests/test-results/apple-clang-15-c++2b/mixed-bounds-check.cpp.execution b/regression-tests/test-results/apple-clang-15-c++2b/mixed-bounds-check.cpp.execution index 1d524e6e1..77c30fa83 100644 --- a/regression-tests/test-results/apple-clang-15-c++2b/mixed-bounds-check.cpp.execution +++ b/regression-tests/test-results/apple-clang-15-c++2b/mixed-bounds-check.cpp.execution @@ -1 +1 @@ -../../../include/cpp2util.h(964) decltype(auto) cpp2::impl::assert_in_bounds(auto &&, std::source_location) [arg = 5, x:auto = std::vector]: Bounds safety violation: out of bounds access attempt detected - attempted access at index 5, [min,max] range is [0,4] +../../../include/cpp2util.h(970) decltype(auto) cpp2::impl::assert_in_bounds(auto &&, std::source_location) [arg = 5, x:auto = std::vector]: Bounds safety violation: out of bounds access attempt detected - attempted access at index 5, [min,max] range is [0,4] diff --git a/regression-tests/test-results/apple-clang-15-c++2b/mixed-bounds-safety-with-assert.cpp.execution b/regression-tests/test-results/apple-clang-15-c++2b/mixed-bounds-safety-with-assert.cpp.execution index 0f1269838..1284e5107 100644 --- a/regression-tests/test-results/apple-clang-15-c++2b/mixed-bounds-safety-with-assert.cpp.execution +++ b/regression-tests/test-results/apple-clang-15-c++2b/mixed-bounds-safety-with-assert.cpp.execution @@ -1 +1 @@ -../../../include/cpp2util.h(776) : Bounds safety violation +../../../include/cpp2util.h(782) : Bounds safety violation diff --git a/regression-tests/test-results/apple-clang-15-c++2b/mixed-initialization-safety-3-contract-violation.cpp.execution b/regression-tests/test-results/apple-clang-15-c++2b/mixed-initialization-safety-3-contract-violation.cpp.execution index d4f3fe0ad..dc708ffb5 100644 --- a/regression-tests/test-results/apple-clang-15-c++2b/mixed-initialization-safety-3-contract-violation.cpp.execution +++ b/regression-tests/test-results/apple-clang-15-c++2b/mixed-initialization-safety-3-contract-violation.cpp.execution @@ -1 +1 @@ -../../../include/cpp2util.h(776) : Contract violation: fill: value must contain at least count elements +../../../include/cpp2util.h(782) : Contract violation: fill: value must contain at least count elements diff --git a/regression-tests/test-results/apple-clang-15-c++2b/mixed-lifetime-safety-and-null-contracts.cpp.execution b/regression-tests/test-results/apple-clang-15-c++2b/mixed-lifetime-safety-and-null-contracts.cpp.execution index ec5bef9c8..ffe7899ea 100644 --- a/regression-tests/test-results/apple-clang-15-c++2b/mixed-lifetime-safety-and-null-contracts.cpp.execution +++ b/regression-tests/test-results/apple-clang-15-c++2b/mixed-lifetime-safety-and-null-contracts.cpp.execution @@ -1,2 +1,2 @@ sending error to my framework... [dynamic null dereference attempt detected] -from source location: ../../../include/cpp2util.h(855) decltype(auto) cpp2::impl::assert_not_null(auto &&, std::source_location) [arg:auto = int *&] +from source location: ../../../include/cpp2util.h(861) decltype(auto) cpp2::impl::assert_not_null(auto &&, std::source_location) [arg:auto = int *&] diff --git a/regression-tests/test-results/apple-clang-15-c++2b/pure2-assert-expected-not-null.cpp.execution b/regression-tests/test-results/apple-clang-15-c++2b/pure2-assert-expected-not-null.cpp.execution index c611b6acf..a625bdf83 100644 --- a/regression-tests/test-results/apple-clang-15-c++2b/pure2-assert-expected-not-null.cpp.execution +++ b/regression-tests/test-results/apple-clang-15-c++2b/pure2-assert-expected-not-null.cpp.execution @@ -1 +1 @@ -../../../include/cpp2util.h(855) decltype(auto) cpp2::impl::assert_not_null(auto &&, std::source_location) [arg:auto = std::expected]: Null safety violation: std::expected has an unexpected value +../../../include/cpp2util.h(861) decltype(auto) cpp2::impl::assert_not_null(auto &&, std::source_location) [arg:auto = std::expected]: Null safety violation: std::expected has an unexpected value diff --git a/regression-tests/test-results/apple-clang-15-c++2b/pure2-assert-optional-not-null.cpp.execution b/regression-tests/test-results/apple-clang-15-c++2b/pure2-assert-optional-not-null.cpp.execution index 57dee71f2..a9d924ed2 100644 --- a/regression-tests/test-results/apple-clang-15-c++2b/pure2-assert-optional-not-null.cpp.execution +++ b/regression-tests/test-results/apple-clang-15-c++2b/pure2-assert-optional-not-null.cpp.execution @@ -1 +1 @@ -../../../include/cpp2util.h(855) decltype(auto) cpp2::impl::assert_not_null(auto &&, std::source_location) [arg:auto = std::optional]: Null safety violation: std::optional does not contain a value +../../../include/cpp2util.h(861) decltype(auto) cpp2::impl::assert_not_null(auto &&, std::source_location) [arg:auto = std::optional]: Null safety violation: std::optional does not contain a value diff --git a/regression-tests/test-results/apple-clang-15-c++2b/pure2-assert-shared-ptr-not-null.cpp.execution b/regression-tests/test-results/apple-clang-15-c++2b/pure2-assert-shared-ptr-not-null.cpp.execution index 60a924d17..64d065485 100644 --- a/regression-tests/test-results/apple-clang-15-c++2b/pure2-assert-shared-ptr-not-null.cpp.execution +++ b/regression-tests/test-results/apple-clang-15-c++2b/pure2-assert-shared-ptr-not-null.cpp.execution @@ -1 +1 @@ -../../../include/cpp2util.h(855) decltype(auto) cpp2::impl::assert_not_null(auto &&, std::source_location) [arg:auto = std::shared_ptr]: Null safety violation: std::shared_ptr is empty +../../../include/cpp2util.h(861) decltype(auto) cpp2::impl::assert_not_null(auto &&, std::source_location) [arg:auto = std::shared_ptr]: Null safety violation: std::shared_ptr is empty diff --git a/regression-tests/test-results/apple-clang-15-c++2b/pure2-assert-unique-ptr-not-null.cpp.execution b/regression-tests/test-results/apple-clang-15-c++2b/pure2-assert-unique-ptr-not-null.cpp.execution index f8b437704..e16aca64a 100644 --- a/regression-tests/test-results/apple-clang-15-c++2b/pure2-assert-unique-ptr-not-null.cpp.execution +++ b/regression-tests/test-results/apple-clang-15-c++2b/pure2-assert-unique-ptr-not-null.cpp.execution @@ -1 +1 @@ -../../../include/cpp2util.h(855) decltype(auto) cpp2::impl::assert_not_null(auto &&, std::source_location) [arg:auto = std::unique_ptr]: Null safety violation: std::unique_ptr is empty +../../../include/cpp2util.h(861) decltype(auto) cpp2::impl::assert_not_null(auto &&, std::source_location) [arg:auto = std::unique_ptr]: Null safety violation: std::unique_ptr is empty diff --git a/regression-tests/test-results/clang-15-c++20/mixed-bounds-check.cpp.execution b/regression-tests/test-results/clang-15-c++20/mixed-bounds-check.cpp.execution index 1d524e6e1..77c30fa83 100644 --- a/regression-tests/test-results/clang-15-c++20/mixed-bounds-check.cpp.execution +++ b/regression-tests/test-results/clang-15-c++20/mixed-bounds-check.cpp.execution @@ -1 +1 @@ -../../../include/cpp2util.h(964) decltype(auto) cpp2::impl::assert_in_bounds(auto &&, std::source_location) [arg = 5, x:auto = std::vector]: Bounds safety violation: out of bounds access attempt detected - attempted access at index 5, [min,max] range is [0,4] +../../../include/cpp2util.h(970) decltype(auto) cpp2::impl::assert_in_bounds(auto &&, std::source_location) [arg = 5, x:auto = std::vector]: Bounds safety violation: out of bounds access attempt detected - attempted access at index 5, [min,max] range is [0,4] diff --git a/regression-tests/test-results/clang-15-c++20/mixed-bounds-safety-with-assert.cpp.execution b/regression-tests/test-results/clang-15-c++20/mixed-bounds-safety-with-assert.cpp.execution index 0f1269838..1284e5107 100644 --- a/regression-tests/test-results/clang-15-c++20/mixed-bounds-safety-with-assert.cpp.execution +++ b/regression-tests/test-results/clang-15-c++20/mixed-bounds-safety-with-assert.cpp.execution @@ -1 +1 @@ -../../../include/cpp2util.h(776) : Bounds safety violation +../../../include/cpp2util.h(782) : Bounds safety violation diff --git a/regression-tests/test-results/clang-15-c++20/mixed-initialization-safety-3-contract-violation.cpp.execution b/regression-tests/test-results/clang-15-c++20/mixed-initialization-safety-3-contract-violation.cpp.execution index d4f3fe0ad..dc708ffb5 100644 --- a/regression-tests/test-results/clang-15-c++20/mixed-initialization-safety-3-contract-violation.cpp.execution +++ b/regression-tests/test-results/clang-15-c++20/mixed-initialization-safety-3-contract-violation.cpp.execution @@ -1 +1 @@ -../../../include/cpp2util.h(776) : Contract violation: fill: value must contain at least count elements +../../../include/cpp2util.h(782) : Contract violation: fill: value must contain at least count elements diff --git a/regression-tests/test-results/clang-15-c++20/mixed-lifetime-safety-and-null-contracts.cpp.execution b/regression-tests/test-results/clang-15-c++20/mixed-lifetime-safety-and-null-contracts.cpp.execution index ec5bef9c8..ffe7899ea 100644 --- a/regression-tests/test-results/clang-15-c++20/mixed-lifetime-safety-and-null-contracts.cpp.execution +++ b/regression-tests/test-results/clang-15-c++20/mixed-lifetime-safety-and-null-contracts.cpp.execution @@ -1,2 +1,2 @@ sending error to my framework... [dynamic null dereference attempt detected] -from source location: ../../../include/cpp2util.h(855) decltype(auto) cpp2::impl::assert_not_null(auto &&, std::source_location) [arg:auto = int *&] +from source location: ../../../include/cpp2util.h(861) decltype(auto) cpp2::impl::assert_not_null(auto &&, std::source_location) [arg:auto = int *&] diff --git a/regression-tests/test-results/clang-15-c++20/pure2-assert-optional-not-null.cpp.execution b/regression-tests/test-results/clang-15-c++20/pure2-assert-optional-not-null.cpp.execution index 57dee71f2..a9d924ed2 100644 --- a/regression-tests/test-results/clang-15-c++20/pure2-assert-optional-not-null.cpp.execution +++ b/regression-tests/test-results/clang-15-c++20/pure2-assert-optional-not-null.cpp.execution @@ -1 +1 @@ -../../../include/cpp2util.h(855) decltype(auto) cpp2::impl::assert_not_null(auto &&, std::source_location) [arg:auto = std::optional]: Null safety violation: std::optional does not contain a value +../../../include/cpp2util.h(861) decltype(auto) cpp2::impl::assert_not_null(auto &&, std::source_location) [arg:auto = std::optional]: Null safety violation: std::optional does not contain a value diff --git a/regression-tests/test-results/clang-15-c++20/pure2-assert-shared-ptr-not-null.cpp.execution b/regression-tests/test-results/clang-15-c++20/pure2-assert-shared-ptr-not-null.cpp.execution index 60a924d17..64d065485 100644 --- a/regression-tests/test-results/clang-15-c++20/pure2-assert-shared-ptr-not-null.cpp.execution +++ b/regression-tests/test-results/clang-15-c++20/pure2-assert-shared-ptr-not-null.cpp.execution @@ -1 +1 @@ -../../../include/cpp2util.h(855) decltype(auto) cpp2::impl::assert_not_null(auto &&, std::source_location) [arg:auto = std::shared_ptr]: Null safety violation: std::shared_ptr is empty +../../../include/cpp2util.h(861) decltype(auto) cpp2::impl::assert_not_null(auto &&, std::source_location) [arg:auto = std::shared_ptr]: Null safety violation: std::shared_ptr is empty diff --git a/regression-tests/test-results/clang-15-c++20/pure2-assert-unique-ptr-not-null.cpp.execution b/regression-tests/test-results/clang-15-c++20/pure2-assert-unique-ptr-not-null.cpp.execution index f8b437704..e16aca64a 100644 --- a/regression-tests/test-results/clang-15-c++20/pure2-assert-unique-ptr-not-null.cpp.execution +++ b/regression-tests/test-results/clang-15-c++20/pure2-assert-unique-ptr-not-null.cpp.execution @@ -1 +1 @@ -../../../include/cpp2util.h(855) decltype(auto) cpp2::impl::assert_not_null(auto &&, std::source_location) [arg:auto = std::unique_ptr]: Null safety violation: std::unique_ptr is empty +../../../include/cpp2util.h(861) decltype(auto) cpp2::impl::assert_not_null(auto &&, std::source_location) [arg:auto = std::unique_ptr]: Null safety violation: std::unique_ptr is empty diff --git a/regression-tests/test-results/gcc-13-c++2b/mixed-bugfix-for-ufcs-non-local.cpp.output b/regression-tests/test-results/gcc-13-c++2b/mixed-bugfix-for-ufcs-non-local.cpp.output index a62e6b086..4b53cb8e1 100644 --- a/regression-tests/test-results/gcc-13-c++2b/mixed-bugfix-for-ufcs-non-local.cpp.output +++ b/regression-tests/test-results/gcc-13-c++2b/mixed-bugfix-for-ufcs-non-local.cpp.output @@ -1,41 +1,41 @@ In file included from mixed-bugfix-for-ufcs-non-local.cpp:6: ../../../include/cpp2util.h:2100:1: error: lambda-expression in template parameter type - 2100 | constexpr auto is( X const& x ) -> bool + 2100 | requires (std::is_same_v && !std::is_same_v && !std::is_same_v) | ^ ../../../include/cpp2util.h:2137:59: note: in expansion of macro ‘CPP2_UFCS_’ - 2137 | + 2137 | { return std::any_cast( x ); } | ^ mixed-bugfix-for-ufcs-non-local.cpp2:13:12: note: in expansion of macro ‘CPP2_UFCS_NONLOCAL’ mixed-bugfix-for-ufcs-non-local.cpp2:13:36: error: template argument 1 is invalid ../../../include/cpp2util.h:2100:1: error: lambda-expression in template parameter type - 2100 | constexpr auto is( X const& x ) -> bool + 2100 | requires (std::is_same_v && !std::is_same_v && !std::is_same_v) | ^ ../../../include/cpp2util.h:2137:59: note: in expansion of macro ‘CPP2_UFCS_’ - 2137 | + 2137 | { return std::any_cast( x ); } | ^ mixed-bugfix-for-ufcs-non-local.cpp2:21:12: note: in expansion of macro ‘CPP2_UFCS_NONLOCAL’ mixed-bugfix-for-ufcs-non-local.cpp2:21:36: error: template argument 1 is invalid ../../../include/cpp2util.h:2100:1: error: lambda-expression in template parameter type - 2100 | constexpr auto is( X const& x ) -> bool + 2100 | requires (std::is_same_v && !std::is_same_v && !std::is_same_v) | ^ ../../../include/cpp2util.h:2137:59: note: in expansion of macro ‘CPP2_UFCS_’ - 2137 | + 2137 | { return std::any_cast( x ); } | ^ mixed-bugfix-for-ufcs-non-local.cpp2:31:12: note: in expansion of macro ‘CPP2_UFCS_NONLOCAL’ mixed-bugfix-for-ufcs-non-local.cpp2:31:36: error: template argument 1 is invalid ../../../include/cpp2util.h:2100:1: error: lambda-expression in template parameter type - 2100 | constexpr auto is( X const& x ) -> bool + 2100 | requires (std::is_same_v && !std::is_same_v && !std::is_same_v) | ^ ../../../include/cpp2util.h:2137:59: note: in expansion of macro ‘CPP2_UFCS_’ - 2137 | + 2137 | { return std::any_cast( x ); } | ^ mixed-bugfix-for-ufcs-non-local.cpp2:33:12: note: in expansion of macro ‘CPP2_UFCS_NONLOCAL’ mixed-bugfix-for-ufcs-non-local.cpp2:33:36: error: template argument 1 is invalid ../../../include/cpp2util.h:2100:1: error: lambda-expression in template parameter type - 2100 | constexpr auto is( X const& x ) -> bool + 2100 | requires (std::is_same_v && !std::is_same_v && !std::is_same_v) | ^ ../../../include/cpp2util.h:2137:59: note: in expansion of macro ‘CPP2_UFCS_’ - 2137 | + 2137 | { return std::any_cast( x ); } | ^ mixed-bugfix-for-ufcs-non-local.cpp2:21:12: note: in expansion of macro ‘CPP2_UFCS_NONLOCAL’ mixed-bugfix-for-ufcs-non-local.cpp2:21:36: error: template argument 1 is invalid diff --git a/regression-tests/test-results/gcc-14-c++2b/mixed-bugfix-for-ufcs-non-local.cpp.output b/regression-tests/test-results/gcc-14-c++2b/mixed-bugfix-for-ufcs-non-local.cpp.output index a62e6b086..4b53cb8e1 100644 --- a/regression-tests/test-results/gcc-14-c++2b/mixed-bugfix-for-ufcs-non-local.cpp.output +++ b/regression-tests/test-results/gcc-14-c++2b/mixed-bugfix-for-ufcs-non-local.cpp.output @@ -1,41 +1,41 @@ In file included from mixed-bugfix-for-ufcs-non-local.cpp:6: ../../../include/cpp2util.h:2100:1: error: lambda-expression in template parameter type - 2100 | constexpr auto is( X const& x ) -> bool + 2100 | requires (std::is_same_v && !std::is_same_v && !std::is_same_v) | ^ ../../../include/cpp2util.h:2137:59: note: in expansion of macro ‘CPP2_UFCS_’ - 2137 | + 2137 | { return std::any_cast( x ); } | ^ mixed-bugfix-for-ufcs-non-local.cpp2:13:12: note: in expansion of macro ‘CPP2_UFCS_NONLOCAL’ mixed-bugfix-for-ufcs-non-local.cpp2:13:36: error: template argument 1 is invalid ../../../include/cpp2util.h:2100:1: error: lambda-expression in template parameter type - 2100 | constexpr auto is( X const& x ) -> bool + 2100 | requires (std::is_same_v && !std::is_same_v && !std::is_same_v) | ^ ../../../include/cpp2util.h:2137:59: note: in expansion of macro ‘CPP2_UFCS_’ - 2137 | + 2137 | { return std::any_cast( x ); } | ^ mixed-bugfix-for-ufcs-non-local.cpp2:21:12: note: in expansion of macro ‘CPP2_UFCS_NONLOCAL’ mixed-bugfix-for-ufcs-non-local.cpp2:21:36: error: template argument 1 is invalid ../../../include/cpp2util.h:2100:1: error: lambda-expression in template parameter type - 2100 | constexpr auto is( X const& x ) -> bool + 2100 | requires (std::is_same_v && !std::is_same_v && !std::is_same_v) | ^ ../../../include/cpp2util.h:2137:59: note: in expansion of macro ‘CPP2_UFCS_’ - 2137 | + 2137 | { return std::any_cast( x ); } | ^ mixed-bugfix-for-ufcs-non-local.cpp2:31:12: note: in expansion of macro ‘CPP2_UFCS_NONLOCAL’ mixed-bugfix-for-ufcs-non-local.cpp2:31:36: error: template argument 1 is invalid ../../../include/cpp2util.h:2100:1: error: lambda-expression in template parameter type - 2100 | constexpr auto is( X const& x ) -> bool + 2100 | requires (std::is_same_v && !std::is_same_v && !std::is_same_v) | ^ ../../../include/cpp2util.h:2137:59: note: in expansion of macro ‘CPP2_UFCS_’ - 2137 | + 2137 | { return std::any_cast( x ); } | ^ mixed-bugfix-for-ufcs-non-local.cpp2:33:12: note: in expansion of macro ‘CPP2_UFCS_NONLOCAL’ mixed-bugfix-for-ufcs-non-local.cpp2:33:36: error: template argument 1 is invalid ../../../include/cpp2util.h:2100:1: error: lambda-expression in template parameter type - 2100 | constexpr auto is( X const& x ) -> bool + 2100 | requires (std::is_same_v && !std::is_same_v && !std::is_same_v) | ^ ../../../include/cpp2util.h:2137:59: note: in expansion of macro ‘CPP2_UFCS_’ - 2137 | + 2137 | { return std::any_cast( x ); } | ^ mixed-bugfix-for-ufcs-non-local.cpp2:21:12: note: in expansion of macro ‘CPP2_UFCS_NONLOCAL’ mixed-bugfix-for-ufcs-non-local.cpp2:21:36: error: template argument 1 is invalid diff --git a/regression-tests/test-results/msvc-2022-c++20/pure2-assert-expected-not-null.cpp.output b/regression-tests/test-results/msvc-2022-c++20/pure2-assert-expected-not-null.cpp.output index a05de73b4..e43c7c890 100644 --- a/regression-tests/test-results/msvc-2022-c++20/pure2-assert-expected-not-null.cpp.output +++ b/regression-tests/test-results/msvc-2022-c++20/pure2-assert-expected-not-null.cpp.output @@ -6,7 +6,7 @@ pure2-assert-expected-not-null.cpp2(7): error C2143: syntax error: missing ';' b pure2-assert-expected-not-null.cpp2(7): error C2143: syntax error: missing ';' before '}' pure2-assert-expected-not-null.cpp2(9): error C2065: 'ex': undeclared identifier pure2-assert-expected-not-null.cpp2(9): error C2672: 'cpp2::impl::assert_not_null': no matching overloaded function found -D:\a\cppfront\cppfront\include\cpp2util.h(855): note: could be 'decltype(auto) cpp2::impl::assert_not_null(_T0 &&,std::source_location)' +D:\a\cppfront\cppfront\include\cpp2util.h(861): note: could be 'decltype(auto) cpp2::impl::assert_not_null(_T0 &&,std::source_location)' pure2-assert-expected-not-null.cpp2(14): error C2039: 'expected': is not a member of 'std' predefined C++ types (compiler internal)(347): note: see declaration of 'std' pure2-assert-expected-not-null.cpp2(14): error C2062: type 'int' unexpected @@ -19,4 +19,4 @@ pure2-assert-expected-not-null.cpp2(14): note: while trying to match the argumen pure2-assert-expected-not-null.cpp2(14): error C2143: syntax error: missing ';' before '}' pure2-assert-expected-not-null.cpp2(15): error C2065: 'ex': undeclared identifier pure2-assert-expected-not-null.cpp2(15): error C2672: 'cpp2::impl::assert_not_null': no matching overloaded function found -D:\a\cppfront\cppfront\include\cpp2util.h(855): note: could be 'decltype(auto) cpp2::impl::assert_not_null(_T0 &&,std::source_location)' +D:\a\cppfront\cppfront\include\cpp2util.h(861): note: could be 'decltype(auto) cpp2::impl::assert_not_null(_T0 &&,std::source_location)' diff --git a/regression-tests/test-results/msvc-2022-c++latest/pure2-regex_10_escapes.cpp.execution b/regression-tests/test-results/msvc-2022-c++latest/pure2-regex_10_escapes.cpp.execution index fe6e6efc9..14d06c270 100644 --- a/regression-tests/test-results/msvc-2022-c++latest/pure2-regex_10_escapes.cpp.execution +++ b/regression-tests/test-results/msvc-2022-c++latest/pure2-regex_10_escapes.cpp.execution @@ -9,26 +9,26 @@ Running tests_10_escapes: 08_y: OK regex: foo(\h)bar parsed_regex: foo(\h)bar str: foo bar result_expr: $1 expected_results 09_y: OK regex: (\H)(\h) parsed_regex: (\H)(\h) str: foo bar result_expr: $1-$2 expected_results o- 10_y: OK regex: (\h)(\H) parsed_regex: (\h)(\H) str: foo bar result_expr: $1-$2 expected_results -b -11_y: OK regex: foo(\v+)bar parsed_regex: foo(\v+)bar str: foo +11_y: OK regex: foo(\v+)bar parsed_regex: foo(\v+)bar str: foo + +bar result_expr: $1 expected_results + -bar result_expr: $1 expected_results +12_y: OK regex: (\V+)(\v) parsed_regex: (\V+)(\v) str: foo + +bar result_expr: $1-$2 expected_results foo- +13_y: OK regex: (\v+)(\V) parsed_regex: (\v+)(\V) str: foo + -12_y: OK regex: (\V+)(\v) parsed_regex: (\V+)(\v) str: foo - - -bar result_expr: $1-$2 expected_results foo- -13_y: OK regex: (\v+)(\V) parsed_regex: (\v+)(\V) str: foo - - -bar result_expr: $1-$2 expected_results - +bar result_expr: $1-$2 expected_results + -b -14_y: OK regex: foo(\v)bar parsed_regex: foo(\v)bar str: foo bar result_expr: $1 expected_results -15_y: OK regex: (\V)(\v) parsed_regex: (\V)(\v) str: foo bar result_expr: $1-$2 expected_results o- +14_y: OK regex: foo(\v)bar parsed_regex: foo(\v)bar str: foo bar result_expr: $1 expected_results +15_y: OK regex: (\V)(\v) parsed_regex: (\V)(\v) str: foo bar result_expr: $1-$2 expected_results o- 16_y: OK regex: (\v)(\V) parsed_regex: (\v)(\V) str: foo bar result_expr: $1-$2 expected_results -b 17_y: OK regex: foo\t\n\r\f\a\ebar parsed_regex: foo\t\n\r\f\a\ebar str: foo bar result_expr: $& expected_results foo diff --git a/source/common.h b/source/common.h index cf1d6e488..844900f8e 100644 --- a/source/common.h +++ b/source/common.h @@ -20,6 +20,15 @@ #pragma GCC diagnostic ignored "-Wdangling-reference" #endif +// Disable some clang conversion warnings: +// cppfront uses signed integer types for indices and container sizes. +// Note: We don't pop the diagnostic because we want them disabled in the +// entire cppfront translation unit. +#ifdef __clang__ + #pragma clang diagnostic push + #pragma clang diagnostic ignored "-Wsign-conversion" +#endif + #include "cpp2util.h" @@ -97,7 +106,7 @@ struct source_line break;case category::cpp1: return "/* 1 */ "; break;case category::cpp2: return "/* 2 */ "; break;case category::rawstring: return "/* R */ "; - break;default: assert(!"illegal category"); abort(); + break;default: assert(false && "ICE: illegal category"); abort(); } } }; diff --git a/source/cppfront.cpp b/source/cppfront.cpp index 3e28e46ae..a5dbb00ca 100644 --- a/source/cppfront.cpp +++ b/source/cppfront.cpp @@ -113,10 +113,10 @@ auto main( auto total = count.cpp1_lines + count.cpp2_lines; auto total_lines = print_with_thousands(total); out << " Cpp1 " - << std::right << std::setw(total_lines.size()) + << std::right << std::setw(unsafe_narrow(total_lines.size())) << print_with_thousands(count.cpp1_lines) << " line" << (count.cpp1_lines != 1 ? "s" : ""); out << "\n Cpp2 " - << std::right << std::setw(total_lines.size()) + << std::right << std::setw(unsafe_narrow(total_lines.size())) << print_with_thousands(count.cpp2_lines) << " line" << (count.cpp2_lines != 1 ? "s" : ""); if (total > 0) { out << " ("; @@ -145,7 +145,7 @@ auto main( for (auto [elapsed, name] : sorted_timers) { std::cout << "\n " - << std::right << std::setw(total_time.size()) + << std::right << std::setw(unsafe_narrow(total_time.size())) << print_with_thousands(elapsed) << " ms" << " in " << name; } } diff --git a/source/parse.h b/source/parse.h index 3c3ca960b..ea07039e3 100644 --- a/source/parse.h +++ b/source/parse.h @@ -1414,7 +1414,7 @@ struct type_id_node break;case keyword: return std::get(id)->to_string() + suffix; break;default: - assert(!"ICE: invalid type_id state"); + assert(false && "ICE: invalid type_id state"); } // else return {}; @@ -1435,7 +1435,7 @@ struct type_id_node break;case keyword: return get(id); break;default: - assert(!"ICE: invalid type_id state"); + assert(false && "ICE: invalid type_id state"); } // else return {}; @@ -1501,7 +1501,7 @@ auto template_argument::to_string() const break;case type_id: return std::get(arg)->to_string(); break;default: - assert(!"ICE: invalid template_argument state"); + assert(false && "ICE: invalid template_argument state"); } // else return {}; @@ -4353,7 +4353,7 @@ auto primary_expression_node::position() const } break;default: - assert (!"illegal primary_expression_node state"); + assert (false && "ICE: illegal primary_expression_node state"); return { 0, 0 }; } } @@ -4476,7 +4476,7 @@ auto statement_node::position() const } break;default: - assert (!"illegal statement_node state"); + assert (false && "ICE: illegal statement_node state"); return { 0, 0 }; } } @@ -6300,7 +6300,7 @@ class parser // And it shouldn't be anything else else { - assert (!"ICE: validate_op should take one token and return bool, or two tokens and return token const* "); + assert (false && "ICE: validate_op should take one token and return bool, or two tokens and return token const* "); } // At this point we may have a valid t.op, so try to parse the next term... @@ -7480,7 +7480,7 @@ class parser return n; } - assert(!"compiler bug: unexpected case"); + assert(false && "ICE: unexpected case"); return {}; } @@ -8102,7 +8102,7 @@ class parser break;case passing_style::forward: error( "a 'forward' parameter shouldn't be const, because it passes along the argument's actual const-ness (and actual value category)", false ); break;default: - assert (!"ICE: missing case"); + assert (false && "ICE: missing case"); } return {}; } @@ -9254,7 +9254,7 @@ class parser // Anything else shouldn't be possible else { - assert(!"ICE: should be unreachable - invalid alias declaration"); + assert(false && "ICE: should be unreachable - invalid alias declaration"); return {}; } diff --git a/source/sema.h b/source/sema.h index 8c54f3b0e..46e2c509d 100644 --- a/source/sema.h +++ b/source/sema.h @@ -253,7 +253,7 @@ struct symbol { } break;default: - assert (!"illegal symbol state"); + assert (false && "ICE: illegal symbol state"); return { 0, 0 }; } } @@ -284,7 +284,7 @@ struct symbol { } break;default: - assert (!"illegal symbol state"); + assert (false && "ICE: illegal symbol state"); return nullptr; } } @@ -1443,7 +1443,7 @@ class sema } break;default: - assert (!"illegal symbol"); + assert (false && "ICE: illegal symbol"); } } diff --git a/source/to_cpp1.h b/source/to_cpp1.h index 011a1e0ac..880d8cb52 100644 --- a/source/to_cpp1.h +++ b/source/to_cpp1.h @@ -257,7 +257,7 @@ class positional_printer switch (phase) { break;case phase0_type_decls : phase = phase1_type_defs_func_decls; break;case phase1_type_defs_func_decls: phase = phase2_func_defs; - break;default : assert(!"ICE: invalid lowering phase"); + break;default : assert(false && "ICE: invalid lowering phase"); } curr_pos = {}; next_comment = 0; // start over with the comments @@ -2349,7 +2349,7 @@ class cppfront } else { - assert(!"ICE: unexpected case"); + assert(false && "ICE: unexpected case"); } assert (iteration_statements.back().stmt); @@ -5833,7 +5833,7 @@ class cppfront } else { - assert(!"ICE: should be unreachable - invalid alias"); + assert(false && "ICE: should be unreachable - invalid alias"); } return; From 873b760ae0f4a07efa73973b729bf0f20aa9661b Mon Sep 17 00:00:00 2001 From: Herb Sutter Date: Fri, 9 Aug 2024 12:43:44 -0700 Subject: [PATCH 22/22] Allow function parameter default arguments Closes #1189 I had been experimenting with not allowing default arguments for function parameters, in part because of the potential for creating order-dependent code; the way to find out whether they're necessary is to not support them and see if that leaves a usability hole. The result of the experiment is that it does leave a hole: There's persistent feedback that default arguments are often useful, and are actually necessary for a few cases including particularly `std::source_location` parameters. As for order independence, there are already ways to opt into creating potentially order-dependent code (such as by deduced return types which depend on function bodies). So I think it's time to enable default arguments, and Cpp2 is still order-independent by default. This example now works: my_function_name: ( fn: *const char = std::source_location::current().function_name() ) = { std::cout << "calling: (fn)$\n"; } main: (args) = { my_function_name(); } // On MSVC 2022, prints: // calling: int __cdecl main(const int,char **) // On GCC 14, prints: // calling: int main(int, char**) --- regression-tests/pure2-default-arguments.cpp2 | 19 ++++++ .../pure2-default-arguments.cpp.output | 4 ++ .../pure2-default-arguments.cpp.output | 66 +++++++++++++++++++ .../pure2-default-arguments.cpp.execution | 2 + .../pure2-default-arguments.cpp.execution | 2 + .../pure2-default-arguments.cpp.output | 1 + .../test-results/pure2-default-arguments.cpp | 53 +++++++++++++++ .../pure2-default-arguments.cpp2.output | 2 + regression-tests/test-results/version | 2 +- source/build.info | 2 +- source/parse.h | 21 +++--- source/to_cpp1.h | 14 +++- 12 files changed, 172 insertions(+), 16 deletions(-) create mode 100644 regression-tests/pure2-default-arguments.cpp2 create mode 100644 regression-tests/test-results/clang-12-c++20/pure2-default-arguments.cpp.output create mode 100644 regression-tests/test-results/gcc-10-c++20/pure2-default-arguments.cpp.output create mode 100644 regression-tests/test-results/gcc-14-c++2b/pure2-default-arguments.cpp.execution create mode 100644 regression-tests/test-results/msvc-2022-c++latest/pure2-default-arguments.cpp.execution create mode 100644 regression-tests/test-results/msvc-2022-c++latest/pure2-default-arguments.cpp.output create mode 100644 regression-tests/test-results/pure2-default-arguments.cpp create mode 100644 regression-tests/test-results/pure2-default-arguments.cpp2.output diff --git a/regression-tests/pure2-default-arguments.cpp2 b/regression-tests/pure2-default-arguments.cpp2 new file mode 100644 index 000000000..7b4419486 --- /dev/null +++ b/regression-tests/pure2-default-arguments.cpp2 @@ -0,0 +1,19 @@ + +// Note: Using source_location requires GCC 11 or higher, +// Clang 16 or higher, MSVC 2019 16.10 or higher. +// Older compilers will emit failures for this test case. +my_function_name: ( + fn: *const char = std::source_location::current().function_name() + ) += { + std::cout << "calling: (fn)$\n"; +} + +f: (x: i32 = 0) = { std::cout << x; } + +main: (args) = { + my_function_name(); + f(); + f(1); + f(2); +} diff --git a/regression-tests/test-results/clang-12-c++20/pure2-default-arguments.cpp.output b/regression-tests/test-results/clang-12-c++20/pure2-default-arguments.cpp.output new file mode 100644 index 000000000..4812dc94c --- /dev/null +++ b/regression-tests/test-results/clang-12-c++20/pure2-default-arguments.cpp.output @@ -0,0 +1,4 @@ +pure2-default-arguments.cpp2:6:61: error: no member named 'source_location' in namespace 'std' + char const* fn = CPP2_UFCS_NONLOCAL(function_name)(std::source_location::current()) + ~~~~~^ +1 error generated. diff --git a/regression-tests/test-results/gcc-10-c++20/pure2-default-arguments.cpp.output b/regression-tests/test-results/gcc-10-c++20/pure2-default-arguments.cpp.output new file mode 100644 index 000000000..78fc177ba --- /dev/null +++ b/regression-tests/test-results/gcc-10-c++20/pure2-default-arguments.cpp.output @@ -0,0 +1,66 @@ +In file included from pure2-default-arguments.cpp:7: +../../../include/cpp2util.h:2086:28: error: local variable ‘obj’ may not appear in this context + 2086 | if constexpr (std::is_same_v< CPP2_TYPEOF(operator_as<17>(x)), T >) { if (x.index() == 17) return operator_as<17>(x); } + | ^~~ +../../../include/cpp2util.h:2047:34: note: in definition of macro ‘CPP2_UFCS_IDENTITY’ + 2047 | if constexpr (std::is_same_v< CPP2_TYPEOF(operator_as< 3>(x)), T >) { if (x.index() == 3) return operator_as<3>(x); } + | ^~~~~~~~~~~ +../../../include/cpp2util.h:2086:15: note: in expansion of macro ‘CPP2_FORWARD’ + 2086 | if constexpr (std::is_same_v< CPP2_TYPEOF(operator_as<17>(x)), T >) { if (x.index() == 17) return operator_as<17>(x); } + | ^~~~~~~~~~~~ +../../../include/cpp2util.h:2107:22: note: in expansion of macro ‘CPP2_UFCS_CONSTRAINT_ARG’ + 2107 | { return !x.has_value(); } + | ^~~~~~~~~ +../../../include/cpp2util.h:2137:59: note: in expansion of macro ‘CPP2_UFCS_’ + 2137 | { return std::any_cast( x ); } + | ^ +pure2-default-arguments.cpp2:6:22: note: in expansion of macro ‘CPP2_UFCS_NONLOCAL’ +../../../include/cpp2util.h:2086:92: error: local variable ‘params’ may not appear in this context + 2086 | if constexpr (std::is_same_v< CPP2_TYPEOF(operator_as<17>(x)), T >) { if (x.index() == 17) return operator_as<17>(x); } + | ^~~~~~ +../../../include/cpp2util.h:2047:34: note: in definition of macro ‘CPP2_UFCS_IDENTITY’ + 2047 | if constexpr (std::is_same_v< CPP2_TYPEOF(operator_as< 3>(x)), T >) { if (x.index() == 3) return operator_as<3>(x); } + | ^~~~~~~~~~~ +../../../include/cpp2util.h:2086:79: note: in expansion of macro ‘CPP2_FORWARD’ + 2086 | if constexpr (std::is_same_v< CPP2_TYPEOF(operator_as<17>(x)), T >) { if (x.index() == 17) return operator_as<17>(x); } + | ^~~~~~~~~~~~ +../../../include/cpp2util.h:2107:22: note: in expansion of macro ‘CPP2_UFCS_CONSTRAINT_ARG’ + 2107 | { return !x.has_value(); } + | ^~~~~~~~~ +../../../include/cpp2util.h:2137:59: note: in expansion of macro ‘CPP2_UFCS_’ + 2137 | { return std::any_cast( x ); } + | ^ +pure2-default-arguments.cpp2:6:22: note: in expansion of macro ‘CPP2_UFCS_NONLOCAL’ +../../../include/cpp2util.h:2087:74: error: local variable ‘obj’ may not appear in this context + 2087 | if constexpr (std::is_same_v< CPP2_TYPEOF(operator_as<18>(x)), T >) { if (x.index() == 18) return operator_as<18>(x); } + | ^~~ +../../../include/cpp2util.h:2047:34: note: in definition of macro ‘CPP2_UFCS_IDENTITY’ + 2047 | if constexpr (std::is_same_v< CPP2_TYPEOF(operator_as< 3>(x)), T >) { if (x.index() == 3) return operator_as<3>(x); } + | ^~~~~~~~~~~ +../../../include/cpp2util.h:2087:61: note: in expansion of macro ‘CPP2_FORWARD’ + 2087 | if constexpr (std::is_same_v< CPP2_TYPEOF(operator_as<18>(x)), T >) { if (x.index() == 18) return operator_as<18>(x); } + | ^~~~~~~~~~~~ +../../../include/cpp2util.h:2107:22: note: in expansion of macro ‘CPP2_UFCS_CONSTRAINT_ARG’ + 2107 | { return !x.has_value(); } + | ^~~~~~~~~ +../../../include/cpp2util.h:2137:59: note: in expansion of macro ‘CPP2_UFCS_’ + 2137 | { return std::any_cast( x ); } + | ^ +pure2-default-arguments.cpp2:6:22: note: in expansion of macro ‘CPP2_UFCS_NONLOCAL’ +../../../include/cpp2util.h:2087:93: error: local variable ‘params’ may not appear in this context + 2087 | if constexpr (std::is_same_v< CPP2_TYPEOF(operator_as<18>(x)), T >) { if (x.index() == 18) return operator_as<18>(x); } + | ^~~~~~ +../../../include/cpp2util.h:2047:34: note: in definition of macro ‘CPP2_UFCS_IDENTITY’ + 2047 | if constexpr (std::is_same_v< CPP2_TYPEOF(operator_as< 3>(x)), T >) { if (x.index() == 3) return operator_as<3>(x); } + | ^~~~~~~~~~~ +../../../include/cpp2util.h:2087:80: note: in expansion of macro ‘CPP2_FORWARD’ + 2087 | if constexpr (std::is_same_v< CPP2_TYPEOF(operator_as<18>(x)), T >) { if (x.index() == 18) return operator_as<18>(x); } + | ^~~~~~~~~~~~ +../../../include/cpp2util.h:2107:22: note: in expansion of macro ‘CPP2_UFCS_CONSTRAINT_ARG’ + 2107 | { return !x.has_value(); } + | ^~~~~~~~~ +../../../include/cpp2util.h:2137:59: note: in expansion of macro ‘CPP2_UFCS_’ + 2137 | { return std::any_cast( x ); } + | ^ +pure2-default-arguments.cpp2:6:22: note: in expansion of macro ‘CPP2_UFCS_NONLOCAL’ +pure2-default-arguments.cpp2:6:61: error: ‘std::source_location’ has not been declared diff --git a/regression-tests/test-results/gcc-14-c++2b/pure2-default-arguments.cpp.execution b/regression-tests/test-results/gcc-14-c++2b/pure2-default-arguments.cpp.execution new file mode 100644 index 000000000..0e56963ff --- /dev/null +++ b/regression-tests/test-results/gcc-14-c++2b/pure2-default-arguments.cpp.execution @@ -0,0 +1,2 @@ +calling: int main(int, char**) +012 \ No newline at end of file diff --git a/regression-tests/test-results/msvc-2022-c++latest/pure2-default-arguments.cpp.execution b/regression-tests/test-results/msvc-2022-c++latest/pure2-default-arguments.cpp.execution new file mode 100644 index 000000000..8f0b4095c --- /dev/null +++ b/regression-tests/test-results/msvc-2022-c++latest/pure2-default-arguments.cpp.execution @@ -0,0 +1,2 @@ +calling: int __cdecl main(const int,char **) +012 \ No newline at end of file diff --git a/regression-tests/test-results/msvc-2022-c++latest/pure2-default-arguments.cpp.output b/regression-tests/test-results/msvc-2022-c++latest/pure2-default-arguments.cpp.output new file mode 100644 index 000000000..f1128b42d --- /dev/null +++ b/regression-tests/test-results/msvc-2022-c++latest/pure2-default-arguments.cpp.output @@ -0,0 +1 @@ +pure2-default-arguments.cpp diff --git a/regression-tests/test-results/pure2-default-arguments.cpp b/regression-tests/test-results/pure2-default-arguments.cpp new file mode 100644 index 000000000..d8d417a0a --- /dev/null +++ b/regression-tests/test-results/pure2-default-arguments.cpp @@ -0,0 +1,53 @@ + +#define CPP2_IMPORT_STD Yes + +//=== Cpp2 type declarations ==================================================== + + +#include "cpp2util.h" + +#line 1 "pure2-default-arguments.cpp2" + + +//=== Cpp2 type definitions and function declarations =========================== + +#line 1 "pure2-default-arguments.cpp2" + +// Note: Using source_location requires GCC 11 or higher, +// Clang 16 or higher, MSVC 2019 16.10 or higher. +// Older compilers will emit failures for this test case. +#line 5 "pure2-default-arguments.cpp2" +auto my_function_name( + char const* fn = CPP2_UFCS_NONLOCAL(function_name)(std::source_location::current()) + ) -> void; + +#line 12 "pure2-default-arguments.cpp2" +auto f(cpp2::impl::in x = 0) -> void; + +auto main(int const argc_, char** argv_) -> int; + +//=== Cpp2 function definitions ================================================= + +#line 1 "pure2-default-arguments.cpp2" + +#line 5 "pure2-default-arguments.cpp2" +auto my_function_name( + char const* fn + ) -> void +{ + std::cout << "calling: " + cpp2::to_string(fn) + "\n"; +} + +#line 12 "pure2-default-arguments.cpp2" +auto f(cpp2::impl::in x) -> void{std::cout << x; } + +#line 14 "pure2-default-arguments.cpp2" +auto main(int const argc_, char** argv_) -> int{ + auto const args = cpp2::make_args(argc_, argv_); +#line 15 "pure2-default-arguments.cpp2" + my_function_name(); + f(); + f(1); + f(2); +} + diff --git a/regression-tests/test-results/pure2-default-arguments.cpp2.output b/regression-tests/test-results/pure2-default-arguments.cpp2.output new file mode 100644 index 000000000..905239d13 --- /dev/null +++ b/regression-tests/test-results/pure2-default-arguments.cpp2.output @@ -0,0 +1,2 @@ +pure2-default-arguments.cpp2... ok (all Cpp2, passes safety checks) + diff --git a/regression-tests/test-results/version b/regression-tests/test-results/version index 8c82ed8e3..ba18fe003 100644 --- a/regression-tests/test-results/version +++ b/regression-tests/test-results/version @@ -1,5 +1,5 @@ -cppfront compiler v0.7.2 Build 9804:1033 +cppfront compiler v0.7.2 Build 9809:1046 Copyright(c) Herb Sutter All rights reserved SPDX-License-Identifier: CC-BY-NC-ND-4.0 diff --git a/source/build.info b/source/build.info index f7c98cb74..ce3054eaa 100644 --- a/source/build.info +++ b/source/build.info @@ -1 +1 @@ -"9804:1033" \ No newline at end of file +"9809:1046" \ No newline at end of file diff --git a/source/parse.h b/source/parse.h index ea07039e3..282067125 100644 --- a/source/parse.h +++ b/source/parse.h @@ -2857,8 +2857,9 @@ struct declaration_node bool member_function_generation = true; // Cache some context - bool is_a_template_parameter = false; - bool is_a_parameter = false; + bool is_a_template_parameter = false; + bool is_a_parameter = false; + bool is_a_statement_parameter = false; // Constructor // @@ -2885,6 +2886,12 @@ struct declaration_node return is_a_parameter; } + auto is_statement_parameter() const + -> bool + { + return is_a_statement_parameter; + } + auto type_member_mark_for_removal() -> bool { @@ -8041,6 +8048,7 @@ class parser pos = start_pos; // backtrack return {}; } + n->declaration->is_a_statement_parameter = is_statement; // And some error checks // @@ -8107,15 +8115,6 @@ class parser return {}; } - if ( - !is_returns - && !is_statement - && n->declaration->initializer - ) - { - error("Cpp2 is currently exploring the path of not allowing default arguments - use overloading instead", false); - return {}; - } if (is_named && is_returns) { auto tok = n->name(); assert(tok); diff --git a/source/to_cpp1.h b/source/to_cpp1.h index 880d8cb52..bc66cd5d7 100644 --- a/source/to_cpp1.h +++ b/source/to_cpp1.h @@ -3291,14 +3291,18 @@ class cppfront ufcs_string += "_TEMPLATE"; } - // If we're in an object declaration (i.e., initializer) - // at namespace scope, use the _NONLOCAL version + // If we're in a namespace-scope object declaration (i.e., initializer) + // or in a default function argument, use the _NONLOCAL version // // Note: If there are other cases where code could execute // in a non-local scope where a capture-default for the UFCS // lambda would not be allowed, then add them here if ( current_declarations.back()->is_namespace() + || ( + current_declarations.back()->is_parameter() + && !current_declarations.back()->is_statement_parameter() + ) || ( current_declarations.back()->is_object() && current_declarations.back()->parent_is_namespace() @@ -4680,6 +4684,10 @@ class cppfront if ( !is_returns && n.declaration->initializer + && ( + is_statement + || printer.get_phase() != printer.phase2_func_defs + ) ) { auto guard = stack_element(current_declarations, &*n.declaration); @@ -4689,7 +4697,7 @@ class cppfront else { printer.print_cpp2( " = ", n.declaration->initializer->position() ); } - emit(*n.declaration->initializer, !is_statement); + emit(*n.declaration->initializer, false); if (is_statement) { printer.print_cpp2( "};", n.declaration->initializer->position() ); }