]> granicus.if.org Git - clang/commitdiff
Improve the AST representation and semantic analysis for extern
authorDouglas Gregor <dgregor@apple.com>
Fri, 4 Sep 2009 22:48:11 +0000 (22:48 +0000)
committerDouglas Gregor <dgregor@apple.com>
Fri, 4 Sep 2009 22:48:11 +0000 (22:48 +0000)
templates. We now distinguish between an explicit instantiation
declaration and an explicit instantiation definition, and know not to
instantiate explicit instantiation declarations. Unfortunately, there
is some remaining confusion w.r.t. instantiation of out-of-line member
function definitions that causes trouble here.

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@81053 91177308-0d34-0410-b5e6-96231b3b80d8

include/clang/AST/Decl.h
include/clang/AST/DeclTemplate.h
lib/AST/Decl.cpp
lib/CodeGen/CodeGenModule.cpp
lib/Sema/Sema.h
lib/Sema/SemaTemplate.cpp
lib/Sema/SemaTemplateInstantiate.cpp
lib/Sema/SemaTemplateInstantiateDecl.cpp
lib/Sema/SemaType.cpp
test/CodeGenCXX/explicit-instantiation.cpp

index 1d6c9c18722bf679f2e8266a432a7a38b3b2d2e3..28456fff9f9285ee9936ef9331e987ea45020444 100644 (file)
@@ -715,6 +715,29 @@ public:
   static bool classof(const OriginalParmVarDecl *D) { return true; }
 };
   
+// \brief Describes the kind of template specialization that a
+// particular template specialization declaration represents.
+enum TemplateSpecializationKind {
+  /// This template specialization was formed from a template-id but
+  /// has not yet been declared, defined, or instantiated.
+  TSK_Undeclared = 0,
+  /// This template specialization was implicitly instantiated from a
+  /// template. (C++ [temp.inst]).
+  TSK_ImplicitInstantiation,
+  /// This template specialization was declared or defined by an
+  /// explicit specialization (C++ [temp.expl.spec]) or partial
+  /// specialization (C++ [temp.class.spec]).
+  TSK_ExplicitSpecialization,
+  /// This template specialization was instantiated from a template
+  /// due to an explicit instantiation declaration request 
+  /// (C++0x [temp.explicit]).
+  TSK_ExplicitInstantiationDeclaration,
+  /// This template specialization was instantiated from a template
+  /// due to an explicit instantiation definition request 
+  /// (C++ [temp.explicit]).
+  TSK_ExplicitInstantiationDefinition  
+};
+  
 /// FunctionDecl - An instance of this class is created to represent a
 /// function declaration or definition. 
 ///
@@ -1077,14 +1100,14 @@ public:
                                       const TemplateArgumentList *TemplateArgs,
                                          void *InsertPos);
 
-  /// \brief Determine whether this is an explicit specialization of a 
-  /// function template or a member function of a class template.
-  bool isExplicitSpecialization() const;
+  /// \brief Determine what kind of template instantiation this function
+  /// represents.
+  TemplateSpecializationKind getTemplateSpecializationKind() const;
 
-  /// \brief Note that this is an explicit specialization of a function template
-  /// or a member function of a class template.
-  void setExplicitSpecialization(bool ES);
-                                 
+  /// \brief Determine what kind of template instantiation this function
+  /// represents.
+  void setTemplateSpecializationKind(TemplateSpecializationKind TSK);
+                       
   // Implement isa/cast/dyncast/etc.
   static bool classof(const Decl *D) {
     return D->getKind() >= FunctionFirst && D->getKind() <= FunctionLast;
index 971d6ea07b3ebf83dc2bda4d349d3036f5d60986..24524dff3838a2a9188b03e7e0daa601f8318443 100644 (file)
@@ -510,9 +510,8 @@ public:
   /// \brief The function template from which this function template 
   /// specialization was generated.
   ///
-  /// The bit will be 0 for an implicit instantiation, 1 for an explicit 
-  /// specialization.
-  llvm::PointerIntPair<FunctionTemplateDecl *, 1> Template;
+  /// The two bits are contain the top 4 values of TemplateSpecializationKind.
+  llvm::PointerIntPair<FunctionTemplateDecl *, 2> Template;
   
   /// \brief The template arguments used to produce the function template
   /// specialization from the function template.
@@ -520,14 +519,17 @@ public:
   
   /// \brief Retrieve the template from which this function was specialized.
   FunctionTemplateDecl *getTemplate() const { return Template.getPointer(); }
-  
-  /// \brief Determine whether this is an explicit specialization.
-  bool isExplicitSpecialization() const { return Template.getInt(); }
-  
-  /// \brief Set whether this is an explicit specialization or an implicit
-  /// instantiation.
-  void setExplicitSpecialization(bool ES) {
-    Template.setInt(ES);
+
+  /// \brief Determine what kind of template specialization this is.
+  TemplateSpecializationKind getTemplateSpecializationKind() const {
+    return (TemplateSpecializationKind)(Template.getInt() + 1);
+  }
+
+  /// \brief Set the template specialization kind.
+  void setTemplateSpecializationKind(TemplateSpecializationKind TSK) {
+    assert(TSK != TSK_Undeclared && 
+         "Cannot encode TSK_Undeclared for a function template specialization");
+    Template.setInt(TSK - 1);
   }
   
   void Profile(llvm::FoldingSetNodeID &ID) {
@@ -871,24 +873,6 @@ public:
   static bool classof(const TemplateTemplateParmDecl *D) { return true; }
 };
 
-// \brief Describes the kind of template specialization that a
-// particular template specialization declaration represents.
-enum TemplateSpecializationKind {
-  /// This template specialization was formed from a template-id but
-  /// has not yet been declared, defined, or instantiated.
-  TSK_Undeclared = 0,
-  /// This template specialization was declared or defined by an
-  /// explicit specialization (C++ [temp.expl.spec]) or partial
-  /// specialization (C++ [temp.class.spec]).
-  TSK_ExplicitSpecialization,
-  /// This template specialization was implicitly instantiated from a
-  /// template. (C++ [temp.inst]).
-  TSK_ImplicitInstantiation,
-  /// This template specialization was instantiated from a template
-  /// due to an explicit instantiation request (C++ [temp.explicit]).
-  TSK_ExplicitInstantiation
-};
-
 /// \brief Represents a class template specialization, which refers to
 /// a class template with a given set of template arguments.
 ///
@@ -927,7 +911,7 @@ class ClassTemplateSpecializationDecl
 
   /// \brief The kind of specialization this declaration refers to.
   /// Really a value of type TemplateSpecializationKind.
-  unsigned SpecializationKind : 2;
+  unsigned SpecializationKind : 3;
 
 protected:
   ClassTemplateSpecializationDecl(ASTContext &Context, Kind DK,
@@ -972,7 +956,8 @@ public:
                      ClassTemplatePartialSpecializationDecl *>
   getInstantiatedFrom() const {
     if (getSpecializationKind() != TSK_ImplicitInstantiation &&
-        getSpecializationKind() != TSK_ExplicitInstantiation)
+        getSpecializationKind() != TSK_ExplicitInstantiationDefinition &&
+        getSpecializationKind() != TSK_ExplicitInstantiationDeclaration)
       return (ClassTemplateDecl*)0;
     
     if (SpecializedPartialSpecialization *PartialSpec 
index 41ba1f1df9006040ad753e083671a1301be72d08..ca1ae63ff85fafbc4e42f0296e00d1857c83de41 100644 (file)
@@ -640,7 +640,7 @@ FunctionDecl::setFunctionTemplateSpecialization(ASTContext &Context,
   
   Info->Function = this;
   Info->Template.setPointer(Template);
-  Info->Template.setInt(0); // Implicit instantiation, unless told otherwise
+  Info->Template.setInt(TSK_ImplicitInstantiation - 1);
   Info->TemplateArguments = TemplateArgs;
   TemplateOrSpecialization = Info;
   
@@ -649,24 +649,35 @@ FunctionDecl::setFunctionTemplateSpecialization(ASTContext &Context,
   Template->getSpecializations().InsertNode(Info, InsertPos);
 }
 
-bool FunctionDecl::isExplicitSpecialization() const {
-  // FIXME: check this property for explicit specializations of member
-  // functions of class templates.
+TemplateSpecializationKind FunctionDecl::getTemplateSpecializationKind() const {
+  // For a function template specialization, query the specialization 
+  // information object.
   FunctionTemplateSpecializationInfo *Info 
     = TemplateOrSpecialization.dyn_cast<FunctionTemplateSpecializationInfo*>();
-  if (!Info)
-    return false;
+  if (Info)
+    return Info->getTemplateSpecializationKind();
+  
+  if (!getInstantiatedFromMemberFunction())
+    return TSK_Undeclared;
   
-  return Info->isExplicitSpecialization();
+  // Find the class template specialization corresponding to this instantiation
+  // of a member function.
+  const DeclContext *Parent = getDeclContext();
+  while (Parent && !isa<ClassTemplateSpecializationDecl>(Parent))
+    Parent = Parent->getParent();
+  
+  if (!Parent)
+    return TSK_Undeclared;
+
+  return cast<ClassTemplateSpecializationDecl>(Parent)->getSpecializationKind();
 }
 
-void FunctionDecl::setExplicitSpecialization(bool ES) {
-  // FIXME: set this property for explicit specializations of member functions
-  // of class templates.
+void 
+FunctionDecl::setTemplateSpecializationKind(TemplateSpecializationKind TSK) {
   FunctionTemplateSpecializationInfo *Info 
     = TemplateOrSpecialization.dyn_cast<FunctionTemplateSpecializationInfo*>();
-  if (Info)
-    Info->setExplicitSpecialization(ES);
+  assert(Info && "Not a function template specialization");
+  Info->setTemplateSpecializationKind(TSK);
 }
 
 //===----------------------------------------------------------------------===//
index cde6e89d66f95fe864ac68427e98e08aad28fb4f..180a68659d521eecfb4c326dc4f0881234c3d749 100644 (file)
@@ -250,9 +250,8 @@ GetLinkageForFunction(ASTContext &Context, const FunctionDecl *FD,
   // The kind of external linkage this function will have, if it is not
   // inline or static.
   CodeGenModule::GVALinkage External = CodeGenModule::GVA_StrongExternal;
-  if (Context.getLangOptions().CPlusPlus &&
-      (FD->getPrimaryTemplate() || FD->getInstantiatedFromMemberFunction()) &&
-      !FD->isExplicitSpecialization())
+  if (Context.getLangOptions().CPlusPlus && 
+      FD->getTemplateSpecializationKind() == TSK_ImplicitInstantiation)
     External = CodeGenModule::GVA_TemplateInstantiation;
       
   if (const CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(FD)) {
index d088387b05570e50c42ac4c0c172e9f7cd28da16..1c118ab64e19237ad1ddbb315ecbccae1da4b670 100644 (file)
@@ -3011,21 +3011,24 @@ public:
   InstantiateClass(SourceLocation PointOfInstantiation,
                    CXXRecordDecl *Instantiation, CXXRecordDecl *Pattern,
                    const MultiLevelTemplateArgumentList &TemplateArgs,
-                   bool ExplicitInstantiation,
+                   TemplateSpecializationKind TSK,
                    bool Complain = true);
 
   bool 
   InstantiateClassTemplateSpecialization(
                            ClassTemplateSpecializationDecl *ClassTemplateSpec,
-                           bool ExplicitInstantiation, bool Complain = true);
+                           TemplateSpecializationKind TSK,
+                           bool Complain = true);
 
   void InstantiateClassMembers(SourceLocation PointOfInstantiation,
                                CXXRecordDecl *Instantiation,
-                            const MultiLevelTemplateArgumentList &TemplateArgs);
+                            const MultiLevelTemplateArgumentList &TemplateArgs,
+                               TemplateSpecializationKind TSK);
 
   void InstantiateClassTemplateSpecializationMembers(
                                           SourceLocation PointOfInstantiation,
-                           ClassTemplateSpecializationDecl *ClassTemplateSpec);
+                           ClassTemplateSpecializationDecl *ClassTemplateSpec,
+                                                TemplateSpecializationKind TSK);
 
   NestedNameSpecifier *
   SubstNestedNameSpecifier(NestedNameSpecifier *NNS,
index 76b6d7ecaa97891fde25f2657cc185e63b341e72..c243765bec78aea05d84899fb3b81c3f52cc193f 100644 (file)
@@ -2934,7 +2934,8 @@ Sema::ActOnExplicitInstantiation(Scope *S,
 
   bool SpecializationRequiresInstantiation = true;
   if (PrevDecl) {
-    if (PrevDecl->getSpecializationKind() == TSK_ExplicitInstantiation) {
+    if (PrevDecl->getSpecializationKind() 
+          == TSK_ExplicitInstantiationDefinition) {
       // This particular specialization has already been declared or
       // instantiated. We cannot explicitly instantiate it.
       Diag(TemplateNameLoc, diag::err_explicit_instantiation_duplicate)
@@ -3028,16 +3029,19 @@ Sema::ActOnExplicitInstantiation(Scope *S,
   //
   // This check comes when we actually try to perform the
   // instantiation.
+  TemplateSpecializationKind TSK
+    = ExternLoc.isInvalid()? TSK_ExplicitInstantiationDefinition 
+                           : TSK_ExplicitInstantiationDeclaration;
   if (SpecializationRequiresInstantiation)
-    InstantiateClassTemplateSpecialization(Specialization, true);
+    InstantiateClassTemplateSpecialization(Specialization, TSK);
   else // Instantiate the members of this class template specialization.
-    InstantiateClassTemplateSpecializationMembers(TemplateLoc, Specialization);
+    InstantiateClassTemplateSpecializationMembers(TemplateLoc, Specialization,
+                                                  TSK);
 
   return DeclPtrTy::make(Specialization);
 }
 
 // Explicit instantiation of a member class of a class template.
-// FIXME: Implement extern template semantics.
 Sema::DeclResult
 Sema::ActOnExplicitInstantiation(Scope *S, 
                                  SourceLocation ExternLoc,
@@ -3092,17 +3096,21 @@ Sema::ActOnExplicitInstantiation(Scope *S,
     }
   }
 
+  TemplateSpecializationKind TSK
+    = ExternLoc.isInvalid()? TSK_ExplicitInstantiationDefinition 
+                           : TSK_ExplicitInstantiationDeclaration;
+  
   if (!Record->getDefinition(Context)) {
     // If the class has a definition, instantiate it (and all of its
     // members, recursively).
     Pattern = cast_or_null<CXXRecordDecl>(Pattern->getDefinition(Context));
     if (Pattern && InstantiateClass(TemplateLoc, Record, Pattern, 
                                     getTemplateInstantiationArgs(Record),
-                                    /*ExplicitInstantiation=*/true))
+                                    TSK))
       return true;
   } else // Instantiate all of the members of the class.
     InstantiateClassMembers(TemplateLoc, Record, 
-                            getTemplateInstantiationArgs(Record));
+                            getTemplateInstantiationArgs(Record), TSK);
 
   // FIXME: We don't have any representation for explicit instantiations of
   // member classes. Such a representation is not needed for compilation, but it
index 0c47e996c8ea0c1da7a4d8888dde8e94f51df7a7..55e81aecbc76ad3cd770f0c32fcafdcc43ad3f20 100644 (file)
@@ -623,8 +623,7 @@ Sema::SubstBaseSpecifiers(CXXRecordDecl *Instantiation,
 /// \param TemplateArgs The template arguments to be substituted into
 /// the pattern.
 ///
-/// \param ExplicitInstantiation whether this is an explicit instantiation
-/// (otherwise, it is an implicit instantiation).
+/// \param TSK the kind of implicit or explicit instantiation to perform.
 ///
 /// \param Complain whether to complain if the class cannot be instantiated due
 /// to the lack of a definition.
@@ -634,7 +633,7 @@ bool
 Sema::InstantiateClass(SourceLocation PointOfInstantiation,
                        CXXRecordDecl *Instantiation, CXXRecordDecl *Pattern,
                        const MultiLevelTemplateArgumentList &TemplateArgs,
-                       bool ExplicitInstantiation,
+                       TemplateSpecializationKind TSK,
                        bool Complain) {
   bool Invalid = false;
 
@@ -650,7 +649,7 @@ Sema::InstantiateClass(SourceLocation PointOfInstantiation,
       Diag(Pattern->getLocation(), diag::note_member_of_template_here);
     } else {
       Diag(PointOfInstantiation, diag::err_template_instantiate_undefined)
-        << ExplicitInstantiation
+        << (TSK != TSK_ImplicitInstantiation)
         << Context.getTypeDeclType(Instantiation);
       Diag(Pattern->getLocation(), diag::note_template_decl_here);
     }
@@ -708,9 +707,10 @@ Sema::InstantiateClass(SourceLocation PointOfInstantiation,
     Consumer.HandleTagDeclDefinition(Instantiation);
 
   // If this is an explicit instantiation, instantiate our members, too.
-  if (!Invalid && ExplicitInstantiation) {
+  if (!Invalid && TSK != TSK_ImplicitInstantiation) {
     Inst.Clear();
-    InstantiateClassMembers(PointOfInstantiation, Instantiation, TemplateArgs);
+    InstantiateClassMembers(PointOfInstantiation, Instantiation, TemplateArgs,
+                            TSK);
   }
 
   return Invalid;
@@ -719,7 +719,7 @@ Sema::InstantiateClass(SourceLocation PointOfInstantiation,
 bool 
 Sema::InstantiateClassTemplateSpecialization(
                            ClassTemplateSpecializationDecl *ClassTemplateSpec,
-                           bool ExplicitInstantiation,
+                           TemplateSpecializationKind TSK,
                            bool Complain) {
   // Perform the actual instantiation on the canonical declaration.
   ClassTemplateSpec = cast<ClassTemplateSpecializationDecl>(
@@ -798,14 +798,12 @@ Sema::InstantiateClassTemplateSpecialization(
   }
 
   // Note that this is an instantiation.
-  ClassTemplateSpec->setSpecializationKind(
-                        ExplicitInstantiation? TSK_ExplicitInstantiation 
-                                             : TSK_ImplicitInstantiation);
+  ClassTemplateSpec->setSpecializationKind(TSK);
 
   bool Result = InstantiateClass(ClassTemplateSpec->getLocation(),
                                  ClassTemplateSpec, Pattern, 
                               getTemplateInstantiationArgs(ClassTemplateSpec),
-                                 ExplicitInstantiation,
+                                 TSK,
                                  Complain);
   
   for (unsigned I = 0, N = Matched.size(); I != N; ++I) {
@@ -822,8 +820,10 @@ Sema::InstantiateClassTemplateSpecialization(
 /// or a member class of a template.
 void
 Sema::InstantiateClassMembers(SourceLocation PointOfInstantiation,
-                        CXXRecordDecl *Instantiation,
-                        const MultiLevelTemplateArgumentList &TemplateArgs) {
+                              CXXRecordDecl *Instantiation,
+                        const MultiLevelTemplateArgumentList &TemplateArgs,
+                              TemplateSpecializationKind TSK) {
+  // FIXME: extern templates
   for (DeclContext::decl_iterator D = Instantiation->decls_begin(),
                                DEnd = Instantiation->decls_end();
        D != DEnd; ++D) {
@@ -839,7 +839,8 @@ Sema::InstantiateClassMembers(SourceLocation PointOfInstantiation,
                "Missing instantiated-from-template information");
         InstantiateClass(PointOfInstantiation, Record,
                          Record->getInstantiatedFromMemberClass(),
-                         TemplateArgs, true);
+                         TemplateArgs,
+                         TSK);
       }
     }
   }
@@ -848,9 +849,11 @@ Sema::InstantiateClassMembers(SourceLocation PointOfInstantiation,
 /// \brief Instantiate the definitions of all of the members of the
 /// given class template specialization, which was named as part of an
 /// explicit instantiation.
-void Sema::InstantiateClassTemplateSpecializationMembers(
+void 
+Sema::InstantiateClassTemplateSpecializationMembers(
                                            SourceLocation PointOfInstantiation,
-                          ClassTemplateSpecializationDecl *ClassTemplateSpec) {
+                            ClassTemplateSpecializationDecl *ClassTemplateSpec,
+                                               TemplateSpecializationKind TSK) {
   // C++0x [temp.explicit]p7:
   //   An explicit instantiation that names a class template
   //   specialization is an explicit instantion of the same kind
@@ -860,7 +863,8 @@ void Sema::InstantiateClassTemplateSpecializationMembers(
   //   containing the explicit instantiation, except as described
   //   below.
   InstantiateClassMembers(PointOfInstantiation, ClassTemplateSpec,
-                          getTemplateInstantiationArgs(ClassTemplateSpec));
+                          getTemplateInstantiationArgs(ClassTemplateSpec),
+                          TSK);
 }
 
 Sema::OwningStmtResult 
index 807115eb67017b188e887d55df5785af31044d1d..b63fc400a58c2eaadff112b01bb0a49ff3e766e7 100644 (file)
@@ -402,6 +402,8 @@ Decl *TemplateDeclInstantiator::VisitClassTemplateDecl(ClassTemplateDecl *D) {
 
 Decl *
 TemplateDeclInstantiator::VisitFunctionTemplateDecl(FunctionTemplateDecl *D) {
+  // FIXME: Dig out the out-of-line definition of this function template?
+  
   TemplateParameterList *TempParams = D->getTemplateParameters();
   TemplateParameterList *InstParams = SubstTemplateParams(TempParams);
   if (!InstParams) 
@@ -496,7 +498,7 @@ Decl *TemplateDeclInstantiator::VisitFunctionDecl(FunctionDecl *D) {
                            D->getStorageClass(),
                            D->isInline(), D->hasWrittenPrototype());
   Function->setLexicalDeclContext(Owner);
-
+  
   // Attach the parameters
   for (unsigned P = 0; P < Params.size(); ++P)
     Params[P]->setOwningFunction(Function);
@@ -622,7 +624,7 @@ TemplateDeclInstantiator::VisitCXXMethodDecl(CXXMethodDecl *D,
                                                     Method->getDeclName(), 
                                                     TemplateParams, Method);
     if (D->isOutOfLine())
-      FunctionTemplate->setLexicalDeclContext(D->getLexicalDeclContext());
+      FunctionTemplate->setLexicalDeclContext(D->getLexicalDeclContext());    
     Method->setDescribedFunctionTemplate(FunctionTemplate);
   } else if (!FunctionTemplate)
     Method->setInstantiationOfMemberFunction(D);
@@ -979,6 +981,15 @@ void Sema::InstantiateFunctionDefinition(SourceLocation PointOfInstantiation,
   if (!Pattern)
     return;
 
+  // C++0x [temp.explicit]p9:
+  //   Except for inline functions, other explicit instantiation declarations
+  //   have the effect of suppressing the implicit instantiation of the entity 
+  //   to which they refer.
+  if (Function->getTemplateSpecializationKind() 
+        == TSK_ExplicitInstantiationDeclaration &&
+      PatternDecl->isOutOfLine() && !PatternDecl->isInline())
+    return;
+  
   InstantiatingTemplate Inst(*this, PointOfInstantiation, Function);
   if (Inst)
     return;
@@ -1080,6 +1091,8 @@ void Sema::InstantiateStaticDataMemberDefinition(
     return;
   }
 
+  // FIXME: extern templates
+  
   InstantiatingTemplate Inst(*this, PointOfInstantiation, Var);
   if (Inst)
     return;
index 11bbe4e2dea03e0c0a9a810827622f9e988ee17a..80cdcd5464e7c48e35e96cd95ff3e6a493bd1e73 100644 (file)
@@ -1781,7 +1781,7 @@ bool Sema::RequireCompleteType(SourceLocation Loc, QualType T,
         if (Loc.isValid())
           ClassTemplateSpec->setLocation(Loc);
         return InstantiateClassTemplateSpecialization(ClassTemplateSpec,
-                                             /*ExplicitInstantiation=*/false,
+                                                      TSK_ImplicitInstantiation,
                                                       /*Complain=*/diag != 0);
       }
     } else if (CXXRecordDecl *Rec 
@@ -1790,7 +1790,7 @@ bool Sema::RequireCompleteType(SourceLocation Loc, QualType T,
         // This record was instantiated from a class within a template.
         return InstantiateClass(Loc, Rec, Pattern, 
                                 getTemplateInstantiationArgs(Rec),
-                                /*ExplicitInstantiation=*/false,
+                                TSK_ImplicitInstantiation,
                                 /*Complain=*/diag != 0);
       }
     }
index 38966aad2debdbe5467296ca285fbe2133260d49..33cbf7f872e390ebcdd9c0c382a641dfa58a17bc 100644 (file)
@@ -1,11 +1,17 @@
 // RUN: clang-cc -emit-llvm -femit-all-decls -o %t %s &&
 // RUN: grep "_ZNK4plusIillEclERKiRKl" %t | count 1
 
+// FIXME: We should not need the -femit-all-decls, because operator() should
+// be emitted as an external symbol rather than with linkonce_odr linkage.
+// This is a Sema problem.
 template<typename T, typename U, typename Result>
 struct plus {
-  Result operator()(const T& t, const U& u) const {
-    return t + u;
-  }
+  Result operator()(const T& t, const U& u) const;
 };
 
+template<typename T, typename U, typename Result>
+Result plus<T, U, Result>::operator()(const T& t, const U& u) const {
+  return t + u;
+}
+
 template struct plus<int, long, long>;