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/cpp2/common.md b/docs/cpp2/common.md
index a85d55a5a..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).
@@ -220,7 +222,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 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/docs/cpp2/contracts.md b/docs/cpp2/contracts.md
index fcdc21a0d..61183ef60 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 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/expressions.md b/docs/cpp2/expressions.md
index 8881ce5cc..bec3df0e5 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`.
@@ -223,18 +243,20 @@ 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`, 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:
-``` 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";
}
@@ -353,3 +375,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/docs/cpp2/functions.md b/docs/cpp2/functions.md
index 4771f7b13..25c1b1c6c 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 the leading `-> _ = { return` and trailing `; }`.
+
+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/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/cppfront/options.md b/docs/cppfront/options.md
index d3cd967ae..62cbeb8b1 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,93 @@ 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 check controls
-## `-add-source-info`, `-a`
+### `-no-comparison-checks`, `-no-c`
-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]
+Disable mixed-sign comparison safety checks. If not disabled, mixed-sign comparisons are diagnosed by default.
-## `-no-comparison-checks`, `-no-c`
+### `-no-div-zero-checks`, `-no-d`
-Disable mixed-sign comparison safety checks. If not disabled, mixed-sign comparisons are diagnosed 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`
+### `-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/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/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/include/cpp2regex.h b/include/cpp2regex.h
index 12867bed2..01fc3440e 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,53 @@ 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) + ")";
+
+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) + ")";
+}
+
+[[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 +1600,7 @@ template match_return::match_return(auto const& matched_,
: matched{ matched_ }
, pos{ pos_ }{}
template match_return::match_return(){}
+
#line 38 "cpp2regex.h2"
//-----------------------------------------------------------------------
//
@@ -2229,9 +2266,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 +2449,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_)
@@ -3111,7 +3148,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 +3499,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 +3642,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 +4018,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/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);
}
}
diff --git a/include/cpp2util.h b/include/cpp2util.h
index 62665049d..d9ae3c3dd 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
@@ -299,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
//-----------------------------------------------------------------------
//
@@ -375,6 +384,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 = decltype(std::ssize(str)){ 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)
@@ -669,17 +712,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... } );
}
@@ -795,7 +831,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
@@ -858,11 +894,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); } \
@@ -885,15 +970,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) ];
@@ -1629,6 +1714,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
@@ -1642,7 +1728,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;
@@ -1755,6 +1854,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
@@ -2190,7 +2295,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));
}
@@ -2239,16 +2360,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'
@@ -2652,9 +2773,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,
@@ -2672,8 +2800,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 {
@@ -2711,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/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/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/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/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/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/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/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-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/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/mixed-bounds-check.cpp.execution b/regression-tests/test-results/apple-clang-15-c++2b/mixed-bounds-check.cpp.execution
index fa8252b3c..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(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(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 312fa7694..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(744) : 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 e58245c78..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(744) : 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 fb8eb511b..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(823) 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 d4f4704ce..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(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(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 bb106dd63..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(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(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 375d8a3bf..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(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(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 21996fccd..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(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(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/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/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/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-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/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/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/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/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-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-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/mixed-bounds-check.cpp.execution b/regression-tests/test-results/clang-15-c++20/mixed-bounds-check.cpp.execution
index fa8252b3c..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(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(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 312fa7694..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(744) : 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 e58245c78..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(744) : 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 fb8eb511b..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(823) 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 bb106dd63..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(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(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 375d8a3bf..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(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(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 21996fccd..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(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(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/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-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-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-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++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/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-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/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
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-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-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-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-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-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-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..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 | class finally_success
+ 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 | finally(finally&& that) noexcept
+ 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 | class finally_success
+ 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 | finally(finally&& that) noexcept
+ 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 | class finally_success
+ 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 | finally(finally&& that) noexcept
+ 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 | class finally_success
+ 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 | finally(finally&& that) noexcept
+ 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 | class finally_success
+ 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 | finally(finally&& that) noexcept
+ 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-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-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/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/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..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 |
+ 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 | ~finally() noexcept { f(); }
+ 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 |
+ 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 | ~finally() noexcept { f(); }
+ 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 |
+ 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 | ~finally() noexcept { f(); }
+ 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 |
+ 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 | ~finally() noexcept { f(); }
+ 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 |
+ 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 | ~finally() noexcept { f(); }
+ 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/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/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/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++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..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(823): 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(823): 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++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++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
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
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/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/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-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/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