This is an archive of the discontinued LLVM Phabricator instance.

[AST] injected-class-name is not a redecl, even in template specializations

Authored by sammccall on Oct 28 2021, 3:50 PM.



Back in the mists of time, the CXXRecordDecl for the injected-class-name was
a redecl of the outer class itself.
This got changed in 470c454a6176ef31474553e408c90f5ee630df89, but only for plain
classes: class template instantation was still detecting the injected-class-name
in the template body and marking its instantiation as a redecl.

This causes some subtle inconsistent behavior between the two, e.g.
hasDefinition() returns true for Foo<int>::Foo but false for Bar::Bar.
This is the root cause of PR51912.

Diff Detail

Event Timeline

sammccall created this revision.Oct 28 2021, 3:50 PM
sammccall requested review of this revision.Oct 28 2021, 3:50 PM
Herald added projects: Restricted Project, Restricted Project. · View Herald TranscriptOct 28 2021, 3:50 PM
Herald added a subscriber: cfe-commits. · View Herald Transcript

After this patch we can revert D110614 except for the testcases (cc @carlosgalvezp) as the old matchers should just work.


These warnings that disappeared are spurious duplicates. They correspond to matching the injected-class-name of the template instantiation (in addition to the main declaration of it).
After this change we only match the main declaration, just like non-template classes.

aaron.ballman added inline comments.Oct 29 2021, 5:44 AM

Why is this call needed? (It seems strange to me that we call it and ignore the return value.)


my understanding is

  • getTypeDeclType is more than a getter, it also has a side-effort -- if the type of the passed Record is empty, it creates a type, and propagates the type to Record->TypeForDecl;
  • from the above line, we delay the type creation when IsInjectedClassName is true;
  • so we need to create a type for the Record by invoking getTypeDeclType;

might be worth a comment.


it is unclear to me what's the intention of the assertion.

sammccall updated this revision to Diff 383351.Oct 29 2021, 7:52 AM

Add a comment, remove some unneccesary special cases from traversals.

sammccall added inline comments.Oct 29 2021, 9:15 AM

That's exactly right. The injected-class-name is an independent decl but yields the same RecordType, and getTypeDeclType has a hacky interface so we can force this link.
(It wasn't needed in the old version of the code, because when it was a redecl getTypeDeclType would yield the same type in any case)

I should have mentioned this is all derived from the injected-class-name handling in Sema::ActOnStartCXXMemberDeclarations(). It lacks comments, but I'll add a few while here.


We needed to do a few weird things (and to assume some things about the input) to initialize the injected-class-name, it verifies we got them all right.

(This assertion is also in ActOnStartCXXMemberDeclarations())

sammccall marked 3 inline comments as done.Oct 29 2021, 9:16 AM
aaron.ballman accepted this revision.Oct 29 2021, 12:03 PM

Aside from the cast request, this LGTM, thank you!


Ah, thank you for the explanation! I think a (void) on the result will also help make it clear that we intentionally are ignoring that result despite it looking like a getter.

This revision is now accepted and ready to land.Oct 29 2021, 12:03 PM
hokein accepted this revision.Oct 29 2021, 12:13 PM

looks good, thanks.


I would add a assertion here: assert(!cast<CXXRecordDecl>(RD)->isInjectedClassName()).




ok, I see. didn't know this is derived from ActOnStartCXXMemberDeclarations.

sammccall marked 4 inline comments as done.Nov 2 2021, 6:31 AM
sammccall added inline comments.

Changed the dyn_cast to a cast instead

This revision was automatically updated to reflect the committed changes.
sammccall marked an inline comment as done.