-
Notifications
You must be signed in to change notification settings - Fork 12.4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Clang reports an undefined function that has been defined #73232
Comments
@llvm/issue-subscribers-clang-frontend Author: Timm Baeder (tbaederr)
Not sure how to title this properly.
The original reproducer is just: #include <string>
constexpr auto z = std::string("", 0); with libstdc++. Using cvise, I get: template <typename _CharT>
struct basic_string {
constexpr void _M_construct();
constexpr basic_string() {
_M_construct();
}
};
void operators() {
basic_string<char *>{};
}
template <typename _CharT>
constexpr void basic_string<_CharT>::_M_construct(){}
constexpr basic_string<char*> z{}; https://godbolt.org/z/7PzreExcf Clang's output is: <source>:18:16: error: constexpr variable 'z' must be initialized by a constant expression
18 | constexpr auto z = basic_string<char *>();
| ^ ~~~~~~~~~~~~~~~~~~~~~~
<source>:6:5: note: undefined function '_M_construct' cannot be used in a constant expression
6 | _M_construct();
| ^
<source>:18:20: note: in call to 'basic_string()'
18 | constexpr auto z = basic_string<char *>();
| ^~~~~~~~~~~~~~~~~~~~~~
<source>:3:18: note: declared here
3 | constexpr void _M_construct();
| ^ The problem vanishes if |
same problem with free functions template <typename T>
constexpr void g(T);
constexpr int f() {
g(0);
return 0;
}
template <typename T>
constexpr void g(T) {}
constexpr auto z = f(); |
We probably want to call |
CC @AaronBallman and @zygoloid, maybe they have an idea |
Ultimately this is a language bug. There's no point of instantiation after the function template is defined and before the end of the TU -- we only get PoIs at uses and at end of TU. What we should probably do is instantiate all used specializations at the point where the function template is defined (if it's constexpr). Constant evaluation shouldn't have side effects. I'm pretty sure this is a duplicate of a (very) old PR. |
See CWG2497. |
Hum, I can see how trying to do instantiations in the middle of constant evaluation would not be great, thanks! Do you think we should wait for core to resolve the issue, or should we try to eagerly instantiate at the end of the definition of constexpr functions? |
I think we should eagerly start instantiating at the end of definitions. (Both function and variable templates are affected by this IIRC.) |
Despite CWG2497 not being resolved, it is reasonable to expect the following code to compile (and which is supported by other compilers) ```cpp template<typename T> constexpr T f(); constexpr int g() { return f<int>(); } // #1 template<typename T> constexpr T f() { return 123; } int k[g()]; // #2 ``` To that end, we eagerly instantiate all referenced specializations of constexpr functions when they are defined. We maintain a map of (pattern, [instantiations]) independant of `PendingInstantiations` to avoid having to iterate that list after each function definition. We should apply the same logic to constexpr variables, but I wanted to keep the PR small. Fixes llvm#73232
…73463) Despite CWG2497 not being resolved, it is reasonable to expect the following code to compile (and which is supported by other compilers) ```cpp template<typename T> constexpr T f(); constexpr int g() { return f<int>(); } // #1 template<typename T> constexpr T f() { return 123; } int k[g()]; // #2 ``` To that end, we eagerly instantiate all referenced specializations of constexpr functions when they are defined. We maintain a map of (pattern, [instantiations]) independent of `PendingInstantiations` to avoid having to iterate that list after each function definition. We should apply the same logic to constexpr variables, but I wanted to keep the PR small. Fixes #73232
Reverted in 19e2174 We need to handle the following code template <typename> constexpr static void fromType();
void registerConverter() { fromType<int>(); }
template <typename> struct QMetaTypeId {};
template <typename T> constexpr void fromType() {
(void)QMetaTypeId<T>{};
} // #1
template <> struct QMetaTypeId<int> {}; // #20428 We can't instantiate |
Normally I'd just say that the code needs to be fixed to give the explicit specialization before it's used or to make sure the instantiation is deferred, but the breakage is probably too much given that this seems to be in Qt core code. I guess the question is, do we follow GCC's problematic model that evaluation can cause instantiation (which the language rules elsewhere explicitly try to avoid), or do we add a workaround just for Qt (detect that pattern somehow and don't eagerly Instantiate)? |
It seems like EDG/MSVC follow the same model. I sent a mail to CWG, but i doubt it will help make progress. |
@zygoloid The current direction taking by the reflection proposal is that arbitrary instantiations can emanate from constexpr evaluation. (Both explicitly as there is a proposed method to instantiate and define classes, and implicitly when querying the properties of not yet instantiated definitions). In that way the model you consider problematic is going to be mandated by the standard Is that something you gave any thought above? It might impact how we resolve this issue |
Thinking about the Qt example some more, if you simply reverse the order of the function definitions: template <typename> constexpr static void fromType();
template <typename> struct QMetaTypeId {};
template <typename T> constexpr void fromType() {
(void)QMetaTypeId<T>{};
}
void registerConverter() { fromType<int>(); }
template <> struct QMetaTypeId<int> {}; ... then the example is clearly invalid (albeit no diagnostic required, because there are two points of instantiation for |
Not sure how to title this properly.
The original reproducer is just:
with libstdc++.
Using cvise, I get:
https://godbolt.org/z/7PzreExcf
Clang's output is:
The problem vanishes if
a
is commented out, or ifbasic_string
is not a template anymore.The text was updated successfully, but these errors were encountered: