From 510eae80bf3b92c7e210817c329e4b417d0012ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johel=20Ernesto=20Guerrero=20Pe=C3=B1a?= Date: Sun, 4 Feb 2024 01:30:03 -0400 Subject: [PATCH] fix: recognize last uses (including of `this`) (#887) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix(sema): recognize last use immediately after declaration * fix(sema): give function expression its own scope level * fix(sema): consider use as function in UFCS call * refactor(sema): remove stale comment * test(sema): also add unit test for `move` parameter * refactor(sema): avoid possible decrement beyond `begin` * fix(sema): move `this` on last use * test: update GCC 13 results * test: add Clang 18 results * test: add overloads with `that` * test: add another case that doesn't work yet * test: rename members to avoid shadowing * fix(sema): move implicit `this` of member on last use * test: remove commented cases that aren't last uses * fix(sema): guard check for `move this` parameter once * test: add case for destructor * test: add missing GCC 13 result * test: regenerate tests * fix(sema): consider use as function in UFCS call * fix(to_cpp1): type-scope variable initialization is not deferred * refactor(util): add UFCS macros to move and forward * fix(to_cpp1): emit last use of function in UFCS as part of macro name * test: regenerate GCC 13 result * fix(to_cpp1): exercise last use in range of for loop * refactor(util): remove UFCS macros of invalid combinations * refactor(to_cpp1): handle range of for loop specifically * refactor(to_cpp1): consider the expression list range * fix(sema): apply last use to call of member function * fix(sema): do not move a member function * fix(to_cpp1): do not move any member function * test: remove non-problematic comment about double move It just works, see . * refactor: regenerate `reflect.h` * refactor(sema): avoid decrementing before begin * fix(sema): extend implicit else branch to containing statement * fix(sema): increase scope to match an explicit else * refactor(sema): move heuristic to not change ending depths * fix(sema): pop consecutive implicit else branches * refactor(parse): put implicit else statements in the actual else node * test: add test cases that combine implicit and explicit access * test: add unit test for all test cases as selections * fix(reflect): fix initialization and regenerate * fix(sema): apply sema phase to generated code * test: regenerate results * test: add test case for another limitation * test: add another test case that fails * fix(sema): improve heuristic to only move from last (implicit) `this` * refactor(reflect): restore implicit `this` access * fix(sema): do not stack an implicit else branch * refactor: use "implicit scope" throughout * fix(sema): use local `move this` to determine last use * fix(sema): adjust condition for looking up to type * fix(sema): use last move on called type-scope variable * test: remove bogus comment * fix(sema): correct unset `this.` case * test: reference open issue * test: add test cases not affected by the open issue * test: clarify FIXME comment * test: review the new tests * perf: replace `std::map` with `mutable` data member * refactor: add `symbol::get_final_position` * refactor(to_cpp1): do not move single named return * test: add new test cases to commented out test case * test: add similar test case to callable case * refactor(sema): rework some outdated whitespace * test: regenerate results after "do not move single named return" * feat(sema): diagnose unused variables in generate code * fix(sema): consider variables in type scope declared later * test: refactor new test to use `f_copy` * refactor(sema): use member token * refactor(sema): update comments * refactor(sema): use the `this` parameter exclusively * refactor(sema): update the comments * refactor(sema): finish focusing on the implicit 'this' * fix(to_cpp1): move returned uninitialized local * fix(to_cpp1): move single named uninitialized return * test: add case with member declared last * refactor(sema): set condition for "at `this.`" correctly * fix(sema): use the better lookup algorithm for this case * fix(to_cpp1): stack current names only in namespaces * fix(to_cpp1): stack current names of bases * test: exercise forward in generated code * test: add stand-in for `std::move_only_function` * test: remove bogus test case * refactor(to_cpp1): rename to `is_class_member_access` * test: add test case showing limitations with base members * test: actually exercise forward in generated code * refactor(to_cpp1): reorder branch * refactor(to_cpp1): remove outdated condition * refactor(to_cpp1): rename to `is_class_member_access` * fix: revert using the empty implicit else branch Refs: e9cc033b36e47de783a3c4ae12174d66fc592fa6, 7f4a60f03315dfe2968ce131249bba930dc762cd * fix(sema): change algorithm to find last uses * test: add test case for recursion logic * refactor(sema): simplify condition for UFCS on member * test: use `f_inout` and `f_copy` in earlier test cases * test: enable commented out tests * test: extend limitation unit test with alternative * test: remove redundant explicit discards * test: add more test cases for the new last use algorithm * test: add missing `new()` * fix: regenerate `reflect.h` * test: add test cases of discovered problems * fix(sema): pop sibling branch conditions on found last use * refactor(sema): localize variables * fix(sema): recognize uses in iteration statements * fix(sema): start from the last symbol, not past-the-end * refactor(sema): add local type `pos_range` * fix(sema): handle loops and (non) captures * test: add similar case but without declaration * test: regenerate results * fix(reflect): update and regenerate `reflect.h` * fix(sema): start for loop at its body * refactor(sema): use `std::span` * refactor(sema): register name deactivation at visit * fix(sema): do not apply use to a hiding name * fix(sema): skip hiding loop parameter * test: revert `gcc-version.output` * fix(sema): recognize use in _next-clause_ * test: add corner case * fix(sema): recognize Cpp1 `using` declaration * refactor(sema): avoid adding duplicate symbols * refactor(sema): clarify similar members with comments * refactor(sema): turn comment into code * refactor(sema): modify local convenience variable * refactor(sema): remove expression scope * refactor(sema): use the right predicate * refactor(sema): remove inactive, stale assertions * refactor(sema): keep using a sentinel * fix(sema): handle a nested true branch * refactor(sema): revert whitespace change * refactor(sema): fix `started_postfix_expression` simpler * refactor(sema): revert stale fix of `scope_depth` * refactor(sema): comment the need of `final_position` at hand-out * refactor(to_cpp1): drop periods from comment * refactor(to_cpp1): reorder arguments for better formatting * refactor(to_cpp1): remove stale non-rvalue context * refactor(to_cpp1): remove useless non-rvalue context * refactor(to_cpp1): clarifiy comment with example * test: regenerate gcc-13 results Commit 4eef0dacd6eb43fbf90847990be80ca4b079608e actually worked around #746. * test: regenerate results * refactor(sema): resolve CI issues * test: revert changes to gcc-13 result * refactor: generalize to `identifier_sym::safe_to_move` * test: fix implementation of `identity` * refactor(sema): add scoped indices of uses * refactor(sema): simplify names of activation entities * fix(sema): do not mark non-captures as captures * refactor(sema): rename to avoid verb prefix According to : > to avoid dealing with English verb-to-adjective conventions * fix(sema): disable implicit move unsequenced with another use * fix(sema): consider _is-as-expression_ too * fix(sema): check all parameters for use * refactor(sema): remove wrong fix for UFCS issue * Minor updates to compile cppfront and the new test cleanly using MSVC And re-remove a few stray `;` that were removed as part of PR #911 and now cause errors because of the pedantic builds I regenerated `reflect.h` and found two changes that weren't already in the PR, so committing those too Also including the new test file's run against MSVC showing the five messages mentioned in the PR review, see PR review for more... * refactor(to_cpp1): pass `0` to `int` parameter instead of `false` * test: discard unused results * refactor(to_cpp1): avoid the UFCS macro to workaround compiler bugs * test: update GCC 13 results * test: remove Clang 18 results * refactor(sema): avoid pointer arithmetic * test: regenerate results * refactor(sema): avoid pointer arithmetic * Add regression test result diffs from my machine MSVC error is resolved New Clang 12 error in `pure2-last-use.cpp2:938` * test: comment Clang 12 limitation * test: apply CI patches * test: apply CI patches * test: apply CI patches * Tweak: Change "final position" to "global token order" * Minor tweaks While I'm at it, clean up redundant headers that now we get from cpp2util.h And it's updating those regression test line-ends... --------- Signed-off-by: Johel Ernesto Guerrero Peña Signed-off-by: Herb Sutter Co-authored-by: Herb Sutter --- .gitignore | 1 + include/cpp2util.h | 49 +- .../pure2-deducing-pointers-error.cpp2 | 5 - regression-tests/pure2-last-use-error.cpp2 | 1 + regression-tests/pure2-last-use.cpp2 | 1044 +++++++++++ .../pure2-statement-scope-parameters.cpp2 | 2 +- .../pure2-last-use.cpp.execution | 1 + .../apple-clang-14/pure2-last-use.cpp.output | 0 ...mixed-bugfix-for-ufcs-non-local.cpp.output | 130 +- .../pure2-bugfix-for-ufcs-noexcept.cpp.output | 10 +- .../pure2-bugfix-for-ufcs-sfinae.cpp.output | 20 +- .../clang-12/pure2-last-use.cpp.output | 4 + .../gcc-10/pure2-last-use.cpp.execution | 1 + .../gcc-10/pure2-last-use.cpp.output | 0 ...mixed-bugfix-for-ufcs-non-local.cpp.output | 40 +- .../gcc-13/pure2-last-use.cpp.execution | 1 + .../gcc-13/pure2-last-use.cpp.output | 0 .../mixed-bounds-safety-with-assert-2.cpp | 2 +- ...d-function-expression-and-std-for-each.cpp | 2 +- ...n-and-std-ranges-for-each-with-capture.cpp | 2 +- ...ion-expression-and-std-ranges-for-each.cpp | 2 +- ...nction-expression-with-pointer-capture.cpp | 2 +- ...ction-expression-with-repeated-capture.cpp | 2 +- ...ed-intro-for-with-counter-include-last.cpp | 2 +- .../mixed-postexpression-with-capture.cpp | 2 +- .../mixed-string-interpolation.cpp | 2 +- .../msvc-2022/pure2-last-use.cpp.execution | 1 + .../msvc-2022/pure2-last-use.cpp.output | 1 + .../test-results/pure2-break-continue.cpp | 16 +- .../pure2-bugfix-for-discard-precedence.cpp | 2 +- ...-for-parameter-decl-list-error.cpp2.output | 1 + .../pure2-deducing-pointers-error.cpp2.output | 3 + regression-tests/test-results/pure2-enum.cpp | 2 +- .../pure2-for-loop-range-with-lambda.cpp | 2 +- .../pure2-intro-example-hello-2022.cpp | 4 +- .../pure2-intro-example-three-loops.cpp | 2 +- .../pure2-last-use-error.cpp2.output | 3 + .../test-results/pure2-last-use.cpp | 1564 +++++++++++++++++ .../test-results/pure2-last-use.cpp2.output | 2 + ...k-up-parameter-across-unnamed-function.cpp | 2 +- regression-tests/test-results/pure2-print.cpp | 2 +- ...2-raw-string-literal-and-interpolation.cpp | 2 +- .../pure2-return-tuple-operator.cpp | 6 +- .../pure2-statement-scope-parameters.cpp | 2 +- .../test-results/pure2-types-basics.cpp | 2 +- .../test-results/pure2-types-inheritance.cpp | 2 +- regression-tests/test-results/pure2-union.cpp | 12 +- source/common.h | 33 +- source/cppfront.cpp | 4 +- source/io.h | 2 - source/lex.h | 46 +- source/parse.h | 13 +- source/reflect.h | 86 +- source/reflect.h2 | 4 +- source/sema.h | 980 +++++++++-- source/to_cpp1.h | 159 +- 56 files changed, 3874 insertions(+), 413 deletions(-) create mode 100644 regression-tests/pure2-last-use-error.cpp2 create mode 100644 regression-tests/pure2-last-use.cpp2 create mode 100644 regression-tests/test-results/apple-clang-14/pure2-last-use.cpp.execution create mode 100644 regression-tests/test-results/apple-clang-14/pure2-last-use.cpp.output create mode 100644 regression-tests/test-results/clang-12/pure2-last-use.cpp.output create mode 100644 regression-tests/test-results/gcc-10/pure2-last-use.cpp.execution create mode 100644 regression-tests/test-results/gcc-10/pure2-last-use.cpp.output create mode 100644 regression-tests/test-results/gcc-13/pure2-last-use.cpp.execution create mode 100644 regression-tests/test-results/gcc-13/pure2-last-use.cpp.output create mode 100644 regression-tests/test-results/msvc-2022/pure2-last-use.cpp.execution create mode 100644 regression-tests/test-results/msvc-2022/pure2-last-use.cpp.output create mode 100644 regression-tests/test-results/pure2-last-use-error.cpp2.output create mode 100644 regression-tests/test-results/pure2-last-use.cpp create mode 100644 regression-tests/test-results/pure2-last-use.cpp2.output diff --git a/.gitignore b/.gitignore index 9886713213..0616ce2a6d 100644 --- a/.gitignore +++ b/.gitignore @@ -28,6 +28,7 @@ *.exe source/gen_version.bat build*/ +*.ifc # Visual Studio cache directory .vs/ diff --git a/include/cpp2util.h b/include/cpp2util.h index eba99ccefa..0f49eb2f3c 100644 --- a/include/cpp2util.h +++ b/include/cpp2util.h @@ -873,6 +873,7 @@ class out { #endif #endif +#define CPP2_UFCS_IDENTITY(...) __VA_ARGS__ #define CPP2_UFCS_REMPARENS(...) __VA_ARGS__ // Ideally, the expression `CPP2_UFCS_IS_NOTHROW` expands to @@ -881,18 +882,18 @@ class out { // we instead make it a template parameter of the UFCS lambda. // But using a template parameter, Clang also ICEs on an application. // So we use these `NOTHROW` macros to fall back to the ideal for when not using GCC. -#define CPP2_UFCS_IS_NOTHROW(QUALID,TEMPKW,...) \ +#define CPP2_UFCS_IS_NOTHROW(MVFWD,QUALID,TEMPKW,...) \ requires { requires requires { std::declval().CPP2_UFCS_REMPARENS QUALID TEMPKW __VA_ARGS__(std::declval()...); }; \ requires noexcept(std::declval().CPP2_UFCS_REMPARENS QUALID TEMPKW __VA_ARGS__(std::declval()...)); } \ || requires { requires !requires { std::declval().CPP2_UFCS_REMPARENS QUALID TEMPKW __VA_ARGS__(std::declval()...); }; \ - requires noexcept(CPP2_UFCS_REMPARENS QUALID __VA_ARGS__(std::declval(), std::declval()...)); } -#define CPP2_UFCS_IS_NOTHROW_PARAM(...) /*empty*/ -#define CPP2_UFCS_IS_NOTHROW_ARG(QUALID,TEMPKW,...) CPP2_UFCS_IS_NOTHROW(QUALID,TEMPKW,__VA_ARGS__) + requires noexcept(MVFWD(CPP2_UFCS_REMPARENS QUALID __VA_ARGS__)(std::declval(), std::declval()...)); } +#define CPP2_UFCS_IS_NOTHROW_PARAM(...) /*empty*/ +#define CPP2_UFCS_IS_NOTHROW_ARG(MVFWD,QUALID,TEMPKW,...) CPP2_UFCS_IS_NOTHROW(MVFWD,QUALID,TEMPKW,__VA_ARGS__) #if defined(__GNUC__) && !defined(__clang__) #undef CPP2_UFCS_IS_NOTHROW_PARAM #undef CPP2_UFCS_IS_NOTHROW_ARG - #define CPP2_UFCS_IS_NOTHROW_PARAM(QUALID,TEMPKW,...) , bool IsNothrow = CPP2_UFCS_IS_NOTHROW(QUALID,TEMPKW,__VA_ARGS__) - #define CPP2_UFCS_IS_NOTHROW_ARG(...) IsNothrow + #define CPP2_UFCS_IS_NOTHROW_PARAM(MVFWD,QUALID,TEMPKW,...) , bool IsNothrow = CPP2_UFCS_IS_NOTHROW(MVFWD,QUALID,TEMPKW,__VA_ARGS__) + #define CPP2_UFCS_IS_NOTHROW_ARG(...) IsNothrow #if __GNUC__ < 11 #undef CPP2_UFCS_IS_NOTHROW_PARAM #undef CPP2_UFCS_IS_NOTHROW_ARG @@ -909,41 +910,43 @@ class out { // But using a template parameter, Clang also ICEs and GCC rejects a local 'F'. // Also, Clang rejects the SFINAE test case when using 'std::declval'. // So we use these `CONSTRAINT` macros to fall back to the ideal for when not using MSVC. -#define CPP2_UFCS_CONSTRAINT_PARAM(...) /*empty*/ -#define CPP2_UFCS_CONSTRAINT_ARG(QUALID,TEMPKW,...) \ +#define CPP2_UFCS_CONSTRAINT_PARAM(...) /*empty*/ +#define CPP2_UFCS_CONSTRAINT_ARG(MVFWD,QUALID,TEMPKW,...) \ requires { CPP2_FORWARD(obj).CPP2_UFCS_REMPARENS QUALID TEMPKW __VA_ARGS__(CPP2_FORWARD(params)...); } \ -|| requires { CPP2_UFCS_REMPARENS QUALID __VA_ARGS__(CPP2_FORWARD(obj), CPP2_FORWARD(params)...); } +|| requires { MVFWD(CPP2_UFCS_REMPARENS QUALID __VA_ARGS__)(CPP2_FORWARD(obj), CPP2_FORWARD(params)...); } #if defined(_MSC_VER) #undef CPP2_UFCS_CONSTRAINT_PARAM #undef CPP2_UFCS_CONSTRAINT_ARG - #define CPP2_UFCS_CONSTRAINT_PARAM(QUALID,TEMPKW,...) , bool IsViable = \ + #define CPP2_UFCS_CONSTRAINT_PARAM(MVFWD,QUALID,TEMPKW,...) , bool IsViable = \ requires { std::declval().CPP2_UFCS_REMPARENS QUALID TEMPKW __VA_ARGS__(std::declval()...); } \ -|| requires { CPP2_UFCS_REMPARENS QUALID __VA_ARGS__(std::declval(), std::declval()...); } +|| requires { MVFWD(CPP2_UFCS_REMPARENS QUALID __VA_ARGS__)(std::declval(), std::declval()...); } #define CPP2_UFCS_CONSTRAINT_ARG(...) IsViable #endif -#define CPP2_UFCS_(LAMBDADEFCAPT,QUALID,TEMPKW,...) \ +#define CPP2_UFCS_(LAMBDADEFCAPT,MVFWD,QUALID,TEMPKW,...) \ [LAMBDADEFCAPT]< \ typename Obj, typename... Params \ - CPP2_UFCS_IS_NOTHROW_PARAM(QUALID,TEMPKW,__VA_ARGS__) \ - CPP2_UFCS_CONSTRAINT_PARAM(QUALID,TEMPKW,__VA_ARGS__) \ + CPP2_UFCS_IS_NOTHROW_PARAM(MVFWD,QUALID,TEMPKW,__VA_ARGS__) \ + CPP2_UFCS_CONSTRAINT_PARAM(MVFWD,QUALID,TEMPKW,__VA_ARGS__) \ > \ CPP2_LAMBDA_NO_DISCARD (Obj&& obj, Params&& ...params) CPP2_FORCE_INLINE_LAMBDA_CLANG \ - noexcept(CPP2_UFCS_IS_NOTHROW_ARG(QUALID,TEMPKW,__VA_ARGS__)) CPP2_FORCE_INLINE_LAMBDA -> decltype(auto) \ - requires CPP2_UFCS_CONSTRAINT_ARG(QUALID,TEMPKW,__VA_ARGS__) { \ + noexcept(CPP2_UFCS_IS_NOTHROW_ARG(MVFWD,QUALID,TEMPKW,__VA_ARGS__)) CPP2_FORCE_INLINE_LAMBDA -> decltype(auto) \ + requires CPP2_UFCS_CONSTRAINT_ARG(MVFWD,QUALID,TEMPKW,__VA_ARGS__) { \ if constexpr (requires{ CPP2_FORWARD(obj).CPP2_UFCS_REMPARENS QUALID TEMPKW __VA_ARGS__(CPP2_FORWARD(params)...); }) { \ return CPP2_FORWARD(obj).CPP2_UFCS_REMPARENS QUALID TEMPKW __VA_ARGS__(CPP2_FORWARD(params)...); \ } else { \ - return CPP2_UFCS_REMPARENS QUALID __VA_ARGS__(CPP2_FORWARD(obj), CPP2_FORWARD(params)...); \ + return MVFWD(CPP2_UFCS_REMPARENS QUALID __VA_ARGS__)(CPP2_FORWARD(obj), CPP2_FORWARD(params)...); \ } \ } -#define CPP2_UFCS(...) CPP2_UFCS_(&,(),,__VA_ARGS__) -#define CPP2_UFCS_TEMPLATE(...) CPP2_UFCS_(&,(),template,__VA_ARGS__) -#define CPP2_UFCS_QUALIFIED_TEMPLATE(QUALID,...) CPP2_UFCS_(&,QUALID,template,__VA_ARGS__) -#define CPP2_UFCS_NONLOCAL(...) CPP2_UFCS_(,(),,__VA_ARGS__) -#define CPP2_UFCS_TEMPLATE_NONLOCAL(...) CPP2_UFCS_(,(),template,__VA_ARGS__) -#define CPP2_UFCS_QUALIFIED_TEMPLATE_NONLOCAL(QUALID,...) CPP2_UFCS_(,QUALID,template,__VA_ARGS__) +#define CPP2_UFCS(...) CPP2_UFCS_(&,CPP2_UFCS_IDENTITY,(),,__VA_ARGS__) +#define CPP2_UFCS_MOVE(...) CPP2_UFCS_(&,std::move,(),,__VA_ARGS__) +#define CPP2_UFCS_FORWARD(...) CPP2_UFCS_(&,CPP2_FORWARD,(),,__VA_ARGS__) +#define CPP2_UFCS_TEMPLATE(...) CPP2_UFCS_(&,CPP2_UFCS_IDENTITY,(),template,__VA_ARGS__) +#define CPP2_UFCS_QUALIFIED_TEMPLATE(QUALID,...) CPP2_UFCS_(&,CPP2_UFCS_IDENTITY,QUALID,template,__VA_ARGS__) +#define CPP2_UFCS_NONLOCAL(...) CPP2_UFCS_(,CPP2_UFCS_IDENTITY,(),,__VA_ARGS__) +#define CPP2_UFCS_TEMPLATE_NONLOCAL(...) CPP2_UFCS_(,CPP2_UFCS_IDENTITY,(),template,__VA_ARGS__) +#define CPP2_UFCS_QUALIFIED_TEMPLATE_NONLOCAL(QUALID,...) CPP2_UFCS_(,CPP2_UFCS_IDENTITY,QUALID,template,__VA_ARGS__) //----------------------------------------------------------------------- diff --git a/regression-tests/pure2-deducing-pointers-error.cpp2 b/regression-tests/pure2-deducing-pointers-error.cpp2 index ee6e5ff183..ce862c1055 100644 --- a/regression-tests/pure2-deducing-pointers-error.cpp2 +++ b/regression-tests/pure2-deducing-pointers-error.cpp2 @@ -28,11 +28,6 @@ main: () -> int = { pa5 := pppa**; pa5 = 0; // caught - // TODO: @filipsajdak please take a look - // The bugfix in get_declaration_of(t) to add `&& ri->position() <= t.position()` - // to the condition is correct; it fixes issue #669 by not looking past the first - // declaration of the name in t. However, that change made the following two - // "caught" cases no longer be caught. fun(a)++; // caught fp := fun(a); fp = 0; // caught diff --git a/regression-tests/pure2-last-use-error.cpp2 b/regression-tests/pure2-last-use-error.cpp2 new file mode 100644 index 0000000000..4de176cc4b --- /dev/null +++ b/regression-tests/pure2-last-use-error.cpp2 @@ -0,0 +1 @@ +issue_890: (x) = { } diff --git a/regression-tests/pure2-last-use.cpp2 b/regression-tests/pure2-last-use.cpp2 new file mode 100644 index 0000000000..eed8167203 --- /dev/null +++ b/regression-tests/pure2-last-use.cpp2 @@ -0,0 +1,1044 @@ +f_inout: (inout _) = { } +f_copy: (copy _...) = { } +pred: (_...) true; +pred_copy: (copy _...) true; +identity: (forward x: T) -> forward _ requires std::is_reference_v == { return x; } +identity_copy: (copy x) x; + +issue_313: () = { + _ = :() -> std::vector = { + a := new(0); + return (a*, identity_copy(a)*); + }; + _ = :() -> std::vector = { + a := new(0); + return (a&**, identity_copy(a)*); + }; + _ = :() -> int = { + a := new(0); + return identity(a)* + identity(a)*; + + b := new(0); + return identity(b)* = identity(b)*; + + c := new(0); + return identity(c)* ^ identity(c)*; + }; + { + a := new(0); + identity(a)* = identity(a)*; + + b := new(0); + _: int = identity(b)* = identity(b)*; + + c := new(0); + if identity(c)* * identity(c)* { } + + d := new(0); + if (identity(d)* - identity(d)*) { } + + e := new(0); + _ = e is (e); + + f := new(0); + _ = f is std::type_identity_t; // OK? + + g := new(0); + for (identity(g)* + identity(g)*) + do (_) + { } + + h := new(0); + while identity(h)* + identity(h)* + { } + + i := new(0); + do { } + while identity(i)* + identity(i)*; + + j := new(0); + k := new(0); + _ = inspect identity(j)* + identity(j)* -> int { + is (identity(k)* + identity(k)*) = 0; + is _ = 0; + }; + + } + { + a := new(0); + _ = :() identity(a$)* + identity(a$)*; + } +} +issue_313_1: (copy x: std::unique_ptr) pre(identity(x)* + identity(x)*) = { } + +issue_350: () = { + x := 21; + + l1 := :(forward x) = { + f_inout(forward x); + }; + + l1(x); + + x++; +} +/* +issue_440_0: () -> int = { + i: int; + if true { + i = 1; + return i; + } + i = 2; + return i; +} + +issue_440_1: () -> (i: int) = { + if true { + i = 1; + return; + } + i = 2; +} +*/ +issue_683: (args) = { + for args do (n) { + _ = n; + } + + n: int; + n = 0; +} + +issue_825: () = { + _ = :(copy b: std::unique_ptr) f_copy(b); + _ = :(move c: std::unique_ptr) f_copy(c); + _ = :(forward d) f_copy(d);(new(0)); +} + +issue_832: () = { + i := 0; + while i { } +} + +make_copy: (copy x) x; + +issue_847_4: (copy v: std::vector) = { + for v.make_copy() + do (copy x) { + f_copy(x); + } +} +issue_847_0: (copy v: std::vector>) = { + // TODO Use 'std::views::as_rvalue` + for v.make_copy() + do (_) { + } +} +issue_847_1: (move v: std::vector>) = { + for v.make_copy() + do (move x) { + f_copy(x); + } +} +issue_847_5: (forward v) = { + for v.make_copy() + do (forward x) { + f_copy(x); + } +} +issue_847_2: (forward v) = { + for v.make_copy() + do (forward _) { + } +} +issue_847_3: (copy x: int) = { for (x) do (_) { } } + +issue_850: () = { + v: std::vector = ( 1, 2, 3, 4, 5 ); + + // Definite last use of v => move-capture v into f's closure + f := :() -> forward _ = { return v$; }; + + // Now we can access the vector captured inside f()... + f().push_back(6); + for f() do(e) std::cout << e; // prints 123456 +} + +issue_857: type = { + a: std::unique_ptr; + b: std::unique_ptr; + operator=: (out this, move that) = { } + operator=: (move this) = { + f_inout(a); + f_copy(this.b); + } + f: (move this) = f_copy(this); + f: (move this, move that) = f_copy(this, that); + g: (move this) = f_copy(this.a); + g: (move this, move that) = f_copy(this.a, that.a); + h: (inout this) = f_inout(a); + i: (move this) = f_copy(a); + j: (move this) = f_copy(a); + k: (move this) = { + f_inout(a); + f_copy(b); + } + l: (move this) = k(); + m: (move this) = this.k(); + n: (_) = { } + n: (move this) = { } + o0:(move this) = n(); + o1:(move this) = this.n(); + o2:(move this) = 0.n(); + o3:(move this) = n(0); + o4:(move this) = n(this); + p0: (move this) = { + f_inout(a); + f_copy(this.a); + } + p1: (move this) = { + f_inout(this.a); + f_copy(a); + } + p2: (move this) = { + f_inout(a); + f_copy(this); + } + p3: (move this) = { + f_inout(this); + f_copy(a); + } + q: (move this) = { + h(); + n(); + } + z: (move this, move that) = { + /*f */ if true { f_copy(this); } + /*f */ else if true { f_copy(this, that); } + /*g */ else if true { f_copy(this.a); } + /*g */ else if true { f_copy(this.a, that.a); } + /*i */ else if true { f_copy(a); } + /*j */ else if true { f_copy(a); } + /*k */ else if true { + /*k */ f_inout(a); + /*k */ f_copy(b); + /*k */ } + /*l */ else if true { k(); } + /*m */ else if true { this.k(); } + /*o1*/ else if true { n(); } + /*o2*/ else if true { this.n(); } + /*o3*/ else if true { n(0); } + /*o4*/ else if true { n(this); } + /*p0*/ else if true { + /*p0*/ f_inout(a); + /*p0*/ f_copy(this.a); + /*p0*/ } + /*p1*/ else if true { + /*p1*/ f_inout(this.a); + /*p1*/ f_copy(a); + /*p1*/ } + /*p2*/ else if true { + /*p2*/ f_inout(a); + /*p2*/ f_copy(this); + /*p2*/ } + /*p3*/ else if true { + /*p3*/ f_inout(this); + /*p3*/ f_copy(a); + /*p3*/ } + /*q */ else { + /*q */ h(); + /*q */ n(); + /*q */ } + } +} + +issue_857_2: @struct type = { + a: std::unique_ptr; // OK: No error about 'a' being unused. +} + +gi: int = 0; +issue_857_3: @struct type = { + i: std::add_lvalue_reference_t = gi; + f: (move this) = f_inout(i); // OK: The implicit `this` is moved, not `i`. +} +issue_857_6: @struct type = { + f: (move this) = f_inout(i); // OK: The implicit `this` is moved, not `i`. + i: std::add_lvalue_reference_t = gi; +} + +// TODO Alias `std::move_only_function`. +move_only_function: type = { + operator=: (out this) = { } + operator=: (out this, move that) = { } + operator(): (move this, _...) -> int = 0; +} + +issue_857_4: @struct type = { + f: std::add_pointer_t; + g: std::add_pointer_t; + mf: move_only_function; + mg: move_only_function; + h0: (move this) = _ = mf(); + h1: (move this) = _ = this.mf(); + h2: (move this, that) = _ = that.mf(); + h3: (move this, that) = _ = that.f; + h4: (move this, x: int) = _ = x.mg(); + h5: (move this, x: int) = _ = f() + x.g(); // FIXME #313. + h6: (move this, x: int) = _ = x.g() + f(); // FIXME #313. + h7: (move this, x: int) = _ = this.f() + x.g(); // FIXME #313. + h8: (move this, x: int) = _ = x.g() + this.f(); // FIXME #313. + i0: (move this, x: int) = { + _ = f(); + _ = x.mg(); + } + i1: (move this, x: int) = { + _ = x.g(); + _ = mf(); + } + i2: (move this, x: int) = { + _ = this.f(); + _ = x.mg(); + } + i3: (move this, x: int) = { + _ = x.g(); + _ = this.mf(); + } + z: (move this, that) = { + x := 0; + /*h0*/ if true { _ = mf(); } + /*h1*/ else if true { _ = this.mf(); } + /*h2*/ else if true { _ = that.mf(); } + /*h3*/ else if true { _ = that.f; } + /*h4*/ else if true { _ = x.mg(); } + /*h5*/ else if true { _ = f() + x.g(); } + /*h6*/ else if true { _ = x.g() + f(); } + /*h7*/ else if true { _ = this.f() + x.g(); } + /*h8*/ else if true { _ = x.g() + this.f(); } + /*i0*/ else if true { + /*i0*/ _ = f(); + /*i0*/ _ = x.mg(); + /*i0*/ } + /*i1*/ else if true { + /*i1*/ _ = x.g(); + /*i1*/ _ = mf(); + /*i1*/ } + /*i2*/ else if true { + /*i2*/ _ = this.f(); + /*i2*/ _ = x.mg(); + /*i2*/ } + /*i3*/ else { + /*i3*/ _ = x.g(); + /*i3*/ _ = this.mf(); + /*i3*/ } + } +} + +issue_857_5: @struct type = { + f: (move this) = f_copy(a); + a: std::unique_ptr; +} + +issue_857_7: @struct type = { + A: std::add_lvalue_reference_t; + this: std::monostate; + F: (move this) = f_inout(A); +} + +issue_857_8: @struct type = { + a: std::unique_ptr; + b: move_only_function; + c: std::add_lvalue_reference_t; + d: (move this) = { } +} +issue_857_9: @struct type = { + this: issue_857_8; + + // Error: Cppfront limitation: + // . +//f0: (move this) = f_copy(a); +//f1: (move this) = _ = b(); + f2: (move this) = f_inout(c); // OK: Happens to work, like non-'move' 'this' parameters. +//f3: (move this) = d(); + + // OK: Explicit 'this' for base members, like in templates. + g0: (move this) = f_copy(this.a); + g1: (move this) = _ = this.b(); + g2: (move this) = f_inout(this.c); + g3: (move this) = this.d(); +} + +issue_869_0: @value type = { + operator=: (out this, move _: std::unique_ptr) = { } +} +issue_869_1: @union type = { + i: issue_869_0; +} +issue_869_2: () -> (res: issue_869_1 = ()) = { res.set_i(new(0)); } + +issue_884: () = { + _ = :() = { + x := new(0); + if true { } + { + { f_inout(x); } + f_copy(x); + } + }; + + _ = :() = { + x := new(0); + if true { + f_copy(x); + } + else { + { f_inout(x); } + f_copy(x); + } + }; + + _ = :() = { + x := new(0); + if true { + f_inout(x); + } + else { + { f_inout(x); } + f_inout(x); + } + f_copy(x); + }; + + _ = :() = { + x := new(0); + f_copy(x); + if true { + _ = 0; + } + else { + { _ = 0; } + _ = 0; + } + _ = 0; + }; + + _ = :() = { + x := new(0); + f_inout(x); + if true { + f_copy(x); + } + else { + { _ = 0; } + _ = 0; + } + _ = 0; + }; + + _ = :() = { + x := new(0); + f_inout(x); + if true { + _ = 0; + } + else { + { f_copy(x); } + _ = 0; + } + _ = 0; + }; + + _ = :() = { + x := new(0); + f_inout(x); + if true { + _ = 0; + } + else { + { _ = 0; } + f_copy(x); + } + _ = 0; + }; + + _ = :() = { + x := new(0); + f_inout(x); + if true { + _ = 0; + } + else { + { _ = 0; } + _ = 0; + } + f_copy(x); + }; + + _ = :() = { + x := new(0); + f_inout(x); + if true { + f_copy(x); + } + else { + { f_copy(x); } + _ = 0; + } + _ = 0; + }; + + _ = :() = { + x := new(0); + f_inout(x); + if true { + f_copy(x); + } + else { + { f_inout(x); } + f_copy(x); + } + _ = 0; + }; + + _ = :() = { + x := new(0); + f_inout(x); + if true { + f_inout(x); + } + else { + { f_inout(x); } + f_inout(x); + } + f_inout(x); + if true { + f_copy(x); + } + }; + + _ = :() = { + x := new(0); + f_inout(x); + if true { + f_inout(x); + } + else { + { f_inout(x); } + f_inout(x); + } + if true { + f_inout(x); + } + f_copy(x); + }; + + _ = :() = { + x := new(0); + f_inout(x); + if true { + f_copy(x); + } + else { + { f_inout(x); } + f_inout(x); + if true { + f_copy(x); + } + } + _ = 0; + }; + + _ = :() = { + x := new(0); + if true { + if true { + if true { + f_copy(x); + } + } + } + else { + } + }; + + _ = :() = { + x := new(0); + if true { + if true { + if true { + f_copy(x); + } + } + } + else { + f_copy(x); + } + }; + + _ = :() = { + x := new(0); + if true { + } + else { + if true { + if true { + f_copy(x); + } + } + } + }; + + _ = :() = { + x := new(0); + if true { + f_copy(x); + } + else { + if true { + if true { + f_copy(x); + } + } + } + }; + + _ = :() = { + x := new(0); + if true { + y := new(0); + f_copy(x); + f_copy(y); + } + else { + if true { + if true { + f_inout(x); + } + f_copy(x); + } + } + }; + + _ = :() = { + x := new(0); + if true { + y := new(0); + if true { } + else { + f_copy(x); + f_copy(y); + } + } + else { + if true { + if true { + y := new(0); + f_copy(y); + f_inout(x); + } + f_copy(x); + } + } + }; + + _ = :() = { + x := new(0); + if true { + y := new(0); + if true { } + else { + f_copy(x); + f_copy(y); + } + } + else { + y := new(0); + if true { + if true { + f_copy(x); + } + else { + f_copy(x); + } + f_copy(y); + } + } + }; + + _ = :() = { + x := new(0); + if true { + f_copy(x); + } + else { + if true { + x := new(0); + if true { + f_inout(x); + } + else { + } + f_copy(x); + } + f_copy(x); + } + }; + + _ = :() = { + x := new(0); + if true { + if true { + x := new(0); + if true { + f_inout(x); + } + else { + } + f_copy(x); + } + f_copy(x); + } + else { + f_copy(x); + } + }; + + _ = :() = { + x := new(0); + + if true { + f_inout(x); + } + + if true { + if true { + f_copy(x); + } + } + }; + + _ = :() = { + x := new(0); + if true { + if true { + f_inout(x); + if true { + } + else { + f_copy(x); + } + } + else { + if true { + } + else { + f_inout(x); + } + f_copy(x); + } + } + else { + if true { + if true { + f_inout(x); + f_copy(x); + } + else { + } + } + else { + if true { + } + else { + f_inout(x); + } + if true { + f_inout(x); + if true { + f_copy(x); + } + else { + } + } + else { + if true { + f_copy(x); + } + else { + f_copy(x); + } + } + } + } + }; +} + +issue_888_0: (copy r: std::string, copy size: int) = { + _ = r.size(); +} +issue_888_1: (copy _: std::string, copy size: move_only_function) = { + _ = 0.size(); +} + +issue_890: () = { + x := new(0); + assert(identity_copy(x)* == 0); + (x := new(0)) assert(identity(x)* == 0); +} + +draw: () = { + pos := 0; + vertex: move_only_function = (); + _ = (pos).vertex(); +} + +enum_0: () = { + underlying_type : std::string; + if true { } + underlying_type = ""; +} +enum_1: () = { + max_value := new(0); + min_value: std::reference_wrapper> = max_value; + + for (0) + do (copy x) + { + v := new(identity_copy(x)); + if pred(v, min_value) { + min_value = std::ref(identity(v)); // Not using 'else' will never move 'v'. + } + if pred(v, max_value) { + max_value = identity_copy(v); + } + } + + y := new(false); + while identity(y)* { + v := new(0); + f_copy(v); + } + + z := new(false); + do { + v := new(0); + f_copy(v); + } while identity(z)*; +} +enum_2: () = { + umax := new(0); + if pred(umax) { + } + else if pred(umax) { + } + else if pred_copy(umax) { + } +} + +union: type = { + destroy: (inout this) = { } + operator=: (move this) = { + destroy(); + _ = this; + } +} + +my_string: @struct type = { + string: std::string; + size: std::size_t = string.size(); +} + +no_pessimizing_move: () -> (ret: std::unique_ptr = ()) = { } + +deferred_non_copyable_0: () = { + p: std::unique_ptr; + p = (); + f_copy(p); +} + +deferred_non_copyable_1: () -> _ = { + p: std::unique_ptr; + p = (); + return p; +} + +deferred_non_copyable_2: () -> (p: std::unique_ptr) = { + p = (); +} + +loops: () = { + _ = :() = { + x := new(0); + for (0) + do (_) + f_inout(x); + }; + + _ = :() = { + x := new(0); + for (0) + next f_inout(x) + do (_) + { } + }; + + _ = :() = { + x := new(0); + for (0) + do (_) + assert(x.get()); + }; + + _ = :() = { + x := new(0); + if true { + f_copy(x); + } + else { + while true { + f_inout(x); + } + } + }; +} + +captures: namespace = { + +// Skip non captured name in function expression + +f: () = { + x := new(0); + f_copy(x); + id := :(x) -> forward _ = x; + y := new(0); + assert(id(y)& == y&); +} + +x: int == 0; + +t: @struct type = { + x: std::unique_ptr; + operator(): (move this) = { + f_copy(x); + _ = :() = { + // Should this move? + // I.e., don't skip non-captured names, just rely on skipping hiding names. + // An odr-use still requires capturing at Cpp1-time, and capturing would move. + static_assert(std::is_same_v>); + using captures::x; + _ = identity(x); + }; + } +} + +g: () = { + _ = :() = { + x := new(0); + f_copy(x); + _ = :() std::array()$; // Fails on Clang 12 (lambda in unevaluated context). + }; + + _ = :() = { + x := new(0); + f_inout(x); + _ = :() -> int = (:() x$*)$(); + }; +} + +} + +loops_and_captures: () = { + _ = :() = { + x := new(0); + f_copy(x); + for (:(x) x) + do (_) + { } + }; + + _ = :() = { + x := new(0); + f_copy(x); + for (:() -> _ = { + using captures::x; + return x; + }) + do (_) + { } + }; + + _ = :() = { + x := new(0); + for (:() x$*) + do (_) + { } + }; +} + +types: @struct type = { + x: std::unique_ptr; + f: (move this) = _ = :() x$*; + g: (move this) = { + for (:() x$*) + do (_) + { } + } +} + +skip_hidden_names: () = { + _ = :() = { + x := new(0); + f_copy(x); + (copy x := new(0)) + f_copy(x); + }; + + _ = :() = { + x := new(0); + _ = :() = { + _ = x$; + x := new(1); + _ = :() = { + _ = x$; + }; + }; + }; + + _ = :() = { + x := new(0); + f_copy(x); + for (0) + do (copy x) + _ = identity_copy(x); + (copy x := new(0)) + f_copy(x); + }; + + _ = :() = { + x := new(0); + f_inout(x); + { + f_copy(x); + using captures::x; + f_inout(x); + } + }; + + _ = :() = { + x := new(0); + f_copy(x); + _ = :() = { + static_assert(std::is_same_v>); + using captures::x; + f_inout(x); + }; + }; +} + +main: (args) = { + issue_683(args); + issue_847_2(std::vector>()); + issue_847_5(args); + issue_850(); + enum_0(); +} diff --git a/regression-tests/pure2-statement-scope-parameters.cpp2 b/regression-tests/pure2-statement-scope-parameters.cpp2 index c856467235..ca69e6272a 100644 --- a/regression-tests/pure2-statement-scope-parameters.cpp2 +++ b/regression-tests/pure2-statement-scope-parameters.cpp2 @@ -3,7 +3,7 @@ main: (args) = { local_int := 42; // 'in' (read-only) statement scope variable - (i := local_int) for args do (arg) { + (i := local_int) for args do (_) { std::cout << i << "\n"; // prints 42 } diff --git a/regression-tests/test-results/apple-clang-14/pure2-last-use.cpp.execution b/regression-tests/test-results/apple-clang-14/pure2-last-use.cpp.execution new file mode 100644 index 0000000000..4632e068d5 --- /dev/null +++ b/regression-tests/test-results/apple-clang-14/pure2-last-use.cpp.execution @@ -0,0 +1 @@ +123456 \ No newline at end of file diff --git a/regression-tests/test-results/apple-clang-14/pure2-last-use.cpp.output b/regression-tests/test-results/apple-clang-14/pure2-last-use.cpp.output new file mode 100644 index 0000000000..e69de29bb2 diff --git a/regression-tests/test-results/clang-12/mixed-bugfix-for-ufcs-non-local.cpp.output b/regression-tests/test-results/clang-12/mixed-bugfix-for-ufcs-non-local.cpp.output index 5754955099..77144b5ba5 100644 --- a/regression-tests/test-results/clang-12/mixed-bugfix-for-ufcs-non-local.cpp.output +++ b/regression-tests/test-results/clang-12/mixed-bugfix-for-ufcs-non-local.cpp.output @@ -1,118 +1,118 @@ mixed-bugfix-for-ufcs-non-local.cpp2:13:12: error: a lambda expression cannot appear in this context template UnnamedTypeParam1_1> bool inline constexpr v0 = false;// Fails on GCC ([GCC109781][]) and Clang 12 (a lambda expression cannot appear in this context) ^ -../../../include/cpp2util.h:944:59: note: expanded from macro 'CPP2_UFCS_NONLOCAL' -#define CPP2_UFCS_NONLOCAL(...) CPP2_UFCS_(,(),,__VA_ARGS__) +../../../include/cpp2util.h:934:59: note: expanded from macro 'CPP2_UFCS_NONLOCAL' +#define CPP2_UFCS_NONLOCAL(...) CPP2_UFCS_(,CPP2_UFCS_IDENTITY,(),,__VA_ARGS__) + ^ +../../../include/cpp2util.h:913:59: note: expanded from macro 'CPP2_UFCS_' +#define CPP2_UFCS_(LAMBDADEFCAPT,MVFWD,QUALID,TEMPKW,...) \ ^ -../../../include/cpp2util.h:925:53: note: expanded from macro 'CPP2_UFCS_' -#define CPP2_UFCS_(LAMBDADEFCAPT,QUALID,TEMPKW,...) \ - ^ mixed-bugfix-for-ufcs-non-local.cpp2:15:3: error: a lambda expression cannot appear in this context t inline constexpr v1 = t();// Fails on Clang 12 (lambda in unevaluated context). ^ -../../../include/cpp2util.h:944:59: note: expanded from macro 'CPP2_UFCS_NONLOCAL' -#define CPP2_UFCS_NONLOCAL(...) CPP2_UFCS_(,(),,__VA_ARGS__) +../../../include/cpp2util.h:934:59: note: expanded from macro 'CPP2_UFCS_NONLOCAL' +#define CPP2_UFCS_NONLOCAL(...) CPP2_UFCS_(,CPP2_UFCS_IDENTITY,(),,__VA_ARGS__) + ^ +../../../include/cpp2util.h:913:59: note: expanded from macro 'CPP2_UFCS_' +#define CPP2_UFCS_(LAMBDADEFCAPT,MVFWD,QUALID,TEMPKW,...) \ ^ -../../../include/cpp2util.h:925:53: note: expanded from macro 'CPP2_UFCS_' -#define CPP2_UFCS_(LAMBDADEFCAPT,QUALID,TEMPKW,...) \ - ^ mixed-bugfix-for-ufcs-non-local.cpp2:21:12: error: a lambda expression cannot appear in this context template UnnamedTypeParam1_2> auto g() -> void; ^ -../../../include/cpp2util.h:944:59: note: expanded from macro 'CPP2_UFCS_NONLOCAL' -#define CPP2_UFCS_NONLOCAL(...) CPP2_UFCS_(,(),,__VA_ARGS__) +../../../include/cpp2util.h:934:59: note: expanded from macro 'CPP2_UFCS_NONLOCAL' +#define CPP2_UFCS_NONLOCAL(...) CPP2_UFCS_(,CPP2_UFCS_IDENTITY,(),,__VA_ARGS__) + ^ +../../../include/cpp2util.h:913:59: note: expanded from macro 'CPP2_UFCS_' +#define CPP2_UFCS_(LAMBDADEFCAPT,MVFWD,QUALID,TEMPKW,...) \ ^ -../../../include/cpp2util.h:925:53: note: expanded from macro 'CPP2_UFCS_' -#define CPP2_UFCS_(LAMBDADEFCAPT,QUALID,TEMPKW,...) \ - ^ mixed-bugfix-for-ufcs-non-local.cpp2:23:36: error: a lambda expression cannot appear in this context auto g([[maybe_unused]] cpp2::in> unnamed_param_1) -> void; ^ -../../../include/cpp2util.h:944:59: note: expanded from macro 'CPP2_UFCS_NONLOCAL' -#define CPP2_UFCS_NONLOCAL(...) CPP2_UFCS_(,(),,__VA_ARGS__) +../../../include/cpp2util.h:934:59: note: expanded from macro 'CPP2_UFCS_NONLOCAL' +#define CPP2_UFCS_NONLOCAL(...) CPP2_UFCS_(,CPP2_UFCS_IDENTITY,(),,__VA_ARGS__) + ^ +../../../include/cpp2util.h:913:59: note: expanded from macro 'CPP2_UFCS_' +#define CPP2_UFCS_(LAMBDADEFCAPT,MVFWD,QUALID,TEMPKW,...) \ ^ -../../../include/cpp2util.h:925:53: note: expanded from macro 'CPP2_UFCS_' -#define CPP2_UFCS_(LAMBDADEFCAPT,QUALID,TEMPKW,...) \ - ^ mixed-bugfix-for-ufcs-non-local.cpp2:27:29: error: a lambda expression cannot appear in this context [[nodiscard]] auto h() -> t; ^ -../../../include/cpp2util.h:944:59: note: expanded from macro 'CPP2_UFCS_NONLOCAL' -#define CPP2_UFCS_NONLOCAL(...) CPP2_UFCS_(,(),,__VA_ARGS__) +../../../include/cpp2util.h:934:59: note: expanded from macro 'CPP2_UFCS_NONLOCAL' +#define CPP2_UFCS_NONLOCAL(...) CPP2_UFCS_(,CPP2_UFCS_IDENTITY,(),,__VA_ARGS__) + ^ +../../../include/cpp2util.h:913:59: note: expanded from macro 'CPP2_UFCS_' +#define CPP2_UFCS_(LAMBDADEFCAPT,MVFWD,QUALID,TEMPKW,...) \ ^ -../../../include/cpp2util.h:925:53: note: expanded from macro 'CPP2_UFCS_' -#define CPP2_UFCS_(LAMBDADEFCAPT,QUALID,TEMPKW,...) \ - ^ mixed-bugfix-for-ufcs-non-local.cpp2:31:12: error: a lambda expression cannot appear in this context template UnnamedTypeParam1_3> using a = bool;// Fails on GCC ([GCC109781][]) and Clang 12 (a lambda expression cannot appear in this context) ^ -../../../include/cpp2util.h:944:59: note: expanded from macro 'CPP2_UFCS_NONLOCAL' -#define CPP2_UFCS_NONLOCAL(...) CPP2_UFCS_(,(),,__VA_ARGS__) +../../../include/cpp2util.h:934:59: note: expanded from macro 'CPP2_UFCS_NONLOCAL' +#define CPP2_UFCS_NONLOCAL(...) CPP2_UFCS_(,CPP2_UFCS_IDENTITY,(),,__VA_ARGS__) + ^ +../../../include/cpp2util.h:913:59: note: expanded from macro 'CPP2_UFCS_' +#define CPP2_UFCS_(LAMBDADEFCAPT,MVFWD,QUALID,TEMPKW,...) \ ^ -../../../include/cpp2util.h:925:53: note: expanded from macro 'CPP2_UFCS_' -#define CPP2_UFCS_(LAMBDADEFCAPT,QUALID,TEMPKW,...) \ - ^ mixed-bugfix-for-ufcs-non-local.cpp2:33:12: error: a lambda expression cannot appear in this context template UnnamedTypeParam1_4> auto inline constexpr b = false;// Fails on GCC ([GCC109781][]). ^ -../../../include/cpp2util.h:944:59: note: expanded from macro 'CPP2_UFCS_NONLOCAL' -#define CPP2_UFCS_NONLOCAL(...) CPP2_UFCS_(,(),,__VA_ARGS__) +../../../include/cpp2util.h:934:59: note: expanded from macro 'CPP2_UFCS_NONLOCAL' +#define CPP2_UFCS_NONLOCAL(...) CPP2_UFCS_(,CPP2_UFCS_IDENTITY,(),,__VA_ARGS__) + ^ +../../../include/cpp2util.h:913:59: note: expanded from macro 'CPP2_UFCS_' +#define CPP2_UFCS_(LAMBDADEFCAPT,MVFWD,QUALID,TEMPKW,...) \ ^ -../../../include/cpp2util.h:925:53: note: expanded from macro 'CPP2_UFCS_' -#define CPP2_UFCS_(LAMBDADEFCAPT,QUALID,TEMPKW,...) \ - ^ mixed-bugfix-for-ufcs-non-local.cpp2:35:13: error: a lambda expression cannot appear in this context using c = t;// Fails on Clang 12 (lambda in unevaluated context) and Clang 12 (a lambda expression cannot appear in this context) ^ -../../../include/cpp2util.h:944:59: note: expanded from macro 'CPP2_UFCS_NONLOCAL' -#define CPP2_UFCS_NONLOCAL(...) CPP2_UFCS_(,(),,__VA_ARGS__) +../../../include/cpp2util.h:934:59: note: expanded from macro 'CPP2_UFCS_NONLOCAL' +#define CPP2_UFCS_NONLOCAL(...) CPP2_UFCS_(,CPP2_UFCS_IDENTITY,(),,__VA_ARGS__) + ^ +../../../include/cpp2util.h:913:59: note: expanded from macro 'CPP2_UFCS_' +#define CPP2_UFCS_(LAMBDADEFCAPT,MVFWD,QUALID,TEMPKW,...) \ ^ -../../../include/cpp2util.h:925:53: note: expanded from macro 'CPP2_UFCS_' -#define CPP2_UFCS_(LAMBDADEFCAPT,QUALID,TEMPKW,...) \ - ^ mixed-bugfix-for-ufcs-non-local.cpp2:37:29: error: a lambda expression cannot appear in this context auto inline constexpr d = t();// Fails on Clang 12 (lambda in unevaluated context). ^ -../../../include/cpp2util.h:944:59: note: expanded from macro 'CPP2_UFCS_NONLOCAL' -#define CPP2_UFCS_NONLOCAL(...) CPP2_UFCS_(,(),,__VA_ARGS__) +../../../include/cpp2util.h:934:59: note: expanded from macro 'CPP2_UFCS_NONLOCAL' +#define CPP2_UFCS_NONLOCAL(...) CPP2_UFCS_(,CPP2_UFCS_IDENTITY,(),,__VA_ARGS__) + ^ +../../../include/cpp2util.h:913:59: note: expanded from macro 'CPP2_UFCS_' +#define CPP2_UFCS_(LAMBDADEFCAPT,MVFWD,QUALID,TEMPKW,...) \ ^ -../../../include/cpp2util.h:925:53: note: expanded from macro 'CPP2_UFCS_' -#define CPP2_UFCS_(LAMBDADEFCAPT,QUALID,TEMPKW,...) \ - ^ mixed-bugfix-for-ufcs-non-local.cpp2:21:12: error: a lambda expression cannot appear in this context template UnnamedTypeParam1_2> auto g() -> void{}// Fails on GCC ([GCC109781][]) and Clang 12 (a lambda expression cannot appear in this context) ^ -../../../include/cpp2util.h:944:59: note: expanded from macro 'CPP2_UFCS_NONLOCAL' -#define CPP2_UFCS_NONLOCAL(...) CPP2_UFCS_(,(),,__VA_ARGS__) +../../../include/cpp2util.h:934:59: note: expanded from macro 'CPP2_UFCS_NONLOCAL' +#define CPP2_UFCS_NONLOCAL(...) CPP2_UFCS_(,CPP2_UFCS_IDENTITY,(),,__VA_ARGS__) + ^ +../../../include/cpp2util.h:913:59: note: expanded from macro 'CPP2_UFCS_' +#define CPP2_UFCS_(LAMBDADEFCAPT,MVFWD,QUALID,TEMPKW,...) \ ^ -../../../include/cpp2util.h:925:53: note: expanded from macro 'CPP2_UFCS_' -#define CPP2_UFCS_(LAMBDADEFCAPT,QUALID,TEMPKW,...) \ - ^ mixed-bugfix-for-ufcs-non-local.cpp2:23:36: error: a lambda expression cannot appear in this context auto g([[maybe_unused]] cpp2::in> unnamed_param_1) -> void{}// Fails on Clang 12 (lambda in unevaluated context). ^ -../../../include/cpp2util.h:944:59: note: expanded from macro 'CPP2_UFCS_NONLOCAL' -#define CPP2_UFCS_NONLOCAL(...) CPP2_UFCS_(,(),,__VA_ARGS__) +../../../include/cpp2util.h:934:59: note: expanded from macro 'CPP2_UFCS_NONLOCAL' +#define CPP2_UFCS_NONLOCAL(...) CPP2_UFCS_(,CPP2_UFCS_IDENTITY,(),,__VA_ARGS__) + ^ +../../../include/cpp2util.h:913:59: note: expanded from macro 'CPP2_UFCS_' +#define CPP2_UFCS_(LAMBDADEFCAPT,MVFWD,QUALID,TEMPKW,...) \ ^ -../../../include/cpp2util.h:925:53: note: expanded from macro 'CPP2_UFCS_' -#define CPP2_UFCS_(LAMBDADEFCAPT,QUALID,TEMPKW,...) \ - ^ mixed-bugfix-for-ufcs-non-local.cpp2:27:29: error: a lambda expression cannot appear in this context [[nodiscard]] auto h() -> t { return o; }// Fails on Clang 12 (lambda in unevaluated context). ^ -../../../include/cpp2util.h:944:59: note: expanded from macro 'CPP2_UFCS_NONLOCAL' -#define CPP2_UFCS_NONLOCAL(...) CPP2_UFCS_(,(),,__VA_ARGS__) +../../../include/cpp2util.h:934:59: note: expanded from macro 'CPP2_UFCS_NONLOCAL' +#define CPP2_UFCS_NONLOCAL(...) CPP2_UFCS_(,CPP2_UFCS_IDENTITY,(),,__VA_ARGS__) + ^ +../../../include/cpp2util.h:913:59: note: expanded from macro 'CPP2_UFCS_' +#define CPP2_UFCS_(LAMBDADEFCAPT,MVFWD,QUALID,TEMPKW,...) \ ^ -../../../include/cpp2util.h:925:53: note: expanded from macro 'CPP2_UFCS_' -#define CPP2_UFCS_(LAMBDADEFCAPT,QUALID,TEMPKW,...) \ - ^ mixed-bugfix-for-ufcs-non-local.cpp2:41:79: error: lambda expression in an unevaluated operand inline CPP2_CONSTEXPR bool u::c = [](cpp2::in> x) mutable -> auto { return x; }(true);// Fails on Clang 12 (lambda in unevaluated context). ^ -../../../include/cpp2util.h:944:59: note: expanded from macro 'CPP2_UFCS_NONLOCAL' -#define CPP2_UFCS_NONLOCAL(...) CPP2_UFCS_(,(),,__VA_ARGS__) +../../../include/cpp2util.h:934:59: note: expanded from macro 'CPP2_UFCS_NONLOCAL' +#define CPP2_UFCS_NONLOCAL(...) CPP2_UFCS_(,CPP2_UFCS_IDENTITY,(),,__VA_ARGS__) + ^ +../../../include/cpp2util.h:913:59: note: expanded from macro 'CPP2_UFCS_' +#define CPP2_UFCS_(LAMBDADEFCAPT,MVFWD,QUALID,TEMPKW,...) \ ^ -../../../include/cpp2util.h:925:53: note: expanded from macro 'CPP2_UFCS_' -#define CPP2_UFCS_(LAMBDADEFCAPT,QUALID,TEMPKW,...) \ - ^ 13 errors generated. diff --git a/regression-tests/test-results/clang-12/pure2-bugfix-for-ufcs-noexcept.cpp.output b/regression-tests/test-results/clang-12/pure2-bugfix-for-ufcs-noexcept.cpp.output index 12f7abc9fa..d966d0dbcc 100644 --- a/regression-tests/test-results/clang-12/pure2-bugfix-for-ufcs-noexcept.cpp.output +++ b/regression-tests/test-results/clang-12/pure2-bugfix-for-ufcs-noexcept.cpp.output @@ -1,10 +1,10 @@ pure2-bugfix-for-ufcs-noexcept.cpp2:5:26: error: lambda expression in an unevaluated operand static_assert(noexcept(CPP2_UFCS(swap)(t(), t())));// Fails on Clang 12 (lambda in unevaluated context) and GCC 10 (static assertion failed) ^ -../../../include/cpp2util.h:941:59: note: expanded from macro 'CPP2_UFCS' -#define CPP2_UFCS(...) CPP2_UFCS_(&,(),,__VA_ARGS__) +../../../include/cpp2util.h:929:59: note: expanded from macro 'CPP2_UFCS' +#define CPP2_UFCS(...) CPP2_UFCS_(&,CPP2_UFCS_IDENTITY,(),,__VA_ARGS__) + ^ +../../../include/cpp2util.h:913:59: note: expanded from macro 'CPP2_UFCS_' +#define CPP2_UFCS_(LAMBDADEFCAPT,MVFWD,QUALID,TEMPKW,...) \ ^ -../../../include/cpp2util.h:925:53: note: expanded from macro 'CPP2_UFCS_' -#define CPP2_UFCS_(LAMBDADEFCAPT,QUALID,TEMPKW,...) \ - ^ 1 error generated. diff --git a/regression-tests/test-results/clang-12/pure2-bugfix-for-ufcs-sfinae.cpp.output b/regression-tests/test-results/clang-12/pure2-bugfix-for-ufcs-sfinae.cpp.output index 8d5dd58565..dfc83f19d8 100644 --- a/regression-tests/test-results/clang-12/pure2-bugfix-for-ufcs-sfinae.cpp.output +++ b/regression-tests/test-results/clang-12/pure2-bugfix-for-ufcs-sfinae.cpp.output @@ -1,19 +1,19 @@ pure2-bugfix-for-ufcs-sfinae.cpp2:1:78: error: lambda expression in an unevaluated operand template [[nodiscard]] auto f() -> std::type_identity_t; ^ -../../../include/cpp2util.h:944:59: note: expanded from macro 'CPP2_UFCS_NONLOCAL' -#define CPP2_UFCS_NONLOCAL(...) CPP2_UFCS_(,(),,__VA_ARGS__) +../../../include/cpp2util.h:934:59: note: expanded from macro 'CPP2_UFCS_NONLOCAL' +#define CPP2_UFCS_NONLOCAL(...) CPP2_UFCS_(,CPP2_UFCS_IDENTITY,(),,__VA_ARGS__) + ^ +../../../include/cpp2util.h:913:59: note: expanded from macro 'CPP2_UFCS_' +#define CPP2_UFCS_(LAMBDADEFCAPT,MVFWD,QUALID,TEMPKW,...) \ ^ -../../../include/cpp2util.h:925:53: note: expanded from macro 'CPP2_UFCS_' -#define CPP2_UFCS_(LAMBDADEFCAPT,QUALID,TEMPKW,...) \ - ^ pure2-bugfix-for-ufcs-sfinae.cpp2:1:78: error: lambda expression in an unevaluated operand template [[nodiscard]] auto f() -> std::type_identity_t{}// Fails on Clang 12 (lambda in unevaluated context). ^ -../../../include/cpp2util.h:944:59: note: expanded from macro 'CPP2_UFCS_NONLOCAL' -#define CPP2_UFCS_NONLOCAL(...) CPP2_UFCS_(,(),,__VA_ARGS__) +../../../include/cpp2util.h:934:59: note: expanded from macro 'CPP2_UFCS_NONLOCAL' +#define CPP2_UFCS_NONLOCAL(...) CPP2_UFCS_(,CPP2_UFCS_IDENTITY,(),,__VA_ARGS__) + ^ +../../../include/cpp2util.h:913:59: note: expanded from macro 'CPP2_UFCS_' +#define CPP2_UFCS_(LAMBDADEFCAPT,MVFWD,QUALID,TEMPKW,...) \ ^ -../../../include/cpp2util.h:925:53: note: expanded from macro 'CPP2_UFCS_' -#define CPP2_UFCS_(LAMBDADEFCAPT,QUALID,TEMPKW,...) \ - ^ 2 errors generated. diff --git a/regression-tests/test-results/clang-12/pure2-last-use.cpp.output b/regression-tests/test-results/clang-12/pure2-last-use.cpp.output new file mode 100644 index 0000000000..5618be2c9b --- /dev/null +++ b/regression-tests/test-results/clang-12/pure2-last-use.cpp.output @@ -0,0 +1,4 @@ +pure2-last-use.cpp2:938:44: error: a lambda expression cannot appear in this context + static_cast([_0 = std::array auto { return identity(x); }(0)>()]() mutable -> auto { return _0; });// Fails on Clang 12 (lambda in unevaluated context). + ^ +1 error generated. diff --git a/regression-tests/test-results/gcc-10/pure2-last-use.cpp.execution b/regression-tests/test-results/gcc-10/pure2-last-use.cpp.execution new file mode 100644 index 0000000000..4632e068d5 --- /dev/null +++ b/regression-tests/test-results/gcc-10/pure2-last-use.cpp.execution @@ -0,0 +1 @@ +123456 \ No newline at end of file diff --git a/regression-tests/test-results/gcc-10/pure2-last-use.cpp.output b/regression-tests/test-results/gcc-10/pure2-last-use.cpp.output new file mode 100644 index 0000000000..e69de29bb2 diff --git a/regression-tests/test-results/gcc-13/mixed-bugfix-for-ufcs-non-local.cpp.output b/regression-tests/test-results/gcc-13/mixed-bugfix-for-ufcs-non-local.cpp.output index 50d1dd852b..0aa7df2e31 100644 --- a/regression-tests/test-results/gcc-13/mixed-bugfix-for-ufcs-non-local.cpp.output +++ b/regression-tests/test-results/gcc-13/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:926:1: error: lambda-expression in template parameter type - 926 | [LAMBDADEFCAPT]< \ +../../../include/cpp2util.h:914:1: error: lambda-expression in template parameter type + 914 | [LAMBDADEFCAPT]< \ | ^ -../../../include/cpp2util.h:944:59: note: in expansion of macro ‘CPP2_UFCS_’ - 944 | #define CPP2_UFCS_NONLOCAL(...) CPP2_UFCS_(,(),,__VA_ARGS__) +../../../include/cpp2util.h:934:59: note: in expansion of macro ‘CPP2_UFCS_’ + 934 | #define CPP2_UFCS_NONLOCAL(...) CPP2_UFCS_(,CPP2_UFCS_IDENTITY,(),,__VA_ARGS__) | ^~~~~~~~~~ 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:926:1: error: lambda-expression in template parameter type - 926 | [LAMBDADEFCAPT]< \ +../../../include/cpp2util.h:914:1: error: lambda-expression in template parameter type + 914 | [LAMBDADEFCAPT]< \ | ^ -../../../include/cpp2util.h:944:59: note: in expansion of macro ‘CPP2_UFCS_’ - 944 | #define CPP2_UFCS_NONLOCAL(...) CPP2_UFCS_(,(),,__VA_ARGS__) +../../../include/cpp2util.h:934:59: note: in expansion of macro ‘CPP2_UFCS_’ + 934 | #define CPP2_UFCS_NONLOCAL(...) CPP2_UFCS_(,CPP2_UFCS_IDENTITY,(),,__VA_ARGS__) | ^~~~~~~~~~ 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:926:1: error: lambda-expression in template parameter type - 926 | [LAMBDADEFCAPT]< \ +../../../include/cpp2util.h:914:1: error: lambda-expression in template parameter type + 914 | [LAMBDADEFCAPT]< \ | ^ -../../../include/cpp2util.h:944:59: note: in expansion of macro ‘CPP2_UFCS_’ - 944 | #define CPP2_UFCS_NONLOCAL(...) CPP2_UFCS_(,(),,__VA_ARGS__) +../../../include/cpp2util.h:934:59: note: in expansion of macro ‘CPP2_UFCS_’ + 934 | #define CPP2_UFCS_NONLOCAL(...) CPP2_UFCS_(,CPP2_UFCS_IDENTITY,(),,__VA_ARGS__) | ^~~~~~~~~~ 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:926:1: error: lambda-expression in template parameter type - 926 | [LAMBDADEFCAPT]< \ +../../../include/cpp2util.h:914:1: error: lambda-expression in template parameter type + 914 | [LAMBDADEFCAPT]< \ | ^ -../../../include/cpp2util.h:944:59: note: in expansion of macro ‘CPP2_UFCS_’ - 944 | #define CPP2_UFCS_NONLOCAL(...) CPP2_UFCS_(,(),,__VA_ARGS__) +../../../include/cpp2util.h:934:59: note: in expansion of macro ‘CPP2_UFCS_’ + 934 | #define CPP2_UFCS_NONLOCAL(...) CPP2_UFCS_(,CPP2_UFCS_IDENTITY,(),,__VA_ARGS__) | ^~~~~~~~~~ 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:926:1: error: lambda-expression in template parameter type - 926 | [LAMBDADEFCAPT]< \ +../../../include/cpp2util.h:914:1: error: lambda-expression in template parameter type + 914 | [LAMBDADEFCAPT]< \ | ^ -../../../include/cpp2util.h:944:59: note: in expansion of macro ‘CPP2_UFCS_’ - 944 | #define CPP2_UFCS_NONLOCAL(...) CPP2_UFCS_(,(),,__VA_ARGS__) +../../../include/cpp2util.h:934:59: note: in expansion of macro ‘CPP2_UFCS_’ + 934 | #define CPP2_UFCS_NONLOCAL(...) CPP2_UFCS_(,CPP2_UFCS_IDENTITY,(),,__VA_ARGS__) | ^~~~~~~~~~ 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/pure2-last-use.cpp.execution b/regression-tests/test-results/gcc-13/pure2-last-use.cpp.execution new file mode 100644 index 0000000000..4632e068d5 --- /dev/null +++ b/regression-tests/test-results/gcc-13/pure2-last-use.cpp.execution @@ -0,0 +1 @@ +123456 \ No newline at end of file diff --git a/regression-tests/test-results/gcc-13/pure2-last-use.cpp.output b/regression-tests/test-results/gcc-13/pure2-last-use.cpp.output new file mode 100644 index 0000000000..e69de29bb2 diff --git a/regression-tests/test-results/mixed-bounds-safety-with-assert-2.cpp b/regression-tests/test-results/mixed-bounds-safety-with-assert-2.cpp index 018df8da8e..ebc3a1b026 100644 --- a/regression-tests/test-results/mixed-bounds-safety-with-assert-2.cpp +++ b/regression-tests/test-results/mixed-bounds-safety-with-assert-2.cpp @@ -33,7 +33,7 @@ auto add_42_to_subrange(auto& rng, cpp2::in start, cpp2::in end) -> vo std::vector v {1, 2, 3, 4, 5}; add_42_to_subrange(v, 1, 3); - for ( auto const& i : v ) + for ( auto const& i : std::move(v) ) std::cout << i << "\n"; } diff --git a/regression-tests/test-results/mixed-function-expression-and-std-for-each.cpp b/regression-tests/test-results/mixed-function-expression-and-std-for-each.cpp index 1ed322444e..118ac0d711 100644 --- a/regression-tests/test-results/mixed-function-expression-and-std-for-each.cpp +++ b/regression-tests/test-results/mixed-function-expression-and-std-for-each.cpp @@ -42,7 +42,7 @@ std::move(callback) ); - for ( auto const& str : vec ) { + for ( auto const& str : std::move(vec) ) { std::cout << str << "\n"; } } diff --git a/regression-tests/test-results/mixed-function-expression-and-std-ranges-for-each-with-capture.cpp b/regression-tests/test-results/mixed-function-expression-and-std-ranges-for-each-with-capture.cpp index 5cfec66f9c..a5f453a0de 100644 --- a/regression-tests/test-results/mixed-function-expression-and-std-ranges-for-each-with-capture.cpp +++ b/regression-tests/test-results/mixed-function-expression-and-std-ranges-for-each-with-capture.cpp @@ -37,7 +37,7 @@ auto callback {[](auto& x) mutable -> void { x += "-ish"; }}; std::ranges::for_each(vec, std::move(callback)); - for ( auto const& str : vec ) + for ( auto const& str : std::move(vec) ) std::cout << str << "\n"; } diff --git a/regression-tests/test-results/mixed-function-expression-and-std-ranges-for-each.cpp b/regression-tests/test-results/mixed-function-expression-and-std-ranges-for-each.cpp index 7fcfc81778..51a261dda6 100644 --- a/regression-tests/test-results/mixed-function-expression-and-std-ranges-for-each.cpp +++ b/regression-tests/test-results/mixed-function-expression-and-std-ranges-for-each.cpp @@ -36,7 +36,7 @@ auto callback {[](auto& x) mutable -> void { x += "-ish"; }}; std::ranges::for_each(vec, std::move(callback)); - for ( auto const& str : vec ) + for ( auto const& str : std::move(vec) ) std::cout << str << "\n"; } diff --git a/regression-tests/test-results/mixed-function-expression-with-pointer-capture.cpp b/regression-tests/test-results/mixed-function-expression-with-pointer-capture.cpp index 9d5b37d186..eed1c1f7a5 100644 --- a/regression-tests/test-results/mixed-function-expression-with-pointer-capture.cpp +++ b/regression-tests/test-results/mixed-function-expression-with-pointer-capture.cpp @@ -38,7 +38,7 @@ auto callback {[](auto& x) mutable -> void { x += "-ish"; }}; std::ranges::for_each(vec, std::move(callback)); - for ( auto const& str : vec ) + for ( auto const& str : std::move(vec) ) std::cout << str << "\n"; } diff --git a/regression-tests/test-results/mixed-function-expression-with-repeated-capture.cpp b/regression-tests/test-results/mixed-function-expression-with-repeated-capture.cpp index 9e38b1c44a..7bcef6d14c 100644 --- a/regression-tests/test-results/mixed-function-expression-with-repeated-capture.cpp +++ b/regression-tests/test-results/mixed-function-expression-with-repeated-capture.cpp @@ -37,7 +37,7 @@ auto callback {[](auto& x) mutable -> void { x += "-ish"; }}; std::ranges::for_each(vec, std::move(callback)); - for ( auto const& str : vec ) + for ( auto const& str : std::move(vec) ) std::cout << str << "\n"; } diff --git a/regression-tests/test-results/mixed-intro-for-with-counter-include-last.cpp b/regression-tests/test-results/mixed-intro-for-with-counter-include-last.cpp index 86a34ef3c4..33c58818d1 100644 --- a/regression-tests/test-results/mixed-intro-for-with-counter-include-last.cpp +++ b/regression-tests/test-results/mixed-intro-for-with-counter-include-last.cpp @@ -30,7 +30,7 @@ { std::vector v {1, 2, 3, 4, 5}; auto counter {42}; - for ( auto const& i : v ) { do { + for ( auto const& i : std::move(v) ) { do { std::cout << i << " " << counter << "\n"; } while (false); counter *= 2; } } diff --git a/regression-tests/test-results/mixed-postexpression-with-capture.cpp b/regression-tests/test-results/mixed-postexpression-with-capture.cpp index 4dc02741c9..ca01a21cd3 100644 --- a/regression-tests/test-results/mixed-postexpression-with-capture.cpp +++ b/regression-tests/test-results/mixed-postexpression-with-capture.cpp @@ -74,7 +74,7 @@ auto insert_at(cpp2::in where, cpp2::in val) -> void cpp2_finally_presuccess.add([&, _1 = CPP2_UFCS(length)(ret)]{if (cpp2::Default.has_handler() && !(CPP2_UFCS(length)(ret) == _1 + 5) ) { cpp2::Default.report_violation(""); }} ); #line 26 "mixed-postexpression-with-capture.cpp2" ret += " and "; -cpp2_finally_presuccess.run(); return std::move(ret); } +cpp2_finally_presuccess.run(); return ret; } #line 29 "mixed-postexpression-with-capture.cpp2" [[nodiscard]] auto make_strings() -> make_strings_ret diff --git a/regression-tests/test-results/mixed-string-interpolation.cpp b/regression-tests/test-results/mixed-string-interpolation.cpp index bd5225f047..22c9fe06e0 100644 --- a/regression-tests/test-results/mixed-string-interpolation.cpp +++ b/regression-tests/test-results/mixed-string-interpolation.cpp @@ -32,7 +32,7 @@ struct custom_struct_with_no_stringize_customization { } custom; std::cout << ("a = " + cpp2::to_string(a) + ", b = " + cpp2::to_string(b) + "\n"); b = 42; - std::cout << ("a^2 + b = " + cpp2::to_string(a * std::move(a) + CPP2_UFCS(value)(std::move(b))) + "\n"); + std::cout << ("a^2 + b = " + cpp2::to_string(a * a + CPP2_UFCS(value)(std::move(b))) + "\n"); std::string_view sv {"my string_view"}; std::cout << ("sv = " + cpp2::to_string(std::move(sv)) + "\n"); diff --git a/regression-tests/test-results/msvc-2022/pure2-last-use.cpp.execution b/regression-tests/test-results/msvc-2022/pure2-last-use.cpp.execution new file mode 100644 index 0000000000..4632e068d5 --- /dev/null +++ b/regression-tests/test-results/msvc-2022/pure2-last-use.cpp.execution @@ -0,0 +1 @@ +123456 \ No newline at end of file diff --git a/regression-tests/test-results/msvc-2022/pure2-last-use.cpp.output b/regression-tests/test-results/msvc-2022/pure2-last-use.cpp.output new file mode 100644 index 0000000000..79703bb60a --- /dev/null +++ b/regression-tests/test-results/msvc-2022/pure2-last-use.cpp.output @@ -0,0 +1 @@ +pure2-last-use.cpp diff --git a/regression-tests/test-results/pure2-break-continue.cpp b/regression-tests/test-results/pure2-break-continue.cpp index b59bad66c1..24e0ad67e1 100644 --- a/regression-tests/test-results/pure2-break-continue.cpp +++ b/regression-tests/test-results/pure2-break-continue.cpp @@ -255,9 +255,9 @@ auto do_break_outer() -> void auto for_continue_inner() -> void { std::vector vi {0, 1, 2}; - for ( auto const& i : vi ) { + for ( auto const& i : std::move(vi) ) { std::vector vj {0, 1, 2}; - for ( auto const& j : vj ) {{ + for ( auto const& j : std::move(vj) ) {{ #line 166 "pure2-break-continue.cpp2" std::cout << i << j << " "; if (j == 1) { @@ -275,10 +275,10 @@ auto for_continue_inner() -> void auto for_continue_outer() -> void { std::vector vi {0, 1, 2}; - for ( auto const& i : vi ) {{ + for ( auto const& i : std::move(vi) ) {{ #line 181 "pure2-break-continue.cpp2" std::vector vj {0, 1, 2}; - for ( auto const& j : vj ) { + for ( auto const& j : std::move(vj) ) { std::cout << i << j << " "; if (j == 1) { goto CONTINUE_outer; @@ -295,9 +295,9 @@ auto for_continue_outer() -> void auto for_break_inner() -> void { std::vector vi {0, 1, 2}; - for ( auto const& i : vi ) { + for ( auto const& i : std::move(vi) ) { std::vector vj {0, 1, 2}; - for ( auto const& j : vj ) {{ + for ( auto const& j : std::move(vj) ) {{ #line 200 "pure2-break-continue.cpp2" std::cout << i << j << " "; if (j == 1) { @@ -315,10 +315,10 @@ auto for_break_inner() -> void auto for_break_outer() -> void { std::vector vi {0, 1, 2}; - for ( auto const& i : vi ) {{ + for ( auto const& i : std::move(vi) ) {{ #line 215 "pure2-break-continue.cpp2" std::vector vj {0, 1, 2}; - for ( auto const& j : vj ) { + for ( auto const& j : std::move(vj) ) { std::cout << i << j << " "; if (j == 1) { goto BREAK_outer; diff --git a/regression-tests/test-results/pure2-bugfix-for-discard-precedence.cpp b/regression-tests/test-results/pure2-bugfix-for-discard-precedence.cpp index 013414c088..ceb05566bd 100644 --- a/regression-tests/test-results/pure2-bugfix-for-discard-precedence.cpp +++ b/regression-tests/test-results/pure2-bugfix-for-discard-precedence.cpp @@ -48,7 +48,7 @@ auto main(int const argc_, char** argv_) -> int{ auto const args = cpp2::make_args(argc_, argv_); #line 8 "pure2-bugfix-for-discard-precedence.cpp2" quantity x {1729}; - static_cast(x + std::move(x));// Not `(void) x + x`; would attempt to add a `void` to `x`. + static_cast(x + x);// Not `(void) x + x`; would attempt to add a `void` to `x`. static_cast(args);// Not `void(args)`; would attempt to declare `args` with `void` type. } diff --git a/regression-tests/test-results/pure2-bugfix-for-parameter-decl-list-error.cpp2.output b/regression-tests/test-results/pure2-bugfix-for-parameter-decl-list-error.cpp2.output index 159b73ef59..7f5cd76fe0 100644 --- a/regression-tests/test-results/pure2-bugfix-for-parameter-decl-list-error.cpp2.output +++ b/regression-tests/test-results/pure2-bugfix-for-parameter-decl-list-error.cpp2.output @@ -1,3 +1,4 @@ pure2-bugfix-for-parameter-decl-list-error.cpp2... pure2-bugfix-for-parameter-decl-list-error.cpp2(1,13): error: invalid parameter list: a comma must be followed by another parameter (at ')') +pure2-bugfix-for-parameter-decl-list-error.cpp2(1,8): error: local variable 'args' is not used; consider changing its name to '_' to make it explicitly anonymous, or removing it entirely if its side effects are not needed diff --git a/regression-tests/test-results/pure2-deducing-pointers-error.cpp2.output b/regression-tests/test-results/pure2-deducing-pointers-error.cpp2.output index 66bd67b333..f8f68b1121 100644 --- a/regression-tests/test-results/pure2-deducing-pointers-error.cpp2.output +++ b/regression-tests/test-results/pure2-deducing-pointers-error.cpp2.output @@ -5,5 +5,8 @@ pure2-deducing-pointers-error.cpp2(20,9): error: = - pointer assignment from nul pure2-deducing-pointers-error.cpp2(21,9): error: += - pointer assignment from null or integer is illegal pure2-deducing-pointers-error.cpp2(25,9): error: = - pointer assignment from null or integer is illegal pure2-deducing-pointers-error.cpp2(29,9): error: = - pointer assignment from null or integer is illegal +pure2-deducing-pointers-error.cpp2(31,11): error: ++ - pointer arithmetic is illegal - use std::span or gsl::span instead +pure2-deducing-pointers-error.cpp2(33,8): error: = - pointer assignment from null or integer is illegal ==> program violates lifetime safety guarantee - see previous errors + ==> program violates bounds safety guarantee - see previous errors diff --git a/regression-tests/test-results/pure2-enum.cpp b/regression-tests/test-results/pure2-enum.cpp index 8eb597f98b..3f2341f201 100644 --- a/regression-tests/test-results/pure2-enum.cpp +++ b/regression-tests/test-results/pure2-enum.cpp @@ -238,7 +238,7 @@ constexpr auto file_attributes::operator=(file_attributes&& that) noexcept -> fi 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 _ret + ")"; + return std::move(_ret) + ")"; } #line 28 "pure2-enum.cpp2" auto main() -> int{ diff --git a/regression-tests/test-results/pure2-for-loop-range-with-lambda.cpp b/regression-tests/test-results/pure2-for-loop-range-with-lambda.cpp index 3e2e505aed..6ffe1b4544 100644 --- a/regression-tests/test-results/pure2-for-loop-range-with-lambda.cpp +++ b/regression-tests/test-results/pure2-for-loop-range-with-lambda.cpp @@ -44,7 +44,7 @@ auto main(int const argc_, char** argv_) -> int{ // OK auto temp {CPP2_UFCS(first)(std::move(ints), [](auto const& x) mutable -> auto { return x; })}; - for ( auto const& i : temp ) { + for ( auto const& i : std::move(temp) ) { std::cout << i; } diff --git a/regression-tests/test-results/pure2-intro-example-hello-2022.cpp b/regression-tests/test-results/pure2-intro-example-hello-2022.cpp index d8eef95950..2aa7366909 100644 --- a/regression-tests/test-results/pure2-intro-example-hello-2022.cpp +++ b/regression-tests/test-results/pure2-intro-example-hello-2022.cpp @@ -28,9 +28,9 @@ auto print_it(auto const& x, auto const& len) -> void; std::vector vec { "hello", "2022"}; - for ( auto& str : vec ) { + for ( auto& str : std::move(vec) ) { auto len {decorate(str)}; - print_it(str, len); + print_it(str, std::move(len)); } } diff --git a/regression-tests/test-results/pure2-intro-example-three-loops.cpp b/regression-tests/test-results/pure2-intro-example-three-loops.cpp index 3a28ce7c99..5ad0a40d26 100644 --- a/regression-tests/test-results/pure2-intro-example-three-loops.cpp +++ b/regression-tests/test-results/pure2-intro-example-three-loops.cpp @@ -55,7 +55,7 @@ auto decorate_and_print(auto& thing) -> void{ } while ( [&]{ --*cpp2::assert_not_null(i) ; return true; }() && cpp2::cmp_greater(*cpp2::assert_not_null(i),0)); std::cout << "\n"; - for ( auto& word : words ) + for ( auto& word : std::move(words) ) decorate_and_print(word); print(std::string{"end of program"}); diff --git a/regression-tests/test-results/pure2-last-use-error.cpp2.output b/regression-tests/test-results/pure2-last-use-error.cpp2.output new file mode 100644 index 0000000000..e39c71621e --- /dev/null +++ b/regression-tests/test-results/pure2-last-use-error.cpp2.output @@ -0,0 +1,3 @@ +pure2-last-use-error.cpp2... +pure2-last-use-error.cpp2(1,13): error: local variable 'x' is not used; consider changing its name to '_' to make it explicitly anonymous, or removing it entirely if its side effects are not needed + diff --git a/regression-tests/test-results/pure2-last-use.cpp b/regression-tests/test-results/pure2-last-use.cpp new file mode 100644 index 0000000000..8070699f19 --- /dev/null +++ b/regression-tests/test-results/pure2-last-use.cpp @@ -0,0 +1,1564 @@ + +#define CPP2_IMPORT_STD Yes + +//=== Cpp2 type declarations ==================================================== + + +#include "cpp2util.h" + +#line 1 "pure2-last-use.cpp2" + +#line 168 "pure2-last-use.cpp2" +class issue_857; + + +#line 256 "pure2-last-use.cpp2" +class issue_857_2; + + +#line 261 "pure2-last-use.cpp2" +class issue_857_3; + + +#line 265 "pure2-last-use.cpp2" +class issue_857_6; + + +#line 271 "pure2-last-use.cpp2" +template class move_only_function; + + +#line 277 "pure2-last-use.cpp2" +class issue_857_4; + + +#line 337 "pure2-last-use.cpp2" +class issue_857_5; + + +#line 342 "pure2-last-use.cpp2" +class issue_857_7; + + +#line 348 "pure2-last-use.cpp2" +class issue_857_8; + + +#line 354 "pure2-last-use.cpp2" +class issue_857_9; + + +#line 371 "pure2-last-use.cpp2" +class issue_869_0; + + +class issue_869_1; + + +#line 838 "pure2-last-use.cpp2" +class cpp2_union; + + +#line 846 "pure2-last-use.cpp2" +class my_string; + + +#line 905 "pure2-last-use.cpp2" +namespace captures { + +#line 919 "pure2-last-use.cpp2" +class t; + + +#line 948 "pure2-last-use.cpp2" +} + +#line 978 "pure2-last-use.cpp2" +class types; + + +//=== Cpp2 type definitions and function declarations =========================== + +#line 1 "pure2-last-use.cpp2" +auto f_inout([[maybe_unused]] auto& unnamed_param_1) -> void; +#line 2 "pure2-last-use.cpp2" +auto f_copy([[maybe_unused]] auto ...unnamed_param_1) -> void; +[[nodiscard]] auto pred([[maybe_unused]] auto const& ...unnamed_param_1) -> auto; +[[nodiscard]] auto pred_copy([[maybe_unused]] auto ...unnamed_param_1) -> auto; +template [[nodiscard]] constexpr auto identity(T&& x) -> auto&& +CPP2_REQUIRES (std::is_reference_v) ; +#line 6 "pure2-last-use.cpp2" +[[nodiscard]] auto identity_copy(auto x) -> auto; + +auto issue_313() -> void; + +#line 72 "pure2-last-use.cpp2" +auto issue_313_1(std::unique_ptr x) -> void; + +auto issue_350() -> void; + +#line 85 "pure2-last-use.cpp2" +/* +issue_440_0: () -> int = { + i: int; + if true { + i = 1; + return i; + } + i = 2; + return i; +} + +issue_440_1: () -> (i: int) = { + if true { + i = 1; + return; + } + i = 2; +} +*/ +auto issue_683(auto const& args) -> void; + +#line 113 "pure2-last-use.cpp2" +auto issue_825() -> void; + +#line 119 "pure2-last-use.cpp2" +auto issue_832() -> void; + +#line 124 "pure2-last-use.cpp2" +[[nodiscard]] auto make_copy(auto x) -> auto; + +auto issue_847_4(std::vector v) -> void; + +#line 132 "pure2-last-use.cpp2" +auto issue_847_0(std::vector> v) -> void; + +#line 138 "pure2-last-use.cpp2" +auto issue_847_1(std::vector>&& v) -> void; + +#line 144 "pure2-last-use.cpp2" +auto issue_847_5(auto&& v) -> void; + +#line 150 "pure2-last-use.cpp2" +auto issue_847_2(auto&& v) -> void; + +#line 155 "pure2-last-use.cpp2" +auto issue_847_3(int x) -> void; + +auto issue_850() -> void; + +#line 168 "pure2-last-use.cpp2" +class issue_857 { + private: std::unique_ptr a; + private: std::unique_ptr b; + public: issue_857(issue_857&& that) noexcept; +#line 171 "pure2-last-use.cpp2" + public: auto operator=(issue_857&& that) noexcept -> issue_857& ; + public: ~issue_857() noexcept; + +#line 176 "pure2-last-use.cpp2" + public: auto f() && -> void; + public: auto f(issue_857&& that) && -> void; + public: auto g() && -> void; + public: auto g(issue_857&& that) && -> void; + public: auto h() & -> void; + public: auto i() && -> void; + public: auto j() && -> void; + public: auto k() && -> void; + +#line 187 "pure2-last-use.cpp2" + public: auto l() && -> void; + public: auto m() && -> void; + public: static auto n([[maybe_unused]] auto const& unnamed_param_1) -> void; + public: auto n() && -> void; + public: auto o0() && -> void; + public: auto o1() && -> void; + public: auto o2() && -> void; + public: auto o3() && -> void; + public: auto o4() && -> void; + public: auto p0() && -> void; + +#line 200 "pure2-last-use.cpp2" + public: auto p1() && -> void; + +#line 204 "pure2-last-use.cpp2" + public: auto p2() && -> void; + +#line 208 "pure2-last-use.cpp2" + public: auto p3() && -> void; + +#line 212 "pure2-last-use.cpp2" + public: auto q() && -> void; + +#line 216 "pure2-last-use.cpp2" + public: auto z(issue_857&& that) && -> void; + +#line 254 "pure2-last-use.cpp2" +}; + +class issue_857_2 { + public: std::unique_ptr a; // OK: No error about 'a' being unused. +}; + +extern int gi; +class issue_857_3 { + public: std::add_lvalue_reference_t i {gi}; + public: auto f() && -> void; +}; +class issue_857_6 { + public: auto f() && -> void; + public: std::add_lvalue_reference_t i {gi}; +}; + +// TODO Alias `std::move_only_function`. +template class move_only_function { + public: explicit move_only_function(); + public: move_only_function([[maybe_unused]] move_only_function&& that) noexcept; +#line 273 "pure2-last-use.cpp2" + public: auto operator=([[maybe_unused]] move_only_function&& that) noexcept -> move_only_function& ; + public: [[nodiscard]] auto operator()([[maybe_unused]] auto const& ...unnamed_param_2) && -> int; +}; + +class issue_857_4 { + public: std::add_pointer_t f; + public: std::add_pointer_t g; + public: move_only_function mf; + public: move_only_function mg; + public: auto h0() && -> void; + public: auto h1() && -> void; + public: auto h2(issue_857_4 const& that) && -> void; + public: auto h3(issue_857_4 const& that) && -> void; + public: auto h4(cpp2::in x) && -> void; + public: auto h5(cpp2::in x) && -> void; + public: auto h6(cpp2::in x) && -> void; + public: auto h7(cpp2::in x) && -> void; + public: auto h8(cpp2::in x) && -> void; + public: auto i0(cpp2::in x) && -> void; + +#line 295 "pure2-last-use.cpp2" + public: auto i1(cpp2::in x) && -> void; + +#line 299 "pure2-last-use.cpp2" + public: auto i2(cpp2::in x) && -> void; + +#line 303 "pure2-last-use.cpp2" + public: auto i3(cpp2::in x) && -> void; + +#line 307 "pure2-last-use.cpp2" + public: auto z(issue_857_4 const& that) && -> void; + +#line 335 "pure2-last-use.cpp2" +}; + +class issue_857_5 { + public: auto f() && -> void; + public: std::unique_ptr a; +}; + +struct issue_857_7_A_as_base { std::add_lvalue_reference_t A; }; +#line 342 "pure2-last-use.cpp2" +class issue_857_7: public issue_857_7_A_as_base, public std::monostate { + +#line 345 "pure2-last-use.cpp2" + public: auto F() && -> void; +}; + +class issue_857_8 { + public: std::unique_ptr a; + public: move_only_function b; + public: std::add_lvalue_reference_t c; + public: auto d() && -> void; +}; +class issue_857_9: public issue_857_8 { + +#line 357 "pure2-last-use.cpp2" + // Error: Cppfront limitation: + // . +//f0: (move this) = f_copy(a); +//f1: (move this) = _ = b(); + public: auto f2() && -> void; +//f3: (move this) = d(); + + // OK: Explicit 'this' for base members, like in templates. + public: auto g0() && -> void; + public: auto g1() && -> void; + public: auto g2() && -> void; + public: auto g3() && -> void; +}; + +class issue_869_0 { + public: explicit issue_869_0([[maybe_unused]] std::unique_ptr&& unnamed_param_2); +#line 372 "pure2-last-use.cpp2" + public: auto operator=([[maybe_unused]] std::unique_ptr&& unnamed_param_2) -> issue_869_0& ; + public: [[nodiscard]] auto operator<=>([[maybe_unused]] issue_869_0 const& that) const& -> std::strong_ordering = default; +public: issue_869_0([[maybe_unused]] issue_869_0 const& that); + +public: auto operator=([[maybe_unused]] issue_869_0 const& that) -> issue_869_0& ; +public: issue_869_0([[maybe_unused]] issue_869_0&& that) noexcept; +public: auto operator=([[maybe_unused]] issue_869_0&& that) noexcept -> issue_869_0& ; +public: explicit issue_869_0(); + +#line 373 "pure2-last-use.cpp2" +}; +class issue_869_1 { +private: cpp2::aligned_storage _storage {}; private: cpp2::i8 _discriminator {-1}; public: [[nodiscard]] auto is_i() const& -> bool; +public: [[nodiscard]] auto i() const& -> issue_869_0 const&; +public: [[nodiscard]] auto i() & -> issue_869_0&; +public: auto set_i(cpp2::in _value) & -> void; +public: auto set_i(auto&& ..._args) & -> void; +private: auto _destroy() & -> void; +public: ~issue_869_1() noexcept; +public: explicit issue_869_1(); +public: issue_869_1(issue_869_1 const& that); + +public: issue_869_1(issue_869_1&& that) noexcept; +public: auto operator=(issue_869_1 const& that) -> issue_869_1& ; +public: auto operator=(issue_869_1&& that) noexcept -> issue_869_1& ; + +#line 376 "pure2-last-use.cpp2" +}; + +using issue_869_2_ret = issue_869_1; +[[nodiscard]] auto issue_869_2() -> issue_869_2_ret; + +#line 379 "pure2-last-use.cpp2" +auto issue_884() -> void; + +#line 776 "pure2-last-use.cpp2" +auto issue_888_0(std::string r, int size) -> void; + +#line 779 "pure2-last-use.cpp2" +auto issue_888_1([[maybe_unused]] std::string unnamed_param_1, move_only_function size) -> void; + +#line 783 "pure2-last-use.cpp2" +auto issue_890() -> void; + +#line 789 "pure2-last-use.cpp2" +auto draw() -> void; + +#line 795 "pure2-last-use.cpp2" +auto enum_0() -> void; + +#line 800 "pure2-last-use.cpp2" +auto enum_1() -> void; + +#line 828 "pure2-last-use.cpp2" +auto enum_2() -> void; + +#line 838 "pure2-last-use.cpp2" +class cpp2_union { + public: auto destroy() & -> void; + public: ~cpp2_union() noexcept; + public: cpp2_union() = default; + public: cpp2_union(cpp2_union const&) = delete; /* No 'that' constructor, suppress copy */ + public: auto operator=(cpp2_union const&) -> void = delete; + + +#line 844 "pure2-last-use.cpp2" +}; + +class my_string { + public: std::string string; + public: std::size_t size {CPP2_UFCS(size)(string)}; +}; + +using no_pessimizing_move_ret = std::unique_ptr; +#line 851 "pure2-last-use.cpp2" +[[nodiscard]] auto no_pessimizing_move() -> no_pessimizing_move_ret; + +auto deferred_non_copyable_0() -> void; + +#line 859 "pure2-last-use.cpp2" +[[nodiscard]] auto deferred_non_copyable_1() -> auto; +using deferred_non_copyable_2_ret = std::unique_ptr; + + +#line 865 "pure2-last-use.cpp2" +[[nodiscard]] auto deferred_non_copyable_2() -> deferred_non_copyable_2_ret; + +#line 869 "pure2-last-use.cpp2" +auto loops() -> void; + +#line 905 "pure2-last-use.cpp2" +namespace captures { + +// Skip non captured name in function expression + +auto f() -> void; + +#line 917 "pure2-last-use.cpp2" +int inline constexpr x = 0; + +class t { + public: std::unique_ptr x; + public: auto operator()() && -> void; + +#line 932 "pure2-last-use.cpp2" +}; + +auto g() -> void; + +#line 948 "pure2-last-use.cpp2" +} + +auto loops_and_captures() -> void; + +#line 978 "pure2-last-use.cpp2" +class types { + public: std::unique_ptr x; + public: auto f() && -> void; + public: auto g() && -> void; + +#line 986 "pure2-last-use.cpp2" +}; + +auto skip_hidden_names() -> void; + +#line 1038 "pure2-last-use.cpp2" +auto main(int const argc_, char** argv_) -> int; + +//=== Cpp2 function definitions ================================================= + +#line 1 "pure2-last-use.cpp2" +auto f_inout([[maybe_unused]] auto& unnamed_param_1) -> void{} +#line 2 "pure2-last-use.cpp2" +auto f_copy([[maybe_unused]] auto ...unnamed_param_1) -> void{} +#line 3 "pure2-last-use.cpp2" +[[nodiscard]] auto pred([[maybe_unused]] auto const& ...unnamed_param_1) -> auto { return true; } +#line 4 "pure2-last-use.cpp2" +[[nodiscard]] auto pred_copy([[maybe_unused]] auto ...unnamed_param_1) -> auto { return true; } +#line 5 "pure2-last-use.cpp2" +template [[nodiscard]] constexpr auto identity(T&& x) -> auto&& +requires (std::is_reference_v) {return CPP2_FORWARD(x); } +#line 6 "pure2-last-use.cpp2" +[[nodiscard]] auto identity_copy(auto x) -> auto { return std::move(x); } + +#line 8 "pure2-last-use.cpp2" +auto issue_313() -> void{ + static_cast([]() mutable -> std::vector{ + auto a {cpp2_new(0)}; + return { *cpp2::assert_not_null(a), *cpp2::assert_not_null(identity_copy(std::move(a))) }; + }); + static_cast([]() mutable -> std::vector{ + auto a {cpp2_new(0)}; + return { *cpp2::assert_not_null(*cpp2::assert_not_null(&a)), *cpp2::assert_not_null(identity_copy(std::move(a))) }; + }); + static_cast([]() mutable -> int{ + auto a {cpp2_new(0)}; + return *cpp2::assert_not_null(identity(a)) + *cpp2::assert_not_null(identity(a)); + + auto b {cpp2_new(0)}; + return *cpp2::assert_not_null(identity(b)) = *cpp2::assert_not_null(identity(b)); + + auto c {cpp2_new(0)}; + return *cpp2::assert_not_null(identity(c)) ^ *cpp2::assert_not_null(identity(c)); + }); + { + auto a {cpp2_new(0)}; + *cpp2::assert_not_null(identity(a)) = *cpp2::assert_not_null(identity(a)); + + auto b {cpp2_new(0)}; + int auto_1 {*cpp2::assert_not_null(identity(b)) = *cpp2::assert_not_null(identity(b))}; + + auto c {cpp2_new(0)}; + if (*cpp2::assert_not_null(identity(c)) * *cpp2::assert_not_null(identity(c))) {} + + auto d {cpp2_new(0)}; + if ((*cpp2::assert_not_null(identity(d)) - *cpp2::assert_not_null(identity(d)))) {} + + auto e {cpp2_new(0)}; + static_cast(cpp2::is(e, (e))); + + auto f {cpp2_new(0)}; + static_cast(cpp2::is>(f));// OK? + + auto g {cpp2_new(0)}; + for ( + [[maybe_unused]] auto const& unnamed_param_1 : { *cpp2::assert_not_null(identity(g)) + *cpp2::assert_not_null(identity(g)) } ) + {} + + auto h {cpp2_new(0)}; + while( *cpp2::assert_not_null(identity(h)) + *cpp2::assert_not_null(identity(h)) ) + {} + + auto i {cpp2_new(0)}; + do {} while ( + *cpp2::assert_not_null(identity(i)) + *cpp2::assert_not_null(identity(i))); + + auto j {cpp2_new(0)}; + auto k {cpp2_new(0)}; + static_cast([&] () -> int { auto&& _expr = *cpp2::assert_not_null(identity(j)) + *cpp2::assert_not_null(identity(j)); + if (cpp2::is(_expr, (*cpp2::assert_not_null(identity(k)) + *cpp2::assert_not_null(identity(k))))) { if constexpr( requires{0;} ) if constexpr( std::is_convertible_v ) return 0; else return int{}; else return int{}; } + else return 0; } + ()); + + } + { + auto a {cpp2_new(0)}; + static_cast([_0 = std::move(a)]() mutable -> auto { return *cpp2::assert_not_null(identity(_0)) + *cpp2::assert_not_null(identity(_0)); }); + } +} +#line 72 "pure2-last-use.cpp2" +auto issue_313_1(std::unique_ptr x) -> void{ + if (cpp2::Default.has_handler() && !(*cpp2::assert_not_null(identity(x)) + *cpp2::assert_not_null(identity(x))) ) { cpp2::Default.report_violation(""); }} + +#line 74 "pure2-last-use.cpp2" +auto issue_350() -> void{ + auto x {21}; + + auto l1 {[](auto&& x) mutable -> void{ + f_inout(CPP2_FORWARD(x)); + }}; + + std::move(l1)(x); + + ++x; +} + +#line 104 "pure2-last-use.cpp2" +auto issue_683(auto const& args) -> void{ + for ( auto const& n : args ) { + static_cast(n); + } + + cpp2::deferred_init n; + n.construct(0); +} + +#line 113 "pure2-last-use.cpp2" +auto issue_825() -> void{ + static_cast([](std::unique_ptr b) mutable -> auto { return f_copy(std::move(b)); }); + static_cast([](std::unique_ptr&& c) mutable -> auto { return f_copy(std::move(c)); }); + static_cast([](auto&& d) mutable -> auto { return f_copy(CPP2_FORWARD(d)); }(cpp2_new(0))); +} + +#line 119 "pure2-last-use.cpp2" +auto issue_832() -> void{ + auto i {0}; + while( i ) {} +} + +#line 124 "pure2-last-use.cpp2" +[[nodiscard]] auto make_copy(auto x) -> auto { return std::move(x); } + +#line 126 "pure2-last-use.cpp2" +auto issue_847_4(std::vector v) -> void{ + for ( + auto x : CPP2_UFCS(make_copy)(std::move(v)) ) { + f_copy(std::move(x)); + } +} +#line 132 "pure2-last-use.cpp2" +auto issue_847_0(std::vector> v) -> void{ + // TODO Use 'std::views::as_rvalue` + for ( + [[maybe_unused]] auto const& unnamed_param_1 : CPP2_UFCS(make_copy)(std::move(v)) ) { + } +} +#line 138 "pure2-last-use.cpp2" +auto issue_847_1(std::vector>&& v) -> void{ + for ( + auto&& x : CPP2_UFCS(make_copy)(std::move(v)) ) { + f_copy(std::move(x)); + } +} +#line 144 "pure2-last-use.cpp2" +auto issue_847_5(auto&& v) -> void{ + for ( + auto&& x : CPP2_UFCS(make_copy)(CPP2_FORWARD(v)) ) { + f_copy(CPP2_FORWARD(x)); + } +} +#line 150 "pure2-last-use.cpp2" +auto issue_847_2(auto&& v) -> void{ + for ( + [[maybe_unused]] auto&& unnamed_param_1 : CPP2_UFCS(make_copy)(CPP2_FORWARD(v)) ) { + } +} +#line 155 "pure2-last-use.cpp2" +auto issue_847_3(int x) -> void{for ( [[maybe_unused]] auto const& unnamed_param_1 : { std::move(x) } ) {}} + +#line 157 "pure2-last-use.cpp2" +auto issue_850() -> void{ + std::vector v {1, 2, 3, 4, 5}; + + // Definite last use of v => move-capture v into f's closure + auto f {[_0 = std::move(v)]() mutable -> auto&&{return _0; }}; + + // Now we can access the vector captured inside f()... + CPP2_UFCS(push_back)(f(), 6); + for ( auto const& e : std::move(f)() ) std::cout << e; // prints 123456 +} + +#line 171 "pure2-last-use.cpp2" + issue_857::issue_857(issue_857&& that) noexcept + : a{ std::move(that).a } + , b{ std::move(that).b }{} +#line 171 "pure2-last-use.cpp2" + auto issue_857::operator=(issue_857&& that) noexcept -> issue_857& { + a = std::move(that).a; + b = std::move(that).b; + return *this; } +#line 172 "pure2-last-use.cpp2" + issue_857::~issue_857() noexcept{ + f_inout(a); + f_copy(std::move((*this)).b); + } +#line 176 "pure2-last-use.cpp2" + auto issue_857::f() && -> void { f_copy(std::move((*this))); } +#line 177 "pure2-last-use.cpp2" + auto issue_857::f(issue_857&& that) && -> void { f_copy(std::move((*this)), std::move(that)); } +#line 178 "pure2-last-use.cpp2" + auto issue_857::g() && -> void { f_copy(std::move((*this)).a); } +#line 179 "pure2-last-use.cpp2" + auto issue_857::g(issue_857&& that) && -> void { f_copy(std::move((*this)).a, std::move(that).a); } +#line 180 "pure2-last-use.cpp2" + auto issue_857::h() & -> void { f_inout(a); } +#line 181 "pure2-last-use.cpp2" + auto issue_857::i() && -> void { f_copy(std::move(*this).a); } +#line 182 "pure2-last-use.cpp2" + auto issue_857::j() && -> void { f_copy(std::move(*this).a); } +#line 183 "pure2-last-use.cpp2" + auto issue_857::k() && -> void{ + f_inout(a); + f_copy(std::move(*this).b); + } +#line 187 "pure2-last-use.cpp2" + auto issue_857::l() && -> void { std::move(*this).k(); } +#line 188 "pure2-last-use.cpp2" + auto issue_857::m() && -> void { CPP2_UFCS(k)(std::move((*this))); } +#line 189 "pure2-last-use.cpp2" + auto issue_857::n([[maybe_unused]] auto const& unnamed_param_1) -> void{} +#line 190 "pure2-last-use.cpp2" + auto issue_857::n() && -> void{} +#line 191 "pure2-last-use.cpp2" + auto issue_857::o0() && -> void { std::move(*this).n(); } +#line 192 "pure2-last-use.cpp2" + auto issue_857::o1() && -> void { CPP2_UFCS(n)(std::move((*this))); } +#line 193 "pure2-last-use.cpp2" + auto issue_857::o2() && -> void { std::move(*this).n(0); } +#line 194 "pure2-last-use.cpp2" + auto issue_857::o3() && -> void { std::move(*this).n(0); } +#line 195 "pure2-last-use.cpp2" + auto issue_857::o4() && -> void { n(std::move((*this))); } +#line 196 "pure2-last-use.cpp2" + auto issue_857::p0() && -> void{ + f_inout(a); + f_copy(std::move((*this)).a); + } +#line 200 "pure2-last-use.cpp2" + auto issue_857::p1() && -> void{ + f_inout((*this).a); + f_copy(std::move(*this).a); + } +#line 204 "pure2-last-use.cpp2" + auto issue_857::p2() && -> void{ + f_inout(a); + f_copy(std::move((*this))); + } +#line 208 "pure2-last-use.cpp2" + auto issue_857::p3() && -> void{ + f_inout((*this)); + f_copy(std::move(*this).a); + } +#line 212 "pure2-last-use.cpp2" + auto issue_857::q() && -> void{ + h(); + std::move(*this).n(); + } +#line 216 "pure2-last-use.cpp2" + auto issue_857::z(issue_857&& that) && -> void{ + if (true) {f_copy(std::move((*this))); }/*f */ + else {if (true) {f_copy(std::move((*this)), std::move(that)); }/*f */ + else {if (true) {f_copy(std::move((*this)).a); }/*g */ + else {if (true) {f_copy(std::move((*this)).a, std::move(that).a); }/*g */ + else {if (true) {f_copy(std::move(*this).a); }/*i */ + else {if (true) {f_copy(std::move(*this).a); }/*j */ + else {if (true) {/*k */ + f_inout(a);/*k */ + f_copy(std::move(*this).b);/*k */ + }/*k */ + else {if (true) {std::move(*this).k(); }/*l */ + else {if (true) {CPP2_UFCS(k)(std::move((*this))); }/*m */ + else {if (true) {std::move(*this).n(); }/*o1*/ + else {if (true) {CPP2_UFCS(n)(std::move((*this))); }/*o2*/ + else {if (true) {std::move(*this).n(0); }/*o3*/ + else {if (true) {n(std::move((*this))); }/*o4*/ + else {if (true) {/*p0*/ + f_inout(a);/*p0*/ + f_copy(std::move((*this)).a);/*p0*/ + }/*p0*/ + else {if (true) {/*p1*/ + f_inout((*this).a);/*p1*/ + f_copy(std::move(*this).a);/*p1*/ + }/*p1*/ + else {if (true) {/*p2*/ + f_inout(a);/*p2*/ + f_copy(std::move((*this)));/*p2*/ + }/*p2*/ + else {if (true) {/*p3*/ + f_inout((*this));/*p3*/ + f_copy(std::move(*this).a);/*p3*/ + }/*p3*/ + else {/*q */ + h();/*q */ + std::move(*this).n();/*q */ + }}}}}}}}}}}}}}}}}/*q */ + } + +#line 260 "pure2-last-use.cpp2" +int gi {0}; + +#line 263 "pure2-last-use.cpp2" + auto issue_857_3::f() && -> void { f_inout(std::move(*this).i); }// OK: The implicit `this` is moved, not `i`. + +#line 266 "pure2-last-use.cpp2" + auto issue_857_6::f() && -> void { f_inout(std::move(*this).i); }// OK: The implicit `this` is moved, not `i`. + +#line 272 "pure2-last-use.cpp2" + template move_only_function::move_only_function(){} +#line 273 "pure2-last-use.cpp2" + template move_only_function::move_only_function([[maybe_unused]] move_only_function&& that) noexcept{} +#line 273 "pure2-last-use.cpp2" + template auto move_only_function::operator=([[maybe_unused]] move_only_function&& that) noexcept -> move_only_function& { + return *this; } +#line 274 "pure2-last-use.cpp2" + template [[nodiscard]] auto move_only_function::operator()([[maybe_unused]] auto const& ...unnamed_param_2) && -> int { return 0; } + +#line 282 "pure2-last-use.cpp2" + auto issue_857_4::h0() && -> void { static_cast(std::move(*this).mf()); } +#line 283 "pure2-last-use.cpp2" + auto issue_857_4::h1() && -> void { static_cast(CPP2_UFCS(mf)(std::move((*this)))); } +#line 284 "pure2-last-use.cpp2" + auto issue_857_4::h2(issue_857_4 const& that) && -> void { static_cast(CPP2_UFCS_MOVE(mf)(that)); } +#line 285 "pure2-last-use.cpp2" + auto issue_857_4::h3(issue_857_4 const& that) && -> void { static_cast(that.f); } +#line 286 "pure2-last-use.cpp2" + auto issue_857_4::h4(cpp2::in x) && -> void { static_cast(CPP2_UFCS_MOVE(mg)(x)); } +#line 287 "pure2-last-use.cpp2" + auto issue_857_4::h5(cpp2::in x) && -> void { static_cast(f() + CPP2_UFCS_MOVE(g)(x)); }// FIXME #313. +#line 288 "pure2-last-use.cpp2" + auto issue_857_4::h6(cpp2::in x) && -> void { static_cast(CPP2_UFCS(g)(x) + std::move(*this).f()); }// FIXME #313. +#line 289 "pure2-last-use.cpp2" + auto issue_857_4::h7(cpp2::in x) && -> void { static_cast(CPP2_UFCS(f)((*this)) + CPP2_UFCS_MOVE(g)(x)); }// FIXME #313. +#line 290 "pure2-last-use.cpp2" + auto issue_857_4::h8(cpp2::in x) && -> void { static_cast(CPP2_UFCS(g)(x) + CPP2_UFCS(f)(std::move((*this)))); }// FIXME #313. +#line 291 "pure2-last-use.cpp2" + auto issue_857_4::i0(cpp2::in x) && -> void{ + static_cast(f()); + static_cast(CPP2_UFCS_MOVE(mg)(x)); + } +#line 295 "pure2-last-use.cpp2" + auto issue_857_4::i1(cpp2::in x) && -> void{ + static_cast(CPP2_UFCS(g)(x)); + static_cast(std::move(*this).mf()); + } +#line 299 "pure2-last-use.cpp2" + auto issue_857_4::i2(cpp2::in x) && -> void{ + static_cast(CPP2_UFCS(f)((*this))); + static_cast(CPP2_UFCS_MOVE(mg)(x)); + } +#line 303 "pure2-last-use.cpp2" + auto issue_857_4::i3(cpp2::in x) && -> void{ + static_cast(CPP2_UFCS(g)(x)); + static_cast(CPP2_UFCS(mf)(std::move((*this)))); + } +#line 307 "pure2-last-use.cpp2" + auto issue_857_4::z(issue_857_4 const& that) && -> void{ + auto x {0}; + if (true) {static_cast(std::move(*this).mf()); }/*h0*/ + else {if (true) {static_cast(CPP2_UFCS(mf)(std::move((*this)))); }/*h1*/ + else {if (true) {static_cast(CPP2_UFCS_MOVE(mf)(that)); }/*h2*/ + else {if (true) {static_cast(that.f); }/*h3*/ + else {if (true) {static_cast(CPP2_UFCS_MOVE(mg)(std::move(x))); }/*h4*/ + else {if (true) {static_cast(f() + CPP2_UFCS_MOVE(g)(std::move(x))); }/*h5*/ + else {if (true) {static_cast(CPP2_UFCS(g)(std::move(x)) + std::move(*this).f()); }/*h6*/ + else {if (true) {static_cast(CPP2_UFCS(f)((*this)) + CPP2_UFCS_MOVE(g)(std::move(x))); }/*h7*/ + else {if (true) {static_cast(CPP2_UFCS(g)(std::move(x)) + CPP2_UFCS(f)(std::move((*this)))); }/*h8*/ + else {if (true) {/*i0*/ + static_cast(f());/*i0*/ + static_cast(CPP2_UFCS_MOVE(mg)(std::move(x)));/*i0*/ + }/*i0*/ + else {if (true) {/*i1*/ + static_cast(CPP2_UFCS(g)(std::move(x)));/*i1*/ + static_cast(std::move(*this).mf());/*i1*/ + }/*i1*/ + else {if (true) {/*i2*/ + static_cast(CPP2_UFCS(f)((*this)));/*i2*/ + static_cast(CPP2_UFCS_MOVE(mg)(std::move(x)));/*i2*/ + }/*i2*/ + else {/*i3*/ + static_cast(CPP2_UFCS(g)(std::move(x)));/*i3*/ + static_cast(CPP2_UFCS(mf)(std::move((*this))));/*i3*/ + }}}}}}}}}}}}/*i3*/ + } + +#line 338 "pure2-last-use.cpp2" + auto issue_857_5::f() && -> void { f_copy(std::move(*this).a); } + +#line 345 "pure2-last-use.cpp2" + auto issue_857_7::F() && -> void { f_inout(std::move(*this).A); } + +#line 352 "pure2-last-use.cpp2" + auto issue_857_8::d() && -> void{} + +#line 361 "pure2-last-use.cpp2" + auto issue_857_9::f2() && -> void { f_inout(c); }// OK: Happens to work, like non-'move' 'this' parameters. + +#line 365 "pure2-last-use.cpp2" + auto issue_857_9::g0() && -> void { f_copy(std::move((*this)).a); } +#line 366 "pure2-last-use.cpp2" + auto issue_857_9::g1() && -> void { static_cast(CPP2_UFCS(b)(std::move((*this)))); } +#line 367 "pure2-last-use.cpp2" + auto issue_857_9::g2() && -> void { f_inout(std::move((*this)).c); } +#line 368 "pure2-last-use.cpp2" + auto issue_857_9::g3() && -> void { CPP2_UFCS(d)(std::move((*this))); } + +#line 372 "pure2-last-use.cpp2" + issue_869_0::issue_869_0([[maybe_unused]] std::unique_ptr&& unnamed_param_2){} +#line 372 "pure2-last-use.cpp2" + auto issue_869_0::operator=([[maybe_unused]] std::unique_ptr&& unnamed_param_2) -> issue_869_0& { + return *this; } + + + issue_869_0::issue_869_0([[maybe_unused]] issue_869_0 const& that){} + +auto issue_869_0::operator=([[maybe_unused]] issue_869_0 const& that) -> issue_869_0& { + return *this;} +issue_869_0::issue_869_0([[maybe_unused]] issue_869_0&& that) noexcept{} +auto issue_869_0::operator=([[maybe_unused]] issue_869_0&& that) noexcept -> issue_869_0& { + return *this;} +issue_869_0::issue_869_0(){} +[[nodiscard]] auto issue_869_1::is_i() const& -> bool { return _discriminator == 0; } +[[nodiscard]] auto issue_869_1::i() const& -> issue_869_0 const& { + if (cpp2::Default.has_handler() && !(is_i()) ) { cpp2::Default.report_violation(""); }return *cpp2::assert_not_null(reinterpret_cast(&_storage)); } +[[nodiscard]] auto issue_869_1::i() & -> issue_869_0& { + if (cpp2::Default.has_handler() && !(is_i()) ) { cpp2::Default.report_violation(""); }return *cpp2::assert_not_null(reinterpret_cast(&_storage)); } +auto issue_869_1::set_i(cpp2::in _value) & -> void{if (!(is_i())) {_destroy();std::construct_at(reinterpret_cast(&_storage), _value);}else {*cpp2::assert_not_null(reinterpret_cast(&_storage)) = _value;}_discriminator = 0;} +auto issue_869_1::set_i(auto&& ..._args) & -> void{if (!(is_i())) {_destroy();std::construct_at(reinterpret_cast(&_storage), CPP2_FORWARD(_args)...);}else {*cpp2::assert_not_null(reinterpret_cast(&_storage)) = issue_869_0{CPP2_FORWARD(_args)...};}_discriminator = 0;} +auto issue_869_1::_destroy() & -> void{ + if (_discriminator == 0) {std::destroy_at(reinterpret_cast(&_storage));} + _discriminator = -1; + } + + issue_869_1::~issue_869_1() noexcept{_destroy();static_cast(std::move((*this)));} +issue_869_1::issue_869_1(){} +issue_869_1::issue_869_1(issue_869_1 const& that) + : _storage{ } + , _discriminator{ -1 }{ + if (CPP2_UFCS(is_i)(that)) {set_i(CPP2_UFCS(i)(that));} + } + + issue_869_1::issue_869_1(issue_869_1&& that) noexcept + : _storage{ } + , _discriminator{ -1 }{ + if (CPP2_UFCS(is_i)(std::move(that))) {set_i(CPP2_UFCS(i)(std::move(that)));} + } + + auto issue_869_1::operator=(issue_869_1 const& that) -> issue_869_1& { + if (CPP2_UFCS(is_i)(that)) {set_i(CPP2_UFCS(i)(that));} + return *this; + } + + auto issue_869_1::operator=(issue_869_1&& that) noexcept -> issue_869_1& { + if (CPP2_UFCS(is_i)(std::move(that))) {set_i(CPP2_UFCS(i)(std::move(that)));} + return *this; + } +#line 377 "pure2-last-use.cpp2" +[[nodiscard]] auto issue_869_2() -> issue_869_2_ret{ + issue_869_1 res {}; CPP2_UFCS(set_i)(res, cpp2_new(0)); +#line 378 "pure2-last-use.cpp2" +return res; } +#line 379 "pure2-last-use.cpp2" +auto issue_884() -> void{ + static_cast([]() mutable -> void{ + auto x {cpp2_new(0)}; + if (true) {} + { + {f_inout(x); } + f_copy(std::move(x)); + } + }); + + static_cast([]() mutable -> void{ + auto x {cpp2_new(0)}; + if (true) { + f_copy(std::move(x)); + } + else { + {f_inout(x); } + f_copy(std::move(x)); + } + }); + + static_cast([]() mutable -> void{ + auto x {cpp2_new(0)}; + if (true) { + f_inout(x); + } + else { + {f_inout(x); } + f_inout(x); + } + f_copy(std::move(x)); + }); + + static_cast([]() mutable -> void{ + auto x {cpp2_new(0)}; + f_copy(std::move(x)); + if (true) { + static_cast(0); + } + else { + {static_cast(0); } + static_cast(0); + } + static_cast(0); + }); + + static_cast([]() mutable -> void{ + auto x {cpp2_new(0)}; + f_inout(x); + if (true) { + f_copy(std::move(x)); + } + else { + {static_cast(0); } + static_cast(0); + } + static_cast(0); + }); + + static_cast([]() mutable -> void{ + auto x {cpp2_new(0)}; + f_inout(x); + if (true) { + static_cast(0); + } + else { + {f_copy(std::move(x)); } + static_cast(0); + } + static_cast(0); + }); + + static_cast([]() mutable -> void{ + auto x {cpp2_new(0)}; + f_inout(x); + if (true) { + static_cast(0); + } + else { + {static_cast(0); } + f_copy(std::move(x)); + } + static_cast(0); + }); + + static_cast([]() mutable -> void{ + auto x {cpp2_new(0)}; + f_inout(x); + if (true) { + static_cast(0); + } + else { + {static_cast(0); } + static_cast(0); + } + f_copy(std::move(x)); + }); + + static_cast([]() mutable -> void{ + auto x {cpp2_new(0)}; + f_inout(x); + if (true) { + f_copy(std::move(x)); + } + else { + {f_copy(std::move(x)); } + static_cast(0); + } + static_cast(0); + }); + + static_cast([]() mutable -> void{ + auto x {cpp2_new(0)}; + f_inout(x); + if (true) { + f_copy(std::move(x)); + } + else { + {f_inout(x); } + f_copy(std::move(x)); + } + static_cast(0); + }); + + static_cast([]() mutable -> void{ + auto x {cpp2_new(0)}; + f_inout(x); + if (true) { + f_inout(x); + } + else { + {f_inout(x); } + f_inout(x); + } + f_inout(x); + if (true) { + f_copy(std::move(x)); + } + }); + + static_cast([]() mutable -> void{ + auto x {cpp2_new(0)}; + f_inout(x); + if (true) { + f_inout(x); + } + else { + {f_inout(x); } + f_inout(x); + } + if (true) { + f_inout(x); + } + f_copy(std::move(x)); + }); + + static_cast([]() mutable -> void{ + auto x {cpp2_new(0)}; + f_inout(x); + if (true) { + f_copy(std::move(x)); + } + else { + {f_inout(x); } + f_inout(x); + if (true) { + f_copy(std::move(x)); + } + } + static_cast(0); + }); + + static_cast([]() mutable -> void{ + auto x {cpp2_new(0)}; + if (true) { + if (true) { + if (true) { + f_copy(std::move(x)); + } + } + } + else { + } + }); + + static_cast([]() mutable -> void{ + auto x {cpp2_new(0)}; + if (true) { + if (true) { + if (true) { + f_copy(std::move(x)); + } + } + } + else { + f_copy(std::move(x)); + } + }); + + static_cast([]() mutable -> void{ + auto x {cpp2_new(0)}; + if (true) { + } + else { + if (true) { + if (true) { + f_copy(std::move(x)); + } + } + } + }); + + static_cast([]() mutable -> void{ + auto x {cpp2_new(0)}; + if (true) { + f_copy(std::move(x)); + } + else { + if (true) { + if (true) { + f_copy(std::move(x)); + } + } + } + }); + + static_cast([]() mutable -> void{ + auto x {cpp2_new(0)}; + if (true) { + auto y {cpp2_new(0)}; + f_copy(std::move(x)); + f_copy(std::move(y)); + } + else { + if (true) { + if (true) { + f_inout(x); + } + f_copy(std::move(x)); + } + } + }); + + static_cast([]() mutable -> void{ + auto x {cpp2_new(0)}; + if (true) { + auto y {cpp2_new(0)}; + if (true) {} + else { + f_copy(std::move(x)); + f_copy(std::move(y)); + } + } + else { + if (true) { + if (true) { + auto y {cpp2_new(0)}; + f_copy(std::move(y)); + f_inout(x); + } + f_copy(std::move(x)); + } + } + }); + + static_cast([]() mutable -> void{ + auto x {cpp2_new(0)}; + if (true) { + auto y {cpp2_new(0)}; + if (true) {} + else { + f_copy(std::move(x)); + f_copy(std::move(y)); + } + } + else { + auto y {cpp2_new(0)}; + if (true) { + if (true) { + f_copy(std::move(x)); + } + else { + f_copy(std::move(x)); + } + f_copy(std::move(y)); + } + } + }); + + static_cast([]() mutable -> void{ + auto x {cpp2_new(0)}; + if (true) { + f_copy(std::move(x)); + } + else { + if (true) { + auto x {cpp2_new(0)}; + if (true) { + f_inout(x); + } + else { + } + f_copy(std::move(x)); + } + f_copy(std::move(x)); + } + }); + + static_cast([]() mutable -> void{ + auto x {cpp2_new(0)}; + if (true) { + if (true) { + auto x {cpp2_new(0)}; + if (true) { + f_inout(x); + } + else { + } + f_copy(std::move(x)); + } + f_copy(std::move(x)); + } + else { + f_copy(std::move(x)); + } + }); + + static_cast([]() mutable -> void{ + auto x {cpp2_new(0)}; + + if (true) { + f_inout(x); + } + + if (true) { + if (true) { + f_copy(std::move(x)); + } + } + }); + + static_cast([]() mutable -> void{ + auto x {cpp2_new(0)}; + if (true) { + if (true) { + f_inout(x); + if (true) { + } + else { + f_copy(std::move(x)); + } + } + else { + if (true) { + } + else { + f_inout(x); + } + f_copy(std::move(x)); + } + } + else { + if (true) { + if (true) { + f_inout(x); + f_copy(std::move(x)); + } + else { + } + } + else { + if (true) { + } + else { + f_inout(x); + } + if (true) { + f_inout(x); + if (true) { + f_copy(std::move(x)); + } + else { + } + } + else { + if (true) { + f_copy(std::move(x)); + } + else { + f_copy(std::move(x)); + } + } + } + } + }); +} + +#line 776 "pure2-last-use.cpp2" +auto issue_888_0(std::string r, int size) -> void{ + static_cast(CPP2_UFCS_MOVE(size)(std::move(r))); +} +#line 779 "pure2-last-use.cpp2" +auto issue_888_1([[maybe_unused]] std::string unnamed_param_1, move_only_function size) -> void{ + static_cast(CPP2_UFCS_MOVE(size)(0)); +} + +#line 783 "pure2-last-use.cpp2" +auto issue_890() -> void{ + auto x {cpp2_new(0)}; + if (cpp2::Default.has_handler() && !(*cpp2::assert_not_null(identity_copy(std::move(x))) == 0) ) { cpp2::Default.report_violation(""); } +{ +auto const& x{cpp2_new(0)}; +#line 786 "pure2-last-use.cpp2" + if (cpp2::Default.has_handler() && !(*cpp2::assert_not_null(identity(x)) == 0) ) { cpp2::Default.report_violation(""); } +} +#line 787 "pure2-last-use.cpp2" +} + +#line 789 "pure2-last-use.cpp2" +auto draw() -> void{ + auto pos {0}; + move_only_function vertex {}; + static_cast(CPP2_UFCS_MOVE(vertex)((std::move(pos)))); +} + +#line 795 "pure2-last-use.cpp2" +auto enum_0() -> void{ + cpp2::deferred_init underlying_type; + if (true) {} + underlying_type.construct(""); +} +#line 800 "pure2-last-use.cpp2" +auto enum_1() -> void{ + auto max_value {cpp2_new(0)}; + std::reference_wrapper const> min_value {max_value}; + + for ( + auto x : { 0 } ) + { + auto v {cpp2_new(identity_copy(std::move(x)))}; + if (pred(v, min_value)) { + min_value = std::ref(identity(v)); // Not using 'else' will never move 'v'. + } + if (pred(v, max_value)) { + max_value = identity_copy(std::move(v)); + } + } + + auto y {cpp2_new(false)}; + while( *cpp2::assert_not_null(identity(y)) ) { + auto v {cpp2_new(0)}; + f_copy(std::move(v)); + } + + auto z {cpp2_new(false)}; + do { + auto v {cpp2_new(0)}; + f_copy(std::move(v)); + } while ( *cpp2::assert_not_null(identity(z))); +} +#line 828 "pure2-last-use.cpp2" +auto enum_2() -> void{ + auto umax {cpp2_new(0)}; + if (pred(umax)) { + } + else {if (pred(umax)) { + } + else {if (pred_copy(std::move(umax))) { + }}} +} + +#line 839 "pure2-last-use.cpp2" + auto cpp2_union::destroy() & -> void{} +#line 840 "pure2-last-use.cpp2" + cpp2_union::~cpp2_union() noexcept{ + destroy(); + static_cast(std::move((*this))); + } + +#line 851 "pure2-last-use.cpp2" +[[nodiscard]] auto no_pessimizing_move() -> no_pessimizing_move_ret{ + std::unique_ptr ret {}; +#line 852 "pure2-last-use.cpp2" +return ret; } +#line 853 "pure2-last-use.cpp2" +auto deferred_non_copyable_0() -> void{ + cpp2::deferred_init> p; + p.construct(); + f_copy(std::move(p.value())); +} + +#line 859 "pure2-last-use.cpp2" +[[nodiscard]] auto deferred_non_copyable_1() -> auto{ + cpp2::deferred_init> p; + p.construct(); + return std::move(p.value()); +} + +#line 865 "pure2-last-use.cpp2" +[[nodiscard]] auto deferred_non_copyable_2() -> deferred_non_copyable_2_ret{ + cpp2::deferred_init> p; +#line 866 "pure2-last-use.cpp2" + p.construct(); +return std::move(p.value()); } + +#line 869 "pure2-last-use.cpp2" +auto loops() -> void{ + static_cast([]() mutable -> void{ + auto x {cpp2_new(0)}; + for ( + [[maybe_unused]] auto const& unnamed_param_1 : { 0 } ) + f_inout(x); + }); + + static_cast([]() mutable -> void{ + auto x {cpp2_new(0)}; + for ( + + [[maybe_unused]] auto const& unnamed_param_1 : { 0 } ) { do + {} while (false); f_inout(x); } + }); + + static_cast([]() mutable -> void{ + auto x {cpp2_new(0)}; + for ( + [[maybe_unused]] auto const& unnamed_param_1 : { 0 } ) + if (cpp2::Default.has_handler() && !(CPP2_UFCS(get)(x)) ) { cpp2::Default.report_violation(""); } + }); + + static_cast([]() mutable -> void{ + auto x {cpp2_new(0)}; + if (true) { + f_copy(std::move(x)); + } + else { + while( true ) { + f_inout(x); + } + } + }); +} + +namespace captures { + +#line 909 "pure2-last-use.cpp2" +auto f() -> void{ + auto x {cpp2_new(0)}; + f_copy(std::move(x)); + auto id {[](auto const& x) mutable -> auto&& { return x; }}; + auto y {cpp2_new(0)}; + if (cpp2::Default.has_handler() && !(&std::move(id)(y) == &y) ) { cpp2::Default.report_violation(""); } +} + +#line 921 "pure2-last-use.cpp2" + auto t::operator()() && -> void{ + f_copy(std::move(*this).x); + static_cast([&]() mutable -> void{ + // Should this move? + // I.e., don't skip non-captured names, just rely on skipping hiding names. + // An odr-use still requires capturing at Cpp1-time, and capturing would move. + static_assert(std::is_same_v>); + using captures::x; + static_cast(identity(x)); + }); + } + +#line 934 "pure2-last-use.cpp2" +auto g() -> void{ + static_cast([]() mutable -> void{ + auto x {cpp2_new(0)}; + f_copy(std::move(x)); + static_cast([_0 = std::array auto { return identity(x); }(0)>()]() mutable -> auto { return _0; });// Fails on Clang 12 (lambda in unevaluated context). + }); + + static_cast([]() mutable -> void{ + auto x {cpp2_new(0)}; + f_inout(x); + static_cast([_0 = ([_0 = std::move(x)]() mutable -> auto { return *cpp2::assert_not_null(_0); })]() mutable -> int { return _0(); }); + }); +} + +} + +#line 950 "pure2-last-use.cpp2" +auto loops_and_captures() -> void{ + static_cast([]() mutable -> void{ + auto x {cpp2_new(0)}; + f_copy(std::move(x)); + for ( + [[maybe_unused]] auto const& unnamed_param_1 : { [](auto const& x) mutable -> auto { return x; } } ) + {} + }); + + static_cast([]() mutable -> void{ + auto x {cpp2_new(0)}; + f_copy(std::move(x)); + for ( + +#line 966 "pure2-last-use.cpp2" + [[maybe_unused]] auto const& unnamed_param_1 : { []() mutable -> auto{using captures::x;return x; } } ) + {} + }); + + static_cast([]() mutable -> void{ + auto x {cpp2_new(0)}; + for ( + [[maybe_unused]] auto const& unnamed_param_1 : { [_0 = std::move(x)]() mutable -> auto { return *cpp2::assert_not_null(_0); } } ) + {} + }); +} + +#line 980 "pure2-last-use.cpp2" + auto types::f() && -> void { static_cast([&, _1 = std::move(*this).x]() mutable -> auto { return *cpp2::assert_not_null(_1); }); } +#line 981 "pure2-last-use.cpp2" + auto types::g() && -> void{ + for ( + [[maybe_unused]] auto const& unnamed_param_1 : { [&, _1 = std::move(*this).x]() mutable -> auto { return *cpp2::assert_not_null(_1); } } ) + {} + } + +#line 988 "pure2-last-use.cpp2" +auto skip_hidden_names() -> void{ + static_cast([]() mutable -> void{ + auto x {cpp2_new(0)}; + f_copy(std::move(x)); +{ +auto x{cpp2_new(0)}; + +#line 993 "pure2-last-use.cpp2" + f_copy(std::move(x)); +} +#line 994 "pure2-last-use.cpp2" + }); + + static_cast([]() mutable -> void{ + auto x {cpp2_new(0)}; + static_cast([_0 = std::move(x)]() mutable -> void{ + static_cast(_0); + auto x {cpp2_new(1)}; + static_cast([_0 = std::move(x)]() mutable -> void{ + static_cast(_0); + }); + }); + }); + + static_cast([]() mutable -> void{ + auto x {cpp2_new(0)}; + f_copy(std::move(x)); + for ( + auto x : { 0 } ) + static_cast(identity_copy(std::move(x))); +{ +auto x{cpp2_new(0)}; + +#line 1014 "pure2-last-use.cpp2" + f_copy(std::move(x)); +} +#line 1015 "pure2-last-use.cpp2" + }); + + static_cast([]() mutable -> void{ + auto x {cpp2_new(0)}; + f_inout(x); + { + f_copy(std::move(x)); + using captures::x; + f_inout(x); + } + }); + + static_cast([]() mutable -> void{ + auto x {cpp2_new(0)}; + f_copy(std::move(x)); + static_cast([]() mutable -> void{ + static_assert(std::is_same_v>); + using captures::x; + f_inout(x); + }); + }); +} + +#line 1038 "pure2-last-use.cpp2" +auto main(int const argc_, char** argv_) -> int{ + auto const args = cpp2::make_args(argc_, argv_); +#line 1039 "pure2-last-use.cpp2" + issue_683(args); + issue_847_2(std::vector>()); + issue_847_5(args); + issue_850(); + enum_0(); +} + diff --git a/regression-tests/test-results/pure2-last-use.cpp2.output b/regression-tests/test-results/pure2-last-use.cpp2.output new file mode 100644 index 0000000000..fd9a45cd0e --- /dev/null +++ b/regression-tests/test-results/pure2-last-use.cpp2.output @@ -0,0 +1,2 @@ +pure2-last-use.cpp2... ok (all Cpp2, passes safety checks) + diff --git a/regression-tests/test-results/pure2-look-up-parameter-across-unnamed-function.cpp b/regression-tests/test-results/pure2-look-up-parameter-across-unnamed-function.cpp index 9d5f0ac24c..ffcb47f821 100644 --- a/regression-tests/test-results/pure2-look-up-parameter-across-unnamed-function.cpp +++ b/regression-tests/test-results/pure2-look-up-parameter-across-unnamed-function.cpp @@ -36,7 +36,7 @@ using g_ret = int; auto pred {[](auto const& e) mutable -> auto { return e == 1; }}; ri = 42; std::move(pred)(ri); - return std::move(ri); // "return;" is implicit" + return ri; // "return;" is implicit" } #line 9 "pure2-look-up-parameter-across-unnamed-function.cpp2" diff --git a/regression-tests/test-results/pure2-print.cpp b/regression-tests/test-results/pure2-print.cpp index d6c36d4c5e..0fe8feae7c 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::cmp_less(*cpp2::assert_not_null(p),0)) { ret = -*cpp2::assert_not_null(std::move(p)); } - ret += strlen(s) - 10 + CPP2_UFCS(strlen)(std::move(s)) * (16 / (3 & 2)) % 3; + ret += strlen(s) - 10 + CPP2_UFCS(strlen)(s) * (16 / (3 & 2)) % 3; map m {}; CPP2_ASSERT_IN_BOUNDS_LITERAL(m, 0) = cpp2::as_("har"); diff --git a/regression-tests/test-results/pure2-raw-string-literal-and-interpolation.cpp b/regression-tests/test-results/pure2-raw-string-literal-and-interpolation.cpp index 0d2dcc2a7e..0a7bc091a6 100644 --- a/regression-tests/test-results/pure2-raw-string-literal-and-interpolation.cpp +++ b/regression-tests/test-results/pure2-raw-string-literal-and-interpolation.cpp @@ -50,6 +50,6 @@ calculations like m["one"] + m["two"] = )test" + cpp2::to_string(CPP2_ASSERT_IN_ std::cout << std::move(raw_str_multi) << std::endl; std::cout << std::move(raw_str_inter) << std::endl; std::cout << std::move(raw_str_inter_multi) << std::endl; - std::cout << (cpp2::to_string(CPP2_ASSERT_IN_BOUNDS(m, "one")) + R"(.)" + cpp2::to_string(CPP2_ASSERT_IN_BOUNDS(m, "two")) + R"(.)" + cpp2::to_string(CPP2_ASSERT_IN_BOUNDS(std::move(m), "three")) + R"(.)" + cpp2::to_string(std::move(i))) << std::endl; + std::cout << (cpp2::to_string(CPP2_ASSERT_IN_BOUNDS(m, "one")) + R"(.)" + cpp2::to_string(CPP2_ASSERT_IN_BOUNDS(m, "two")) + R"(.)" + cpp2::to_string(CPP2_ASSERT_IN_BOUNDS(m, "three")) + R"(.)" + cpp2::to_string(std::move(i))) << std::endl; } diff --git a/regression-tests/test-results/pure2-return-tuple-operator.cpp b/regression-tests/test-results/pure2-return-tuple-operator.cpp index d2079519f1..01c1fe6f5c 100644 --- a/regression-tests/test-results/pure2-return-tuple-operator.cpp +++ b/regression-tests/test-results/pure2-return-tuple-operator.cpp @@ -84,13 +84,13 @@ auto main() -> int{ A a {}; auto t1 {a()}; - std::cout << t1.x << " , " << std::move(t1).y << "\n"; + std::cout << t1.x << " , " << t1.y << "\n"; auto t2 {*cpp2::assert_not_null(a)}; - std::cout << t2.x << " , " << std::move(t2).y << "\n"; + std::cout << t2.x << " , " << t2.y << "\n"; auto t3 {CPP2_ASSERT_IN_BOUNDS_LITERAL(std::move(a), 0)}; - std::cout << t3.x << " , " << std::move(t3).y << "\n"; + std::cout << t3.x << " , " << t3.y << "\n"; } diff --git a/regression-tests/test-results/pure2-statement-scope-parameters.cpp b/regression-tests/test-results/pure2-statement-scope-parameters.cpp index 2ffed45480..7594c37451 100644 --- a/regression-tests/test-results/pure2-statement-scope-parameters.cpp +++ b/regression-tests/test-results/pure2-statement-scope-parameters.cpp @@ -30,7 +30,7 @@ auto const& i{local_int}; // 'in' (read-only) statement scope variable #line 6 "pure2-statement-scope-parameters.cpp2" - for ( auto const& arg : args ) { + for ( [[maybe_unused]] auto const& unnamed_param_1 : args ) { std::cout << i << "\n"; // prints 42 } } diff --git a/regression-tests/test-results/pure2-types-basics.cpp b/regression-tests/test-results/pure2-types-basics.cpp index d38a66b4aa..c94d650ca7 100644 --- a/regression-tests/test-results/pure2-types-basics.cpp +++ b/regression-tests/test-results/pure2-types-basics.cpp @@ -157,7 +157,7 @@ namespace N { #line 38 "pure2-types-basics.cpp2" auto myclass::print() && -> void{ - std::cout << (" (move print) data: " + cpp2::to_string(data) + ", more: " + cpp2::to_string(more) + "\n"); + std::cout << (" (move print) data: " + cpp2::to_string(data) + ", more: " + cpp2::to_string(std::move(*this).more) + "\n"); } #line 42 "pure2-types-basics.cpp2" diff --git a/regression-tests/test-results/pure2-types-inheritance.cpp b/regression-tests/test-results/pure2-types-inheritance.cpp index ca3139ec77..e5bd13ca6d 100644 --- a/regression-tests/test-results/pure2-types-inheritance.cpp +++ b/regression-tests/test-results/pure2-types-inheritance.cpp @@ -128,7 +128,7 @@ namespace N { #line 34 "pure2-types-inheritance.cpp2" Cyborg::~Cyborg() noexcept { - std::cout << ("Tired but satisfied after another successful day, " + cpp2::to_string(name) + " checks out and goes home to their family\n"); } + std::cout << ("Tired but satisfied after another successful day, " + cpp2::to_string(std::move(*this).name) + " checks out and goes home to their family\n"); } #line 38 "pure2-types-inheritance.cpp2" auto make_speak(cpp2::in h) -> void{ diff --git a/regression-tests/test-results/pure2-union.cpp b/regression-tests/test-results/pure2-union.cpp index 64d653993a..031b3390c7 100644 --- a/regression-tests/test-results/pure2-union.cpp +++ b/regression-tests/test-results/pure2-union.cpp @@ -88,21 +88,21 @@ auto main() -> int; [[nodiscard]] auto name_or_number::name() & -> std::string& { if (cpp2::Default.has_handler() && !(is_name()) ) { cpp2::Default.report_violation(""); }return *cpp2::assert_not_null(reinterpret_cast(&_storage)); } auto name_or_number::set_name(cpp2::in _value) & -> void{if (!(is_name())) {_destroy();std::construct_at(reinterpret_cast(&_storage), _value);}else {*cpp2::assert_not_null(reinterpret_cast(&_storage)) = _value;}_discriminator = 0;} -auto name_or_number::set_name(auto&& ..._args) & -> void{if (!(is_name())) {_destroy();std::construct_at(reinterpret_cast(&_storage), _args...);}else {*cpp2::assert_not_null(reinterpret_cast(&_storage)) = std::string{_args...};}_discriminator = 0;} +auto name_or_number::set_name(auto&& ..._args) & -> void{if (!(is_name())) {_destroy();std::construct_at(reinterpret_cast(&_storage), CPP2_FORWARD(_args)...);}else {*cpp2::assert_not_null(reinterpret_cast(&_storage)) = std::string{CPP2_FORWARD(_args)...};}_discriminator = 0;} [[nodiscard]] auto name_or_number::is_num() const& -> bool { return _discriminator == 1; } [[nodiscard]] auto name_or_number::num() const& -> cpp2::i32 const& { if (cpp2::Default.has_handler() && !(is_num()) ) { cpp2::Default.report_violation(""); }return *cpp2::assert_not_null(reinterpret_cast(&_storage)); } [[nodiscard]] auto name_or_number::num() & -> cpp2::i32& { if (cpp2::Default.has_handler() && !(is_num()) ) { cpp2::Default.report_violation(""); }return *cpp2::assert_not_null(reinterpret_cast(&_storage)); } auto name_or_number::set_num(cpp2::in _value) & -> void{if (!(is_num())) {_destroy();std::construct_at(reinterpret_cast(&_storage), _value);}else {*cpp2::assert_not_null(reinterpret_cast(&_storage)) = _value;}_discriminator = 1;} -auto name_or_number::set_num(auto&& ..._args) & -> void{if (!(is_num())) {_destroy();std::construct_at(reinterpret_cast(&_storage), _args...);}else {*cpp2::assert_not_null(reinterpret_cast(&_storage)) = cpp2::i32{_args...};}_discriminator = 1;} +auto name_or_number::set_num(auto&& ..._args) & -> void{if (!(is_num())) {_destroy();std::construct_at(reinterpret_cast(&_storage), CPP2_FORWARD(_args)...);}else {*cpp2::assert_not_null(reinterpret_cast(&_storage)) = cpp2::i32{CPP2_FORWARD(_args)...};}_discriminator = 1;} auto name_or_number::_destroy() & -> void{ if (_discriminator == 0) {std::destroy_at(reinterpret_cast(&_storage));} if (_discriminator == 1) {std::destroy_at(reinterpret_cast(&_storage));} _discriminator = -1; } -name_or_number::~name_or_number() noexcept{_destroy();} +name_or_number::~name_or_number() noexcept{_destroy();static_cast(std::move((*this)));} name_or_number::name_or_number(){} name_or_number::name_or_number(name_or_number const& that) : _storage{ } @@ -144,21 +144,21 @@ template [[nodiscard]] auto name_or_other::name() const& -> std: template [[nodiscard]] auto name_or_other::name() & -> std::string& { if (cpp2::Default.has_handler() && !(is_name()) ) { cpp2::Default.report_violation(""); }return *cpp2::assert_not_null(reinterpret_cast(&_storage)); } template auto name_or_other::set_name(cpp2::in _value) & -> void{if (!(is_name())) {_destroy();std::construct_at(reinterpret_cast(&_storage), _value);}else {*cpp2::assert_not_null(reinterpret_cast(&_storage)) = _value;}_discriminator = 0;} -template auto name_or_other::set_name(auto&& ..._args) & -> void{if (!(is_name())) {_destroy();std::construct_at(reinterpret_cast(&_storage), _args...);}else {*cpp2::assert_not_null(reinterpret_cast(&_storage)) = std::string{_args...};}_discriminator = 0;} +template auto name_or_other::set_name(auto&& ..._args) & -> void{if (!(is_name())) {_destroy();std::construct_at(reinterpret_cast(&_storage), CPP2_FORWARD(_args)...);}else {*cpp2::assert_not_null(reinterpret_cast(&_storage)) = std::string{CPP2_FORWARD(_args)...};}_discriminator = 0;} template [[nodiscard]] auto name_or_other::is_other() const& -> bool { return _discriminator == 1; } template [[nodiscard]] auto name_or_other::other() const& -> T const& { if (cpp2::Default.has_handler() && !(is_other()) ) { cpp2::Default.report_violation(""); }return *cpp2::assert_not_null(reinterpret_cast(&_storage)); } template [[nodiscard]] auto name_or_other::other() & -> T& { if (cpp2::Default.has_handler() && !(is_other()) ) { cpp2::Default.report_violation(""); }return *cpp2::assert_not_null(reinterpret_cast(&_storage)); } template auto name_or_other::set_other(cpp2::in _value) & -> void{if (!(is_other())) {_destroy();std::construct_at(reinterpret_cast(&_storage), _value);}else {*cpp2::assert_not_null(reinterpret_cast(&_storage)) = _value;}_discriminator = 1;} -template auto name_or_other::set_other(auto&& ..._args) & -> void{if (!(is_other())) {_destroy();std::construct_at(reinterpret_cast(&_storage), _args...);}else {*cpp2::assert_not_null(reinterpret_cast(&_storage)) = T{_args...};}_discriminator = 1;} +template auto name_or_other::set_other(auto&& ..._args) & -> void{if (!(is_other())) {_destroy();std::construct_at(reinterpret_cast(&_storage), CPP2_FORWARD(_args)...);}else {*cpp2::assert_not_null(reinterpret_cast(&_storage)) = T{CPP2_FORWARD(_args)...};}_discriminator = 1;} template auto name_or_other::_destroy() & -> void{ if (_discriminator == 0) {std::destroy_at(reinterpret_cast(&_storage));} if (_discriminator == 1) {std::destroy_at(reinterpret_cast(&_storage));} _discriminator = -1; } - template name_or_other::~name_or_other() noexcept{_destroy();} + template name_or_other::~name_or_other() noexcept{_destroy();static_cast(std::move((*this)));} template name_or_other::name_or_other(){} template name_or_other::name_or_other(name_or_other const& that) : _storage{ } diff --git a/source/common.h b/source/common.h index dd634379a5..07c3b496ac 100644 --- a/source/common.h +++ b/source/common.h @@ -14,7 +14,7 @@ // We want cppfront to build cleanly at very high warning levels, with warnings // as errors -- so disable a handful that fire incorrectly due to compiler bugs #ifdef _MSC_VER - #pragma warning(disable: 4456) + #pragma warning(disable: 4456 4706) #endif #if defined(__GNUC__) && __GNUC__ >= 13 && !defined(__clang_major__) #pragma GCC diagnostic ignored "-Wdangling-reference" @@ -93,6 +93,7 @@ struct source_line using lineno_t = int32_t; using colno_t = int32_t; // not int16_t... encountered >80,000 char line during testing +using index_t = int32_t; struct source_position { @@ -132,7 +133,7 @@ struct string_parts { string_parts(const std::string& beginseq, const std::string& endseq, - adds_sequences strateg) + adds_sequences strateg) : begin_seq{beginseq} , end_seq{endseq} , strategy{strateg} @@ -149,16 +150,16 @@ struct string_parts { void clear() { parts.clear(); } auto generate() const -> std::string - { - if (parts.empty()) { - return (strategy & on_the_beginning ? begin_seq : std::string{}) - + (strategy & on_the_end ? end_seq : std::string{}); + { + if (parts.empty()) { + return (strategy & on_the_beginning ? begin_seq : std::string{}) + + (strategy & on_the_end ? end_seq : std::string{}); } - auto result = std::visit(begin_visit{begin_seq, strategy}, + auto result = std::visit(begin_visit{begin_seq, strategy}, parts.front()); - if (std::ssize(parts) > 1) { + if (std::ssize(parts) > 1) { auto it1 = parts.cbegin(); auto it2 = parts.cbegin()+1; for(;it2 != parts.cend(); ++it1, ++it2) { @@ -531,6 +532,20 @@ auto contains( } +// Print an integer with 1,000's separators (always commas, not locale-driven) +auto print_with_thousands(std::integral auto val) + -> std::string +{ + auto ret = std::to_string(val % 10); + auto pos = 0; + while ((val /= 10) > 0) { + if ((++pos % 3) == 0) { ret = ',' + ret; } + ret = std::to_string(val % 10) + ret; + } + return ret; +} + + // In keep trying to write string+string_view, and it ought to Just Work without // the current workarounds. Not having that is a minor impediment to using safe // and efficient string_views, which we should be encouraging. So for my own use @@ -925,7 +940,7 @@ class stackinstr static auto print(auto&& ee, std::string_view label) { std::cout << "\n=== Stack debug information: " << label << " stack ===\n"; - for (auto& e: ee) + for (auto& e: ee) if (e.ptr) { std::cout << " " << std::setw(6) diff --git a/source/cppfront.cpp b/source/cppfront.cpp index 390b12f859..9e9a7821fe 100644 --- a/source/cppfront.cpp +++ b/source/cppfront.cpp @@ -73,8 +73,8 @@ auto main( } if (flag_verbose) { - out << " Cpp1: " << count.cpp1_lines << " line" << (count.cpp1_lines != 1 ? "s" : ""); - out << "\n Cpp2: " << count.cpp2_lines << " line" << (count.cpp2_lines != 1 ? "s" : ""); + out << " Cpp1: " << print_with_thousands(count.cpp1_lines) << " line" << (count.cpp1_lines != 1 ? "s" : ""); + out << "\n Cpp2: " << print_with_thousands(count.cpp2_lines) << " line" << (count.cpp2_lines != 1 ? "s" : ""); auto total = count.cpp1_lines + count.cpp2_lines; if (total > 0) { out << " ("; diff --git a/source/io.h b/source/io.h index 462f96a76d..48e56ffc38 100644 --- a/source/io.h +++ b/source/io.h @@ -20,8 +20,6 @@ #include "common.h" #include -#include -#include #include diff --git a/source/lex.h b/source/lex.h index b6ca22c5e0..d872df7dfb 100644 --- a/source/lex.h +++ b/source/lex.h @@ -300,7 +300,9 @@ class token v.start(*this, depth); } - auto remove_prefix_if(std::string_view prefix) { + auto remove_prefix_if(std::string_view prefix) + -> void + { if ( sv.size() > prefix.size() && sv.starts_with(prefix) @@ -311,10 +313,24 @@ class token } } + auto set_global_token_order(index_t fp) const + -> void + { + assert(global_token_order == 0); // we only expect to set this once + global_token_order = fp; + } + + auto get_global_token_order() const + -> index_t + { + return global_token_order; + } + private: std::string_view sv; source_position pos; lexeme lex_type; + mutable index_t global_token_order = 0; }; static_assert (CHAR_BIT == 8); @@ -388,7 +404,7 @@ auto expand_string_literal( ++pos; auto current_start = pos; // the current offset before which the string has been added to ret - auto parts = string_parts{std::string(text.substr(0, current_start)), // begin sequence ", U", u8" depends on the string type + auto parts = string_parts{std::string(text.substr(0, current_start)), // begin sequence ", U", u8" depends on the string type "\"", // end sequence string_parts::on_both_ends}; // add opening and closing sequence to generated string @@ -1127,8 +1143,8 @@ auto lex_line( const std::string& opening_seq, const std::string& closing_seq, string_parts::adds_sequences closing_strategy, - std::string_view part, - int pos_to_replace, + std::string_view part, + int pos_to_replace, int size_to_replace ) -> bool { auto parts = expand_raw_string_literal(opening_seq, closing_seq, closing_strategy, part, errors, source_position(lineno, pos_to_replace + 1)); @@ -1162,7 +1178,7 @@ auto lex_line( auto peek3 = peek(3); //G encoding-prefix: one of - //G 'u8' 'u' 'uR' 'u8R' 'U' 'UR' 'L' 'LR' 'R' + //G 'u8' 'u' 'uR' 'u8R' 'U' 'UR' 'L' 'LR' 'R' //G auto is_encoding_prefix_and = [&](char next) { if (line[i] == next) { return 1; } // " @@ -1171,15 +1187,15 @@ auto lex_line( else if (peek1 == '8' && peek2 == next) { return 3; } // u8" else if (peek1 == 'R' && peek2 == next) { return 3; } // uR" else if (peek1 == '8' && peek2 == 'R' && peek3 == next) { return 4; } // u8R" - } - else if (line[i] == 'U') { + } + else if (line[i] == 'U') { if ( peek1 == next) { return 2; } // U" else if (peek1 == 'R' && peek2 == next) { return 3; } // UR" - } - else if (line[i] == 'L') { + } + else if (line[i] == 'L') { if ( peek1 == next ) { return 2; } // L" - else if (peek1 == 'R' && peek2 == next) { return 3; } // LR" - } + else if (peek1 == 'R' && peek2 == next) { return 3; } // LR" + } else if (line[i] == 'R' && peek1 == next) { return 2; } // R" return 0; }; @@ -1211,7 +1227,7 @@ auto lex_line( auto part = line.substr(i, end_pos-i); if (const auto& rsm = raw_string_multiline.value(); rsm.should_interpolate) { - + auto closing_strategy = end_pos == line.npos ? string_parts::no_ends : string_parts::on_the_end; auto size_to_replace = end_pos == line.npos ? std::ssize(line) - i : end_pos - i + std::ssize(rsm.closing_seq); @@ -1437,7 +1453,7 @@ auto lex_line( // if peek(j-2) is 'R' it means that we deal with raw-string literal auto R_pos = i + 1; auto seq_pos = i + 3; - + if (auto paren_pos = line.find("(", seq_pos); paren_pos != std::string::npos) { auto opening_seq = line.substr(i, paren_pos - i + 1); auto closing_seq = ")"+line.substr(seq_pos, paren_pos-seq_pos)+"\""; @@ -1654,9 +1670,9 @@ auto lex_line( //G else if (auto j = is_encoding_prefix_and('\"')) { // if peek(j-2) is 'R' it means that we deal with raw-string literal - if (peek(j-2) == 'R') { + if (peek(j-2) == 'R') { auto seq_pos = i + j; - + if (auto paren_pos = line.find("(", seq_pos); paren_pos != std::string::npos) { auto opening_seq = line.substr(i, paren_pos - i + 1); auto closing_seq = ")"+line.substr(seq_pos, paren_pos-seq_pos)+"\""; diff --git a/source/parse.h b/source/parse.h index 9b16e6eaf6..4c18356eba 100644 --- a/source/parse.h +++ b/source/parse.h @@ -19,9 +19,6 @@ #define CPP2_PARSE_H #include "lex.h" -#include -#include -#include namespace cpp2 { @@ -2229,6 +2226,7 @@ auto statement_node::visit(auto& v, int depth) try_visit(statement, v, depth); try_visit(statement, v, depth); try_visit(statement, v, depth); + try_visit(statement, v, depth); try_visit(statement, v, depth); try_visit(statement, v, depth); try_visit(statement, v, depth); @@ -2473,6 +2471,10 @@ struct function_type_node r->visit(v, depth+1); v.end(function_returns_tag{}, depth); } + + for (auto const& c : contracts) { + c->visit(v, depth+1); + } v.end(*this, depth); } }; @@ -4131,7 +4133,7 @@ auto primary_expression_node::visit(auto& v, int depth) struct next_expression_tag { }; -struct loop_body_tag { token const* identifier; }; +struct loop_body_tag { iteration_statement_node const* n; }; auto iteration_statement_node::visit(auto& v, int depth) -> void @@ -4158,9 +4160,10 @@ auto iteration_statement_node::visit(auto& v, int depth) else { assert(range && parameter && body); range->visit(v, depth+1); - v.start(loop_body_tag{identifier}, depth); + v.start(loop_body_tag{this}, depth); parameter->visit(v, depth+1); body->visit(v, depth+1); + v.end(loop_body_tag{this}, depth); } v.end(*this, depth); } diff --git a/source/reflect.h b/source/reflect.h index c071d8c462..2730ba14bb 100644 --- a/source/reflect.h +++ b/source/reflect.h @@ -39,7 +39,7 @@ class alias_declaration; #line 962 "reflect.h2" class value_member_info; -#line 1482 "reflect.h2" +#line 1484 "reflect.h2" } } @@ -701,7 +701,7 @@ auto basic_enum( cpp2::in bitwise ) -> void; -#line 1151 "reflect.h2" +#line 1152 "reflect.h2" //----------------------------------------------------------------------- // // "An enum[...] is a totally ordered value type that stores a @@ -713,7 +713,7 @@ auto basic_enum( // auto cpp2_enum(meta::type_declaration& t) -> void; -#line 1177 "reflect.h2" +#line 1178 "reflect.h2" //----------------------------------------------------------------------- // // "flag_enum expresses an enumeration that stores values @@ -726,7 +726,7 @@ auto cpp2_enum(meta::type_declaration& t) -> void; // auto flag_enum(meta::type_declaration& t) -> void; -#line 1209 "reflect.h2" +#line 1210 "reflect.h2" //----------------------------------------------------------------------- // // "As with void*, programmers should know that unions [...] are @@ -753,14 +753,14 @@ auto flag_enum(meta::type_declaration& t) -> void; auto cpp2_union(meta::type_declaration& t) -> void; -#line 1365 "reflect.h2" +#line 1367 "reflect.h2" //----------------------------------------------------------------------- // // print - output a pretty-printed visualization of t // auto print(cpp2::in t) -> void; -#line 1375 "reflect.h2" +#line 1377 "reflect.h2" //----------------------------------------------------------------------- // // apply_metafunctions @@ -771,7 +771,7 @@ auto print(cpp2::in t) -> void; auto const& error ) -> bool; -#line 1482 "reflect.h2" +#line 1484 "reflect.h2" } } @@ -1433,7 +1433,7 @@ auto interface(meta::type_declaration& t) -> void CPP2_UFCS(require)(mf, CPP2_UFCS(make_public)(mf), "interface functions must be public"); CPP2_UFCS(default_to_virtual)(mf); - has_dtor |= CPP2_UFCS(is_destructor)(mf); + has_dtor |= CPP2_UFCS(is_destructor)(std::move(mf)); } } @@ -1596,7 +1596,7 @@ auto cpp2_struct(meta::type_declaration& t) -> void auto mf {CPP2_UFCS(as_function)(m)}; CPP2_UFCS(require)(t, !(CPP2_UFCS(is_virtual)(mf)), "a struct may not have a virtual function"); - CPP2_UFCS(require)(t, !(CPP2_UFCS(has_name)(mf, "operator=")), + CPP2_UFCS(require)(t, !(CPP2_UFCS(has_name)(std::move(mf), "operator=")), "a struct may not have a user-defined operator="); } } @@ -1645,28 +1645,29 @@ std::string value{"-1"}; auto is_default_or_numeric {is_empty_or_a_decimal_number(init)}; found_non_numeric |= !(CPP2_UFCS(empty)(init)) && !(is_default_or_numeric); - CPP2_UFCS(require)(m, !(is_default_or_numeric) || !(found_non_numeric) || CPP2_UFCS(has_name)(mo, "none"), + CPP2_UFCS(require)(m, !(std::move(is_default_or_numeric)) || !(found_non_numeric) || CPP2_UFCS(has_name)(mo, "none"), (cpp2::to_string(CPP2_UFCS(name)(mo)) + ": enumerators with non-numeric values must come after all default and numeric values")); - nextval(value, init); + nextval(value, std::move(init)); auto v {std::strtoll(&CPP2_ASSERT_IN_BOUNDS_LITERAL(value, 0), nullptr, 10)}; // for non-numeric values we'll just get 0 which is okay for now if (cpp2::cmp_less(v,min_value)) { min_value = v; } if (cpp2::cmp_greater(v,max_value)) { - max_value = v; + max_value = std::move(v); } // Adding local variable 'e' to work around a Clang warning value_member_info e {cpp2::as_(CPP2_UFCS(name)(mo)), "", value}; - CPP2_UFCS(push_back)(enumerators, e); + CPP2_UFCS(push_back)(enumerators, std::move(e)); CPP2_UFCS(mark_for_removal_from_enclosing_type)(mo); + static_cast(std::move(mo)); } } -#line 1027 "reflect.h2" +#line 1028 "reflect.h2" if ((CPP2_UFCS(empty)(enumerators))) { CPP2_UFCS(error)(t, "an enumeration must contain at least one enumerator value"); return ; @@ -1688,7 +1689,7 @@ std::string value{"-1"}; else {if (cpp2::cmp_greater_eq(min_value,std::numeric_limits::min()) && cpp2::cmp_less_eq(max_value,std::numeric_limits::max())) { underlying_type.value() = "i32"; } - else {if (cpp2::cmp_greater_eq(std::move(min_value),std::numeric_limits::min()) && cpp2::cmp_less_eq(max_value,std::numeric_limits::max())) { + else {if (cpp2::cmp_greater_eq(std::move(min_value),std::numeric_limits::min()) && cpp2::cmp_less_eq(std::move(max_value),std::numeric_limits::max())) { underlying_type.value() = "i64"; } else { @@ -1712,7 +1713,7 @@ std::string value{"-1"}; } } -#line 1073 "reflect.h2" +#line 1074 "reflect.h2" // 2. Replace: Erase the contents and replace with modified contents // // Note that most values and functions are declared as '==' compile-time values, i.e. Cpp1 'constexpr' @@ -1760,7 +1761,7 @@ std::string to_string{" to_string: (this) -> std::string = { \n"}; // Provide a 'to_string' function to print enumerator name(s) -#line 1118 "reflect.h2" +#line 1119 "reflect.h2" { if (bitwise) { to_string += " _ret : std::string = \"(\";\n"; @@ -1769,7 +1770,7 @@ std::string to_string{" to_string: (this) -> std::string = { \n"}; } for ( - auto const& e : enumerators ) { + auto const& e : std::move(enumerators) ) { if (e.name != "_") {// ignore unnamed values if (bitwise) { if (e.name != "none") { @@ -1792,10 +1793,10 @@ std::string to_string{" to_string: (this) -> std::string = { \n"}; CPP2_UFCS(add_member)(t, std::move(to_string)); } } -#line 1148 "reflect.h2" +#line 1149 "reflect.h2" } -#line 1160 "reflect.h2" +#line 1161 "reflect.h2" auto cpp2_enum(meta::type_declaration& t) -> void { // Let basic_enum do its thing, with an incrementing value generator @@ -1812,7 +1813,7 @@ auto cpp2_enum(meta::type_declaration& t) -> void ); } -#line 1187 "reflect.h2" +#line 1188 "reflect.h2" auto flag_enum(meta::type_declaration& t) -> void { // Let basic_enum do its thing, with a power-of-two value generator @@ -1834,7 +1835,7 @@ auto flag_enum(meta::type_declaration& t) -> void ); } -#line 1233 "reflect.h2" +#line 1234 "reflect.h2" auto cpp2_union(meta::type_declaration& t) -> void { std::vector alternatives {}; @@ -1843,7 +1844,7 @@ auto value{0}; // 1. Gather: All the user-written members, and find/compute the max size -#line 1240 "reflect.h2" +#line 1241 "reflect.h2" for ( auto const& m : CPP2_UFCS(get_members)(t) ) { do @@ -1862,13 +1863,14 @@ auto value{0}; // Adding local variable 'e' to work around a Clang warning value_member_info e {cpp2::as_(CPP2_UFCS(name)(mo)), CPP2_UFCS(type)(mo), cpp2::as_(value)}; - CPP2_UFCS(push_back)(alternatives, e); + CPP2_UFCS(push_back)(alternatives, std::move(e)); CPP2_UFCS(mark_for_removal_from_enclosing_type)(mo); + static_cast(std::move(mo)); } while (false); ++value; } } -#line 1263 "reflect.h2" +#line 1265 "reflect.h2" std::string discriminator_type {}; if (cpp2::cmp_less(CPP2_UFCS(ssize)(alternatives),std::numeric_limits::max())) { discriminator_type = "i8"; @@ -1883,7 +1885,7 @@ auto value{0}; discriminator_type = "i64"; }}} -#line 1278 "reflect.h2" +#line 1280 "reflect.h2" // 2. Replace: Erase the contents and replace with modified contents CPP2_UFCS(remove_marked_members)(t); @@ -1892,12 +1894,12 @@ std::string storage{" _storage: cpp2::aligned_storage t) -> void { std::cout << CPP2_UFCS(print)(t) << "\n"; } -#line 1379 "reflect.h2" +#line 1381 "reflect.h2" [[nodiscard]] auto apply_metafunctions( declaration_node& n, type_declaration& rtype, @@ -2095,7 +2097,7 @@ auto print(cpp2::in t) -> void && !(CPP2_UFCS(arguments_were_used)(rtype)))) { - error(name + " did not use its template arguments - did you mean to write '" + name + " <" + CPP2_ASSERT_IN_BOUNDS_LITERAL(args, 0) + "> type' (with the spaces)?"); + error(name + " did not use its template arguments - did you mean to write '" + name + " <" + CPP2_ASSERT_IN_BOUNDS_LITERAL(std::move(args), 0) + "> type' (with the spaces)?"); return false; } } @@ -2103,7 +2105,7 @@ auto print(cpp2::in t) -> void return true; } -#line 1482 "reflect.h2" +#line 1484 "reflect.h2" } } diff --git a/source/reflect.h2 b/source/reflect.h2 index 8c6b061281..290c254b4a 100644 --- a/source/reflect.h2 +++ b/source/reflect.h2 @@ -1022,6 +1022,7 @@ basic_enum: ( enumerators.push_back( e ); mo.mark_for_removal_from_enclosing_type(); + _ = mo; } if (enumerators.empty()) { @@ -1258,6 +1259,7 @@ union: (inout t : meta::type_declaration) alternatives.push_back( e ); mo.mark_for_removal_from_enclosing_type(); + _ = mo; } discriminator_type: std::string = (); @@ -1334,7 +1336,7 @@ union: (inout t : meta::type_declaration) } // Add the destructor - t.add_member( " operator=: (move this) = { _destroy(); }" ); + t.add_member( " operator=: (move this) = { _destroy(); _ = this; }" ); // Add default constructor t.add_member( " operator=: (out this) = { }" ); diff --git a/source/sema.h b/source/sema.h index 8a11de9ca1..a290373347 100644 --- a/source/sema.h +++ b/source/sema.h @@ -33,7 +33,7 @@ auto parser::apply_type_metafunctions( declaration_node& n ) auto rtype = meta::type_declaration{ &n, cs }; return apply_metafunctions( - n, + n, rtype, [&](std::string const& msg) { error( msg, false ); } ); @@ -76,17 +76,33 @@ struct declaration_sym { assert (declaration); return declaration->position(); } + + auto get_token() const + -> token const* + { + return identifier; + } }; struct identifier_sym { + enum kind { use, using_declaration, deactivation } kind_ = use; bool standalone_assignment_to = false; + bool is_captured = false; + bool safe_to_move = true; + int safe_to_move_context = 0; token const* identifier = {}; identifier_sym( bool a, - token const* id + token const* id, + kind k = use, + bool mv = true, + int mvc = 0 ) - : standalone_assignment_to{a} + : kind_{k} + , standalone_assignment_to{a} + , safe_to_move{mv} + , safe_to_move_context{mvc} , identifier{id} { } @@ -96,6 +112,30 @@ struct identifier_sym { assert (identifier); return identifier->position(); } + + auto get_token() const + -> token const* + { + return identifier; + } + + auto is_use() const + -> bool + { + return kind_ == use; + } + + auto is_using_declaration() const + -> bool + { + return kind_ == using_declaration; + } + + auto is_deactivation() const + -> bool + { + return kind_ == deactivation; + } }; struct selection_sym { @@ -116,6 +156,13 @@ struct selection_sym { assert (selection); return selection->position(); } + + auto get_token() const + -> token const* + { + assert (selection); + return selection->identifier; + } }; struct compound_sym { @@ -139,6 +186,12 @@ struct compound_sym { assert (compound); return compound->position(); } + + auto get_token() const + -> token const* + { + return nullptr; + } }; struct symbol { @@ -155,7 +208,7 @@ struct symbol { bool start = true; symbol(int depth, declaration_sym const& sym) : depth{depth}, sym{sym}, start{sym.start} { } - symbol(int depth, identifier_sym const& sym) : depth{depth}, sym{sym} { } + symbol(int depth, identifier_sym const& sym) : depth{depth}, sym{sym}, start{!sym.is_deactivation()} { } symbol(int depth, selection_sym const& sym) : depth{depth}, sym{sym}, start{sym.start} { } symbol(int depth, compound_sym const& sym) : depth{depth}, sym{sym}, start{sym.start} { } @@ -189,6 +242,46 @@ struct symbol { return { 0, 0 }; } } + + auto get_token() const + -> token const* + { + switch (sym.index()) + { + break;case declaration: { + auto const& s = std::get(sym); + return s.get_token(); + } + + break;case identifier: { + auto const& s = std::get(sym); + return s.get_token(); + } + + break;case selection: { + auto const& s = std::get(sym); + return s.get_token(); + } + + break;case compound: { + auto const& s = std::get(sym); + return s.get_token(); + } + + break;default: + assert (!"illegal symbol state"); + return nullptr; + } + } + + auto get_global_token_order() const + -> index_t + { + if (auto t = get_token()) { + return t->get_global_token_order(); + } + return 0; + } }; @@ -213,18 +306,21 @@ auto is_definite_initialization(token const* t) // Keep a list of all token*'s found that are definite last uses // for a local variable or copy or forward parameter x, which we -// will rewrite to move or forward from the variable. +// will rewrite to move or forward from the variable. // struct last_use { token const* t; bool is_forward; + bool safe_to_move; last_use( token const* t_, - bool is_forward_ = false + bool is_forward_ = false, + bool safe_to_move_ = true ) : t{t_} , is_forward{is_forward_} + , safe_to_move{safe_to_move_} { } bool operator==(last_use const& that) { return t == that.t; } @@ -276,23 +372,26 @@ class sema } // Get the declaration of t within the same named function or beyond it + // For a this parameter, optionally include uses of implicit this // auto get_declaration_of( token const* t, - bool look_beyond_current_function = false - ) + bool look_beyond_current_function = false, + bool include_implicit_this = false + ) const -> declaration_sym const* { if (!t) { return {}; } - return get_declaration_of(*t, look_beyond_current_function); + return get_declaration_of(*t, look_beyond_current_function, include_implicit_this); } auto get_declaration_of( token const& t, - bool look_beyond_current_function = false - ) + bool look_beyond_current_function = false, + bool include_implicit_this = false + ) const -> declaration_sym const* { // First find the position the query is coming from @@ -300,7 +399,7 @@ class sema auto i = symbols.cbegin(); while ( i != symbols.cend() - && i->position() < t.position() + && i->get_global_token_order() < t.get_global_token_order() ) { ++i; @@ -322,18 +421,27 @@ class sema // Then look backward to find the first declaration of // this name that is not deeper (in a nested scope) // and is in the same function - for ( - auto ri = std::make_reverse_iterator(i+1); - ri != symbols.crend() && ri->position() <= t.position(); // TODO: See pure2-deducing-pointers-error.cpp2 - ++ri - ) + using I = std::vector::const_iterator; + auto advance = [](I& i, int n, I bound) { // TODO Use `std::ranges::advance` + auto in = i; + if (std::abs(n) >= std::abs(bound - i)) { + i = bound; + } + else { + std::advance(i, n); + } + return n - (i - in); + }; + advance(i, -int(i->position() > t.position()), symbols.cbegin()); + advance(i, 1, symbols.cend()); + while (advance(i, -1, symbols.begin()) == 0) { if ( - ri->sym.index() == symbol::active::declaration - && ri->depth <= depth + i->sym.index() == symbol::active::declaration + && i->depth <= depth ) { - auto const& decl = std::get(ri->sym); + auto const& decl = std::get(i->sym); // Conditionally look beyond the start of the current named (has identifier) function // (an unnamed function is ok to look beyond) @@ -355,13 +463,67 @@ class sema { return &decl; } - depth = ri->depth; + + // If we reached a 'move this' parameter, look it up in the type members + if ( + include_implicit_this + && decl.identifier + && *decl.identifier == "this" + ) + { + if (auto n = decl.declaration; + decl.parameter + && decl.parameter->pass == passing_style::move + && n + && n->parent_is_function() + && (n = n->parent_declaration)->parent_is_type() + && n->my_statement + && n->my_statement->compound_parent + && std::any_of( + n->my_statement->compound_parent->statements.begin(), + n->my_statement->compound_parent->statements.end(), + [&t, n](std::unique_ptr const& s) mutable { + return s + && s->statement.index() == statement_node::declaration + && (n = &*std::get(s->statement))->identifier + && n->identifier->to_string() == t; + }) + ) + { + return &decl; + } + return nullptr; + } + + depth = i->depth; } } return nullptr; } + auto is_captured(token const& t) const + -> bool + { + // TODO Use 'std::lower_bound' by filtering final positions of 0. + auto it = std::find_if( + symbols.begin(), + symbols.end(), + [&](symbol const& s) -> bool { + return s.get_global_token_order() == t.get_global_token_order(); + }); + + if (identifier_sym const* sym = nullptr; + it != symbols.end() + && (sym = std::get_if(&it->sym)) + && sym->is_use() + ) + { + return sym->is_captured; + } + return false; + } + //----------------------------------------------------------------------- // Factor out the uninitialized var decl test @@ -421,14 +583,22 @@ class sema break;case symbol::active::identifier: { auto const& sym = std::get(s.sym); assert (sym.identifier); - if (auto use = is_definite_last_use(sym.identifier)) { + if (last_use const* use = nullptr; + sym.is_use() + && (use = is_definite_last_use(sym.identifier)) + ) + { o << "*** " << sym.identifier->position().to_string() << " DEFINITE LAST " << (use->is_forward ? "FORWARDING" : "POTENTIALLY MOVING") << " USE OF "; } - if (is_definite_initialization(sym.identifier)) { + if ( + sym.is_use() + && is_definite_initialization(sym.identifier) + ) + { o << "*** " << sym.identifier->position().to_string() << " DEFINITE INITIALIZATION OF "; } @@ -513,26 +683,17 @@ class sema // It's a local (incl. named return value or copy or move or forward parameter) // - auto is_potentially_movable_local = [&](symbol const& s) + auto is_local_declaration = [&](symbol const& s) -> declaration_sym const* { if (auto const* sym = std::get_if(&s.sym)) { if ( sym->start && sym->declaration->is_object() - && (!sym->parameter - || sym->parameter->pass == passing_style::copy - || sym->parameter->pass == passing_style::move - || sym->parameter->pass == passing_style::forward - ) ) { // Must be in function scope - if ( - sym->declaration->parent_declaration - && sym->declaration->parent_declaration->is_function() - ) - { + if (sym->declaration->parent_is_function()) { return sym; } else { @@ -563,13 +724,14 @@ class sema // If this is a copy, move, or forward parameter or a local variable, // identify and tag its definite last uses to `std::move` from them + // If it's some other parameter, just check that it is used // - if (auto decl = is_potentially_movable_local(symbols[sympos])) { + if (auto decl = is_local_declaration(symbols[sympos])) { assert (decl->identifier); find_definite_last_uses( decl->identifier, sympos, - decl->parameter && decl->parameter->pass == passing_style::forward + decl->parameter ? std::optional{decl->parameter->pass} : std::optional{} ); } } @@ -582,95 +744,337 @@ class sema // given position and depth in the symbol/scope table // auto find_definite_last_uses( - token const* id, - int pos, - bool is_forward + token const* id, + int pos, + std::optional pass ) const -> void { - auto i = pos; - auto depth = symbols[pos].depth; + auto is_a_use = [&](identifier_sym const* sym) -> bool { + assert(!sym || sym->identifier); + declaration_sym const* decl = nullptr; + return sym + && sym->is_use() + && ( + *sym->identifier == *id + // For 'this', do include member names with implicit 'this.' + || ( + *id == "this" + && (decl = get_declaration_of(sym->get_token(), false, true)) + && decl->identifier + && *decl->identifier == "this" + ) + ); + }; - // Maintain a stack of the depths of the most recently seen - // selection statements, using the current depth-2 as a sentinel - auto selections = std::vector{depth-2}; + auto i = pos + 1; - // Scan forward to the end of this scope, keeping track of - // the trailing nest of selection statements - while ( - i+1 < std::ssize(symbols) - && symbols[i+1].depth >= depth - ) + struct pos_range { - assert (std::ssize(symbols) > 1); - if (symbols[i].sym.index() == symbol::selection) { - auto const& s = std::get(symbols[i].sym); - if (s.start) { - selections.push_back(symbols[i].depth); + bool is_loop; + int first; + int last = 0; + + pos_range (bool l, int f) : is_loop{l}, first{f} { } + bool within(int x) const { return first <= x && x <= last; } + bool skip () const { return !is_loop; } + }; + + // Ranges of positions which includes non-nested + // 1. Iteration statements (a use isn't a last use) + // 2. Ranges to skip (a last use can't be found in these) + // - Function expressions (except in a capture) + // - Where id is hidden by another declaration + auto pos_ranges = std::vector{{false, 0}}; // Keep sentinel for simpler code + + auto skip_hidden_name = [&](bool record_pos_range) -> bool { + auto skip_to = [&](token const* identifier_end) + { + // Can afford to just skip id and not member names + // because in Cpp2 you can't shadow member names + // + // TODO When local types are supported + // consider where 'this' is implicitly declared + // For example, see https://cpp2.godbolt.org/z/onfW6hns1 + + if (record_pos_range) { + pos_ranges.emplace_back(false, i - 1); } - //else { - // assert (symbols[i].depth-1 == selections.back()); - // selections.pop_back(); - //} + ++i; + identifier_sym const* sym = nullptr; + while ( + i < std::ssize(symbols) + && ( + !(sym = std::get_if(&symbols[i].sym)) + || sym->identifier != identifier_end + ) + ) + { + ++i; + } + assert(sym->identifier == identifier_end && sym->is_deactivation()); + if (record_pos_range) { + pos_ranges.back().last = i; + } + }; + + if (auto decl = std::get_if(&symbols[i].sym); + decl + && decl->start + && decl->identifier + && *decl->identifier == *id + && *decl->identifier != "_" + ) + { + skip_to(decl->identifier); + return true; } - ++i; - } + else if (auto sym = std::get_if(&symbols[i].sym); + sym + && sym->is_using_declaration() + ) + { + assert(sym->identifier); + skip_to(sym->identifier); + return true; + } + return false; + }; - // i is now at the end of id's scope, so start scanning backwards - // until we find the first definite last use - for (auto found = false; i > pos; --i) + auto skip_function_expression = [&]() -> bool { + if (auto decl = std::get_if(&symbols[i].sym); + decl + && decl->start + && decl->declaration->is_function_expression() + ) + { + // Record the skipped subranges without captures + auto function_expression_end = decl->declaration; + pos_ranges.emplace_back(false, i - 1); + ++i; + while ( + i < std::ssize(symbols) + && ( + !(decl = std::get_if(&symbols[i].sym)) + || decl->declaration != function_expression_end + ) + ) + { + if (skip_hidden_name(false)) { + continue; + } + else if ( + auto sym = std::get_if(&symbols[i].sym); + is_a_use(sym) + && sym->is_captured + ) + { + pos_ranges.back().last = i - 1; + pos_ranges.emplace_back(false, i + 1); + } + ++i; + } + assert(decl && decl->declaration == function_expression_end && !decl->start); + pos_ranges.back().last = i; + return true; + } + return false; + }; + + // Scan forward to the end of this scope + for (auto start_depth = symbols[pos].depth; + i < std::ssize(symbols) + && symbols[i].depth >= start_depth; + ++i + ) { - // Once we find something, don't continue back further - // than the closest enclosing selection statement if ( - found - && symbols[i].depth <= selections.back() + skip_function_expression() + || skip_hidden_name(true) ) { - break; + continue; } - if (symbols[i].sym.index() == symbol::active::identifier) + // Record the loops + else if (auto sym = std::get_if(&symbols[i].sym); + sym + && sym->identifier + && ( + *sym->identifier == "for" + || *sym->identifier == "while" + || *sym->identifier == "do" + ) + ) { - auto const& sym = std::get(symbols[i].sym); - assert (sym.identifier); + auto loop_depth = symbols[i].depth; + auto loop_id = sym->identifier; - // If we find a use of this identifier - if (*sym.identifier == *id) + // If id is the loop parameter, this is its end + if ( + *loop_id == "for" + && sym->is_deactivation() + ) + { + assert(symbols[i].depth == start_depth && "Messed up in a nested loop"); + ++i; + break; + } + assert(symbols[i].start); + + pos_ranges.emplace_back(true, i); + + // Scan forward to the end of this loop + ++i; + while ( + i < std::ssize(symbols) + && ( + symbols[i].depth > loop_depth + || !(sym = std::get_if(&symbols[i].sym)) + || sym->identifier != loop_id + ) + ) { if ( - !found - || symbols[i].depth > selections.back()+1 + skip_function_expression() + || skip_hidden_name(true) ) { - definite_last_uses.emplace_back( sym.identifier, is_forward ); - found = true; + continue; } + ++i; + } + assert(sym && sym->identifier == loop_id && sym->is_deactivation()); + pos_ranges.back().last = i; + } + } - // Pop any of the last branches that we're outside of - while (symbols[i].depth <= selections.back()) { - selections.pop_back(); - assert (!selections.empty()); // won't remove sentinel - } - // Then skip over the earlier part of the current branch - while ( - i > pos - && symbols[i].depth > selections.back() + 1 + // i is now at the end of id's scope, so start scanning backwards + // until we find the first definite last uses + --i; + bool found = false; + auto branch_depth = 0; + while (i > pos) + { + // Drop skipped ranges + while (pos_ranges.back().first >= i) { + pos_ranges.pop_back(); + } + // Skip ranges where a use of id would name another declaration + while ( + pos_ranges.back().within(i) + && pos_ranges.back().skip() + ) + { + i = pos_ranges.back().first; + pos_ranges.pop_back(); + } + + // If found in a branch and we are at its start, + // pop out of any containing scope of the branch + if (compound_sym const* comp = nullptr; + branch_depth != 0 + && (comp = std::get_if(&symbols[i].sym)) + && comp->kind_ == compound_sym::is_true + && branch_depth + 1 == symbols[i].depth + ) + { + while ( + i > pos + && ( + !(comp = std::get_if(&symbols[i].sym)) + || branch_depth <= symbols[i].depth ) - { - --i; - } + ) + { + --i; + } + + // If found in a branch, + // keep popping to its sibling (false branch) + // or to its containing scope (true branch) + if ( + i > pos + && comp + && ( + comp->kind_ == compound_sym::is_false + || comp->kind_ == compound_sym::is_true + ) + ) + { + branch_depth = symbols[i].depth - 1; + } + else + { + branch_depth = 0; } + continue; + } + + auto sym = std::get_if(&symbols[i].sym); + + if (!is_a_use(sym)) + { + --i; + continue; + } + + // If found in a loop, it wasn't a last use + // Set up to pop out of the scope containing the loop + if (pos_ranges.back().within(i)) + { + assert(pos_ranges.back().is_loop && "Other ranges are skipped."); + i = pos_ranges.back().first; // The scope to pop is the scope of the loop + pos_ranges.pop_back(); + } + else + { + definite_last_uses.emplace_back( + sym->identifier, + pass == passing_style::forward, + sym->safe_to_move + && ( + !pass + || pass == passing_style::copy + || pass == passing_style::move + || pass == passing_style::forward + ) + ); + } + found = true; + + compound_sym const* comp = nullptr; + + // Pop out of any containing scope of the last use + for (auto found_depth = symbols[i--].depth; + i > pos + && ( + !(comp = std::get_if(&symbols[i].sym)) + || comp->kind_ == compound_sym::is_scope + || found_depth <= symbols[i].depth + ); + --i + ) + { + } + assert(!comp || symbols[i].start); + + // If found in a branch, record its depth + if ( + i > pos + && comp + && comp->kind_ != compound_sym::is_scope + ) + { + branch_depth = symbols[i].depth - 1; } } // If we arrived back at the declaration without finding a use - // and this isn't generated code (ignore that for now) // and this is a user-named object (not 'this', 'that', or '_') if ( - i == pos - && id->position().lineno > 0 - && *id != "this" + !found + && *id != "this" && *id != "that" && *id != "_" ) @@ -700,13 +1104,7 @@ class sema ) const -> bool { - // If this is a member variable in a constructor, the name doesn't - // appear lexically right in the constructor, so prepending "this." - // to the printed name might make the error more readable to the programmer auto name = decl->identifier->to_string(); - if (decl->declaration->parent_is_type()) { - name += " (aka this." + name + ")"; - } struct stack_entry{ int pos; // start of this selection statement @@ -759,7 +1157,11 @@ class sema auto const& sym = std::get(symbols[pos].sym); assert (sym.identifier); - if (is_definite_initialization(sym.identifier)) { + if ( + sym.is_use() + && is_definite_initialization(sym.identifier) + ) + { errors.emplace_back( sym.identifier->position(), "local variable " + name @@ -1613,16 +2015,146 @@ class sema //----------------------------------------------------------------------- // Visitor functions // - int scope_depth = 0; - bool started_standalone_assignment_expression = false; - bool started_postfix_expression = false; - bool is_out_expression = false; - bool inside_next_expression = false; - bool inside_parameter_list = false; - bool inside_parameter_identifier = false; - bool inside_returns_list = false; - bool just_entered_for = false; - parameter_declaration_node const* inside_out_parameter = {}; + int scope_depth = 0; + int safe_to_move_context = 1; + bool started_standalone_assignment_expression = false; + bool started_postfix_expression = false; + std::vector started_postfix_operators_ = {false}; + bool started_prefix_operators = false; + bool is_out_expression = false; + bool inside_next_expression = false; + bool inside_parameter_list = false; + bool inside_parameter_identifier = false; + bool inside_returns_list = false; + bool just_entered_for = false; + token const* prev_token = nullptr; + token const* prev2_token = nullptr; + token const* accessed_member_for_ufcs = nullptr; + parameter_declaration_node const* inside_out_parameter = {}; + std::vector> uses_in_expression = {}; + std::vector> uses_at_postfix_expression = {}; + std::vector> indices_of_uses_per_scope = {{}}; + std::vector> indices_of_activations_per_scope = {{}}; + + auto started_postfix_operators() -> bool + { + assert(!started_postfix_operators_.empty()); + return started_postfix_operators_.back(); + } + + auto push(std::vector>& uses) -> void + { + uses.emplace_back( + unsafe_narrow(std::ssize(indices_of_uses_per_scope) - 1), + unsafe_narrow(std::ssize(indices_of_uses_per_scope.back())) + ); + } + + auto get(std::vector>& uses) -> std::span + { + assert(!uses.empty()); + auto [i, j] = uses.back(); + return std::span{indices_of_uses_per_scope[i]}.subspan(j); + } + + // Mark uses that are not safe to move + auto pop_uses_in_expression() -> void + { + auto identifier = [](symbol& s) { return &std::get(s.sym); }; + + auto range = get(uses_in_expression); + for (auto i : range) + { + auto x = identifier(symbols[i]); + for (auto j : range) + { + auto y = identifier(symbols[j]); + if ( + i != j + && *x->identifier == *y->identifier + && x->safe_to_move_context != y->safe_to_move_context + && !x->is_captured + && !y->is_captured + ) + { + x->safe_to_move = false; + y->safe_to_move = false; + } + } + } + + uses_in_expression.pop_back(); + } + + auto push_lifetime_scope() -> void + { + indices_of_uses_per_scope.emplace_back(); + indices_of_activations_per_scope.emplace_back(); + } + + auto push_use(identifier_sym sym) -> void + { + assert(!indices_of_uses_per_scope.empty()); + assert(sym.is_use()); + assert(sym.identifier); + + indices_of_uses_per_scope.back().push_back(cpp2::unsafe_narrow(std::ssize(symbols))); + symbols.emplace_back(scope_depth, sym); + } + + auto push_activation(declaration_sym decl) -> void + { + assert(!indices_of_activations_per_scope.empty()); + assert(decl.start); + + if ( + decl.identifier + && *decl.identifier != "_" + ) + { + indices_of_activations_per_scope.back().push_back(cpp2::unsafe_narrow(std::ssize(symbols))); + } + symbols.emplace_back(scope_depth, decl); + } + + auto push_activation(identifier_sym sym) -> void + { + assert(!indices_of_activations_per_scope.empty()); + assert(sym.is_using_declaration()); + assert(sym.identifier); + + indices_of_activations_per_scope.back().push_back(cpp2::unsafe_narrow(std::ssize(symbols))); + symbols.emplace_back(scope_depth, sym); + } + + auto pop_lifetime_scope() -> void + { + assert(!indices_of_uses_per_scope.empty()); + assert(!indices_of_activations_per_scope.empty()); + + for (auto i : indices_of_activations_per_scope.back()) { + if (auto decl = std::get_if(&symbols[i].sym)) + { + symbols.emplace_back( scope_depth, identifier_sym( false, decl->identifier, identifier_sym::deactivation ) ); + } + else if (auto sym = std::get_if(&symbols[i].sym)) + { + symbols.emplace_back( scope_depth, identifier_sym( false, sym->identifier, identifier_sym::deactivation ) ); + } + } + + indices_of_uses_per_scope.pop_back(); + indices_of_activations_per_scope.pop_back(); + } + + auto end(translation_unit_node const&, int) -> void + { + assert(uses_in_expression.empty()); + assert(uses_at_postfix_expression.empty()); + assert(started_postfix_operators_.size() == 1); + assert(indices_of_uses_per_scope.size() == 1); + assert(indices_of_activations_per_scope.size() == 1); + } auto start(next_expression_tag const&, int) -> void { @@ -1637,6 +2169,7 @@ class sema auto start(parameter_declaration_list_node const&, int) -> void { inside_parameter_list = true; + push_lifetime_scope(); } auto end(parameter_declaration_list_node const&, int) -> void @@ -1673,15 +2206,8 @@ class sema inside_out_parameter = &n; } - if ( - n.pass == passing_style::copy - || n.pass == passing_style::move - || n.pass == passing_style::forward - ) - { - // Handle variables in unnamed functions. For such cases scope_depth is increased by +1 - auto depth = scope_depth + ((n.declaration->parent_is_function() && n.declaration->parent_declaration->name() == nullptr) ? 1 : 0 ); - symbols.emplace_back( depth, declaration_sym( true, n.declaration.get(), n.declaration->name(), n.declaration->initializer.get(), &n)); + if (n.pass != passing_style::out) { + push_activation( declaration_sym( true, n.declaration.get(), n.declaration->name(), n.declaration->initializer.get(), &n)); } } @@ -1705,13 +2231,31 @@ class sema inside_returns_list = false; } - auto start(loop_body_tag const &n, int) -> void + auto start(iteration_statement_node const& n, int) -> void { - if (*n.identifier == "for") { - just_entered_for = true; + if (*n.identifier != "for") { + symbols.emplace_back( scope_depth, identifier_sym( false, n.identifier ) ); } } + auto end(iteration_statement_node const& n, int) -> void + { + symbols.emplace_back( scope_depth, identifier_sym( false, n.identifier, identifier_sym::deactivation ) ); + } + + auto start(loop_body_tag const& n, int) -> void + { + assert(*n.n->identifier == "for"); + symbols.emplace_back( scope_depth, identifier_sym( false, n.n->identifier ) ); + push_lifetime_scope(); + just_entered_for = true; + } + + auto end(loop_body_tag const&, int) -> void + { + pop_lifetime_scope(); + } + auto start(declaration_node const& n, int) -> void { // Skip the first declaration after entering a 'for', @@ -1727,7 +2271,10 @@ class sema // Skip type scope (member) variables && !(n.parent_is_type() && n.is_object()) // Skip unnamed variables - && n.identifier + && ( + n.identifier + || n.is_function_expression() + ) // Skip non-out parameters && ( !inside_parameter_list @@ -1735,7 +2282,7 @@ class sema ) ) { - symbols.emplace_back( scope_depth, declaration_sym( true, &n, n.name(), n.initializer.get(), inside_out_parameter ) ); + push_activation( declaration_sym( true, &n, n.name(), n.initializer.get(), inside_out_parameter ) ); if (!n.is_object()) { ++scope_depth; } @@ -1744,12 +2291,22 @@ class sema auto end(declaration_node const& n, int) -> void { + if (auto f = std::get_if(&n.type)) { + if (std::get_if(&(*f)->returns)) { + pop_lifetime_scope(); + } + pop_lifetime_scope(); + } + if ( !n.is_alias() // Skip type scope (member) variables && !(n.parent_is_type() && n.is_object()) // Skip unnamed variables - && n.identifier + && ( + n.identifier + || n.is_function_expression() + ) // Skip non-out parameters && ( !inside_parameter_list @@ -1764,18 +2321,81 @@ class sema } } + auto is_binary_operator(lexeme l) -> bool { + switch (l) { + break;case lexeme::PlusPlus: + case lexeme::MinusMinus: + case lexeme::Arrow: + case lexeme::Tilde: + case lexeme::Not: return false; + break;case lexeme::Plus: + case lexeme::Minus: return !started_prefix_operators; + break;case lexeme::Multiply: + case lexeme::Ampersand: return !started_postfix_operators(); + break;default: return is_operator(l); + } + } + auto start(token const& t, int) -> void { - // We currently only care to look at identifiers - if (t.type() != lexeme::Identifier) { + auto guard = finally([&]() { + prev2_token = prev_token; + prev_token = &t; + }); + + if ( + is_binary_operator(t.type()) + || ( + t.type() == cpp2::lexeme::Keyword + && ( + t == "is" + || t == "as" + ) + ) + ) + { + ++safe_to_move_context; + } + + // Mark captures + if (t.type() == lexeme::Dollar) + { + for (auto i : get(uses_at_postfix_expression)) + { + auto& sym = std::get(symbols[i].sym); + assert(sym.is_use()); + sym.is_captured = true; + } + } + + // Further look at variable identifiers only + if ( + t.type() != lexeme::Identifier + && t != "this" + && !accessed_member_for_ufcs + ) + { return; } + // By giving tokens an order during sema + // generated code can be equally checked + static index_t global_token_counter = 1; + t.set_global_token_order( global_token_counter++ ); + + auto started_member_access = + prev_token + && prev_token->type() == lexeme::Dot; + auto started_this_member_access = + started_member_access + && prev2_token + && *prev2_token == "this"; + // If this is the first identifier since we started a new assignment, // expression, then it's the left-hand side (target) of the assignment - else if (started_standalone_assignment_expression) + if (started_standalone_assignment_expression) { - symbols.emplace_back( scope_depth, identifier_sym( true, &t ) ); + push_use( identifier_sym( true, &t ) ); started_standalone_assignment_expression = false; // we were the consumer for this information } @@ -1785,31 +2405,70 @@ class sema // this an id-expression and add a sema rule to disallow complex expressions else if (is_out_expression) { - symbols.emplace_back( scope_depth, identifier_sym( true, &t ) ); + push_use( identifier_sym( true, &t ) ); is_out_expression = false; } // Otherwise it's just an identifier use (if it's not a parameter name) and - // it's the first identifier of a postfix_expressions (not a member name or something else) - else if (started_postfix_expression) + // it's the first identifier of a postfix_expressions + // or the function name in a UFCS expression + else if ( + started_postfix_expression + || started_member_access + || started_this_member_access + || accessed_member_for_ufcs + ) { started_postfix_expression = false; - if (!inside_parameter_identifier && !inside_next_expression) + if (!inside_parameter_identifier) { // Put this into the table if it's a use of an object in scope // or it's a 'copy' parameter (but to be a use it must be after // the declaration, not the token in the decl's name itself) - if (auto decl = get_declaration_of(t); + // including an implicit 'this' access of a member name + auto push_use = [&](token const* u) { + this->push_use( identifier_sym( false, u, {}, !inside_next_expression, safe_to_move_context ) ); + }; + if (accessed_member_for_ufcs) { + if (t == "(") { + push_use(accessed_member_for_ufcs); + } + accessed_member_for_ufcs = nullptr; + } + else if (auto decl = get_declaration_of(t, false, !started_this_member_access); decl && decl->declaration->name() != &t ) { - symbols.emplace_back( scope_depth, identifier_sym( false, &t ) ); + if (!started_member_access) { + push_use(&t); + } + else { + accessed_member_for_ufcs = &t; + } } } } } + auto end(statement_node const& n, int) -> void + { + if (n.parameters) { + pop_lifetime_scope(); + } + } + + auto start(using_statement_node const& n, int) -> void + { + if (auto id = get_if(&n.id->id); + !n.for_namespace + && id + ) + { + push_activation( identifier_sym( false, (*id)->ids.back().id->identifier, identifier_sym::using_declaration ) ); + } + } + auto start(selection_statement_node const& n, int) -> void { active_selections.push_back( &n ); @@ -1853,10 +2512,12 @@ class sema compound_sym{ true, &n, kind_of(n) } ); ++scope_depth; + push_lifetime_scope(); } auto end(compound_statement_node const& n, int) -> void { + pop_lifetime_scope(); symbols.emplace_back( scope_depth, compound_sym{ false, &n, kind_of(n) } @@ -1864,7 +2525,21 @@ class sema --scope_depth; } - auto start(assignment_expression_node const& n, int) + template auto start(T const&, int) -> void + requires std::is_same_v + || std::is_same_v + { + push(uses_in_expression); + } + + template auto end(T const&, int) -> void + requires std::is_same_v + || std::is_same_v + { + pop_uses_in_expression(); + } + + auto start(assignment_expression_node const& n, int) -> void { if ( n.is_standalone_expression() @@ -1879,8 +2554,35 @@ class sema } } - auto start(postfix_expression_node const&, int) { - started_postfix_expression = true; + auto start(bit_and_expression_node const&, int) -> void + { + started_postfix_operators_.push_back(false); + } + + auto end(bit_and_expression_node const&, int) -> void + { + started_postfix_operators_.pop_back(); + } + + auto start(prefix_expression_node const&, int) -> void + { + started_prefix_operators = true; + } + + auto start(postfix_expression_node const& n, int) -> void + { + started_prefix_operators = false; + started_postfix_operators_.push_back(true); + if (auto id = std::get_if(&n.expr->expr)) { + started_postfix_expression = (*id)->is_unqualified(); + } + push(uses_at_postfix_expression); + } + + auto end(postfix_expression_node const&, int) -> void + { + started_postfix_operators_.pop_back(); + uses_at_postfix_expression.pop_back(); } auto start(auto const&, int) -> void diff --git a/source/to_cpp1.h b/source/to_cpp1.h index 28c3983a99..ecc98d3e03 100644 --- a/source/to_cpp1.h +++ b/source/to_cpp1.h @@ -19,9 +19,6 @@ #define CPP2_TO_CPP1_H #include "sema.h" -#include -#include -#include #include namespace cpp2 { @@ -1749,32 +1746,61 @@ class cppfront // auto emit( unqualified_id_node const& n, - bool in_synthesized_multi_return = false, - bool is_local_name = true, - bool is_qualified = false + int synthesized_multi_return_size = 0, + bool is_local_name = true, + bool is_qualified = false, + bool is_class_member_access = false ) -> void { STACKINSTR assert( n.identifier ); auto last_use = is_definite_last_use(n.identifier); + auto decl = sema.get_declaration_of(*n.identifier, false, true); + bool add_forward = last_use && last_use->is_forward - && !in_non_rvalue_context.back(); + && last_use->safe_to_move + && !in_non_rvalue_context.back() + && !is_class_member_access; bool add_move = !add_forward && ( - in_synthesized_multi_return + !last_use + || last_use->safe_to_move + ) + && ( + synthesized_multi_return_size > 1 + || ( + synthesized_multi_return_size == 1 + && decl + && !decl->initializer + ) || (last_use && !suppress_move_from_last_use) ) - && !in_non_rvalue_context.back(); + && !in_non_rvalue_context.back() + && !is_class_member_access; + + // Add `std::move(*this).` when implicitly moving a member on last use + // This way, members of lvalue reference type won't be implicitly moved + bool add_this = + add_move + && synthesized_multi_return_size == 0 + && decl + && decl->identifier + && *decl->identifier == "this" + && *n.identifier != "this"; if ( add_move && *(n.identifier - 1) == "return" && *(n.identifier + 1) == ";" + && ( + !decl + || decl->initializer + ) ) { add_move = false; @@ -1800,6 +1826,9 @@ class cppfront if (add_forward) { printer.print_cpp2("CPP2_FORWARD(", {n.position().lineno, n.position().colno - 8}); } + if (add_this) { + printer.print_cpp2("*this).", n.position()); + } assert(n.identifier); emit(*n.identifier, is_qualified); // inform the identifier if we know this is qualified @@ -1824,13 +1853,13 @@ class cppfront && !in_parameter_list ) { - if (auto decl = sema.get_declaration_of(*n.identifier); + if ( is_local_name && !(*n.identifier == "this") && !(*n.identifier == "that") && decl && ( - in_synthesized_multi_return + synthesized_multi_return_size != 0 // note pointer equality: if we're not in the actual declaration of n.identifier || decl->identifier != n.identifier ) @@ -1849,13 +1878,16 @@ class cppfront printer.print_cpp2(".value()", n.position()); } } - else if (in_synthesized_multi_return) { + else if (synthesized_multi_return_size != 0) { printer.print_cpp2(".value()", n.position()); } if ( - add_move - || add_forward + !add_this + && ( + add_move + || add_forward + ) ) { printer.print_cpp2(")", n.position()); @@ -1883,7 +1915,7 @@ class cppfront if (id.scope_op) { emit(*id.scope_op); } - emit(*id.id, false, true, true); // inform the unqualified-id that it's qualified + emit(*id.id, 0, true, true); // inform the unqualified-id that it's qualified } printer.emit_to_string(); @@ -1907,7 +1939,7 @@ class cppfront printer.print_cpp2("auto", pos); } else { - try_emit(n.id, false, false); + try_emit(n.id, 0, false); try_emit(n.id); try_emit(n.id); } @@ -1923,12 +1955,13 @@ class cppfront // auto emit( id_expression_node const& n, - bool is_local_name = true + bool is_local_name = true, + bool is_class_member_access = false ) -> void { STACKINSTR try_emit(n.id); - try_emit(n.id, false, is_local_name); + try_emit(n.id, 0, is_local_name, false, is_class_member_access); } @@ -1974,7 +2007,7 @@ class cppfront //----------------------------------------------------------------------- // auto emit( - compound_statement_node const& n, + compound_statement_node const& n, function_prolog const& function_prolog = {}, std::vector const& function_epilog = {} ) @@ -2189,8 +2222,6 @@ class cppfront -> void { STACKINSTR assert(n.identifier); - in_non_rvalue_context.push_back(true); - auto guard = finally([&]{ in_non_rvalue_context.pop_back(); }); iteration_statements.push_back({ &n, false}); auto labelname = std::string{}; @@ -2394,6 +2425,7 @@ class cppfront else if ( !is_parameter_name && sema.get_declaration_of(*tok) + && !sema.is_captured(*tok) ) { errors.emplace_back( @@ -2487,7 +2519,7 @@ class cppfront assert(param->declaration->identifier); printer.emit_to_string(&stmt); - emit(*param->declaration->identifier, true); + emit(*param->declaration->identifier, cpp2::unsafe_narrow(parameters.size())); printer.emit_to_string(); } @@ -2858,8 +2890,8 @@ class cppfront } - auto source_order_name_lookup(unqualified_id_node const& id) - -> source_order_name_lookup_res + auto source_order_name_lookup(std::string_view identifier) + -> source_order_name_lookup_res { for ( auto first = current_names.rbegin(), last = current_names.rend() - 1; @@ -2871,7 +2903,7 @@ class cppfront auto decl = get_if(&*first); decl && *decl - && (*decl)->has_name(*id.identifier) + && (*decl)->has_name(identifier) ) { return *decl; @@ -2880,7 +2912,7 @@ class cppfront auto using_ = get_if(&*first); using_ && using_->identifier - && *using_->identifier == *id.identifier + && *using_->identifier == identifier ) { return *using_; @@ -2890,6 +2922,38 @@ class cppfront return {}; } + auto lookup_finds_type_scope_function(id_expression_node const& n) + -> bool + { + if (!n.is_unqualified()) + { + return false; + } + + auto const& id = *get(n.id); + auto lookup = source_order_name_lookup(*id.identifier); + + if ( + !lookup + || get_if(&*lookup) + ) + { + return false; + } + + auto decl = get(*lookup); + + if ( + !decl->is_function() + || !decl->has_name() + || !decl->parent_is_type() + ) + { + return false; + } + + return true; + } auto lookup_finds_variable_with_placeholder_type_under_initialization(id_expression_node const& n) -> bool @@ -2900,7 +2964,7 @@ class cppfront } auto const& id = *get(n.id); - auto lookup = source_order_name_lookup(id); + auto lookup = source_order_name_lookup(*id.identifier); if ( !lookup @@ -3220,7 +3284,7 @@ class cppfront "(" + print_to_string(id, false) + "::)," - + print_to_string(*cpp2::assert_not_null(id.ids.back().id), false, true, true); + + print_to_string(*cpp2::assert_not_null(id.ids.back().id), 0, true, true); } ufcs_string += "_TEMPLATE"; } @@ -3255,9 +3319,41 @@ class cppfront ufcs_string += "_NONLOCAL"; } + // If it's a last use, emit as part of the macro name + // + // Note: This ensures a valid member call is still prioritized + // by leveraging the last use only in the non-member branch + // For example, `x.f()` won't emit as 'CPP2_UFCS(std::move(f))(x)' + // to never take the branch that wants to call `x.std::move(f)()` + if (auto last_use = is_definite_last_use(i->id_expr->get_token()); + last_use + && last_use->safe_to_move + && !lookup_finds_type_scope_function(*i->id_expr) + ) + { + if (last_use->is_forward) { + ufcs_string += "_FORWARD"; + } + else { + ufcs_string += "_MOVE"; + } + + in_non_rvalue_context.push_back(true); + funcname = print_to_string(*i->id_expr); + in_non_rvalue_context.pop_back(); + } + // Second, emit the UFCS argument list - prefix.emplace_back(ufcs_string + "(" + funcname + ")(", args.value().open_pos ); + // If the computed function name is an explicit member access + // we don't need to go through the UFCS macro + // Note: This also works around compiler bugs + if (funcname.starts_with("std::move(*this).")) { + prefix.emplace_back(funcname + "(", args.value().open_pos ); + } + else { + prefix.emplace_back(ufcs_string + "(" + funcname + ")(", args.value().open_pos ); + } suffix.emplace_back(")", args.value().close_pos ); if (!args.value().text_chunks.empty()) { for (auto&& e: args.value().text_chunks) { @@ -3355,7 +3451,7 @@ class cppfront args.reset(); } - auto print = print_to_string(*i->id_expr, false /*not a local name*/); + auto print = print_to_string(*i->id_expr, false /*not a local name*/, i->op->type() == lexeme::Dot); suffix.emplace_back( print, i->id_expr->position() ); } @@ -5443,7 +5539,7 @@ class cppfront auto guard0 = stack_value(having_signature_emitted, &n); auto guard1 = stack_element(current_declarations, &n); current_names.push_back(&n); - auto guard2 = stack_size_if(current_names, n.is_function()); + auto guard2 = stack_size_if(current_names, !n.is_namespace()); // Handle aliases @@ -5955,6 +6051,7 @@ class cppfront separator = ","; } } + current_names.push_back(decl.get()); } // Then we'll switch to start the body == other members else