[Bug libstdc++/103240] std::type_info::before gives wrong answer for ARM EABI

2023-05-05 Thread frankhb1989 at gmail dot com via Gcc-bugs
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=103240

--- Comment #9 from frankhb1989 at gmail dot com ---
(In reply to Jonathan Wakely from comment #8)

> I don't think that's true. If __GXX_MERGED_TYPEINFO_NAMES is true then the
> out-of-line definition is correct. You can't just redefine that macro to 1
> in your code and expect it to affect the out-of-line definition in the
> library. Even if you recompile the library with 
> -D__GXX_MERGED_TYPEINFO_NAMES=1 that doesn't magically make it true. If the
> platform ABI and compiler and linker really do guarantee that all typeinfo
> names are unique, then address comparison works correctly.
> 

Yes, -D__GXX_MERGED_TYPEINFO_NAMES=1 is harmful and unnecessary to work around
the issue. As illustrated, it just works for some of my cases accidentally, and
fails for some other cases.

Further, I want to make sure no out-of-line definition of type_info::before is
called in the system libraries (which I can't rebuild). Does libstdc++ have
calls to type_info::before internally?

> You can't expect that to give meaningful results though, you would need to
> rebuild the entire compiler for meaningful results with redefined macros,
> and even then it would fail when you use RTTI across dynamic library
> boundaries.
> 
> You can't just redefine those macros and expect the compiler and OS to
> change how they work.
> 

I don't quite get this point about the compiler. Do you mean not only libstdc++
and the linker, but also the compiler's codegen of the mangled names works
differently depending on the macro definitions, besides the possible duplicate
(but having identical mangled names) definitions of the internal objects
providing the mangle names? Will it affect the section layout of the object
code? Are there existing optimizations relying on the assumptions?

I know it will not be formally supported to have consistent behavior just by
rebuilding the user programs, and it will be plain wrong without things like
ICF or in cases dynamic loading is relied on, but I'm curious: what are the
actual consequences from the compiler's view, when (1) all TUs of the program
code eventually use these macro definitions consistently, (2) definitions of
RTTI information for identical types are totally merged; and, (3) each instance
of the call to the out-of-line definition of type_info::before is avoided?

I am interesting in such questions because there are cases where rebuiding the
toolchain is impossible. It is not even possible to rebuilt libstdc++ for
production system unless more than one instance can be deployed side by side
(e.g. by static linking), because I cannot make sure no other part of the
environment have relied on the bug-to-bug compatibility to the existing system
libraries. In my case, I have to make sure which parts of the deployed code are
actually affected before the toolchain update (either system-wide or not) is
possible. And before that, the redefinition of macros seems the only viable
workaround (with certain limitations).

> > Accidentally the
> > inline definition of std::type_info::before does the right thing
> 
> I don't think it's an accident.
> 

Good news to me.

> > (hopefully), so __GXX_TYPEINFO_EQUALITY_INLINE == 1 just works. Otherwise
> > there would be no easy workaround without the modification on the standard
> > headers.
> > 
> > Forcing address comparisons is wrong in general, but with some additional
> > assumptions to rule out all potential offending features, then all type_info
> > objects follow ODR in the strict sense, so this just works. When this is an
> > issue, __GXX_TYPEINFO_EQUALITY_INLINE == 1 && __GXX_MERGED_TYPEINFO_NAMES ==
> > 0 should be safe for all names not from unnamed namespaces. This is a real
> > problem for MinGW (at least with GNU ld which does not perform ICF on
> > PE/COFF AFAIK), where __GXX_TYPEINFO_EQUALITY_INLINE == 1 &&
> > __GXX_MERGED_TYPEINFO_NAMES == 1 causes something like
> > (shared_ptr<...>) not unique across module boundaries, and my code
> > fails elsewhere due to this reason.
> 
> So stop redefining those macros then, you're lying to the compiler.
> 

Sadly, we *have to* lie for some degrees... ODR in ISO C++ is already
conceptually violated in cases when dynamic libraries are taken into account.
There is no such strong guarantee (like ICF) for all implementations, so
implementation-specific details are already kicked in.

> __GXX_MERGED_TYPEINFO_NAMES=1 is a lie on your target. Don't do that.

Agreed for the current case, though.

[Bug libstdc++/103240] std::type_info::before gives wrong answer for ARM EABI

2023-05-04 Thread redi at gcc dot gnu.org via Gcc-bugs
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=103240

--- Comment #8 from Jonathan Wakely  ---
(In reply to frankhb1989 from comment #7)
> (In reply to Jonathan Wakely from comment #6)
> 
> > What are the mangled type names that are unordered? (that's all you need to
> > describe, everything about flat_map and partition_point is irrelevant; just
> > tell us which names are unordered).
> 
> Here are the pair of the unordered names in the actual case:
> 
> 1.
> St5_BindIFPFN3NPL15ReductionStatusERNS0_8TermNodeERNS0_11ContextNodeEESt17ref
> erence_wrapperIS2_ESt12_PlaceholderILi1
> 2.
> St5_BindIFZN3NPL2A112_GLOBAL__N_113DoDefineOrSetILb0EE4CallIJEEENS0_15Reducti
> onStatusERNS0_8TermNodeERNS0_11ContextNodeESt10shared_ptrINS0_11EnvironmentEE
> S8_DpOT_EUlRKS7_RKSD_E_S7_SD_EE

Thanks. This second one has "*St5_BindIFZN3NPL..." in type_info::__name, so
yes, they'll be incorrectly ordered with the out-of-line definition.

t1.before(t2) will do a string comparison, but t2.before(t1) will do a pointer
comparison and depend on the string table layout.

> The unordered pair occurs iff. __GXX_TYPEINFO_EQUALITY_INLINE == 0, i.e. the
> out-of-line definition is used, and regardless to the value of
> __GXX_MERGED_TYPEINFO_NAMES (either 0 or 1).

I don't think that's true. If __GXX_MERGED_TYPEINFO_NAMES is true then the
out-of-line definition is correct. You can't just redefine that macro to 1 in
your code and expect it to affect the out-of-line definition in the library.
Even if you recompile the library with 
-D__GXX_MERGED_TYPEINFO_NAMES=1 that doesn't magically make it true. If the
platform ABI and compiler and linker really do guarantee that all typeinfo
names are unique, then address comparison works correctly.

The problem case is when both __GXX_MERGED_TYPEINFO_NAMES and
__GXX_TYPEINFO_EQUALITY_INLINE are zero.


> As above, I've later tested all 4 combinations for sure.

You can't expect that to give meaningful results though, you would need to
rebuild the entire compiler for meaningful results with redefined macros, and
even then it would fail when you use RTTI across dynamic library boundaries.

You can't just redefine those macros and expect the compiler and OS to change
how they work.

> Accidentally the
> inline definition of std::type_info::before does the right thing

I don't think it's an accident.

> (hopefully), so __GXX_TYPEINFO_EQUALITY_INLINE == 1 just works. Otherwise
> there would be no easy workaround without the modification on the standard
> headers.
> 
> Forcing address comparisons is wrong in general, but with some additional
> assumptions to rule out all potential offending features, then all type_info
> objects follow ODR in the strict sense, so this just works. When this is an
> issue, __GXX_TYPEINFO_EQUALITY_INLINE == 1 && __GXX_MERGED_TYPEINFO_NAMES ==
> 0 should be safe for all names not from unnamed namespaces. This is a real
> problem for MinGW (at least with GNU ld which does not perform ICF on
> PE/COFF AFAIK), where __GXX_TYPEINFO_EQUALITY_INLINE == 1 &&
> __GXX_MERGED_TYPEINFO_NAMES == 1 causes something like
> (shared_ptr<...>) not unique across module boundaries, and my code
> fails elsewhere due to this reason.

So stop redefining those macros then, you're lying to the compiler.

__GXX_MERGED_TYPEINFO_NAMES=1 is a lie on your target. Don't do that.

[Bug libstdc++/103240] std::type_info::before gives wrong answer for ARM EABI

2023-05-04 Thread frankhb1989 at gmail dot com via Gcc-bugs
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=103240

--- Comment #7 from frankhb1989 at gmail dot com ---
(In reply to Jonathan Wakely from comment #6)

> What are the mangled type names that are unordered? (that's all you need to
> describe, everything about flat_map and partition_point is irrelevant; just
> tell us which names are unordered).

Here are the pair of the unordered names in the actual case:

1.
St5_BindIFPFN3NPL15ReductionStatusERNS0_8TermNodeERNS0_11ContextNodeEESt17reference_wrapperIS2_ESt12_PlaceholderILi1
2.
St5_BindIFZN3NPL2A112_GLOBAL__N_113DoDefineOrSetILb0EE4CallIJEEENS0_15ReductionStatusERNS0_8TermNodeERNS0_11ContextNodeESt10shared_ptrINS0_11EnvironmentEES8_DpOT_EUlRKS7_RKSD_E_S7_SD_EE

Here is the reduced program (exactly from the actual case) to show the sequence
and the ordering:

#include 
#include 
#include 
#include 
#include 
#include 
#include 

std::vector v;

namespace ystdex
{

template
struct expanded_caller
{};

}

namespace NPL
{

enum class ReductionStatus
{
Clean
};

struct TermNode
{};

struct ContextNode
{};

struct Environment
{};

namespace A1
{

template
void
bar()
{
static struct Init
{
Init()
{
//  std::cout << typeid(T).name() << std::endl;
v.push_back(typeid(T));
}
} init;
}

template
void
foo(C&&)
{
bar>();
}

template
void
foo2(C&&)
{
bar();
}

namespace
{

struct EvalSequence
{};

}

namespace
{

template
struct DoDefineOrSet
{
template
static ReductionStatus
Call(TermNode& term, ContextNode& ctx, std::shared_ptr p,
TermNode& t, A&&... args)
{
foo2(std::bind([&](const TermNode& saved,
const std::shared_ptr& p_e, const A&...){
return ReductionStatus::Clean;
}, std::move(t), std::move(p), std::move(args)...));
return ReductionStatus::Clean;
}
};

template
ReductionStatus
LambdaVauWithEnvironment(TermNode&, ContextNode&, bool)
{
[]{};
foo([]{});
return ReductionStatus::Clean;
}

}

}

ReductionStatus
f1(TermNode&, ContextNode&)
{
return ReductionStatus::Clean;
}

}

int main()
{
using namespace std;
using namespace placeholders;
using namespace NPL;
using namespace A1;
TermNode t;
ContextNode c;

cout << "__GXX_TYPEINFO_EQUALITY_INLINE = " <<
__GXX_TYPEINFO_EQUALITY_INLINE << endl;
cout << "__GXX_MERGED_TYPEINFO_NAMES = " << __GXX_MERGED_TYPEINFO_NAMES
<< endl;

foo2(bind(f1, ref(t), _1));
foo2(EvalSequence{});
DoDefineOrSet::Call(t, c, {}, t);
LambdaVauWithEnvironment<1, 1>(t, c, true);
for(auto& id : v)
cout << "i = " << ( - [0]) << ", v[i] = " << id.name() <<
endl;
for(std::size_t i = 0; i < v.size(); ++i)
for(auto j = i; j < v.size(); ++j)
if(i != j)
cout << "i = " << i << ", " << "j = " << j <<
", v[i] < v[j] = " << (v[i] < v[j]) << ", v[j] < v[i] = " << (v[j] < v[i]) <<
endl;
}

//  g++ -std=c++11 -U__GXX_TYPEINFO_EQUALITY_INLINE
-D__GXX_TYPEINFO_EQUALITY_INLINE=0 -U__GXX_MERGED_TYPEINFO_NAMES
-D__GXX_MERGED_TYPEINFO_NAMES=0 a.cc

This is tested with x86_64-w64-mingw32-g++. Linking may fail on platforms where
libstdc++ is configured without the out-of-line definition of
std::type_info::before. Here is the output:

__GXX_TYPEINFO_EQUALITY_INLINE = 0
__GXX_MERGED_TYPEINFO_NAMES = 0
i = 0, v[i] =
St5_BindIFPFN3NPL15ReductionStatusERNS0_8TermNodeERNS0_11ContextNodeEESt17reference_wrapperIS2_ESt12_PlaceholderILi1
i = 1, v[i] = N3NPL2A112_GLOBAL__N_112EvalSequenceE
i = 2, v[i] =
St5_BindIFZN3NPL2A112_GLOBAL__N_113DoDefineOrSetILb0EE4CallIJEEENS0_15ReductionStatusERNS0_8TermNodeERNS0_11ContextNodeESt10shared_ptrINS0_11EnvironmentEES8_DpOT_EUlRKS7_RKSD_E_S7_SD_EE
i = 3, v[i] =
N6ystdex15expanded_callerIFN3NPL15ReductionStatusERNS1_11ContextNodeEEZNS1_2A112_GLOBAL__N_124LambdaVauWithEnvironmentILy1ELy1EEES2_RNS1_8TermNodeES4_bEUlvE0_EE
i = 0, j = 1, v[i] < v[j] = 0, v[j] < v[i] = 1
i = 0, j = 2, v[i] < v[j] = 1, v[j] < v[i] = 1
i = 0, j = 3, v[i] < v[j] = 0, v[j] < v[i] = 1
i = 1, j = 2, v[i] < v[j] = 0, v[j] < v[i] = 1
i = 1, j = 3, v[i] < v[j] = 0, v[j] < v[i] = 1
i = 2, j = 3, v[i] < v[j] = 0, v[j] < v[i] = 1

The unordered pair occurs iff. __GXX_TYPEINFO_EQUALITY_INLINE == 0, i.e. the
out-of-line definition is used, and regardless to the value of
__GXX_MERGED_TYPEINFO_NAMES (either 0 or 1).

The sequence (v[0], v[1], ...) here is exactly the inserted one in the actual
case, where the underlying sequence (of type vector) using
less as key_compare. Insertions of v[0] and v[1] are OK, and then
insertion of v[2] breaks the assumption of ordering in the vector. At this
point it is already corrupted. Assuming it is correct when

[Bug libstdc++/103240] std::type_info::before gives wrong answer for ARM EABI

2023-05-02 Thread redi at gcc dot gnu.org via Gcc-bugs
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=103240

--- Comment #6 from Jonathan Wakely  ---
(In reply to frankhb1989 from comment #5)
> There are multiple issues.
> 
> 1. The out-of-line definition and the inline definition of
> std::type_info::before do different things. Specifically, only one name is
> detected against to '*' in the former, but two in the latter. It seems the
> latter is more correct. ("More", because it is still not quite correct, see
> below.)

I think the latter is completely correct.

> 2. As stated in https://reviews.llvm.org/D100134, mixture of different
> methods of comparisons breaks the ordering.

I don't think that's a problem for our inline implementation.

For non-unique A, unique B, non-unique C we would always do string comparisons,
and we would consistently have "*B" < "A" < "C" in all TUs.

We would only do pointer comparisons for when comparing two unique types, e.g.
comparing "*B" and "*B" or comparing "*B" and "*D".

> I've actually encountered the problem with std::type_index as the key type
> when using flat_map::emplace (like
> https://github.com/WG21-SG14/SG14/blob/master/SG14/flat_map.h, which uses
> std::partition_point to find the position of insertion). The insertion
> suddenly fails when std::partition_point meets two std::type_info objects x
> and y which are unordered. It works with the workaround
> '-U__GXX_MERGED_TYPEINFO_NAMES -D__GXX_MERGED_TYPEINFO_NAMES=1' in the
> compiler command line, but it seems not enough in general without fixing the
> issue 2 here. (Although the same sequence of key on std::map does not fail,
> occasionally, due to less comparisons are made.)

What are the mangled type names that are unordered? (that's all you need to
describe, everything about flat_map and partition_point is irrelevant; just
tell us which names are unordered).

Is this using the inline implementation? (I assume so, otherwise redefining
__GXX_MERGED_TYPEINFO_NAMES won't do anything). How does forcing address
comparisons for all types solve anything? That isn't just "not enough in
general" it's **wrong** in general.

> 3. Not sure the original change in r179236 is correct.

It was an improvement, at least.

> 4. '||' in the condition of the inline definition of std::type_info::before
> seems an overkill. '&&' may be enough. Assuming string comparison is more
> expensive, this is a simple optimization. But once the issue 2 is fixed, the
> change would look quite different.

I don't think that's correct. We can only do a pointer comparison if both
strings begin with '*'. If we compared pointers for "*foo" and "bar" (where
"bar" is not unique), we might have "*foo" < "bar"  in one TU and "*foo" >
"bar" in another TU where "bar" has a different address. We need to do a string
comparison if either of them is not unique. Which is what the inline
implementation does.

It also correctly handles the problem case described by Richard Smith, because
all unique names start with '*' and all non-unique names start with one of
[A-Za-z_], and those are ordered after '*', at least for ASCII and UTF-8.

I agree there are issues with the non-inline implementation. We could just make
it match the inline one:

--- a/libstdc++-v3/libsupc++/tinfo2.cc
+++ b/libstdc++-v3/libsupc++/tinfo2.cc
@@ -33,15 +33,11 @@ using std::type_info;
 bool
 type_info::before (const type_info ) const _GLIBCXX_NOEXCEPT
 {
-#if __GXX_MERGED_TYPEINFO_NAMES
-  return name () < arg.name ();
-#else
-  /* The name() method will strip any leading '*' prefix. Therefore
- take care to look at __name rather than name() when looking for
- the "pointer" prefix.  */
-  return (__name[0] == '*') ? name () < arg.name ()
-:  __builtin_strcmp (name (), arg.name ()) < 0;
+#if !__GXX_MERGED_TYPEINFO_NAMES
+  if (__name[0] == '*' || arg.__name[0] == '*')
+return __builtin_strcmp (__name, arg.__name) < 0;
 #endif
+  return __name < arg.__name
 }

 #endif

[Bug libstdc++/103240] std::type_info::before gives wrong answer for ARM EABI

2023-04-29 Thread frankhb1989 at gmail dot com via Gcc-bugs
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=103240

--- Comment #5 from frankhb1989 at gmail dot com ---
There are multiple issues.

1. The out-of-line definition and the inline definition of
std::type_info::before do different things. Specifically, only one name is
detected against to '*' in the former, but two in the latter. It seems the
latter is more correct. ("More", because it is still not quite correct, see
below.)

2. As stated in https://reviews.llvm.org/D100134, mixture of different methods
of comparisons breaks the ordering.

I've actually encountered the problem with std::type_index as the key type when
using flat_map::emplace (like
https://github.com/WG21-SG14/SG14/blob/master/SG14/flat_map.h, which uses
std::partition_point to find the position of insertion). The insertion suddenly
fails when std::partition_point meets two std::type_info objects x and y which
are unordered. It works with the workaround '-U__GXX_MERGED_TYPEINFO_NAMES
-D__GXX_MERGED_TYPEINFO_NAMES=1' in the compiler command line, but it seems not
enough in general without fixing the issue 2 here. (Although the same sequence
of key on std::map does not fail, occasionally, due to less comparisons are
made.)

3. Not sure the original change in r179236 is correct.

4. '||' in the condition of the inline definition of std::type_info::before
seems an overkill. '&&' may be enough. Assuming string comparison is more
expensive, this is a simple optimization. But once the issue 2 is fixed, the
change would look quite different.

[Bug libstdc++/103240] std::type_info::before gives wrong answer for ARM EABI

2023-04-29 Thread frankhb1989 at gmail dot com via Gcc-bugs
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=103240

frankhb1989 at gmail dot com changed:

   What|Removed |Added

 CC||frankhb1989 at gmail dot com

--- Comment #4 from frankhb1989 at gmail dot com ---
This should affect all targets without __GXX_MERGED_TYPEINFO_NAMES enabled.
Actually the test case also fails on (some old versions of) MinGW GCC in MSYS2.

[Bug libstdc++/103240] std::type_info::before gives wrong answer for ARM EABI

2021-11-23 Thread cvs-commit at gcc dot gnu.org via Gcc-bugs
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=103240

--- Comment #3 from CVS Commits  ---
The releases/gcc-11 branch has been updated by Jonathan Wakely
:

https://gcc.gnu.org/g:ec6ba81a038be488cec5e3bdc4f30b7876d9c5ea

commit r11-9270-gec6ba81a038be488cec5e3bdc4f30b7876d9c5ea
Author: Jonathan Wakely 
Date:   Tue Nov 16 21:03:21 2021 +

libstdc++: Fix std::type_info::before for ARM [PR103240]

The r179236 fix for std::type_info::operator== should also have been
applied to std::type_info::before. Otherwise two distinct types can
compare equivalent due to using a string comparison, when they should do
a pointer comparison.

libstdc++-v3/ChangeLog:

PR libstdc++/103240
* libsupc++/tinfo2.cc (type_info::before): Use unadjusted name
to check for the '*' prefix.
* testsuite/util/testsuite_shared.cc: Add type_info object for
use in new test.
* testsuite/18_support/type_info/103240.cc: New test.

(cherry picked from commit 054bf99841aad3869c70643b2ba2d9f85770c980)

[Bug libstdc++/103240] std::type_info::before gives wrong answer for ARM EABI

2021-11-17 Thread redi at gcc dot gnu.org via Gcc-bugs
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=103240

--- Comment #2 from Jonathan Wakely  ---
Fixed on trunk, backports to follow.

[Bug libstdc++/103240] std::type_info::before gives wrong answer for ARM EABI

2021-11-17 Thread cvs-commit at gcc dot gnu.org via Gcc-bugs
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=103240

--- Comment #1 from CVS Commits  ---
The master branch has been updated by Jonathan Wakely :

https://gcc.gnu.org/g:054bf99841aad3869c70643b2ba2d9f85770c980

commit r12-5342-g054bf99841aad3869c70643b2ba2d9f85770c980
Author: Jonathan Wakely 
Date:   Tue Nov 16 21:03:21 2021 +

libstdc++: Fix std::type_info::before for ARM [PR103240]

The r179236 fix for std::type_info::operator== should also have been
applied to std::type_info::before. Otherwise two distinct types can
compare equivalent due to using a string comparison, when they should do
a pointer comparison.

libstdc++-v3/ChangeLog:

PR libstdc++/103240
* libsupc++/tinfo2.cc (type_info::before): Use unadjusted name
to check for the '*' prefix.
* testsuite/util/testsuite_shared.cc: Add type_info object for
use in new test.
* testsuite/18_support/type_info/103240.cc: New test.

[Bug libstdc++/103240] std::type_info::before gives wrong answer for ARM EABI

2021-11-14 Thread redi at gcc dot gnu.org via Gcc-bugs
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=103240

Jonathan Wakely  changed:

   What|Removed |Added

   Assignee|unassigned at gcc dot gnu.org  |redi at gcc dot gnu.org
 Status|NEW |ASSIGNED

[Bug libstdc++/103240] std::type_info::before gives wrong answer for ARM EABI

2021-11-14 Thread redi at gcc dot gnu.org via Gcc-bugs
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=103240

Jonathan Wakely  changed:

   What|Removed |Added

 Status|UNCONFIRMED |NEW
   Last reconfirmed||2021-11-15
 Ever confirmed|0   |1