Timm =?utf-8?q?Bäder?= <tbae...@redhat.com>,
Timm =?utf-8?q?Bäder?= <tbae...@redhat.com>,
Timm =?utf-8?q?Bäder?= <tbae...@redhat.com>,
Timm =?utf-8?q?Bäder?= <tbae...@redhat.com>,
Timm =?utf-8?q?Bäder?= <tbae...@redhat.com>,
Timm =?utf-8?q?Bäder?= <tbae...@redhat.com>,
Timm =?utf-8?q?Bäder?= <tbae...@redhat.com>,
Timm =?utf-8?q?Bäder?= <tbae...@redhat.com>,
Timm =?utf-8?q?Bäder?= <tbae...@redhat.com>,
Timm =?utf-8?q?Bäder?= <tbae...@redhat.com>,
Timm =?utf-8?q?Bäder?= <tbae...@redhat.com>,
Timm =?utf-8?q?Bäder?= <tbae...@redhat.com>,
Timm =?utf-8?q?Bäder?= <tbae...@redhat.com>
Message-ID:
In-Reply-To: <llvm.org/llvm/llvm-project/pull/68...@github.com>


================
@@ -107,6 +112,14 @@ class Boolean final {
     return Boolean(!Value.isZero());
   }
 
+  static Boolean bitcastFromMemory(const std::byte *Buff, unsigned BitWidth) {
+    assert(BitWidth == 8);
+    bool Val = static_cast<bool>(*Buff);
+    return Boolean(Val);
+  }
+
+  void bitcastToMemory(std::byte *Buff) { std::memcpy(Buff, &V, sizeof(V)); }
----------------
sethp wrote:

There's a tradeoff to be made, here: booleans are among the primitive types 
that have padding bits, though I believe them to be by far the most commonly 
used (other contenders include `long double` and `_BitInt(4)`). 

The wrinkle is something like this:

```c++
constexpr bool b() {
    return std::bit_cast<bool, uint8_t>(0x02);
}

int main() {
    if constexpr (b() == 0)
        return b();

    return 1;    
}
```

For that sample, `gcc` produces a binary that exits with code 2: 
https://compiler-explorer.com/z/jes71o85h , and current clang refuses to 
compile with a `note: value 2 cannot be represented in type 'bool'`. To 
preserve that behavior, I think that means here `bitcastToMemory` ought to be a 
fail-able operation that checks all bits above the LSB are zero.

There's another question about whether it's worth giving `bool` special 
treatment: should _only_ `Boolean::bitcastToMemory` be a fail-able operation? 
In other words, given

```c++
constexpr auto a = std::bit_cast<uint8_t>(false);
constexpr auto b = std::bit_cast<__int128_t>((long double)0);

constexpr auto c = std::bit_cast<bool>('\x02');
constexpr auto d = std::bit_cast<long double>((__int128_t)~0);
```

Right now, `a` & `d` succeed. `b` & `c` fail to produce constant values for 
different reasons: `c` because the evaluator sees that it'd lose information by 
allowing the bit-cast, and `b` because it doesn't want to check.

I see two fully consistent interpretations, here (considering both 
implementation & semantics):

1. `a` fails for the same reason as `b`, and then `Interp` is free to allow `c` 
& `d` as a simple memcpy + mask (to throw away any padding bits)
2. `a` and `b` both succeed, but then `c` and `d` must fail for the same 
reason. 
 
Both are backwards-incompatible changes, though: code that used to compile with 
the old constant interpreter would fail under the new one. The third choice 
preserves backwards compatibility by holding `bool` out as the [only type whose 
values get checked for 
represent-ability](https://github.com/llvm/llvm-project/blob/2e0d3f81e4f5b623bb476bfac0278cfc6d1bd4bc/clang/lib/AST/ExprConstant.cpp#L7626-L7651).
 (NB: that's a link to the code from #74775 (the current implementation is 
subtly different, but the behavior is the same because the current evaluator 
doesn't [fully] support `_BitInt(X)`)

https://github.com/llvm/llvm-project/pull/68288
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to