Skip to content

Commit 510eae8

Browse files
JohelEGPhsutter
andauthored
fix: recognize last uses (including of this) (#887)
* 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 <https://cpp1.godbolt.org/z/6fhjxrz65>. * 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: e9cc033, 7f4a60f * 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<int>()` * 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 4eef0da 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 <#231 (comment)>: > 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 <johelegp@gmail.com> Signed-off-by: Herb Sutter <herb.sutter@gmail.com> Co-authored-by: Herb Sutter <herb.sutter@gmail.com>
1 parent 8412611 commit 510eae8

File tree

56 files changed

+3874
-413
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

56 files changed

+3874
-413
lines changed

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
*.exe
2929
source/gen_version.bat
3030
build*/
31+
*.ifc
3132

3233
# Visual Studio cache directory
3334
.vs/

include/cpp2util.h

+26-23
Original file line numberDiff line numberDiff line change
@@ -873,6 +873,7 @@ class out {
873873
#endif
874874
#endif
875875

876+
#define CPP2_UFCS_IDENTITY(...) __VA_ARGS__
876877
#define CPP2_UFCS_REMPARENS(...) __VA_ARGS__
877878

878879
// Ideally, the expression `CPP2_UFCS_IS_NOTHROW` expands to
@@ -881,18 +882,18 @@ class out {
881882
// we instead make it a template parameter of the UFCS lambda.
882883
// But using a template parameter, Clang also ICEs on an application.
883884
// So we use these `NOTHROW` macros to fall back to the ideal for when not using GCC.
884-
#define CPP2_UFCS_IS_NOTHROW(QUALID,TEMPKW,...) \
885+
#define CPP2_UFCS_IS_NOTHROW(MVFWD,QUALID,TEMPKW,...) \
885886
requires { requires requires { std::declval<Obj>().CPP2_UFCS_REMPARENS QUALID TEMPKW __VA_ARGS__(std::declval<Params>()...); }; \
886887
requires noexcept(std::declval<Obj>().CPP2_UFCS_REMPARENS QUALID TEMPKW __VA_ARGS__(std::declval<Params>()...)); } \
887888
|| requires { requires !requires { std::declval<Obj>().CPP2_UFCS_REMPARENS QUALID TEMPKW __VA_ARGS__(std::declval<Params>()...); }; \
888-
requires noexcept(CPP2_UFCS_REMPARENS QUALID __VA_ARGS__(std::declval<Obj>(), std::declval<Params>()...)); }
889-
#define CPP2_UFCS_IS_NOTHROW_PARAM(...) /*empty*/
890-
#define CPP2_UFCS_IS_NOTHROW_ARG(QUALID,TEMPKW,...) CPP2_UFCS_IS_NOTHROW(QUALID,TEMPKW,__VA_ARGS__)
889+
requires noexcept(MVFWD(CPP2_UFCS_REMPARENS QUALID __VA_ARGS__)(std::declval<Obj>(), std::declval<Params>()...)); }
890+
#define CPP2_UFCS_IS_NOTHROW_PARAM(...) /*empty*/
891+
#define CPP2_UFCS_IS_NOTHROW_ARG(MVFWD,QUALID,TEMPKW,...) CPP2_UFCS_IS_NOTHROW(MVFWD,QUALID,TEMPKW,__VA_ARGS__)
891892
#if defined(__GNUC__) && !defined(__clang__)
892893
#undef CPP2_UFCS_IS_NOTHROW_PARAM
893894
#undef CPP2_UFCS_IS_NOTHROW_ARG
894-
#define CPP2_UFCS_IS_NOTHROW_PARAM(QUALID,TEMPKW,...) , bool IsNothrow = CPP2_UFCS_IS_NOTHROW(QUALID,TEMPKW,__VA_ARGS__)
895-
#define CPP2_UFCS_IS_NOTHROW_ARG(...) IsNothrow
895+
#define CPP2_UFCS_IS_NOTHROW_PARAM(MVFWD,QUALID,TEMPKW,...) , bool IsNothrow = CPP2_UFCS_IS_NOTHROW(MVFWD,QUALID,TEMPKW,__VA_ARGS__)
896+
#define CPP2_UFCS_IS_NOTHROW_ARG(...) IsNothrow
896897
#if __GNUC__ < 11
897898
#undef CPP2_UFCS_IS_NOTHROW_PARAM
898899
#undef CPP2_UFCS_IS_NOTHROW_ARG
@@ -909,41 +910,43 @@ class out {
909910
// But using a template parameter, Clang also ICEs and GCC rejects a local 'F'.
910911
// Also, Clang rejects the SFINAE test case when using 'std::declval'.
911912
// So we use these `CONSTRAINT` macros to fall back to the ideal for when not using MSVC.
912-
#define CPP2_UFCS_CONSTRAINT_PARAM(...) /*empty*/
913-
#define CPP2_UFCS_CONSTRAINT_ARG(QUALID,TEMPKW,...) \
913+
#define CPP2_UFCS_CONSTRAINT_PARAM(...) /*empty*/
914+
#define CPP2_UFCS_CONSTRAINT_ARG(MVFWD,QUALID,TEMPKW,...) \
914915
requires { CPP2_FORWARD(obj).CPP2_UFCS_REMPARENS QUALID TEMPKW __VA_ARGS__(CPP2_FORWARD(params)...); } \
915-
|| requires { CPP2_UFCS_REMPARENS QUALID __VA_ARGS__(CPP2_FORWARD(obj), CPP2_FORWARD(params)...); }
916+
|| requires { MVFWD(CPP2_UFCS_REMPARENS QUALID __VA_ARGS__)(CPP2_FORWARD(obj), CPP2_FORWARD(params)...); }
916917
#if defined(_MSC_VER)
917918
#undef CPP2_UFCS_CONSTRAINT_PARAM
918919
#undef CPP2_UFCS_CONSTRAINT_ARG
919-
#define CPP2_UFCS_CONSTRAINT_PARAM(QUALID,TEMPKW,...) , bool IsViable = \
920+
#define CPP2_UFCS_CONSTRAINT_PARAM(MVFWD,QUALID,TEMPKW,...) , bool IsViable = \
920921
requires { std::declval<Obj>().CPP2_UFCS_REMPARENS QUALID TEMPKW __VA_ARGS__(std::declval<Params>()...); } \
921-
|| requires { CPP2_UFCS_REMPARENS QUALID __VA_ARGS__(std::declval<Obj>(), std::declval<Params>()...); }
922+
|| requires { MVFWD(CPP2_UFCS_REMPARENS QUALID __VA_ARGS__)(std::declval<Obj>(), std::declval<Params>()...); }
922923
#define CPP2_UFCS_CONSTRAINT_ARG(...) IsViable
923924
#endif
924925

925-
#define CPP2_UFCS_(LAMBDADEFCAPT,QUALID,TEMPKW,...) \
926+
#define CPP2_UFCS_(LAMBDADEFCAPT,MVFWD,QUALID,TEMPKW,...) \
926927
[LAMBDADEFCAPT]< \
927928
typename Obj, typename... Params \
928-
CPP2_UFCS_IS_NOTHROW_PARAM(QUALID,TEMPKW,__VA_ARGS__) \
929-
CPP2_UFCS_CONSTRAINT_PARAM(QUALID,TEMPKW,__VA_ARGS__) \
929+
CPP2_UFCS_IS_NOTHROW_PARAM(MVFWD,QUALID,TEMPKW,__VA_ARGS__) \
930+
CPP2_UFCS_CONSTRAINT_PARAM(MVFWD,QUALID,TEMPKW,__VA_ARGS__) \
930931
> \
931932
CPP2_LAMBDA_NO_DISCARD (Obj&& obj, Params&& ...params) CPP2_FORCE_INLINE_LAMBDA_CLANG \
932-
noexcept(CPP2_UFCS_IS_NOTHROW_ARG(QUALID,TEMPKW,__VA_ARGS__)) CPP2_FORCE_INLINE_LAMBDA -> decltype(auto) \
933-
requires CPP2_UFCS_CONSTRAINT_ARG(QUALID,TEMPKW,__VA_ARGS__) { \
933+
noexcept(CPP2_UFCS_IS_NOTHROW_ARG(MVFWD,QUALID,TEMPKW,__VA_ARGS__)) CPP2_FORCE_INLINE_LAMBDA -> decltype(auto) \
934+
requires CPP2_UFCS_CONSTRAINT_ARG(MVFWD,QUALID,TEMPKW,__VA_ARGS__) { \
934935
if constexpr (requires{ CPP2_FORWARD(obj).CPP2_UFCS_REMPARENS QUALID TEMPKW __VA_ARGS__(CPP2_FORWARD(params)...); }) { \
935936
return CPP2_FORWARD(obj).CPP2_UFCS_REMPARENS QUALID TEMPKW __VA_ARGS__(CPP2_FORWARD(params)...); \
936937
} else { \
937-
return CPP2_UFCS_REMPARENS QUALID __VA_ARGS__(CPP2_FORWARD(obj), CPP2_FORWARD(params)...); \
938+
return MVFWD(CPP2_UFCS_REMPARENS QUALID __VA_ARGS__)(CPP2_FORWARD(obj), CPP2_FORWARD(params)...); \
938939
} \
939940
}
940941

941-
#define CPP2_UFCS(...) CPP2_UFCS_(&,(),,__VA_ARGS__)
942-
#define CPP2_UFCS_TEMPLATE(...) CPP2_UFCS_(&,(),template,__VA_ARGS__)
943-
#define CPP2_UFCS_QUALIFIED_TEMPLATE(QUALID,...) CPP2_UFCS_(&,QUALID,template,__VA_ARGS__)
944-
#define CPP2_UFCS_NONLOCAL(...) CPP2_UFCS_(,(),,__VA_ARGS__)
945-
#define CPP2_UFCS_TEMPLATE_NONLOCAL(...) CPP2_UFCS_(,(),template,__VA_ARGS__)
946-
#define CPP2_UFCS_QUALIFIED_TEMPLATE_NONLOCAL(QUALID,...) CPP2_UFCS_(,QUALID,template,__VA_ARGS__)
942+
#define CPP2_UFCS(...) CPP2_UFCS_(&,CPP2_UFCS_IDENTITY,(),,__VA_ARGS__)
943+
#define CPP2_UFCS_MOVE(...) CPP2_UFCS_(&,std::move,(),,__VA_ARGS__)
944+
#define CPP2_UFCS_FORWARD(...) CPP2_UFCS_(&,CPP2_FORWARD,(),,__VA_ARGS__)
945+
#define CPP2_UFCS_TEMPLATE(...) CPP2_UFCS_(&,CPP2_UFCS_IDENTITY,(),template,__VA_ARGS__)
946+
#define CPP2_UFCS_QUALIFIED_TEMPLATE(QUALID,...) CPP2_UFCS_(&,CPP2_UFCS_IDENTITY,QUALID,template,__VA_ARGS__)
947+
#define CPP2_UFCS_NONLOCAL(...) CPP2_UFCS_(,CPP2_UFCS_IDENTITY,(),,__VA_ARGS__)
948+
#define CPP2_UFCS_TEMPLATE_NONLOCAL(...) CPP2_UFCS_(,CPP2_UFCS_IDENTITY,(),template,__VA_ARGS__)
949+
#define CPP2_UFCS_QUALIFIED_TEMPLATE_NONLOCAL(QUALID,...) CPP2_UFCS_(,CPP2_UFCS_IDENTITY,QUALID,template,__VA_ARGS__)
947950

948951

949952
//-----------------------------------------------------------------------

regression-tests/pure2-deducing-pointers-error.cpp2

-5
Original file line numberDiff line numberDiff line change
@@ -28,11 +28,6 @@ main: () -> int = {
2828
pa5 := pppa**;
2929
pa5 = 0; // caught
3030

31-
// TODO: @filipsajdak please take a look
32-
// The bugfix in get_declaration_of(t) to add `&& ri->position() <= t.position()`
33-
// to the condition is correct; it fixes issue #669 by not looking past the first
34-
// declaration of the name in t. However, that change made the following two
35-
// "caught" cases no longer be caught.
3631
fun(a)++; // caught
3732
fp := fun(a);
3833
fp = 0; // caught
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
issue_890: (x) = { }

0 commit comments

Comments
 (0)