If class is absent from instantiation and is incomplete, instantiate it as
incomplete class thus avoiding compiler crash.
This change fixes PR18653.
Details
Diff Detail
- Repository
- rL LLVM
Event Timeline
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.Files:
lib/Sema/SemaTemplateInstantiate.cpp lib/Sema/SemaTemplateInstantiateDecl.cpp test/SemaTemplate/instantiate-local-class.cppIndex: 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 wehave a
// forward reference to a label declaration. Return null to indicatethat
// 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/
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>(); |
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
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
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? |