[Bug c++/113349] New: internal compiler error: in tsubst

2024-01-12 Thread federico at kircheis dot it via Gcc-bugs
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=113349

Bug ID: 113349
   Summary: internal compiler error: in tsubst
   Product: gcc
   Version: 14.0
Status: UNCONFIRMED
  Severity: normal
  Priority: P3
 Component: c++
  Assignee: unassigned at gcc dot gnu.org
  Reporter: federico at kircheis dot it
  Target Milestone: ---

While trying to destructure a tuple, I got this internal compiler error


: In substitution of 'template template
using types(), void()))>::el = std::tuple_element_t())> [with long unsigned int N = 1; T = s]':
:36:50:   required from here
   36 | static_assert(std::is_same_v::el<1>>);
  |  ^
:7:28: internal compiler error: in tsubst, at cp/pt.cc:16312
7 | #define MAKE_TUPLE(V, ...) []{\
  |^~~~
8 | auto [ __VA_ARGS__ ] = makeV(); \
  | 
9 | return std::make_tuple( __VA_ARGS__);\
  | ~~
   10 | }()
  | ~   
:19:26: note: in expansion of macro 'MAKE_TUPLE'
   19 | struct types {
  |  ^~
0x2640f8c internal_error(char const*, ...)
???:0
0xa502dd fancy_abort(char const*, int, char const*)
???:0
0xc92c62 tsubst(tree_node*, tree_node*, int, tree_node*)
???:0
0xca446c tsubst_lambda_expr(tree_node*, tree_node*, int, tree_node*)
???:0
0xc93417 tsubst(tree_node*, tree_node*, int, tree_node*)
???:0
0xc99729 tsubst_template_args(tree_node*, tree_node*, int, tree_node*)
???:0
0xc7c365 instantiate_template(tree_node*, tree_node*, int)
???:0
0xc9420c tsubst(tree_node*, tree_node*, int, tree_node*)
???:0
0xc914dc lookup_template_class(tree_node*, tree_node*, tree_node*, tree_node*,
int, int)
???:0
0xccd98f finish_template_type(tree_node*, tree_node*, int)
???:0
0xc51fda c_parse_file()
???:0
0xda59d9 c_common_parse_file()
???:0
Please submit a full bug report, with preprocessed source (by using
-freport-bug).
Please include the complete backtrace with any bug report.
See  for instructions.
Compiler returned: 1


The code looks like


#include 
#include 

template 
V makeV();

#define MAKE_TUPLE(V, ...) []{\
auto [ __VA_ARGS__ ] = makeV(); \
return std::make_tuple( __VA_ARGS__);\
}()


template 
struct types{
using type = void;
};

template 
struct types {
template
using el = std::tuple_element_t;
};
/*
template 
struct types {
template
using el = std::tuple_element_t;
};
*/
struct s{
int& i;
char* b;
float f;
};
//static_assert(std::is_same_v::el<0>>); // fails
static_assert(std::is_same_v::el<1>>);
static_assert(std::is_same_v::el<2>>);


This code compiles fine with clang and MSVC, and is, to the best of my
knowledge, valid C++

Online example with latest gcc (and clang msvc too)

https://godbolt.org/z/sPxK5avEW

[Bug c++/112553] noexcept lambda does not generate call to terminate

2023-11-15 Thread federico at kircheis dot it via Gcc-bugs
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=112553

--- Comment #3 from Federico Kircheis  ---
I stand corrected, sorry for the report.


Where can I find official information/documentation about the personality
function used by gcc?

The fact that clang and msvc handle this case differently made me believe gcc
was incorrect.

[Bug c++/112553] New: noexcept lambda does not generate call to terminate

2023-11-15 Thread federico at kircheis dot it via Gcc-bugs
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=112553

Bug ID: 112553
   Summary: noexcept lambda does not generate call to terminate
   Product: gcc
   Version: 13.2.1
Status: UNCONFIRMED
  Severity: normal
  Priority: P3
 Component: c++
  Assignee: unassigned at gcc dot gnu.org
  Reporter: federico at kircheis dot it
  Target Milestone: ---

Consider following example


void do_something();

void bar(){
[]() noexcept{
do_something();
}();
}




If "do_something" throws, as the lambda is marked noexcept, there should be a
call to std::terminate, but it is not the case: https://godbolt.org/z/xjqzTdrWG

clang and msvc generate the call to std::terminate

[Bug c++/112546] -Wmaybe-uninitialized and -Wuninitialized does not detect usage of uninitilazed value in a lambda

2023-11-15 Thread federico at kircheis dot it via Gcc-bugs
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=112546

--- Comment #1 from Federico Kircheis  ---
I looked at some reports at https://gcc.gnu.org/bugzilla/show_bug.cgi?id=24639

It seems that most related issues rely on global variables or parameters, but
in fact I found some other examples with local variables


void foo(int);

void baz1(){
int i;
[&]{++i;}(); // no warning
foo(i);
}

void baz2(){
int i;
++i; // warning
foo(i);
}

void baz3(){
int i;
int& r = i;
++r; // warning
foo(i);
}

void baz4(){
int i;
int& r = i;
int& rr = r;
++rr; // no warning
foo(i);
}

void baz5(){
int i;
struct {int& j;}  r{i};
++r.j; // no warning
foo(i);
}



only baz2 and baz3 trigger a warning, in all function a local uninitialized
variable is unconditionally modified.
(the call to function foo is to ensure that the code is not marked as
dead/unused and thus ignored)

[Bug c++/112546] New: -Wmaybe-uninitialized and -Wuninitialized does not detect usage of uninitilazed value in a lambda

2023-11-15 Thread federico at kircheis dot it via Gcc-bugs
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=112546

Bug ID: 112546
   Summary: -Wmaybe-uninitialized and -Wuninitialized does not
detect usage of uninitilazed value in a lambda
   Product: gcc
   Version: 13.2.1
Status: UNCONFIRMED
  Severity: normal
  Priority: P3
 Component: c++
  Assignee: unassigned at gcc dot gnu.org
  Reporter: federico at kircheis dot it
  Target Milestone: ---

I noticed the issue when comparing this three equivalent bar functions:



struct data{
int i;
};
data foo();
void bar(const data& d);

void bar1(){
data d = [&]{bar(d); return foo();}();
}

void bar2(){
data d = (bar(d), void(), foo());
}


void bar3(){
data d;
bar(d);
d = foo();
}



example on godbolt: https://godbolt.org/z/Wxj6rbKb9
With "-Wmaybe-uninitialized", GCC detects in bar2 and bar3 that d is possible
used inside bar without being initialized, but it fails to do so when using a
lambda.

In fact, I noticed that it fails to report (with "-Wuninitialized") even when
the value is used directly in the lambda:



void bar4(){
data d = [&]{ ++d.i; bar(d); return foo();}();
}


[Bug c++/112335] missed optimization on reset and assign unique_ptr

2023-11-03 Thread federico at kircheis dot it via Gcc-bugs
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=112335

--- Comment #9 from Federico Kircheis  ---
Great, thank you for the clarifications, your redacted example makes now sense.


> https://godbolt.org/z/WGPTesEb3 shows that bar3 is not the same as bar1, 
> because it runs an additional destructor if operator delete() plays silly 
> games.

And this is what I meant before that such program is not supported by the
standard


void bar3(std::unique_ptr& ps1, std::unique_ptr& ps2){
ps1.reset();
ps1.reset();
ps1 = std::move(ps2);
}
---

is equivalent to


void bar3(std::unique_ptr& ps1, std::unique_ptr& ps2){
ps1.reset();
assert(ps1 == nullptr);
ps1.reset();
assert(ps1 == nullptr);
ps1 = std::move(ps2);
}


which is not.

And the same holds if "if (!global) global.reset(new s);" is moved into "~s()",
and removed from "operator delete".

If the users changes delete in such a way, at this point we have UB, as the
postcondition of unique_ptr::reset does not hold (the example given by Andrew)

Yes, the second ps1.reset(); is trivial to remove.

[Bug c++/112335] missed optimization on reset and assign unique_ptr

2023-11-03 Thread federico at kircheis dot it via Gcc-bugs
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=112335

--- Comment #7 from Federico Kircheis  ---
Thank you Jonathan, I've then misunderstood the example of Andrew, I thought he
was trying to create a scenario where after reset the pointer is not nullptr.

> Demo: https://godbolt.org/z/PWd96j4fz

Thank you for the example, but honestly, I think I am still missing something.

If you modify the main this way


int main()
{
global = nullptr;
std::unique_ptr local1(new s);
bar1(global, local1);
global = nullptr;
std::unique_ptr local2(new s);
bar1(global, local2);
}



or this way



int main()
{
global = nullptr;
std::unique_ptr local1(new s);
bar2(global, local1);
global = nullptr;
std::unique_ptr local2(new s);
bar2(global, local2);
}


the output is the same.
First time it prints 0, the second time a non-0 address.

> But a much simpler example where the difference is observable is when the two 
> arguments to bar1 and bar2 are the same object:
> https://godbolt.org/z/z8fsTan8K

And this is absolutely correct, did not think of it.

> I'm not sure what you mean here. The postcondition holds unless the deleter 
> *also* destroys the unique_ptr itself.

As written, I was not sure if this plays a role; this is what the standard says
(and I#ve misunderstood the example of Andrew).
A similar effect can be observed in the following example


#include 

using s = int; // for simplicity

void bar1(std::unique_ptr& ps1, std::unique_ptr& ps2){
ps1.reset();
ps1 = std::move(ps2);
}
void bar2(std::unique_ptr& ps1, std::unique_ptr& ps2){
ps1 = std::move(ps2);
}

void bar3(std::unique_ptr& ps1, std::unique_ptr& ps2){
ps1.reset();
ps1.reset();
ps1 = std::move(ps2);
}


While you are right that bar1 and bar2 have different behavior if they point to
the same object, bar1 and bar3 are fundamentally the same function, even in
case of aliasing.
Unless the post-condition of reset is not hold; otherwise the second
"ps1.reset()" could be completely removed, as ps1 is nullptr.

[Bug c++/112335] missed optimization on reset and assign unique_ptr

2023-11-02 Thread federico at kircheis dot it via Gcc-bugs
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=112335

--- Comment #5 from Federico Kircheis  ---
Ok, the described case would be something like


std::unique_ptr t;
__thread bool tt;
inline s::~s()
{
  if (tt)
return;
  tt = true;
  t.reset(new s);
  tt = false;
}

std::unique_ptr t2;
bar(t, t2);



The postcondition of p.reset() is that p == nullptr.


void reset(pointer p = pointer()) noexcept;
Preconditions: The expression get_deleter()(get()) is well-formed, has
well-defined behavior, and does not throw exceptions.
Effects: Assigns p to the stored pointer, and then if and only if the old value
of the stored pointer, old_p, was not equal to nullptr, calls
get_deleter()(old_p). [Note: The order of these operations is significant
because the call to get_deleter() may destroy *this. — end note]
Postconditions: get() == p. [Note: The postcondition does not hold if the call
to get_deleter() destroys *this since this->get() is no longer a valid
expression. — end note]


I do not think that the case you have in mind is supported by the standard, but
the postcondition does not hold with some deleters, so not sure if that could
be relevant here

[Bug c++/112335] missed optimization on reset and assign unique_ptr

2023-11-02 Thread federico at kircheis dot it via Gcc-bugs
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=112335

--- Comment #3 from Federico Kircheis  ---
Or maybe I've misunderstood your comment.

Do you have a specific scenario in mind where the two snippets would exhibit
different behaviors?

[Bug c++/112335] missed optimization on reset and assign unique_ptr

2023-11-02 Thread federico at kircheis dot it via Gcc-bugs
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=112335

--- Comment #2 from Federico Kircheis  ---
> Well s::~s could touch the reference std::unique_ptr (ps1).

In both cases, s::~s is called only once.

Also during the move-assignment no user-provided-code is involved (except the
destruction of ps1, and even case of a user-provided deleter, it is only called
if the pointer is not nullptr.

[Bug c++/112335] New: missed optimization on reset and assign unique_ptr

2023-11-01 Thread federico at kircheis dot it via Gcc-bugs
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=112335

Bug ID: 112335
   Summary: missed optimization on reset and assign unique_ptr
   Product: gcc
   Version: 14.0
Status: UNCONFIRMED
  Severity: normal
  Priority: P3
 Component: c++
  Assignee: unassigned at gcc dot gnu.org
  Reporter: federico at kircheis dot it
  Target Milestone: ---

Given following snippet


#include 

struct s{
s();
~s() noexcept;
};



void bar1(std::unique_ptr& ps1, std::unique_ptr& ps2){
ps1.reset();
ps1 = std::move(ps2);
}

void bar2(std::unique_ptr& ps1, std::unique_ptr& ps2){
ps1 = std::move(ps2);
}


If I am not mistaken, bar1 and bar2 should have exactly the same behavior, but
on https://godbolt.org/z/q4nKsPq7z it is possible to see that the generated
code differs.

[Bug c++/111388] New: std:.get_if variant, unnecessary branch when outside of if statement

2023-09-12 Thread federico at kircheis dot it via Gcc-bugs
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=111388

Bug ID: 111388
   Summary: std:.get_if variant, unnecessary branch when outside
of if statement
   Product: gcc
   Version: 13.2.1
Status: UNCONFIRMED
  Severity: normal
  Priority: P3
 Component: c++
  Assignee: unassigned at gcc dot gnu.org
  Reporter: federico at kircheis dot it
  Target Milestone: ---

Example of code (https://godbolt.org/z/M1bWf5sz3)



#include 

struct interface{
virtual ~interface()= default;

virtual int foo() const = 0;
};

struct a : interface{
int foo() const override;
};
struct b : interface{
int foo() const override;
};

class a_or_b{
std::variant ab;
public:
a_or_b() = delete;
a_or_b(a _) : ab(_){}
a_or_b(b _) : ab(_){}
interface* operator->() noexcept {
if (interface* ptr = std::get_if<0>(); ptr) {
return ptr;
} 
#if 0
else if (interface* ptr = std::get_if<1>(); ptr) {
   return ptr;
}
#else
return std::get_if<1>();
#endif
}
};


int bar3(a_or_b& ab){
return ab->foo()+1;
}



With `#if 1`, the generated code looks like


bar3(a_or_b&):
sub rsp, 8
mov rax, QWORD PTR [rdi]
call[QWORD PTR [rax+16]]
add rsp, 8
add eax, 1
ret


while with `#if 0`, the assembly looks like


bar3(a_or_b&):
cmp BYTE PTR [rdi+8], 0
jne .L2
sub rsp, 8
mov rax, QWORD PTR [rdi]
call[QWORD PTR [rax+16]]
add rsp, 8
add eax, 1
ret
bar3(a_or_b&) [clone .cold]:
.L2:
mov rax, QWORD PTR ds:0
ud2


If I'm not mistake, with `return std::get_if<1>();` the compiler verifies if
the return of get_if is nullptr, and if it is, then sets the return value to
nullptr, which is unnecessary.

With `if 1`, the result is passed as is.

AFAIK the generated assembly is functionally equivalant, but the "more safe"(1)
version less optimal


1) more safe as in "there is no UB if the class changes and the variant could
be empty or hold another type".

NOTE: replacing "std::get_if<0>"/"std::get_if<1>" with
"std::get_if/std::get_if" does not make a relevante difference, the
generated code is the same for both "if 0" and "if 1".


For what is worth, clang generates the same code for "if 0" and "if 1".

[Bug c++/101557] the value of '' is not usable in a constant expression

2023-04-16 Thread federico at kircheis dot it via Gcc-bugs
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=101557

--- Comment #5 from Federico Kircheis  ---
Today I just found a possible workaround that involves macros and lambdas...


struct node {
const char* d;
const node& left;
};

#define LEAF(a) []()-> const node&{ constexpr static auto a = node{"a",Null};
return a; }()
#define NODE(a,b) []()-> const node&{constexpr static auto a = node{"a",b};
return a; }()

constexpr node Null = node{"",Null};

constexpr auto a = node{"a",NODE(a,LEAF(b))};
constexpr auto a2 = node{"a",node{"a", node{"a", Null}}};


constexpr int mysize(const node& n) noexcept{
return ( == ) ? 0 : 1 + mysize(n.left); 
}
constexpr auto size0 = mysize(Null);
static_assert(size0 == 0, "");

constexpr auto size3 = mysize(a);
static_assert(size3 == 3, "");


"mysize(a)" compiles, while "mysize(a2)" does not, even if the two
datastructures are  equivalent


Unfortunately clang does not accept it

[Bug c++/107264] New: -O1 disables -Wfree-nonheap-object on a map

2022-10-14 Thread federico at kircheis dot it via Gcc-bugs
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=107264

Bug ID: 107264
   Summary: -O1 disables -Wfree-nonheap-object on a map
   Product: gcc
   Version: 12.2.0
Status: UNCONFIRMED
  Severity: normal
  Priority: P3
 Component: c++
  Assignee: unassigned at gcc dot gnu.org
  Reporter: federico at kircheis dot it
  Target Milestone: ---

Consider following code


#include 

struct Opaque;


void foo1(std::map v){
for (auto itr = v.begin(); itr != v.end(); ++itr) {
delete >second;
}
}


when compiled without parameters, gcc diagnoses "warning: 'void operator
delete(void*, std::size_t)' called on pointer '' with nonzero offset 8
[-Wfree-nonheap-object]
delete >second;"

But when compiling at least with -O1, the diagnosis is done, eve when
explicitely adding "-Wfree-nonheap-object" as parameter:
https://godbolt.org/z/Percs3nqa

I've tested different scenarios (see https://godbolt.org/z/eba9axb1h), with a
for-each loop, for example, the diagnosis is active even with -O1.