[cfe-dev] static constexpr member function of a local struct in a function template is instantiated too late

Richard Smith richard at metafoo.co.uk
Thu Aug 21 18:40:18 PDT 2014


On Thu, Aug 21, 2014 at 6:22 PM, Michael Park <mcypark at gmail.com> wrote:

> Hi Richard, thanks for the reply!
>
> Funnily enough, the member functions of local classes that have deduced
> return types actually don't run into this problem because the definition
> gets instantiated eagerly in order to deduce the return type.
> So the following actually works in clang++-3.4.2.
>
> template <typename T>
>> void F() {
>>   struct Num {
>>     static constexpr auto Get() { return 42; }
>>   };
>>   static_assert(Num::Get() == 42, "");
>>
>> }
>> int main() {
>>   F<int>();
>> }
>>
>
>  I tried the recommended approach of instantiating at the end of the local
> class instead, like so:
>
> // DR1484 clarifies that the members of a local class are instantiated as
>> part
>> // of the instantiation of their enclosing entity.
>> if (D->isCompleteDefinition() && D->isLocalClass()) {
>>   SemaRef.InstantiateClass(D->getLocation(), Record, D, TemplateArgs,
>>                            TSK_ImplicitInstantiation,
>>                            /*Complain=*/true);
>>   SemaRef.InstantiateClassMembers(D->getLocation(), Record, TemplateArgs,
>>                                   TSK_ImplicitInstantiation);
>>
> *  SemaRef.PerformPendingInstantiations(/*LocalOnly=*/true);*
>
> }
>
>
> Although there are *PeformPendingInstantiations *calls that should be
> removed, it effectively will be a no-op so I just tried this out for now.
> It actually passes *make test* and also passes the example above!
>
> But I worry that instantiating at the end of the local class is still too
> late, because I run into the same issue if I bring the *static_assert* into
> the body of the local struct.
>
> template <typename T>
>
> void F() {
>
>   struct Num {
>>     static constexpr int Get() { return 42; }
>>     static_assert(range == 42, "");
>>   };
>
> }
>
>
The core language is somewhat unclear on whether that's supposed to work.
Note that it doesn't work if F is not a template. I think the standard
committee will end up deciding that this case is ill-formed, so I'm happy
for us to reject it for now.

This case also works if I change the return type of *Get* to *auto*.
>
> Even I bring out the local struct to namespace scope, it still breaks. But
> I think this might be a different bug. (In this case changing *Get*'s
> return type to *auto* doesn't resolve the problem.)
>

=) Yep. See http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#1626


> struct Num {
>>   static constexpr int Get() { return 42; }
>>   static_assert(Get() == 42, "");
>> };
>
>
> P.S. Thanks so much for helping out.
>
>
> On 20 August 2014 22:31, Richard Smith <richard at metafoo.co.uk> wrote:
>
>> On Tue, Aug 12, 2014 at 8:09 PM, Michael Park <mcypark at gmail.com> wrote:
>>
>>> Hello,
>>>
>>
>> Hi and welcome! (And sorry for the delay.)
>>
>>
>>> I'm new to clang development and was hoping to get some help in tackling
>>> this bug that I found/filed: http://llvm.org/bugs/show_bug.cgi?id=20625
>>>
>>> Example:
>>>
>>> template <typename T>
>>>> void F() {
>>>>   struct Num {
>>>>     static constexpr int Get() { return 42; }
>>>>   };
>>>>   constexpr int n = Num::Get();
>>>> }
>>>> int main() {
>>>>   F<int>();
>>>> }
>>>
>>>
>>> The summary of the issue is that the local struct *Num* and its static
>>> constexpr function *Get* is not yet instantiated when we validate the
>>> constexpr variable initialization of *n* during *F*'s instantiation.
>>>
>>> More details:
>>>
>>> *Num* and *Get* gets added to *PendingLocalImplicitInstantiations* in
>>> *SubstStmt* at *SemaTemplateInstantiationDecl.cpp:3437*, and is
>>> instantiated later by *PerformPendingInstantiations* at
>>> *SemaTemplateInstantiationDecl.cpp:3458*. However, the validation of
>>> the constexpr variable initialization of *n* happens in *SubstStmt* at
>>> which point we don't yet have the definition of *Get* instantiated.
>>>
>>> I'm not exactly sure what the correct approach would be to solve the
>>> problem. It seems that the local implicit instantiations need to be
>>> performed earlier, or the validation of constexpr variables need to be
>>> delayed.
>>>
>>> If someone can point me in the right direction it would be appreciated.
>>>
>>
>> The implicit instantiations should be performed sooner. (FWIW, the same
>> problem will affect member functions of local classes that have deduced
>> return types.) Perhaps the easiest way to address this would be to perform
>> all the local implicit instantiations discovered while instantiating a
>> local class when we reach the end of that local class, instead of delaying
>> them until the end of the surrounding function. We try to delay these
>> instantiations as much as we can, to reduce the stack usage in recursive
>> template instantiation, but that's probably not going to be significant
>> here because there's not likely to be many layers of AST between the
>> function and the class definition.
>>
>> One place to put this fix would be around
>> SemaTemplateInstantiateDecl.cpp:1104:
>>
>>   // DR1484 clarifies that the members of a local class are instantiated
>> as part
>>   // of the instantiation of their enclosing entity.
>>   if (D->isCompleteDefinition() && D->isLocalClass()) {
>>     SemaRef.InstantiateClass(D->getLocation(), Record, D, TemplateArgs,
>>                              TSK_ImplicitInstantiation,
>>                              /*Complain=*/true);
>>     SemaRef.InstantiateClassMembers(D->getLocation(), Record,
>> TemplateArgs,
>>                                     TSK_ImplicitInstantiation);
>>   }
>>
>> You could save, perform, and restore the pending local implicit
>> instantiations around these calls.
>>
>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.llvm.org/pipermail/cfe-dev/attachments/20140821/23c865ee/attachment.html>


More information about the cfe-dev mailing list