This is an archive of the discontinued LLVM Phabricator instance.

Instantiate incomplete class used in template method.
ClosedPublic

Authored by sepavloff on Mar 12 2015, 12:23 AM.

Details

Summary

If class is absent from instantiation and is incomplete, instantiate it as
incomplete class thus avoiding compiler crash.
This change fixes PR18653.

Diff Detail

Repository
rL LLVM

Event Timeline

sepavloff updated this revision to Diff 21801.Mar 12 2015, 12:23 AM
sepavloff retitled this revision from to Instantiate incomplete class used in template method..
sepavloff updated this object.
sepavloff edited the test plan for this revision. (Show Details)
sepavloff added a subscriber: Unknown Object (MLST).

Ping.

Thanks,
--Serge

2015-03-12 13:23 GMT+06:00 Serge Pavlov <sepavloff@gmail.com>:

If class is absent from instantiation and is incomplete, instantiate it as
incomplete class thus avoiding compiler crash.
This change fixes PR18653.

http://reviews.llvm.org/D8281

Files:

lib/Sema/SemaTemplateInstantiate.cpp
lib/Sema/SemaTemplateInstantiateDecl.cpp
test/SemaTemplate/instantiate-local-class.cpp

Index: lib/Sema/SemaTemplateInstantiate.cpp

  • lib/Sema/SemaTemplateInstantiate.cpp

+++ lib/Sema/SemaTemplateInstantiate.cpp
@@ -2792,6 +2792,12 @@

  isa<TemplateTemplateParmDecl>(D))
return nullptr;

+ If the declaration we are looking for is an incomplete type
declaration, we
+
may not have a chance to instantiate it.
+ if (const TypeDecl *TD = dyn_cast<TypeDecl>(D))
+ if (TD->getTypeForDecl()->isIncompleteType())
+ return nullptr;
+

// If we didn't find the decl, then we either have a sema bug, or we

have a

// forward reference to a label declaration.  Return null to indicate

that

// we have an uninstantiated label.

Index: lib/Sema/SemaTemplateInstantiateDecl.cpp

  • lib/Sema/SemaTemplateInstantiateDecl.cpp

+++ lib/Sema/SemaTemplateInstantiateDecl.cpp
@@ -4399,6 +4399,15 @@

  isa<TemplateTemplateParmDecl>(D))
return D;

+ If the declaration we are looking for is an incomplete type
declaration,
+
we may not have a chance to instantiate it.
+ if (const TypeDecl *TD = dyn_cast<TypeDecl>(D))
+ if (TD->getTypeForDecl()->isIncompleteType()) {
+ Decl *Inst = SubstDecl(D, CurContext, TemplateArgs);
+ CurrentInstantiationScope->InstantiatedLocal(D, Inst);
+ return cast<TypeDecl>(Inst);
+ }
+

if (D->isInvalidDecl())
  return nullptr;

Index: test/SemaTemplate/instantiate-local-class.cpp

  • test/SemaTemplate/instantiate-local-class.cpp

+++ test/SemaTemplate/instantiate-local-class.cpp
@@ -194,3 +194,13 @@

void f() { F<int>(); }

};
}
+
+namespace PR18653 {
+ template <class T> void f();
+ template <class T> struct str {
+ void m() {
+ f<class newclass>();
+ }
+ };
+ template struct str<int>;
+}

EMAIL PREFERENCES

http://reviews.llvm.org/settings/panel/emailpreferences/
rsmith added a subscriber: rsmith.Mar 23 2015, 1:57 PM

This looks like a good approach to me.

lib/Sema/SemaTemplateInstantiate.cpp
2797–2798 ↗(On Diff #21801)

I think this should only apply to TagDecls. You should then instantiate if !TD->isThisDeclarationADefiinition() && !TD->isFreeStanding(). Ideally, the freestanding check would instead check isEmbeddedInDeclarator, but it looks like we don't instantiate the tag early enough for that to work in cases like

template<typename T> void f() { void g(struct x); }
template void f<int>();
sepavloff updated this revision to Diff 23254.Apr 5 2015, 12:49 PM

Updated patch

Thank you for advice and explanation! Changed the patch accordingly.

rsmith added a comment.Apr 5 2015, 5:16 PM

Please add a testcase that uses a forward declaration and then defines it, such as:

template<typename T> void f() {
  void g(struct x);
  struct x {};
}
template void f<int>();

I think a case like that will still assert with your current patch (because x is not an incomplete type, but it won't have had a declaration instantiated when we instantiate the declaration of g).

lib/Sema/SemaTemplateInstantiate.cpp
2802 ↗(On Diff #23254)

This should use the same condition as the other change.

Hi Richard,

2015-04-06 6:16 GMT+06:00 Richard Smith <richard@metafoo.co.uk>:

Please add a testcase that uses a forward declaration and then defines it,
such as:

template<typename T> void f() {
  void g(struct x);
  struct x {};
}
template void f<int>();

I think a case like that will still assert with your current patch
(because x is not an incomplete type, but it won't have had a declaration
instantiated when we instantiate the declaration of g).

Yes, you are right, this case was not handled. Added instantiation of the
type for which we see forward declaration.

Comment at: lib/Sema/SemaTemplateInstantiate.cpp:2802
@@ +2801,3 @@
+ if (const TagDecl *TD = dyn_cast<TagDecl>(D))
+ if (TD->getTypeForDecl()->isIncompleteType())

+ return nullptr;

This should use the same condition as the other change.

Fixed.

Thanks,
--Serge

sepavloff updated this revision to Diff 23288.Apr 6 2015, 12:49 PM

Updated patch.

rsmith added inline comments.Apr 6 2015, 1:23 PM
lib/Sema/SemaTemplateInstantiateDecl.cpp
4406–4407 ↗(On Diff #23288)

What's the purpose of this change? If we have a reference to a particular TagDecl, why don't we always instantiate that declaration?

2015-04-07 2:23 GMT+06:00 Richard Smith <richard@metafoo.co.uk>:

Comment at: lib/Sema/SemaTemplateInstantiateDecl.cpp:4406-4407
@@ +4405,4 @@
+ const TagDecl *DeclToInstantiate = nullptr;
+ if (TagDecl *Def = TD->getDefinition())
+ DeclToInstantiate = Def;
+ else if (!TD->isThisDeclarationADefinition() &&

!TD->isFreeStanding())

What's the purpose of this change? If we have a reference to a particular
TagDecl, why don't we always instantiate that declaration?

If check for invalid declaration is made prior to this one, it seems that

we can always instantiate the type.
This fix however allows to compile the following code:

template<typename T> void f3() {
  struct AA {
    struct x3 vvv;
  } xx;
  return xx.aaa;
  struct x3 { int aaa; };
}

It is because at the point where incomplete type is used we instantiate its
definition, which is defined later.Strange, but gcc also compiles this
code. Not sure why this code should be accepted.

Thanks,
--Serge

sepavloff updated this revision to Diff 23685.Apr 13 2015, 11:02 AM

Simplified patch.

sepavloff updated this revision to Diff 24457.Apr 27 2015, 1:22 AM

Rephased comments, added more tests.

rsmith accepted this revision.Apr 27 2015, 4:47 PM
rsmith added a reviewer: rsmith.

OK, this seems like a clear step forward.

lib/Sema/SemaTemplateInstantiateDecl.cpp
4440 ↗(On Diff #24457)

Is there anything we can assert here to check that we're not accidentally taking this codepath for a tag decl that should be separately instantiated?

This revision is now accepted and ready to land.Apr 27 2015, 4:47 PM
This revision was automatically updated to reflect the committed changes.