Conversation

So I started writing a blog post explaining constexpr & co., but turns out that it's even more cursed than I initially thought. const char *g() { return "dynamic"; } constexpr const char *f(bool p) { return p ? "static" : g(); } compiles, i.e. constexpr-ness is "duck-typed". 💀
Quote Tweet
C++ quiz time! Without checking, does the following code compile: constexpr int f(int x) { return x; } constexpr int g(int x) { constexpr int fx = f(x); return fx; }
Show this poll
1
4
More precisely, here you have a constexpr function f that cannot be called for certain constant arguments (false) in a constexpr context (right jargon?); you get a compiler error at the usage site, just like templates which error on instantiation instead of on definition.
1
2
Interestingly enough, they're not even "fully" duck-typed! If you generalize the example using the duck-typed mental model, it does not compile: const char *g() { return "dynamic"; } constexpr const char *f() { return g(); } At least one path must return a constant expression.
1
2
Based on a playground, it seems like Rust does the right thing (can't call non-const fn in const fn), but the diagnostic suggests looking at GitHub issue 57563 and adding a crate feature, so I'm not sure if that's intentional or something that's not-yet-implemented.
1
One thing I've been wondering about is if `const` expressions could be seen as a form of staging, but where the modality thing is put on the binders (like in QTT), rather than on some sort of modal constructor (like in Meta OCaml).
1
Don't tweet spoilers for my blog post! 🤫😆 Yeah, I do think that's the right direction. There's a couple of approaches you could take: make it a property of types (akin to Granule's multiplicities), or make it a property of bindings. Haven't worked out the details though.
1
The bindings approach would be similar to what Racket does for it's macro system (haven't looked at that in much detail though).
1