This is an archive of the discontinued LLVM Phabricator instance.

-fdelayed-template-parsing: handle cases where a late-parsed function is not a direct member of a template, but rather nested inside a class that's a member of a template (PR19613)
ClosedPublic

Authored by hans on Apr 29 2014, 6:05 PM.

Diff Detail

Event Timeline

hans updated this revision to Diff 8948.Apr 29 2014, 6:05 PM
hans retitled this revision from to -fdelayed-template-parsing: handle cases where a late-parsed function is not a direct member of a template, but rather nested inside a class that's a member of a template (PR19613).
hans updated this object.
hans edited the test plan for this revision. (Show Details)
hans added reviewers: rnk, majnemer.
hans added a subscriber: Unknown Object (MLST).
hans added a reviewer: rsmith.Apr 30 2014, 9:38 AM

Both Reid and David suggested I add Richard to look at this interesting piece of code :)

rsmith edited edge metadata.Apr 30 2014, 10:42 AM

Neither the before code nor the after code looks right...

The outermost lexical level (and possibly others in the future) can have multiple template parameter lists, and you should use getNumTemplateParameterLists/getTemplateParameterList on each level to add all of them (these exist on DeclaratorDecl for FunctionDecls and on TagDecl for the other cases). For instance:

template<typename A> struct S {
  template<typename B> struct T;
};
template<typename A> template<typename B> struct S<A>::T {
  template<typename C, typename D> struct U;
  template<typename C> struct U<C, C> {
    template<typename E> void f() { /* ... */ }
  };
};
hans updated this revision to Diff 8998.Apr 30 2014, 3:56 PM
hans edited edge metadata.

New patch addressing Richard's feedback.

rsmith added inline comments.Apr 30 2014, 6:09 PM
include/clang/Sema/Sema.h
4886–4888

Can we fold these three functions together, so we can just say 'reenter the scope of this declaration' and enter all its template parameter lists at once? It's a bit strange that 'ActOnReenterDeclaratorTemplateScope' doesn't actually enter the innermost template scope if the declarator is itself declaring a template.

lib/Parse/ParseTemplate.cpp
1251–1252

This side also might have surrounding template parameter lists, if a partial specialization is defined out of line:

template<typename T> struct S {
  template<typename U> struct X;
  template<typename U> struct X<U*>;
};
template<typename T> template<typename U> struct S<T>::X<U*> {
  void f() {}
};
1254

Why 'record' rather than 'class'? We're pretty consistent in using 'ClassTemplate' (even though we use the unfortunate name 'CXXRecordDecl').

1255–1265

These steps seem backwards: we should reenter the surrounding template scopes for the class template before we reenter the class template itself.

lib/Sema/SemaDeclCXX.cpp
5983–5999

Maybe factor this out into a static helper template rather than essentially duplicating it?

hans added a comment.May 1 2014, 10:27 AM

Thanks for the review! New patch coming up.

include/clang/Sema/Sema.h
4886–4888

Done. I've folded them into one function.

lib/Parse/ParseTemplate.cpp
1251–1252

Thanks! Added this to the battery of tests.

1254

ActOnReenterRecordTemplateScope has gone away now.

1255–1265

You're right. I've changed the order.

lib/Sema/SemaDeclCXX.cpp
5983–5999

Since I baked the three ActOnReenter*TemplateScope together, I think the code became simpler.

hans updated this revision to Diff 9013.May 1 2014, 10:27 AM

New patch version.

No objection to the current patch other than the dyn_cast<Decl> thing, but some thoughts.

include/clang/Sema/Sema.h
4887–4888

It's unusual to have an out parameter and a void return type. Have you considered returning the number of template parameter lists that were entered?

lib/Parse/ParseTemplate.cpp
1232–1233

(Not really related to your patch, but...) Shouldn't we be resetting TemplateParameterDepth to 0 somewhere in here? If we decide to lazily parse a templated function definition while we're half-way through parsing another template, our depths will get messed up. (IIRC, we've turned off delayed parsing for constexpr functions and functions with deduced return types, perhaps due to this bug...)

1251–1252

No need for the dyn_cast here. Every DeclContext is a Decl.

1258–1266

Could ActOnReenterTemplateScope do this itself? (That is, entering a templated decl could also enter its template, and thus enter all the template parameter lists on the same logical declaration.)

hans added inline comments.May 1 2014, 1:34 PM
include/clang/Sema/Sema.h
4887–4888

Oops, yeah that makes a lot of sense. Done.

lib/Parse/ParseTemplate.cpp
1232–1233

I don't have a deep enough understanding of this to answer, but I do know that we don't have any tests that fail if I reset TemplateParameterDepth to 0 at the end of this function..

1251–1252

Done. I got confused because DeclContext doesn't inherit from Decl, but I guess every subclass of DeclContext does.

1258–1266

I failed some tests when I tried that before, but that seems to have been unrelated because it works now and makes things much nicer.

hans updated this revision to Diff 9020.May 1 2014, 1:34 PM

Addressing Richard's comments.

rsmith added a comment.May 1 2014, 3:17 PM

Thanks, that looks nicer.

lib/Parse/ParseTemplate.cpp
1252–1258

cast, not dyn_cast, please.

1260–1264

Can you push the function template case into ActOnReenterTemplateScope too?

lib/Sema/SemaDeclCXX.cpp
6000–6002

You should handle all TagDecls here, not just CXXRecordDecls. Under the resolution of DR1607, this is valid:

template<typename T> struct A {
  enum class E;
};
template<typename T> enum class A<T>::E {
  n = (true ? 0 : [] {
    struct Q { Q() { /* delay parsed */ } } q;
    return 0;
  }())
};

... and instantiating A<T>::E will instantiate the default constructor of Q (note, we currently reject this because we went for a more conservative tentative fix for issue 1607).

hans added inline comments.May 1 2014, 3:50 PM
lib/Parse/ParseTemplate.cpp
1252–1258

Done.

1260–1264

I can make Actions.ActOnReenterTemplateScope(getCurScope(), FunD); be covered by the loop above if I make sure not to call Actions.PushDeclContext() on it.

I have no idea why we then do an extra Actions.ActOnReenterTemplateScope(getCurScope(), LPT.D); since that should have the same effect. But if I remove that, we start failing tests, so I guess it needs to be there.

lib/Sema/SemaDeclCXX.cpp
6000–6002

"Done."

hans updated this revision to Diff 9023.May 1 2014, 3:50 PM

Addressing Richard's comments.

rsmith accepted this revision.May 1 2014, 4:16 PM
rsmith edited edge metadata.

I'm well into nitpicking mode now; please go ahead and commit after this round.

... and then we can talk about function definitions inside lambdas inside variable templates. I'm sure that'll make your day =)

lib/Parse/ParseTemplate.cpp
1252

Maybe sink this into its only use.

1260–1264

I meant, can you make ActOnReenterTemplateScope "do the right thing" when given the pattern declaration of a function template? That is, call getDescribedFunctionTemplate on it, and handle the template parameter list from the function template too. (That's why you still need this extra line -- LPT.D here is the template, FunD is the pattern within the template.)

lib/Sema/SemaDeclCXX.cpp
5987

Please add a comment saying that it doesn't matter what order we collect the template parameter lists in, so future readers don't worry that they're in the wrong order like I just did =)

5989–5990

Maybe just assert in this case? People shouldn't be passing TemplateDecls in here.

This revision is now accepted and ready to land.May 1 2014, 4:16 PM
hans added inline comments.May 1 2014, 7:07 PM
lib/Parse/ParseTemplate.cpp
1252

Done.

1260–1264

OK, managed to get this work eventually.

lib/Sema/SemaDeclCXX.cpp
5987

Done.

5989–5990

They do, but as you pointed out offline, we should probably continue with the templated decl in that case, so doing that now.

hans closed this revision.May 1 2014, 7:08 PM

r207822