diff --git a/.gitignore b/.gitignore index 988671321..0616ce2a6 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 eba99ccef..0f49eb2f3 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 ee6e5ff18..ce862c105 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 000000000..4de176cc4 --- /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 000000000..eed816720 --- /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 c85646723..ca69e6272 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 000000000..4632e068d --- /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 000000000..e69de29bb 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 575495509..77144b5ba 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 12f7abc9f..d966d0dbc 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 8d5dd5856..dfc83f19d 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 000000000..5618be2c9 --- /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 000000000..4632e068d --- /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 000000000..e69de29bb 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 50d1dd852..0aa7df2e3 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 000000000..4632e068d --- /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 000000000..e69de29bb 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 018df8da8..ebc3a1b02 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 1ed322444..118ac0d71 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 5cfec66f9..a5f453a0d 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 7fcfc8177..51a261dda 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 9d5b37d18..eed1c1f7a 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 9e38b1c44..7bcef6d14 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 86a34ef3c..33c58818d 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 4dc02741c..ca01a21cd 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 bd5225f04..22c9fe06e 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 000000000..4632e068d --- /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 000000000..79703bb60 --- /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 b59bad66c..24e0ad67e 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 013414c08..ceb05566b 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 159b73ef5..7f5cd76fe 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 66bd67b33..f8f68b112 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 8eb597f98..3f2341f20 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 3e2e505ae..6ffe1b454 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 d8eef9595..2aa736690 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 3a28ce7c9..5ad0a40d2 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 000000000..e39c71621 --- /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 000000000..8070699f1 --- /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 000000000..fd9a45cd0 --- /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 9d5f0ac24..ffcb47f82 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 d6c36d4c5..0fe8feae7 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 0d2dcc2a7..0a7bc091a 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 d2079519f..01c1fe6f5 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 2ffed4548..7594c3745 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 d38a66b4a..c94d650ca 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 ca3139ec7..e5bd13ca6 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 64d653993..031b3390c 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 dd634379a..07c3b496a 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 390b12f85..9e9a7821f 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 462f96a76..48e56ffc3 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 b6ca22c5e..d872df7df 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 9b16e6eaf..4c18356eb 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 c071d8c46..2730ba14b 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 8c6b06128..290c254b4 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 8a11de9ca..a29037334 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 28c3983a9..ecc98d3e0 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