This is an archive of the discontinued LLVM Phabricator instance.

[clang] don't instantiate templates with injected arguments
ClosedPublic

Authored by mizvekov on Sep 29 2021, 9:00 AM.

Details

Summary

There is a special situation with templates in local classes,
as can be seen in this example with generic lambdas in function scope:

template<class T1> void foo() {
    (void)[]<class T2>() {
      struct S {
        void bar() { (void)[]<class T3>(T2) {}; }
      };
    };
};
template void foo<int>();

As a consequence of the resolution of DR1484, bar is instantiated during the
substitution of foo, and in this context we would substitute the lambda within
it with it's own parameters "injected" (turned into arguments).

This can't be properly dealt with for at least a couple of reasons:

  • The 'TemplateTypeParm' type itself can only deal with canonical replacement types, which the injected arguments are not.
  • If T3 were constrained in the example above, our (non-conforming) eager substitution of type constraints would just leave that parameter dangling.

Instead of substituting with injected parameters, this patch just leaves those
inner levels unreplaced.

Since injected arguments appear to be unused within the users of
getTemplateInstantiationArgs, this patch just removes that support there and
leaves a couple of asserts in place.

Signed-off-by: Matheus Izvekov <mizvekov@gmail.com>

Diff Detail

Event Timeline

mizvekov created this revision.Sep 29 2021, 9:00 AM
mizvekov published this revision for review.Sep 29 2021, 11:26 AM
Herald added a project: Restricted Project. · View Herald TranscriptSep 29 2021, 11:26 AM
Herald added a subscriber: cfe-commits. · View Herald Transcript
rsmith accepted this revision.Sep 29 2021, 12:59 PM

I think it's almost always true that we don't substitute into a template unless we've already substituted into all enclosing templates. The only exception I can think of is alias templates, which are substituted early, even in dependent contexts. So a case like:

template<typename T> struct A {
  template<typename U> using B = decltype([](auto){}(U()));
  void f(B<int>);
};
A<int> ai;

... might run into some problems here. But I would note we already crash on that testcase (and GCC miscompiles it, treating B<int> as int rather than void). So I suspect that's another whole can of worms.

This revision is now accepted and ready to land.Sep 29 2021, 12:59 PM
This revision was landed with ongoing or failed builds.Sep 29 2021, 2:19 PM
This revision was automatically updated to reflect the committed changes.