Compiling and linking the following code with Clang built from trunk r121764 fails: template <typename T> class ClassTemplate { friend void T::func(); }; class C { public: void func() { ClassTemplate<C> ct; } }; int main() { C c; c.func(); } $ clang++ /tmp/c.cc /usr/bin/ld: /tmp/cc-QB69fY.o: in function main:/tmp/c.cc(.text+0x14): error: undefined reference to 'C::func()' clang: error: linker command failed with exit code 1 (use -v to see invocation) If C::func() is defined out-of-line, it works. It seems like instantiating ClassTemplate<> with its friend declaration of T::func() somehow makes the in-line definition of C::func() disappear.
If I add a "return 0" after the comment but before the call to Visit here // All of the Visit implementations for the various potential friend // declarations have to be carefully written to work for friend // objects, with the most important detail being that the target // decl should almost certainly not be placed in Owner. Decl *NewND = Visit(ND); in TemplateDeclInstantiator::VisitFriendDecl(), the problem goes away. +rjmccall who added the comment – maybe it's obvious to him what needs to be done here.
Further down the stack, removing this block if (isFriend) { if (Qualifier) { CXXScopeSpec SS; SS.setScopeRep(Qualifier); SS.setRange(D->getQualifierRange()); DC = SemaRef.computeDeclContext(SS); if (DC && SemaRef.RequireCompleteDeclContext(SS, DC)) return 0; } else { DC = SemaRef.FindInstantiatedContext(D->getLocation(), D->getDeclContext(), TemplateArgs); } if (!DC) return 0; } from TemplateDeclInstantiator::VisitCXXMethodDecl() helps.
Proposed patch (will add a test case): Index: lib/Sema/SemaTemplateInstantiateDecl.cpp =================================================================== --- lib/Sema/SemaTemplateInstantiateDecl.cpp (revision 121783) +++ lib/Sema/SemaTemplateInstantiateDecl.cpp (working copy) @@ -1437,7 +1436,7 @@ ? cast<NamedDecl>(FunctionTemplate) : Method); if (isFriend) - Record->makeDeclVisibleInContext(DeclToAdd); + Owner->makeDeclVisibleInContext(DeclToAdd); else Owner->addDecl(DeclToAdd); }
Probably better proposed patch: @@ -1436,9 +1435,7 @@ NamedDecl *DeclToAdd = (TemplateParams ? cast<NamedDecl>(FunctionTemplate) : Method); - if (isFriend) - Record->makeDeclVisibleInContext(DeclToAdd); - else + if (!isFriend) Owner->addDecl(DeclToAdd); }
Patch attempt at http://lists.cs.uiuc.edu/pipermail/cfe-dev/2010-December/012590.html
I fixed this in a totally different way in r121833.
Our builders are happy again – thanks!