[cfe-dev] Possible invalid code accepted in constexpr functions

Richard Smith via cfe-dev cfe-dev at lists.llvm.org
Tue Aug 22 10:28:08 PDT 2017


On 17 Aug 2017 4:24 am, "Kenneth Camann via cfe-dev" <cfe-dev at lists.llvm.org>
wrote:

Hi all,

I found some cases where clang accepts some invalid code. To give some
context, here is what I was trying to do:

Given a pointer to a member subobject that I know lives inside an object of
class type `S`, I would like to get a pointer to the enclosing `S` in a
constexpr function. This seems like a reasonable thing to want to do, but I
could not think of a way to do it that is standard-conforming, and the ways
I did think of have the problems described below.

clang currently allows expressions in constexpr contexts that are
explicitly excluded from being core constantant expressions in [expr.const]
item (2.13) -- i.e., "a conversion from type cv void * to a
pointer-to-object type" cannot be a core constant expression.

gcc allows these too, and is actually far more liberal in what it accepts
(it will accept a reinterpret_cast in a constexpr function). They track
this as a bug although some code in libstdc++ apparently relies on it:
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=49171

It seems that clang never allows a reinterpret_cast in constexpr function,
but sometimes accepts a sneaky equivalent, i.e. this trick:

static_cast<char *>(static_cast<void *>(&x)) [and then back again]

But whether it accepts this or not seems random. The following compiles
with clang 4.0:

struct S {
  int a;
  int b;
};


constexpr S *getS(int *pb) { // Note: pb is a pointer to a `b` inside `S`
  char *pbC = static_cast<char *>(static_cast<void *>(pb)) -
      __builtin_offsetof(S, b);
  return static_cast<S *>(static_cast<void *>(pbC));
};


Did you try actually calling getS in a context that requires a constant
expression? The "constexpr function never produces a constant expression"
diagnostic is strictly best-effort.

But the following does not compile:

constexpr S *getS(int *pb) {
  int S::*memb = &S::b;

  // Try to do something like the offsetof stuff above,
  // but by deriving the offset from a member pointer
  const auto pdiff = static_cast<char *>(static_cast<void *>(
    &(static_cast(nullptr)->*memb))) - static_cast<char *>(nullptr);

  char *pbC = static_cast<char *>(static_cast<void *>(pb)) - pdiff;
  return static_cast<S *>(static_cast<void *>(pbC));
}

> error: constexpr function never produces a constant expression

> note: cast from 'void *' is not allowed in a constant expression

I could probably dig through libclangSema to find out why this is, but what
I'm after is a bit more high level:

1) Like the gcc guys, is there some reason (maybe specifically related to
offsetof magic?) that you want to accept certain forms of this trick, or do
you consider it a bug? I'm willing to rely on UB, but not a bug.

2) There was a proposal I found on github to allow "limited
reinterpret_cast" in constexpr (http://apolukhin.github.io/co
nstexpr_algorithms/reinterpret.html) but it was never mailed out. Since
many you of are involved with WG21: is this an issue anyone is interesting
in pursuing, i.e., ""reasonable" limited use of casting for doing simple
pointer math against the structure layout?

Thanks for your help,
Ken

_______________________________________________
cfe-dev mailing list
cfe-dev at lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-dev
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.llvm.org/pipermail/cfe-dev/attachments/20170822/6afe25c6/attachment.html>


More information about the cfe-dev mailing list