llvm / llvm-project

The LLVM Project is a collection of modular and reusable compiler and toolchain technologies.
http://llvm.org
Other
26.76k stars 10.96k forks source link

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

Open languagelawyer opened 4 years ago

languagelawyer commented 4 years ago
Bugzilla Link 46308
Version trunk
OS Linux
CC @zygoloid

Extended Description

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

ec04fc15-fa35-46f2-80e1-5d271f2ef708 commented 4 years ago
  • 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.

languagelawyer 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:

don't you think this worths a core issue?

[1] https://timsong-cpp.github.io/cppwp/n4659/basic.compound#def:pointer_to

languagelawyer 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.

ec04fc15-fa35-46f2-80e1-5d271f2ef708 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.

llvmbot commented 10 months ago

@llvm/issue-subscribers-clang-frontend