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(_val) } { } + constexpr auto file_attributes::operator=(cpp2::impl::in _val) -> file_attributes& { _value = cpp2::unsafe_narrow(_val); return *this; } @@ -228,18 +282,50 @@ constexpr file_attributes::file_attributes(file_attributes&& that) noexcept constexpr auto file_attributes::operator=(file_attributes&& that) noexcept -> file_attributes& { _value = std::move(that)._value; return *this;} -[[nodiscard]] auto file_attributes::to_string() const& -> std::string{ +[[nodiscard]] auto file_attributes::to_string_impl(cpp2::impl::in prefix, cpp2::impl::in separator) const& -> std::string{ - std::string _ret {"("}; +std::string ret {"("}; + +std::string sep {}; +if ((*this) == none) {return "(none)"; } + +auto pref {cpp2::to_string(prefix)}; +if (((*this) & cached) == cached) {ret += sep + pref + "cached";sep = separator;} +if (((*this) & current) == current) {ret += sep + pref + "current";sep = separator;} +if (((*this) & obsolete) == obsolete) {ret += sep + pref + "obsolete";sep = separator;} +if (((*this) & cached_and_current) == cached_and_current) {ret += sep + cpp2::move(pref) + "cached_and_current";sep = separator;} +return cpp2::move(ret) + ")"; +} + +[[nodiscard]] auto file_attributes::to_string() const& -> std::string { return to_string_impl("", ", "); } +[[nodiscard]] auto file_attributes::to_code() const& -> std::string { return to_string_impl("file_attributes::", " | "); } +[[nodiscard]] auto file_attributes::from_string(cpp2::impl::in s) -> file_attributes{ + +auto ret {none}; +do {{ +for ( auto const& x : cpp2::string_util::split_string_list(s) ) { +if ("cached" == x) {ret |= cached;} +else {if ("current" == x) {ret |= current;} +else {if ("obsolete" == x) {ret |= obsolete;} +else {if ("cached_and_current" == x) {ret |= cached_and_current;} +else {if ("none" == x) {ret |= none;} +else {goto BREAK_outer;} +#line 1 "pure2-enum.cpp2" +}}}} +} + +return ret; +} CPP2_CONTINUE_BREAK(outer) } + while ( +false +); +CPP2_UFCS(report_violation)(cpp2::type_safety, CPP2_UFCS(c_str)(("can't convert string '" + cpp2::to_string(s) + "' to flag_enum of type file_attributes"))); +return none; +} + +[[nodiscard]] auto file_attributes::from_code(cpp2::impl::in s) -> file_attributes{ +std::string str {s}; return from_string(cpp2::string_util::replace_all(cpp2::move(str), "file_attributes::", "")); } - std::string _comma {}; - if ((*this) == none) {return "(none)"; } - if (((*this) & cached) == cached) {_ret += _comma + "cached";_comma = ", ";} - if (((*this) & current) == current) {_ret += _comma + "current";_comma = ", ";} - if (((*this) & obsolete) == obsolete) {_ret += _comma + "obsolete";_comma = ", ";} - if (((*this) & cached_and_current) == cached_and_current) {_ret += _comma + "cached_and_current";_comma = ", ";} - return cpp2::move(_ret) + ")"; - } #line 28 "pure2-enum.cpp2" auto main() -> int{ auto j {janus::past}; @@ -251,12 +337,17 @@ auto main() -> int{ skat_game x {skat_game::clubs}; auto x2 {skat_game::diamonds}; x2 = x; + auto x3 {skat_game::from_string("hearts")}; + auto x4 {skat_game::from_code("skat_game::hearts")}; // if x == 9 { } // error, can't compare skat_game and integer // if x == rgb::red { } // error, can't compare skat_game and rgb color std::cout << "x.to_string() is " + cpp2::to_string(CPP2_UFCS(to_string)(x)) + "\n"; std::cout << "x2.to_string() is " + cpp2::to_string(CPP2_UFCS(to_string)(cpp2::move(x2))) + "\n"; + std::cout << "x3.to_string() is " + cpp2::to_string(CPP2_UFCS(to_string)(x3)) + "\n"; + std::cout << "x3.to_code() is " + cpp2::to_string(CPP2_UFCS(to_code)(x3)) + "\n"; + std::cout << "x4.to_string() is " + cpp2::to_string(CPP2_UFCS(to_string)(cpp2::move(x3))) + "\n"; std::cout << "with if else: "; if (x == skat_game::diamonds) { // ok, can compare two skat_games @@ -333,5 +424,15 @@ auto main() -> int{ else if (cpp2::impl::is(_expr, cpp2::has_flags(cpp2::move(f2)))) { if constexpr( requires{"includes all f2's flags ('cached' and 'current')";} ) if constexpr( std::is_convertible_v ) return "includes all f2's flags ('cached' and 'current')"; else return std::string{}; else return std::string{}; } else return "something else"; } () << "\n"; + + auto f_from_string {file_attributes::from_string("cached_and_current")}; + std::cout << "f_from_string is " << CPP2_UFCS(to_string)(f_from_string) << "\n"; + + f_from_string = file_attributes::from_string("(current, obsolete)"); + std::cout << "f_from_string is " << CPP2_UFCS(to_string)(f_from_string) << "\n"; + std::cout << "f_from_string.to_code() is " << CPP2_UFCS(to_code)(f_from_string) << "\n"; + + f_from_string = file_attributes::from_code("(file_attributes::cached | file_attributes::obsolete)"); + std::cout << "f_from_string is " << CPP2_UFCS(to_string)(cpp2::move(f_from_string)) << "\n"; } diff --git a/regression-tests/test-results/pure2-last-use.cpp b/regression-tests/test-results/pure2-last-use.cpp index 21c9caaa5..00b935bd7 100644 --- a/regression-tests/test-results/pure2-last-use.cpp +++ b/regression-tests/test-results/pure2-last-use.cpp @@ -55,25 +55,25 @@ class issue_869_0; class issue_869_1; -#line 844 "pure2-last-use.cpp2" +#line 845 "pure2-last-use.cpp2" class cpp2_union; -#line 852 "pure2-last-use.cpp2" +#line 853 "pure2-last-use.cpp2" class my_string; -#line 911 "pure2-last-use.cpp2" +#line 912 "pure2-last-use.cpp2" namespace captures { -#line 925 "pure2-last-use.cpp2" +#line 926 "pure2-last-use.cpp2" class t; -#line 954 "pure2-last-use.cpp2" +#line 955 "pure2-last-use.cpp2" } -#line 984 "pure2-last-use.cpp2" +#line 985 "pure2-last-use.cpp2" class types; @@ -139,9 +139,12 @@ auto issue_847_4(std::vector v) -> void; auto issue_847_0(std::vector> v) -> void; #line 139 "pure2-last-use.cpp2" -auto issue_847_1(std::vector>&& v) -> void; - -#line 145 "pure2-last-use.cpp2" +// issue_847_1: (move v: std::vector>) = { +// for v.make_copy() +// do (move x) { +// f_inout(x); // error, can't pass rvalue to inout param +// } +// } auto issue_847_5(auto&& v) -> void; #line 151 "pure2-last-use.cpp2" @@ -159,9 +162,10 @@ class issue_857 { public: issue_857(issue_857&& that) noexcept; #line 172 "pure2-last-use.cpp2" public: auto operator=(issue_857&& that) noexcept -> issue_857& ; - public: ~issue_857() noexcept; - -#line 177 "pure2-last-use.cpp2" +// operator=: (move this) = { +// f_inout(a); // error, can't pass rvalue to inout param +// f_inout(this.b); // error, can't pass rvalue to inout param +// } //f: (move this) = f_copy(this); //f: (move this, move that) = f_copy(this, that); //g: (move this) = f_copy(this.a); @@ -388,7 +392,7 @@ class issue_857_9: public issue_857_8 { //f3: (move this) = d(); // OK: Explicit 'this' for base members, like in templates. - public: auto g0() && -> void; + //g0: (move this) = f_inout(this.a); // error, can't pass rvalue to inout param //g1: (move this) = _ = this.b(); public: auto g2() && -> void; //g3: (move this) = this.d(); @@ -453,10 +457,10 @@ auto enum_0() -> void; #line 806 "pure2-last-use.cpp2" auto enum_1() -> void; -#line 834 "pure2-last-use.cpp2" +#line 835 "pure2-last-use.cpp2" auto enum_2() -> void; -#line 844 "pure2-last-use.cpp2" +#line 845 "pure2-last-use.cpp2" class cpp2_union { public: auto destroy() & -> void; public: ~cpp2_union() noexcept; @@ -465,7 +469,7 @@ class cpp2_union { public: auto operator=(cpp2_union const&) -> void = delete; -#line 850 "pure2-last-use.cpp2" +#line 851 "pure2-last-use.cpp2" }; class my_string { @@ -473,34 +477,34 @@ class my_string { public: std::size_t size {CPP2_UFCS(size)(string)}; public: my_string(auto const& string_, auto const& size_); -#line 855 "pure2-last-use.cpp2" +#line 856 "pure2-last-use.cpp2" }; using no_pessimizing_move_ret = std::unique_ptr; -#line 857 "pure2-last-use.cpp2" +#line 858 "pure2-last-use.cpp2" [[nodiscard]] auto no_pessimizing_move() -> no_pessimizing_move_ret; auto deferred_non_copyable_0() -> void; -#line 865 "pure2-last-use.cpp2" +#line 866 "pure2-last-use.cpp2" [[nodiscard]] auto deferred_non_copyable_1() -> auto; using deferred_non_copyable_2_ret = std::unique_ptr; -#line 871 "pure2-last-use.cpp2" +#line 872 "pure2-last-use.cpp2" [[nodiscard]] auto deferred_non_copyable_2() -> deferred_non_copyable_2_ret; -#line 875 "pure2-last-use.cpp2" +#line 876 "pure2-last-use.cpp2" auto loops() -> void; -#line 911 "pure2-last-use.cpp2" +#line 912 "pure2-last-use.cpp2" namespace captures { // Skip non captured name in function expression auto f() -> void; -#line 923 "pure2-last-use.cpp2" +#line 924 "pure2-last-use.cpp2" int inline constexpr x{ 0 }; class t { @@ -510,36 +514,36 @@ class t { public: auto operator=(auto const& x_) -> t& ; -#line 938 "pure2-last-use.cpp2" +#line 939 "pure2-last-use.cpp2" }; auto g() -> void; -#line 954 "pure2-last-use.cpp2" +#line 955 "pure2-last-use.cpp2" } auto loops_and_captures() -> void; -#line 984 "pure2-last-use.cpp2" +#line 985 "pure2-last-use.cpp2" class types { public: std::unique_ptr x; public: types(auto const& x_); public: auto operator=(auto const& x_) -> types& ; -#line 986 "pure2-last-use.cpp2" +#line 987 "pure2-last-use.cpp2" // f: (move this) = _ = :() x$*; // g: (move this) = { // for (:() x$*) // do (_) // { } // } -#line 992 "pure2-last-use.cpp2" +#line 993 "pure2-last-use.cpp2" }; auto skip_hidden_names() -> void; -#line 1044 "pure2-last-use.cpp2" +#line 1045 "pure2-last-use.cpp2" auto main(int const argc_, char** argv_) -> int; //=== Cpp2 function definitions ================================================= @@ -684,13 +688,7 @@ auto issue_847_0(std::vector> v) -> void{ [[maybe_unused]] auto const& unnamed_param_1 : CPP2_UFCS(make_copy)(cpp2::move(v)) ) { } } -#line 139 "pure2-last-use.cpp2" -auto issue_847_1(std::vector>&& v) -> void{ - for ( - auto&& x : CPP2_UFCS(make_copy)(cpp2::move(v)) ) { - f_inout(cpp2::move(x)); - } -} + #line 145 "pure2-last-use.cpp2" auto issue_847_5(auto&& v) -> void{ for ( @@ -728,11 +726,6 @@ auto issue_850() -> void{ a = std::move(that).a; b = std::move(that).b; return *this; } -#line 173 "pure2-last-use.cpp2" - issue_857::~issue_857() noexcept{ - f_inout(a); - f_inout(cpp2::move((*this)).b); - } #line 181 "pure2-last-use.cpp2" auto issue_857::h() & -> void { f_inout(a); } @@ -815,9 +808,6 @@ auto issue_857_5::operator=(auto const& a_) -> issue_857_5& { #line 362 "pure2-last-use.cpp2" auto issue_857_9::f2() && -> void { f_inout(c); }// OK: Happens to work, like non-'move' 'this' parameters. -#line 366 "pure2-last-use.cpp2" - auto issue_857_9::g0() && -> void { f_inout(cpp2::move((*this)).a); } - #line 368 "pure2-last-use.cpp2" auto issue_857_9::g2() && -> void { f_inout(cpp2::move((*this)).c); } @@ -1317,7 +1307,8 @@ auto enum_0() -> void{ #line 806 "pure2-last-use.cpp2" auto enum_1() -> void{ auto max_value {cpp2_new(0)}; - std::reference_wrapper const> min_value {cpp2::move(max_value)}; + std::reference_wrapper const> min_value {max_value}; + static_cast(cpp2::move(max_value)); // for (0) // do (copy x) @@ -1343,7 +1334,7 @@ auto enum_1() -> void{ f_copy(std::move(cpp2::move(v))); } while ( *cpp2::impl::assert_not_null(identity(z))); } -#line 834 "pure2-last-use.cpp2" +#line 835 "pure2-last-use.cpp2" auto enum_2() -> void{ auto umax {cpp2_new(0)}; if (pred(umax)) { @@ -1354,9 +1345,9 @@ auto enum_2() -> void{ }}} } -#line 845 "pure2-last-use.cpp2" - auto cpp2_union::destroy() & -> void{} #line 846 "pure2-last-use.cpp2" + auto cpp2_union::destroy() & -> void{} +#line 847 "pure2-last-use.cpp2" cpp2_union::~cpp2_union() noexcept{ destroy(); static_cast(cpp2::move((*this))); @@ -1366,33 +1357,33 @@ auto enum_2() -> void{ : string{ string_ } , size{ size_ }{} -#line 857 "pure2-last-use.cpp2" +#line 858 "pure2-last-use.cpp2" [[nodiscard]] auto no_pessimizing_move() -> no_pessimizing_move_ret{ std::unique_ptr ret {}; -#line 858 "pure2-last-use.cpp2" -return ret; } #line 859 "pure2-last-use.cpp2" +return ret; } +#line 860 "pure2-last-use.cpp2" auto deferred_non_copyable_0() -> void{ cpp2::impl::deferred_init> p; p.construct(); f_copy(std::move(cpp2::move(p.value()))); } -#line 865 "pure2-last-use.cpp2" +#line 866 "pure2-last-use.cpp2" [[nodiscard]] auto deferred_non_copyable_1() -> auto{ cpp2::impl::deferred_init> p; p.construct(); return std::move(cpp2::move(p.value())); } -#line 871 "pure2-last-use.cpp2" +#line 872 "pure2-last-use.cpp2" [[nodiscard]] auto deferred_non_copyable_2() -> deferred_non_copyable_2_ret{ cpp2::impl::deferred_init> p; -#line 872 "pure2-last-use.cpp2" +#line 873 "pure2-last-use.cpp2" p.construct(); return std::move(p.value()); } -#line 875 "pure2-last-use.cpp2" +#line 876 "pure2-last-use.cpp2" auto loops() -> void{ static_cast([]() mutable -> void{ auto x {cpp2_new(0)}; @@ -1431,7 +1422,7 @@ auto loops() -> void{ namespace captures { -#line 915 "pure2-last-use.cpp2" +#line 916 "pure2-last-use.cpp2" auto f() -> void{ auto x {cpp2_new(0)}; f_copy(std::move(cpp2::move(x))); @@ -1440,7 +1431,7 @@ auto f() -> void{ if (cpp2::cpp2_default.is_active() && !(&cpp2::move(id)(y) == &y) ) { cpp2::cpp2_default.report_violation(""); } } -#line 927 "pure2-last-use.cpp2" +#line 928 "pure2-last-use.cpp2" auto t::operator()() && -> void{ f_copy(std::move(cpp2::move(*this).x)); static_cast([&]() mutable -> void{ @@ -1459,7 +1450,7 @@ auto f() -> void{ auto t::operator=(auto const& x_) -> t& { x = x_; return *this;} -#line 940 "pure2-last-use.cpp2" +#line 941 "pure2-last-use.cpp2" auto g() -> void{ static_cast([]() mutable -> void{ auto x {cpp2_new(0)}; @@ -1469,14 +1460,14 @@ auto g() -> void{ static_cast([]() mutable -> void{ auto x {cpp2_new(0)}; - f_inout(cpp2::move(x)); - // _ = :() -> int = (:() x$*)$(); + f_inout(x); + static_cast([_0 = ([_0 = cpp2::move(x)]() mutable -> auto { return *cpp2::impl::assert_not_null(_0); })]() mutable -> int { return _0(); }); }); } } -#line 956 "pure2-last-use.cpp2" +#line 957 "pure2-last-use.cpp2" auto loops_and_captures() -> void{ static_cast([]() mutable -> void{ auto x {cpp2_new(0)}; @@ -1491,7 +1482,7 @@ auto loops_and_captures() -> void{ f_copy(std::move(cpp2::move(x))); for ( -#line 972 "pure2-last-use.cpp2" +#line 973 "pure2-last-use.cpp2" [[maybe_unused]] auto const& unnamed_param_1 : { []() mutable -> auto{using captures::x;return x; } } ) {} }); @@ -1510,7 +1501,7 @@ types::types(auto const& x_) auto types::operator=(auto const& x_) -> types& { x = x_; return *this;} -#line 994 "pure2-last-use.cpp2" +#line 995 "pure2-last-use.cpp2" auto skip_hidden_names() -> void{ static_cast([]() mutable -> void{ auto x {cpp2_new(0)}; @@ -1518,10 +1509,10 @@ auto skip_hidden_names() -> void{ { auto x{cpp2_new(0)}; -#line 999 "pure2-last-use.cpp2" +#line 1000 "pure2-last-use.cpp2" f_copy(std::move(cpp2::move(x))); } -#line 1000 "pure2-last-use.cpp2" +#line 1001 "pure2-last-use.cpp2" }); // _ = :() = { @@ -1544,10 +1535,10 @@ auto x{cpp2_new(0)}; // do (copy x) // _ = identity_copy(x); -#line 1020 "pure2-last-use.cpp2" +#line 1021 "pure2-last-use.cpp2" f_copy(std::move(cpp2::move(x))); } -#line 1021 "pure2-last-use.cpp2" +#line 1022 "pure2-last-use.cpp2" }); static_cast([]() mutable -> void{ @@ -1571,10 +1562,10 @@ auto x{cpp2_new(0)}; }); } -#line 1044 "pure2-last-use.cpp2" +#line 1045 "pure2-last-use.cpp2" auto main(int const argc_, char** argv_) -> int{ auto const args = cpp2::make_args(argc_, argv_); -#line 1045 "pure2-last-use.cpp2" +#line 1046 "pure2-last-use.cpp2" issue_683(args); issue_847_2(std::vector>()); issue_847_5(args); diff --git a/regression-tests/test-results/pure2-print.cpp b/regression-tests/test-results/pure2-print.cpp index a244f77e2..a131b4991 100644 --- a/regression-tests/test-results/pure2-print.cpp +++ b/regression-tests/test-results/pure2-print.cpp @@ -105,7 +105,7 @@ requires (true) inline CPP2_CONSTEXPR T outer::object_alias{ 42 }; if (cpp2::impl::cmp_less(*cpp2::impl::assert_not_null(p),0)) { ret = -*cpp2::impl::assert_not_null(cpp2::move(p)); } - ret += strlen(s) - 10 + CPP2_UFCS(strlen)(s) * (16 / (3 & 2)) % 3; + ret += strlen(s) - 10 + CPP2_UFCS(strlen)(s) * (16 / CPP2_ASSERT_NOT_ZERO(CPP2_TYPEOF(16),(3 & 2))) % 3; map m {}; CPP2_ASSERT_IN_BOUNDS_LITERAL(m, 0) = cpp2::impl::as_("har"); diff --git a/regression-tests/test-results/pure2-unsafe.cpp b/regression-tests/test-results/pure2-unsafe.cpp new file mode 100644 index 000000000..7069e8084 --- /dev/null +++ b/regression-tests/test-results/pure2-unsafe.cpp @@ -0,0 +1,43 @@ + +#define CPP2_IMPORT_STD Yes + +//=== Cpp2 type declarations ==================================================== + + +#include "cpp2util.h" + +#line 1 "pure2-unsafe.cpp2" + + +//=== Cpp2 type definitions and function declarations =========================== + +#line 1 "pure2-unsafe.cpp2" + +#line 2 "pure2-unsafe.cpp2" +auto f(cpp2::impl::in i, std::string& s) -> void; + +#line 12 "pure2-unsafe.cpp2" +auto main() -> int; + +//=== Cpp2 function definitions ================================================= + +#line 1 "pure2-unsafe.cpp2" + +#line 2 "pure2-unsafe.cpp2" +auto f(cpp2::impl::in i, std::string& s) -> void{ + // j := i as i16; // error, maybe-lossy narrowing + auto j {cpp2::unsafe_narrow(i)}; // ok, 'unsafe' is explicit + + void* pv {&s}; + // pi := pv as *std::string; // error, unsafe cast + auto ps {cpp2::unsafe_cast(cpp2::move(pv))}; // ok, 'unsafe' is explicit + *cpp2::impl::assert_not_null(ps) = "plugh"; +} + +#line 12 "pure2-unsafe.cpp2" +auto main() -> int{ + std::string str {"xyzzy"}; + f(42, str); + std::cout << cpp2::move(str); +} + diff --git a/regression-tests/test-results/pure2-unsafe.cpp2.output b/regression-tests/test-results/pure2-unsafe.cpp2.output new file mode 100644 index 000000000..2a3ca4d58 --- /dev/null +++ b/regression-tests/test-results/pure2-unsafe.cpp2.output @@ -0,0 +1,2 @@ +pure2-unsafe.cpp2... ok (all Cpp2, passes safety checks) + diff --git a/regression-tests/test-results/version b/regression-tests/test-results/version index 949bd2a76..ba18fe003 100644 --- a/regression-tests/test-results/version +++ b/regression-tests/test-results/version @@ -1,5 +1,5 @@ -cppfront compiler v0.7.2 Build 9727:1056 +cppfront compiler v0.7.2 Build 9809:1046 Copyright(c) Herb Sutter All rights reserved SPDX-License-Identifier: CC-BY-NC-ND-4.0 diff --git a/source/build.info b/source/build.info index 185f85719..ce3054eaa 100644 --- a/source/build.info +++ b/source/build.info @@ -1 +1 @@ -"9727:1056" \ No newline at end of file +"9809:1046" \ No newline at end of file diff --git a/source/common.h b/source/common.h index b9b41c1bf..844900f8e 100644 --- a/source/common.h +++ b/source/common.h @@ -20,6 +20,15 @@ #pragma GCC diagnostic ignored "-Wdangling-reference" #endif +// Disable some clang conversion warnings: +// cppfront uses signed integer types for indices and container sizes. +// Note: We don't pop the diagnostic because we want them disabled in the +// entire cppfront translation unit. +#ifdef __clang__ + #pragma clang diagnostic push + #pragma clang diagnostic ignored "-Wsign-conversion" +#endif + #include "cpp2util.h" @@ -97,7 +106,7 @@ struct source_line break;case category::cpp1: return "/* 1 */ "; break;case category::cpp2: return "/* 2 */ "; break;case category::rawstring: return "/* R */ "; - break;default: assert(!"illegal category"); abort(); + break;default: assert(false && "ICE: illegal category"); abort(); } } }; @@ -653,9 +662,9 @@ class cmdline_processor int max_flag_length = 0; std::unordered_map labels = { - { 2, "Additional dynamic safety checks and contract information" }, + { 2, "Additional dynamic safety check controls" }, { 4, "Support for constrained target environments" }, - { 8, "Cpp1 file emission options" }, + { 8, "Cpp1 file content options" }, { 9, "Cppfront output options" } }; diff --git a/source/cppfront.cpp b/source/cppfront.cpp index 3e28e46ae..a5dbb00ca 100644 --- a/source/cppfront.cpp +++ b/source/cppfront.cpp @@ -113,10 +113,10 @@ auto main( auto total = count.cpp1_lines + count.cpp2_lines; auto total_lines = print_with_thousands(total); out << " Cpp1 " - << std::right << std::setw(total_lines.size()) + << std::right << std::setw(unsafe_narrow(total_lines.size())) << print_with_thousands(count.cpp1_lines) << " line" << (count.cpp1_lines != 1 ? "s" : ""); out << "\n Cpp2 " - << std::right << std::setw(total_lines.size()) + << std::right << std::setw(unsafe_narrow(total_lines.size())) << print_with_thousands(count.cpp2_lines) << " line" << (count.cpp2_lines != 1 ? "s" : ""); if (total > 0) { out << " ("; @@ -145,7 +145,7 @@ auto main( for (auto [elapsed, name] : sorted_timers) { std::cout << "\n " - << std::right << std::setw(total_time.size()) + << std::right << std::setw(unsafe_narrow(total_time.size())) << print_with_thousands(elapsed) << " ms" << " in " << name; } } diff --git a/source/lex.h b/source/lex.h index bf48449cf..5d5cd04e0 100644 --- a/source/lex.h +++ b/source/lex.h @@ -85,7 +85,8 @@ enum class lexeme : std::int8_t { Dot, DotDot, Ellipsis, - EllipsisEq, + EllipsisLess, + EllipsisEqual, QuestionMark, At, Dollar, @@ -184,7 +185,8 @@ auto _as(lexeme l) break;case lexeme::Dot: return "Dot"; break;case lexeme::DotDot: return "DotDot"; break;case lexeme::Ellipsis: return "Ellipsis"; - break;case lexeme::EllipsisEq: return "EllipsisEq"; + break;case lexeme::EllipsisLess: return "EllipsisLess"; + break;case lexeme::EllipsisEqual: return "EllipsisEqual"; break;case lexeme::QuestionMark: return "QuestionMark"; break;case lexeme::At: return "At"; break;case lexeme::Dollar: return "Dollar"; @@ -1114,15 +1116,15 @@ auto lex_line( //G one of: 'import' 'module' 'export' 'is' 'as' //G auto do_is_keyword = [&](std::vector const& r) { - auto remaining_line = std::string_view(line).substr(unsafe_narrow(i)); + auto remaining_line = std::string_view(line).substr(i); auto m = std::find_if(r.begin(), r.end(), [&](std::string_view s) { return remaining_line.starts_with(s); }); if (m != r.end()) { // If we matched and what's next is EOL or a non-identifier char, we matched! if ( - i+std::ssize(*m) == std::ssize(line) // EOL - || !is_identifier_continue(line[unsafe_narrow(i)+std::size(*m)]) // non-identifier char + i+std::ssize(*m) == std::ssize(line) // EOL + || !is_identifier_continue(line[i+std::size(*m)]) // non-identifier char ) { return static_cast(std::ssize(*m)); @@ -1440,10 +1442,11 @@ auto lex_line( //G //G punctuator: one of - //G '.' '..' '...' '..=' + //G '.' '..' '...' '..<' '..=' break;case '.': if (peek1 == '.' && peek2 == '.') { store(3, lexeme::Ellipsis); } - else if (peek1 == '.' && peek2 == '=') { store(3, lexeme::EllipsisEq); } + else if (peek1 == '.' && peek2 == '<') { store(3, lexeme::EllipsisLess); } + else if (peek1 == '.' && peek2 == '=') { store(3, lexeme::EllipsisEqual); } else if (peek1 == '.') { store(2, lexeme::DotDot); } else { store(1, lexeme::Dot); } diff --git a/source/parse.h b/source/parse.h index 287747b0d..282067125 100644 --- a/source/parse.h +++ b/source/parse.h @@ -61,7 +61,8 @@ auto is_postfix_operator(lexeme l) case lexeme::Tilde: case lexeme::Dollar: case lexeme::Ellipsis: - case lexeme::EllipsisEq: + case lexeme::EllipsisLess: + case lexeme::EllipsisEqual: return true; break;default: return false; @@ -1413,7 +1414,7 @@ struct type_id_node break;case keyword: return std::get(id)->to_string() + suffix; break;default: - assert(!"ICE: invalid type_id state"); + assert(false && "ICE: invalid type_id state"); } // else return {}; @@ -1434,7 +1435,7 @@ struct type_id_node break;case keyword: return get(id); break;default: - assert(!"ICE: invalid type_id state"); + assert(false && "ICE: invalid type_id state"); } // else return {}; @@ -1500,7 +1501,7 @@ auto template_argument::to_string() const break;case type_id: return std::get(arg)->to_string(); break;default: - assert(!"ICE: invalid template_argument state"); + assert(false && "ICE: invalid template_argument state"); } // else return {}; @@ -2273,7 +2274,13 @@ struct parameter_declaration_node std::unique_ptr declaration; - parameter_declaration_node(parameter_declaration_list_node const* my) : my_list{my} { } + // Out-of-line definition of the ctor is necessary due to the forward-declared + // type(s) used in a std::unique_ptr as a member + parameter_declaration_node(parameter_declaration_list_node const* my); + + // Out-of-line definition of the dtor is necessary due to the forward-declared + // type(s) used in a std::unique_ptr as a member + ~parameter_declaration_node(); // API // @@ -2850,8 +2857,9 @@ struct declaration_node bool member_function_generation = true; // Cache some context - bool is_a_template_parameter = false; - bool is_a_parameter = false; + bool is_a_template_parameter = false; + bool is_a_parameter = false; + bool is_a_statement_parameter = false; // Constructor // @@ -2859,6 +2867,10 @@ struct declaration_node : parent_declaration{parent} { } + // Out-of-line definition of the dtor is necessary due to the forward-declared + // type(s) used in a std::unique_ptr as a member + ~declaration_node(); + // API // @@ -2874,6 +2886,12 @@ struct declaration_node return is_a_parameter; } + auto is_statement_parameter() const + -> bool + { + return is_a_statement_parameter; + } + auto type_member_mark_for_removal() -> bool { @@ -4342,7 +4360,7 @@ auto primary_expression_node::position() const } break;default: - assert (!"illegal primary_expression_node state"); + assert (false && "ICE: illegal primary_expression_node state"); return { 0, 0 }; } } @@ -4465,7 +4483,7 @@ auto statement_node::position() const } break;default: - assert (!"illegal statement_node state"); + assert (false && "ICE: illegal statement_node state"); return { 0, 0 }; } } @@ -4512,7 +4530,13 @@ struct translation_unit_node } }; -// Definitions of out-of-line dtors for nodes with unique_ptr members of forward-declared types +// Definitions of out-of-line ctors & dtors for nodes with unique_ptr members of forward-declared types + +parameter_declaration_node::parameter_declaration_node(parameter_declaration_list_node const* my) + : my_list{my} +{ } + +parameter_declaration_node::~parameter_declaration_node() = default; type_id_node::~type_id_node() = default; @@ -4536,6 +4560,8 @@ inspect_expression_node::~inspect_expression_node() = default; statement_node::~statement_node() = default; +declaration_node::~declaration_node() = default; + //----------------------------------------------------------------------- // @@ -6011,7 +6037,7 @@ class parser //G postfix-expression '(' expression-list? ','? ')' //G postfix-expression '.' id-expression //G postfix-expression '..' id-expression - //G postfix-expression '...' primary-expression + //G postfix-expression '..<' primary-expression //G postfix-expression '..=' primary-expression //G auto postfix_expression() @@ -6031,8 +6057,8 @@ class parser || curr().type() == lexeme::LeftParen || curr().type() == lexeme::Dot || curr().type() == lexeme::DotDot - || curr().type() == lexeme::Ellipsis - || curr().type() == lexeme::EllipsisEq + || curr().type() == lexeme::EllipsisLess + || curr().type() == lexeme::EllipsisEqual ) ) { @@ -6127,8 +6153,8 @@ class parser } else if ( ( - term.op->type() == lexeme::Ellipsis - || term.op->type() == lexeme::EllipsisEq + term.op->type() == lexeme::EllipsisLess + || term.op->type() == lexeme::EllipsisEqual ) && n->expr->to_string() != "sizeof" ) @@ -6281,7 +6307,7 @@ class parser // And it shouldn't be anything else else { - assert (!"ICE: validate_op should take one token and return bool, or two tokens and return token const* "); + assert (false && "ICE: validate_op should take one token and return bool, or two tokens and return token const* "); } // At this point we may have a valid t.op, so try to parse the next term... @@ -6731,11 +6757,6 @@ class parser return {}; } - if (curr().type() == lexeme::Multiply) { - error("'T*' is not a valid Cpp2 type; use '*T' for a pointer instead", false); - return {}; - } - if ( allow_constraint && n->is_wildcard() @@ -7466,7 +7487,7 @@ class parser return n; } - assert(!"compiler bug: unexpected case"); + assert(false && "ICE: unexpected case"); return {}; } @@ -8027,6 +8048,7 @@ class parser pos = start_pos; // backtrack return {}; } + n->declaration->is_a_statement_parameter = is_statement; // And some error checks // @@ -8088,20 +8110,11 @@ class parser break;case passing_style::forward: error( "a 'forward' parameter shouldn't be const, because it passes along the argument's actual const-ness (and actual value category)", false ); break;default: - assert (!"ICE: missing case"); + assert (false && "ICE: missing case"); } return {}; } - if ( - !is_returns - && !is_statement - && n->declaration->initializer - ) - { - error("Cpp2 is currently exploring the path of not allowing default arguments - use overloading instead", false); - return {}; - } if (is_named && is_returns) { auto tok = n->name(); assert(tok); @@ -9240,7 +9253,7 @@ class parser // Anything else shouldn't be possible else { - assert(!"ICE: should be unreachable - invalid alias declaration"); + assert(false && "ICE: should be unreachable - invalid alias declaration"); return {}; } diff --git a/source/reflect.h b/source/reflect.h index 7b89be4d5..bd7d97213 100644 --- a/source/reflect.h +++ b/source/reflect.h @@ -39,7 +39,7 @@ class alias_declaration; #line 1006 "reflect.h2" class value_member_info; -#line 1632 "reflect.h2" +#line 1692 "reflect.h2" } } @@ -708,7 +708,7 @@ auto basic_enum( cpp2::impl::in bitwise ) -> void; -#line 1207 "reflect.h2" +#line 1267 "reflect.h2" //----------------------------------------------------------------------- // // "An enum[...] is a totally ordered value type that stores a @@ -720,7 +720,7 @@ auto basic_enum( // auto cpp2_enum(meta::type_declaration& t) -> void; -#line 1233 "reflect.h2" +#line 1293 "reflect.h2" //----------------------------------------------------------------------- // // "flag_enum expresses an enumeration that stores values @@ -733,7 +733,7 @@ auto cpp2_enum(meta::type_declaration& t) -> void; // auto flag_enum(meta::type_declaration& t) -> void; -#line 1265 "reflect.h2" +#line 1325 "reflect.h2" //----------------------------------------------------------------------- // // "As with void*, programmers should know that unions [...] are @@ -760,14 +760,14 @@ auto flag_enum(meta::type_declaration& t) -> void; auto cpp2_union(meta::type_declaration& t) -> void; -#line 1436 "reflect.h2" +#line 1496 "reflect.h2" //----------------------------------------------------------------------- // // print - output a pretty-printed visualization of t // auto print(cpp2::impl::in t) -> void; -#line 1446 "reflect.h2" +#line 1506 "reflect.h2" //----------------------------------------------------------------------- // // regex - creates regular expressions from members @@ -784,7 +784,7 @@ auto print(cpp2::impl::in t) -> void; // auto regex_gen(meta::type_declaration& t) -> void; -#line 1513 "reflect.h2" +#line 1573 "reflect.h2" //----------------------------------------------------------------------- // // apply_metafunctions @@ -795,7 +795,7 @@ auto regex_gen(meta::type_declaration& t) -> void; auto const& error ) -> bool; -#line 1632 "reflect.h2" +#line 1692 "reflect.h2" } } @@ -1837,52 +1837,121 @@ std::string value{"-1"}; // Generate the common functions CPP2_UFCS(add_member)(t, " get_raw_value : (this) -> " + cpp2::to_string(cpp2::move(underlying_type.value())) + " == _value;"); - CPP2_UFCS(add_member)(t, " operator= : (out this) == { _value = " + cpp2::to_string(cpp2::move(default_value)) + "._value; }"); + CPP2_UFCS(add_member)(t, " operator= : (out this) == { _value = " + cpp2::to_string(default_value) + "._value; }"); CPP2_UFCS(add_member)(t, " operator= : (out this, that) == { }"); CPP2_UFCS(add_member)(t, " operator<=> : (this, that) -> std::strong_ordering;"); { -std::string to_string{" to_string: (this) -> std::string = { \n"}; +std::string to_string_impl{" to_string_impl: (this, prefix: std::string_view"}; - // Provide a 'to_string' function to print enumerator name(s) + // Provide 'to_string' and 'to_code' functions to print enumerator + // name(s) as human-readable strings or as code expressions -#line 1172 "reflect.h2" +#line 1173 "reflect.h2" { if (bitwise) { - to_string += " _ret : std::string = \"(\";\n" - " _comma : std::string = ();\n" - " if this == none { return \"(none)\"; }\n"; + to_string_impl += ", separator: std::string_view ) -> std::string = { \n" + " ret : std::string = \"(\";\n" + " sep : std::string = ();\n" + " if this == none { return \"(none)\"; }\n"; } + else { + to_string_impl += ") -> std::string = { \n"; + } + + to_string_impl += " pref := cpp2::to_string(prefix);\n"; for ( - auto const& e : cpp2::move(enumerators) ) { + auto const& e : enumerators ) { if (e.name != "_") {// ignore unnamed values if (bitwise) { if (e.name != "none") { - to_string += " if (this & " + cpp2::to_string(e.name) + ") == " + cpp2::to_string(e.name) + " { " - "_ret += _comma + \"" + cpp2::to_string(e.name) + "\"; _comma = \", \"; " - "}\n"; + to_string_impl += " if (this & " + cpp2::to_string(e.name) + ") == " + cpp2::to_string(e.name) + " { " + "ret += sep + pref + \"" + cpp2::to_string(e.name) + "\"; sep = separator; " + "}\n"; } } else { - to_string += " if this == " + cpp2::to_string(e.name) + " { return \"" + cpp2::to_string(e.name) + "\"; }\n"; + to_string_impl += " if this == " + cpp2::to_string(e.name) + " { return pref + \"" + cpp2::to_string(e.name) + "\"; }\n"; } } } if (bitwise) { - to_string += " return _ret+\")\";\n}\n"; + to_string_impl += " return ret+\")\";\n}\n"; } else { - to_string += " return \"invalid " + cpp2::to_string(CPP2_UFCS(name)(t)) + " value\";\n}\n"; + to_string_impl += " return \"invalid " + cpp2::to_string(CPP2_UFCS(name)(t)) + " value\";\n}\n"; } - CPP2_UFCS(add_member)(t, cpp2::move(to_string)); + CPP2_UFCS(add_member)(t, cpp2::move(to_string_impl)); } } -#line 1204 "reflect.h2" + +#line 1212 "reflect.h2" + if (bitwise) { + CPP2_UFCS(add_member)(t, " to_string: (this) -> std::string = to_string_impl( \"\", \", \" );"); + CPP2_UFCS(add_member)(t, " to_code : (this) -> std::string = to_string_impl( \"" + cpp2::to_string(CPP2_UFCS(name)(t)) + "::\", \" | \" );"); + } + else { + CPP2_UFCS(add_member)(t, " to_string: (this) -> std::string = to_string_impl( \"\" );"); + CPP2_UFCS(add_member)(t, " to_code : (this) -> std::string = to_string_impl( \"" + cpp2::to_string(CPP2_UFCS(name)(t)) + "::\" );"); + } +{ +std::string from_string{" from_string: (s: std::string_view) -> " + cpp2::to_string(CPP2_UFCS(name)(t)) + " = { \n"}; + + // Provide a 'from_string' function to parse strings into enumerators + +#line 1223 "reflect.h2" + { + std::string_view prefix {""}; + std::string_view combine_op {"return"}; + + // For flags, accept a list that we break apart and then |= together + if (bitwise) + { + prefix = "flag_"; + combine_op = "ret |="; + + from_string += " ret := none;\n" + " outer: do {\n" + " for cpp2::string_util::split_string_list(s) do (x) {\n"; + } + // Otherwise, accept just a single string + else { + from_string += " x := s;\n"; + } +{ +std::string_view else_{""}; + +#line 1243 "reflect.h2" + for ( + auto const& e : cpp2::move(enumerators) ) { + from_string += " " + cpp2::to_string(else_) + "if \"" + cpp2::to_string(e.name) + "\" == x { " + cpp2::to_string(combine_op) + " " + cpp2::to_string(e.name) + "; }\n"; + else_ = "else "; + } } -#line 1216 "reflect.h2" +#line 1249 "reflect.h2" + if (bitwise) { + from_string += " else { break outer; }\n" + " }\n" + " return ret;\n" + " } while false;\n"; + } + + from_string += " cpp2::type_safety.report_violation( (\"can't convert string '\" + cpp2::to_string(s) + \"' to " + cpp2::to_string(cpp2::move(prefix)) + "enum of type " + cpp2::to_string(CPP2_UFCS(name)(t)) + "\").c_str() );\n" + " return " + cpp2::to_string(cpp2::move(default_value)) + ";\n" + " }\n\n"; + + CPP2_UFCS(add_member)(t, cpp2::move(from_string)); + } +} + +#line 1263 "reflect.h2" + CPP2_UFCS(add_member)(t, " from_code: (s: std::string_view) -> " + cpp2::to_string(CPP2_UFCS(name)(t)) + " = { str: std::string = s; return from_string( cpp2::string_util::replace_all(str, \"" + cpp2::to_string(CPP2_UFCS(name)(t)) + "::\", \"\" ) ); }"); +} + +#line 1276 "reflect.h2" auto cpp2_enum(meta::type_declaration& t) -> void { // Let basic_enum do its thing, with an incrementing value generator @@ -1899,7 +1968,7 @@ auto cpp2_enum(meta::type_declaration& t) -> void ); } -#line 1243 "reflect.h2" +#line 1303 "reflect.h2" auto flag_enum(meta::type_declaration& t) -> void { // Let basic_enum do its thing, with a power-of-two value generator @@ -1921,7 +1990,7 @@ auto flag_enum(meta::type_declaration& t) -> void ); } -#line 1289 "reflect.h2" +#line 1349 "reflect.h2" auto cpp2_union(meta::type_declaration& t) -> void { std::vector alternatives {}; @@ -1930,7 +1999,7 @@ auto value{0}; // 1. Gather: All the user-written members, and find/compute the max size -#line 1296 "reflect.h2" +#line 1356 "reflect.h2" for ( auto const& m : CPP2_UFCS(get_members)(t) ) { do @@ -1960,7 +2029,7 @@ auto value{0}; } while (false); ++value; } } -#line 1324 "reflect.h2" +#line 1384 "reflect.h2" std::string discriminator_type {}; if (cpp2::impl::cmp_less(CPP2_UFCS(ssize)(alternatives),std::numeric_limits::max())) { discriminator_type = "i8"; @@ -1975,7 +2044,7 @@ auto value{0}; discriminator_type = "i64"; }}} -#line 1339 "reflect.h2" +#line 1399 "reflect.h2" // 2. Replace: Erase the contents and replace with modified contents CPP2_UFCS(remove_marked_members)(t); @@ -1984,7 +2053,7 @@ std::string storage{" _storage: cpp2::aligned_storage t) -> void { std::cout << CPP2_UFCS(print)(t) << "\n"; } -#line 1460 "reflect.h2" +#line 1520 "reflect.h2" auto regex_gen(meta::type_declaration& t) -> void { auto has_default {false}; - auto prefix {"regex"}; - std::string postfix {"_mod"}; // TODO: remove mod syntax when 'm.initializer()' can be '("pat", "mod")' + auto exact_name {"regex"}; + auto prefix {"regex_"}; std::map expressions {}; for ( auto& m : CPP2_UFCS(get_member_objects)(t) ) { std::string name {CPP2_UFCS(name)(m)}; - if (CPP2_UFCS(starts_with)(name, prefix)) + if (CPP2_UFCS(starts_with)(name, prefix) || name == exact_name) { if (!(CPP2_UFCS(has_initializer)(m))) { CPP2_UFCS(error)(t, "Regular expression must have an initializer."); } CPP2_UFCS(mark_for_removal_from_enclosing_type)(m); - if (name == prefix) { + if (name == exact_name) { if (has_default) { CPP2_UFCS(error)(t, "Type can only contain one default named regular expression."); } @@ -2146,7 +2215,7 @@ auto regex_gen(meta::type_declaration& t) -> void } } -#line 1517 "reflect.h2" +#line 1577 "reflect.h2" [[nodiscard]] auto apply_metafunctions( declaration_node& n, type_declaration& rtype, @@ -2261,7 +2330,7 @@ auto regex_gen(meta::type_declaration& t) -> void return true; } -#line 1632 "reflect.h2" +#line 1692 "reflect.h2" } } diff --git a/source/reflect.h2 b/source/reflect.h2 index 9f82035a0..9989aedbe 100644 --- a/source/reflect.h2 +++ b/source/reflect.h2 @@ -1167,40 +1167,100 @@ basic_enum: ( t.add_member( " operator= : (out this, that) == { }"); t.add_member( " operator<=> : (this, that) -> std::strong_ordering;"); - // Provide a 'to_string' function to print enumerator name(s) - (copy to_string: std::string = " to_string: (this) -> std::string = { \n") + // Provide 'to_string' and 'to_code' functions to print enumerator + // name(s) as human-readable strings or as code expressions + (copy to_string_impl: std::string = " to_string_impl: (this, prefix: std::string_view") { if bitwise { - to_string += " _ret : std::string = \"(\";\n" - " _comma : std::string = ();\n" - " if this == none { return \"(none)\"; }\n"; + to_string_impl += ", separator: std::string_view ) -> std::string = { \n" + " ret : std::string = \"(\";\n" + " sep : std::string = ();\n" + " if this == none { return \"(none)\"; }\n"; + } + else { + to_string_impl += ") -> std::string = { \n"; } + to_string_impl += " pref := cpp2::to_string(prefix);\n"; + for enumerators do (e) { if e.name != "_" { // ignore unnamed values if bitwise { if e.name != "none" { - to_string += " if (this & (e.name)$) == (e.name)$ { " - "_ret += _comma + \"(e.name)$\"; _comma = \", \"; " - "}\n"; + to_string_impl += " if (this & (e.name)$) == (e.name)$ { " + "ret += sep + pref + \"(e.name)$\"; sep = separator; " + "}\n"; } } else { - to_string += " if this == (e.name)$ { return \"(e.name)$\"; }\n"; + to_string_impl += " if this == (e.name)$ { return pref + \"(e.name)$\"; }\n"; } } } if bitwise { - to_string += " return _ret+\")\";\n}\n"; + to_string_impl += " return ret+\")\";\n}\n"; } else { - to_string += " return \"invalid (t.name())$ value\";\n}\n"; + to_string_impl += " return \"invalid (t.name())$ value\";\n}\n"; } - t.add_member( to_string ); + t.add_member( to_string_impl ); + } + + if bitwise { + t.add_member( " to_string: (this) -> std::string = to_string_impl( \"\", \", \" );" ); + t.add_member( " to_code : (this) -> std::string = to_string_impl( \"(t.name())$::\", \" | \" );" ); + } + else { + t.add_member( " to_string: (this) -> std::string = to_string_impl( \"\" );" ); + t.add_member( " to_code : (this) -> std::string = to_string_impl( \"(t.name())$::\" );" ); } + + // Provide a 'from_string' function to parse strings into enumerators + (copy from_string: std::string = " from_string: (s: std::string_view) -> (t.name())$ = { \n") + { + prefix : std::string_view = ""; + combine_op: std::string_view = "return"; + + // For flags, accept a list that we break apart and then |= together + if bitwise + { + prefix = "flag_"; + combine_op = "ret |="; + + from_string += " ret := none;\n" + " outer: do {\n" + " for cpp2::string_util::split_string_list(s) do (x) {\n"; + } + // Otherwise, accept just a single string + else { + from_string += " x := s;\n"; + } + + (copy else_: std::string_view = "") + for enumerators + do (e) { + from_string += " (else_)$if \"(e.name)$\" == x { (combine_op)$ (e.name)$; }\n"; + else_ = "else "; + } + + if bitwise { + from_string += " else { break outer; }\n" + " }\n" + " return ret;\n" + " } while false;\n"; + } + + from_string += " cpp2::type_safety.report_violation( (\"can't convert string '\" + cpp2::to_string(s) + \"' to (prefix)$enum of type (t.name())$\").c_str() );\n" + " return (default_value)$;\n" + " }\n\n"; + + t.add_member( from_string ); + } + + t.add_member( " from_code: (s: std::string_view) -> (t.name())$ = { str: std::string = s; return from_string( cpp2::string_util::replace_all(str, \"(t.name())$::\", \"\" ) ); }" ); } @@ -1460,22 +1520,22 @@ print: (t: meta::type_declaration) = regex_gen: (inout t: meta::type_declaration) = { has_default := false; - prefix := "regex"; - postfix : std::string = "_mod"; // TODO: remove mod syntax when 'm.initializer()' can be '("pat", "mod")' + exact_name := "regex"; + prefix := "regex_"; expressions : std::map = (); for t.get_member_objects() do (inout m) { name: std::string = m.name(); - if name.starts_with(prefix) + if name.starts_with(prefix) || name == exact_name { if !m.has_initializer() { t.error("Regular expression must have an initializer."); } m.mark_for_removal_from_enclosing_type(); - if name == prefix { + if name == exact_name { if has_default { t.error("Type can only contain one default named regular expression."); } diff --git a/source/sema.h b/source/sema.h index bc4ec0d96..46e2c509d 100644 --- a/source/sema.h +++ b/source/sema.h @@ -253,7 +253,7 @@ struct symbol { } break;default: - assert (!"illegal symbol state"); + assert (false && "ICE: illegal symbol state"); return { 0, 0 }; } } @@ -284,7 +284,7 @@ struct symbol { } break;default: - assert (!"illegal symbol state"); + assert (false && "ICE: illegal symbol state"); return nullptr; } } @@ -1443,7 +1443,7 @@ class sema } break;default: - assert (!"illegal symbol"); + assert (false && "ICE: illegal symbol"); } } @@ -2068,13 +2068,7 @@ class sema // An increment/decrement function must have a single 'inout' parameter, // and if it's a member flag it if we know the type is not copyable - if ( - n.my_decl - && ( - n.my_decl->has_name("operator++") - || n.my_decl->has_name("operator--") - ) - ) + if (n.is_increment_or_decrement()) { if ( (*n.parameters).ssize() != 1 @@ -2095,18 +2089,6 @@ class sema ); return false; } - - if ( - n.my_decl->parent_declaration - && n.my_decl->parent_declaration->cannot_be_a_copy_constructible_type() - ) - { - errors.emplace_back( - n.position(), - "a user-defined " + n.my_decl->name()->to_string() + " in type scope must be a member of a copyable type" - ); - return false; - } } return true; diff --git a/source/to_cpp1.h b/source/to_cpp1.h index 765b4e2be..bc66cd5d7 100644 --- a/source/to_cpp1.h +++ b/source/to_cpp1.h @@ -131,6 +131,14 @@ static cmdline_processor::register_flag cmd_safe_null_pointers( []{ flag_safe_null_pointers = false; } ); +static auto flag_safe_zero_division = true; +static cmdline_processor::register_flag cmd_safe_zero_division( + 2, + "no-div-zero-checks", + "Disable integer division by zero checks", + []{ flag_safe_zero_division = false; } +); + static auto flag_safe_subscripts = true; static cmdline_processor::register_flag cmd_safe_subscripts( 2, @@ -149,7 +157,7 @@ static cmdline_processor::register_flag cmd_safe_comparisons( static auto flag_cpp1_filename = std::string{}; static cmdline_processor::register_flag cmd_cpp1_filename( - 8, + 9, "output filename", "Output to 'filename' (can be 'stdout') - default is *.cpp/*.h", nullptr, @@ -249,7 +257,7 @@ class positional_printer switch (phase) { break;case phase0_type_decls : phase = phase1_type_defs_func_decls; break;case phase1_type_defs_func_decls: phase = phase2_func_defs; - break;default : assert(!"ICE: invalid lowering phase"); + break;default : assert(false && "ICE: invalid lowering phase"); } curr_pos = {}; next_comment = 0; // start over with the comments @@ -1627,8 +1635,8 @@ class cppfront pos = n.position(); } - // Implicit "cpp2::" qualification of Cpp2 fixed-width type aliases - // and cpp2::finally + // Implicit "cpp2::" qualification of utilities we want to + // make usable without cpp2:: qualification if ( !is_qualified && ( @@ -1636,6 +1644,8 @@ class cppfront || n == "finally" || n == "cpp1_ref" || n == "cpp1_rvalue_ref" + || n == "unsafe_narrow" + || n == "unsafe_cast" ) ) { @@ -1669,6 +1679,9 @@ class cppfront { printer.print_cpp2("cpp2_"+n.to_string(), pos); } + else if (n == "type_of") { + printer.print_cpp2("CPP2_TYPEOF", pos); + } else { printer.print_cpp2(n, pos, true); } @@ -2336,7 +2349,7 @@ class cppfront } else { - assert(!"ICE: unexpected case"); + assert(false && "ICE: unexpected case"); } assert (iteration_statements.back().stmt); @@ -3278,14 +3291,18 @@ class cppfront ufcs_string += "_TEMPLATE"; } - // If we're in an object declaration (i.e., initializer) - // at namespace scope, use the _NONLOCAL version + // If we're in a namespace-scope object declaration (i.e., initializer) + // or in a default function argument, use the _NONLOCAL version // // Note: If there are other cases where code could execute // in a non-local scope where a capture-default for the UFCS // lambda would not be allowed, then add them here if ( current_declarations.back()->is_namespace() + || ( + current_declarations.back()->is_parameter() + && !current_declarations.back()->is_statement_parameter() + ) || ( current_declarations.back()->is_object() && current_declarations.back()->parent_is_namespace() @@ -3464,9 +3481,12 @@ class cppfront { prefix.emplace_back( "cpp2::range(", i->op->position() ); auto print = print_to_string( *i->last_expr ); - if (i->op->type() == lexeme::EllipsisEq) { + if (i->op->type() == lexeme::EllipsisEqual) { print += ",true"; } + else { + assert(i->op->type() == lexeme::EllipsisLess); + } suffix.emplace_back( "," + print + ")", i->last_expr->position()); } @@ -3868,6 +3888,7 @@ class cppfront } // If it's "_ =" then emit static_cast() bool emit_discard = false; + auto last_expr = decltype(n.expr.get()){}; if ( !n.terms.empty() && n.terms.front().op->type() == lexeme::Assignment @@ -3881,6 +3902,7 @@ class cppfront } else { + last_expr = n.expr.get(); emit(*n.expr); } suppress_move_from_last_use = false; @@ -3972,7 +3994,33 @@ class cppfront } // Otherwise, just emit the general expression as usual else { + // If this is a division, wrap the denominator in a not-zero check + auto suffix = std::string{}; + if ( + flag_safe_zero_division + && ( + x.op->type() == lexeme::Slash + || x.op->type() == lexeme::SlashEq + ) + ) + { + assert(last_expr && "ICE: shouldn't get here without having captured a pointer to the numerator expression"); + if (auto lit = x.expr->get_literal(); + lit + && lit->get_token()->type() == lexeme::DecimalLiteral + ) + { + printer.print_cpp2( "CPP2_ASSERT_NOT_ZERO_LITERAL(CPP2_TYPEOF(" + print_to_string(*last_expr) + "),", x.op->position() ); + } + else + { + printer.print_cpp2( "CPP2_ASSERT_NOT_ZERO(CPP2_TYPEOF(" + print_to_string(*last_expr) + "),", x.op->position() ); + } + suffix = ")"; + } + last_expr = x.expr.get(); emit(*x.expr); + printer.print_cpp2( suffix, x.expr->position() ); } } @@ -4636,6 +4684,10 @@ class cppfront if ( !is_returns && n.declaration->initializer + && ( + is_statement + || printer.get_phase() != printer.phase2_func_defs + ) ) { auto guard = stack_element(current_declarations, &*n.declaration); @@ -4645,7 +4697,7 @@ class cppfront else { printer.print_cpp2( " = ", n.declaration->initializer->position() ); } - emit(*n.declaration->initializer, !is_statement); + emit(*n.declaration->initializer, false); if (is_statement) { printer.print_cpp2( "};", n.declaration->initializer->position() ); } @@ -5789,7 +5841,7 @@ class cppfront } else { - assert(!"ICE: should be unreachable - invalid alias"); + assert(false && "ICE: should be unreachable - invalid alias"); } return; @@ -6548,12 +6600,18 @@ class cppfront printer.print_cpp2( suffix2, n.position() ); // If this is ++ or --, also generate a Cpp1 postfix version of the operator + // (as long as we don't know for sure this isn't a copyable type) if (func->is_increment_or_decrement()) { if (generating_postfix_inc_dec_from) { assert (generating_postfix_inc_dec_from == &n); } - else { + else if ( + !n.parent_declaration + || !n.parent_declaration->is_type() + || !n.parent_declaration->cannot_be_a_copy_constructible_type() + ) + { need_to_generate_postfix_inc_dec = true; } }