Quuxplusone / LLVMBugzillaTest

0 stars 0 forks source link

Not a constant expression: comparison between unequal pointers to void has unspecified result #45278

Open Quuxplusone opened 4 years ago

Quuxplusone commented 4 years ago
Bugzilla Link PR46308
Status NEW
Importance P normal
Reported by language.lawyer@gmail.com
Reported on 2020-06-12 16:15:45 -0700
Last modified on 2020-06-15 16:28:33 -0700
Version trunk
Hardware PC Linux
CC blitzrakete@gmail.com, erik.pilkington@gmail.com, llvm-bugs@lists.llvm.org, richard-llvm@metafoo.co.uk
Fixed by commit(s)
Attachments
Blocks
Blocked by
See also
About the following program:

struct S
{
    int i;
    int j;
};

constexpr S s {};

static_assert(&s.i < (void*)&s.j);

clang in C++14, C++17 and C++2a modes says that the expression in static_assert
is not a constant expression:

$ clang++ prog.cc -std=c++14
prog.cc:9:15: error: static_assert expression is not an integral constant
expression
static_assert(&s.i < (void*)&s.j, "");
              ^~~~~~~~~~~~~~~~~~
prog.cc:9:20: note: comparison between unequal pointers to void has unspecified
result
static_assert(&s.i < (void*)&s.j, "");
                   ^

(https://wandbox.org/permlink/IXwrV0M75To3wUUB)

However, the special handling of pointers to void has been removed from
[expr.rel] by http://wg21.link/n3624 (applied in [1]), so the result of
comparing unequal pointers to void is no longer unspecified, so the expression
is a constant expression.

[1]
https://github.com/cplusplus/draft/commit/89281b5795f47b9196c37ce8008b4873a76e90ef#diff-64a673ab7a20068092a4cf86b36f4334L3647-L3650
Quuxplusone commented 4 years ago
After N3624, the rule for when one pointer compares greater than another is
defined only for pointer to object types. 'void*' is not a pointer to object
type because 'void' is not an object type, so it is never the case that one
pointer to void compares greater than another; consequently, unequal pointers
to void always fall through to the "Otherwise, the result of each of the
[relational comparison] operators is unspecified." wording.

So while N3624 did remove the explicit handling of 'void*', it didn't change
the behavior of relational comparisons of unequal 'void*' pointers: their
results are still unspecified.
Quuxplusone commented 4 years ago
I don't see where [expr.rel] says that pointers must be pointers to object
*types*.

It says "pointers to objects", but this means pointer values, not types:
pointers need to point to objects, but not to functions. Cast to pointer to
void doesn't change a pointer value.
Quuxplusone commented 4 years ago
Well, ok. In C++14, "pointers to objects" could prolly only mean "pointers to
object types", but in C++17/C++20, "pointers to objects" could be read as
meaning pointer values [1].

Given that:
 - the wording in [expr.rel] could be read with 2 different meanings;
 - there is implementation divergence: GCC and MSVC accept the code (however, their constexpr evaluator is known to be not strict enough in more unambiguous cases)
 - it is unclear what is the purpose of restricting pointer *types* rather than *values*

don't you think this worths a core issue?

[1] https://timsong-cpp.github.io/cppwp/n4659/basic.compound#def:pointer_to
Quuxplusone commented 4 years ago
(In reply to language.lawyer from comment #3)
>  - there is implementation divergence: GCC and MSVC accept the code
> (however, their constexpr evaluator is known to be not strict enough in more
> unambiguous cases)

GCC and MSVC (and also EDG, which likewise doesn't diagnose this even in strict
mode) apparently don't implement the "comparison with unspecified result is not
constant" rule in general, not only in this case. For example, they all accept:

struct Base { int i; };
struct S : Base { int j; };
constexpr S s {};
static_assert(&s.i < &s.j, "");

... even though the rules are clear that this comparison is non-constant
because its result is unspecified.

> don't you think this worths a core issue?

I've mailed the core reflector (and CC'd you). We'll see how that goes.