]> granicus.if.org Git - clang/commitdiff
WIP implementation of explicit instantiation of function templates,
authorDouglas Gregor <dgregor@apple.com>
Fri, 25 Sep 2009 18:43:00 +0000 (18:43 +0000)
committerDouglas Gregor <dgregor@apple.com>
Fri, 25 Sep 2009 18:43:00 +0000 (18:43 +0000)
member functions of class template specializations, and static data
members. The mechanics are (mostly) present, but the semantic analysis
is very weak.

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

include/clang/AST/DeclarationName.h
include/clang/Basic/DiagnosticSemaKinds.td
include/clang/Basic/PartialDiagnostic.h
include/clang/Parse/Action.h
lib/Parse/ParseDecl.cpp
lib/Sema/Sema.h
lib/Sema/SemaTemplate.cpp
lib/Sema/SemaTemplateDeduction.cpp
test/Parser/cxx-template-decl.cpp
test/SemaTemplate/explicit-instantiation.cpp [new file with mode: 0644]

index 9850e8fff092a757d43362a2857bc552989d3598..9f9bfa6e3d49e4895ecb5fc00b07f08a4b904780 100644 (file)
@@ -16,6 +16,7 @@
 #include "clang/Basic/IdentifierTable.h"
 #include "clang/AST/Type.h"
 #include "clang/AST/CanonicalType.h"
+#include "clang/Basic/PartialDiagnostic.h"
 
 namespace llvm {
   template <typename T> struct DenseMapInfo;
@@ -327,7 +328,15 @@ inline const DiagnosticBuilder &operator<<(const DiagnosticBuilder &DB,
   return DB;
 }
 
-
+/// Insertion operator for partial diagnostics.  This allows binding
+/// DeclarationName's into a partial diagnostic with <<.
+inline const PartialDiagnostic &operator<<(const PartialDiagnostic &PD,
+                                           DeclarationName N) {
+  PD.AddTaggedVal(N.getAsOpaqueInteger(),
+                  Diagnostic::ak_declarationname);
+  return PD;
+}
+  
 }  // end namespace clang
 
 namespace llvm {
index 106b6a3e026217b5958c7ad494750a424efb0ed1..095b7ab38753cab646b5a89a8e79e58eb5321a3a 100644 (file)
@@ -1036,7 +1036,26 @@ def note_nontemplate_decl_here : Note<
     "non-templated declaration is here">;
 def err_explicit_instantiation_out_of_scope : Error<
   "explicit instantiation of %0 not in a namespace enclosing %1">;
-
+def err_explicit_instantiation_requires_name : Error<
+  "explicit instantiation declaration requires a name">;
+def err_explicit_instantiation_of_typedef : Error<
+  "explicit instantiation of typedef %0">;
+def err_explicit_instantiation_not_known : Error<
+  "explicit instantiation of %0 does not refer to a function template, member "
+  "function, member class, or static data member">;
+def note_explicit_instantiation_here : Note<
+  "explicit instantiation refers here">;
+def err_explicit_instantiation_data_member_not_instantiated : Error<
+  "explicit instantiation refers to static data member %q0 that is not an "
+  "instantiation">;
+def err_explicit_instantiation_member_function_not_instantiated : Error<
+  "explicit instantiation refers to member function %q0 that is not an "
+  "instantiation">;
+def err_explicit_instantiation_ambiguous : Error<
+  "partial ordering for explicit instantiation of %0 is ambiguous">;
+def note_explicit_instantiation_candidate : Note<
+  "explicit instantiation candidate function template here %0">;
+  
 // C++ typename-specifiers
 def err_typename_nested_not_found : Error<"no type named %0 in %1">;
 def err_typename_nested_not_found_global : Error<
index b4ba793e7ee18404df4d1706c636c71d4208a5a2..d893dd3aef8f640913dc2bb8b5d3d8af62db0f1f 100644 (file)
 #include "clang/AST/Type.h"
 #include "clang/Basic/Diagnostic.h"
 #include "clang/Basic/SourceLocation.h"
+#include "llvm/ADT/STLExtras.h"
 
 namespace clang {
 
+class DeclarationName;
+  
 class PartialDiagnostic {
   struct Storage {
     Storage() : NumDiagArgs(0), NumDiagRanges(0) { }
@@ -132,6 +135,9 @@ public:
     PD.AddSourceRange(R);
     return PD;
   }
+  
+  friend const PartialDiagnostic &operator<<(const PartialDiagnostic &PD,
+                                             DeclarationName N);
 };
 
 inline PartialDiagnostic PDiag(unsigned DiagID) {
index 8fdf779acde1391f791856ba4dcb9f32131055d8..4be5a763a92d5112150ae0429c10ef3917423128 100644 (file)
@@ -1898,6 +1898,38 @@ public:
     return DeclResult();
   }
 
+  /// \brief Process the explicit instantiation of a function template or a
+  /// member of a class template.
+  ///
+  /// This routine is invoked when an explicit instantiation of a
+  /// function template or member function of a class template specialization 
+  /// is encountered. In the following example,
+  /// ActOnExplicitInstantiation will be invoked to force the
+  /// instantiation of X<int>:
+  ///
+  /// \code
+  /// template<typename T> void f(T);
+  /// template void f(int); // explicit instantiation
+  /// \endcode
+  ///
+  /// \param S the current scope
+  ///
+  /// \param ExternLoc the location of the 'extern' keyword that specifies that
+  /// this is an extern template (if any).
+  ///
+  /// \param TemplateLoc the location of the 'template' keyword that
+  /// specifies that this is an explicit instantiation.
+  ///
+  /// \param D the declarator describing the declaration to be implicitly
+  /// instantiated.
+  virtual DeclResult ActOnExplicitInstantiation(Scope *S,
+                                                SourceLocation ExternLoc,
+                                                SourceLocation TemplateLoc,
+                                                Declarator &D) {
+    return DeclResult();
+  }
+                             
+                              
   /// \brief Called when the parser has parsed a C++ typename
   /// specifier that ends in an identifier, e.g., "typename T::type".
   ///
index 75ff82772a10b6925141512d858226966a06e84f..9525eb364c346d0d8d46fd1b5a715e85e567cbc7 100644 (file)
@@ -419,13 +419,36 @@ Parser::DeclPtrTy Parser::ParseDeclarationAfterDeclarator(Declarator &D,
   }
 
   // Inform the current actions module that we just parsed this declarator.
-  DeclPtrTy ThisDecl = TemplateInfo.TemplateParams?
-      Actions.ActOnTemplateDeclarator(CurScope,
+  DeclPtrTy ThisDecl;
+  switch (TemplateInfo.Kind) {
+  case ParsedTemplateInfo::NonTemplate:
+    ThisDecl = Actions.ActOnDeclarator(CurScope, D);
+    break;
+      
+  case ParsedTemplateInfo::Template:
+  case ParsedTemplateInfo::ExplicitSpecialization:
+    ThisDecl = Actions.ActOnTemplateDeclarator(CurScope,
                              Action::MultiTemplateParamsArg(Actions,
                                           TemplateInfo.TemplateParams->data(),
                                           TemplateInfo.TemplateParams->size()),
-                                    D)
-    : Actions.ActOnDeclarator(CurScope, D);
+                                               D);
+    break;
+      
+  case ParsedTemplateInfo::ExplicitInstantiation: {
+    Action::DeclResult ThisRes 
+      = Actions.ActOnExplicitInstantiation(CurScope,
+                                           TemplateInfo.ExternLoc,
+                                           TemplateInfo.TemplateLoc,
+                                           D);
+    if (ThisRes.isInvalid()) {
+      SkipUntil(tok::semi, true, true);
+      return DeclPtrTy();
+    }
+    
+    ThisDecl = ThisRes.get();
+    break;
+    }
+  }
 
   // Parse declarator '=' initializer.
   if (Tok.is(tok::equal)) {
index 8377d5093919f4d6a987d81c522663224e9662a5..56f4693cd24e7611d3c47f83ee38c3e5f3ff7fc6 100644 (file)
@@ -2550,6 +2550,11 @@ public:
                              SourceLocation NameLoc,
                              AttributeList *Attr);
 
+  virtual DeclResult ActOnExplicitInstantiation(Scope *S,
+                                                SourceLocation ExternLoc,
+                                                SourceLocation TemplateLoc,
+                                                Declarator &D);
+    
   bool CheckTemplateArgumentList(TemplateDecl *Template,
                                  SourceLocation TemplateLoc,
                                  SourceLocation LAngleLoc,
@@ -2779,6 +2784,15 @@ public:
   FunctionTemplateDecl *getMoreSpecializedTemplate(FunctionTemplateDecl *FT1,
                                                    FunctionTemplateDecl *FT2,
                                            TemplatePartialOrderingContext TPOC);
+  FunctionDecl *getMostSpecialized(FunctionDecl **Specializations,
+                                   unsigned NumSpecializations,
+                                   TemplatePartialOrderingContext TPOC,
+                                   SourceLocation Loc,
+                                   const PartialDiagnostic &NoneDiag,
+                                   const PartialDiagnostic &AmbigDiag,
+                                   const PartialDiagnostic &CandidateDiag,
+                                   unsigned *Index = 0);
+                                   
   ClassTemplatePartialSpecializationDecl *
   getMoreSpecializedPartialSpecialization(
                                   ClassTemplatePartialSpecializationDecl *PS1,
index 509237a855a786759dd93c51e307f9110abed80e..37fbf453742277fd35044f123e4deb2f5daba5f5 100644 (file)
@@ -17,6 +17,7 @@
 #include "clang/AST/DeclTemplate.h"
 #include "clang/Parse/DeclSpec.h"
 #include "clang/Basic/LangOptions.h"
+#include "clang/Basic/PartialDiagnostic.h"
 #include "llvm/Support/Compiler.h"
 #include "llvm/ADT/StringExtras.h"
 using namespace clang;
@@ -3326,6 +3327,180 @@ Sema::ActOnExplicitInstantiation(Scope *S,
   return TagD;
 }
 
+Sema::DeclResult Sema::ActOnExplicitInstantiation(Scope *S,
+                                                  SourceLocation ExternLoc,
+                                                  SourceLocation TemplateLoc,
+                                                  Declarator &D) {
+  // Explicit instantiations always require a name.
+  DeclarationName Name = GetNameForDeclarator(D);
+  if (!Name) {
+    if (!D.isInvalidType())
+      Diag(D.getDeclSpec().getSourceRange().getBegin(),
+           diag::err_explicit_instantiation_requires_name)
+        << D.getDeclSpec().getSourceRange()
+        << D.getSourceRange();
+    
+    return true;
+  }
+
+  // The scope passed in may not be a decl scope.  Zip up the scope tree until
+  // we find one that is.
+  while ((S->getFlags() & Scope::DeclScope) == 0 ||
+         (S->getFlags() & Scope::TemplateParamScope) != 0)
+    S = S->getParent();
+
+  // Determine the type of the declaration.
+  QualType R = GetTypeForDeclarator(D, S, 0);
+  if (R.isNull())
+    return true;
+  
+  if (D.getDeclSpec().getStorageClassSpec() == DeclSpec::SCS_typedef) {
+    // Cannot explicitly instantiate a typedef.
+    Diag(D.getIdentifierLoc(), diag::err_explicit_instantiation_of_typedef)
+      << Name;
+    return true;
+  }
+
+  // Determine what kind of explicit instantiation we have.
+  TemplateSpecializationKind TSK
+    = ExternLoc.isInvalid()? TSK_ExplicitInstantiationDefinition
+                           : TSK_ExplicitInstantiationDeclaration;
+  
+  LookupResult Previous = LookupParsedName(S, &D.getCXXScopeSpec(),
+                                           Name, LookupOrdinaryName);
+
+  if (!R->isFunctionType()) {
+    // C++ [temp.explicit]p1:
+    //   A [...] static data member of a class template can be explicitly 
+    //   instantiated from the member definition associated with its class 
+    //   template.
+    if (Previous.isAmbiguous()) {
+      return DiagnoseAmbiguousLookup(Previous, Name, D.getIdentifierLoc(),
+                                     D.getSourceRange());
+    }
+    
+    VarDecl *Prev = dyn_cast_or_null<VarDecl>(Previous.getAsDecl());
+    if (!Prev || !Prev->isStaticDataMember()) {
+      // We expect to see a data data member here.
+      Diag(D.getIdentifierLoc(), diag::err_explicit_instantiation_not_known)
+        << Name;
+      for (LookupResult::iterator P = Previous.begin(), PEnd = Previous.end();
+           P != PEnd; ++P)
+        Diag(P->getLocation(), diag::note_explicit_instantiation_here);
+      return true;
+    }
+    
+    if (!Prev->getInstantiatedFromStaticDataMember()) {
+      // FIXME: Check for explicit specialization?
+      Diag(D.getIdentifierLoc(), 
+           diag::err_explicit_instantiation_data_member_not_instantiated)
+        << Prev;
+      Diag(Prev->getLocation(), diag::note_explicit_instantiation_here);
+      // FIXME: Can we provide a note showing where this was declared?
+      return true;
+    }
+    
+    // Instantiate static data member.
+    // FIXME: Note that this is an explicit instantiation.
+    if (TSK == TSK_ExplicitInstantiationDefinition)
+      InstantiateStaticDataMemberDefinition(D.getIdentifierLoc(), Prev, false);
+    
+    // FIXME: Create an ExplicitInstantiation node?
+    return DeclPtrTy();
+  }
+  
+  // C++ [temp.explicit]p1:
+  //   A [...] function [...] can be explicitly instantiated from its template. 
+  //   A member function [...] of a class template can be explicitly 
+  //  instantiated from the member definition associated with its class 
+  //  template.
+  // FIXME: Implement this!
+  llvm::SmallVector<FunctionDecl *, 8> Matches;
+  for (LookupResult::iterator P = Previous.begin(), PEnd = Previous.end();
+       P != PEnd; ++P) {
+    NamedDecl *Prev = *P;
+    if (CXXMethodDecl *Method = dyn_cast<CXXMethodDecl>(Prev)) {
+      // FIXME: If there were any explicitly-specified template arguments, 
+      // don't look for Method declarations.
+      if (Context.hasSameUnqualifiedType(Method->getType(), R)) {
+        Matches.clear();
+        Matches.push_back(Method);
+        break;
+      }
+    }
+    
+    FunctionTemplateDecl *FunTmpl = dyn_cast<FunctionTemplateDecl>(Prev);
+    if (!FunTmpl)
+      continue;
+
+    TemplateDeductionInfo Info(Context);
+    FunctionDecl *Specialization = 0;
+    if (TemplateDeductionResult TDK
+          = DeduceTemplateArguments(FunTmpl, /*FIXME:*/false, 0, 0, 
+                                    R, Specialization, Info)) {
+      // FIXME: Keep track of almost-matches?
+      (void)TDK;
+      continue;
+    }
+    
+    Matches.push_back(Specialization);
+  }
+  
+  // Find the most specialized function template specialization.
+  FunctionDecl *Specialization
+    = getMostSpecialized(Matches.data(), Matches.size(), TPOC_Other, 
+                         D.getIdentifierLoc(), 
+          PartialDiagnostic(diag::err_explicit_instantiation_not_known) << Name,
+          PartialDiagnostic(diag::err_explicit_instantiation_ambiguous) << Name,
+                PartialDiagnostic(diag::note_explicit_instantiation_candidate));
+
+  if (!Specialization)
+    return true;
+  
+  switch (Specialization->getTemplateSpecializationKind()) {
+  case TSK_Undeclared:
+    Diag(D.getIdentifierLoc(), 
+         diag::err_explicit_instantiation_member_function_not_instantiated)
+      << Specialization
+      << (Specialization->getTemplateSpecializationKind() ==
+          TSK_ExplicitSpecialization);
+    Diag(Specialization->getLocation(), diag::note_explicit_instantiation_here);
+    return true;
+
+  case TSK_ExplicitSpecialization:
+    // C++ [temp.explicit]p4:
+    //   For a given set of template parameters, if an explicit instantiation
+    //   of a template appears after a declaration of an explicit 
+    //   specialization for that template, the explicit instantiation has no 
+    //   effect.
+    break;      
+
+  case TSK_ExplicitInstantiationDefinition:
+    // FIXME: Check that we aren't trying to perform an explicit instantiation
+    // declaration now.
+    // Fall through
+      
+  case TSK_ImplicitInstantiation:
+  case TSK_ExplicitInstantiationDeclaration:
+    // Instantiate the function, if this is an explicit instantiation 
+    // definition.
+    if (TSK == TSK_ExplicitInstantiationDefinition)
+      InstantiateFunctionDefinition(D.getIdentifierLoc(), Specialization, 
+                                    false);
+      
+    // FIXME: setTemplateSpecializationKind doesn't (yet) work for 
+    // non-templated member functions.
+    if (!Specialization->getPrimaryTemplate())
+      break;
+      
+    Specialization->setTemplateSpecializationKind(TSK);
+    break;
+  }
+
+  // FIXME: Create some kind of ExplicitInstantiationDecl here.
+  return DeclPtrTy();
+}
+
 Sema::TypeResult
 Sema::ActOnDependentTag(Scope *S, unsigned TagSpec, TagUseKind TUK,
                         const CXXScopeSpec &SS, IdentifierInfo *Name,
index b5aa075bd64b42c58683c37dff13c08382be4229..64b7f8b140e166abdcafb57f278854e26d44c04d 100644 (file)
@@ -1885,6 +1885,124 @@ Sema::getMoreSpecializedTemplate(FunctionTemplateDecl *FT1,
     return 0;
 }
 
+/// \brief Determine if the two templates are equivalent.
+static bool isSameTemplate(TemplateDecl *T1, TemplateDecl *T2) {
+  if (T1 == T2)
+    return true;
+  
+  if (!T1 || !T2)
+    return false;
+  
+  return T1->getCanonicalDecl() == T2->getCanonicalDecl();
+}
+
+/// \brief Retrieve the most specialized of the given function template
+/// specializations.
+///
+/// \param Specializations the set of function template specializations that
+/// we will be comparing.
+///
+/// \param NumSpecializations the number of function template specializations in
+/// \p Specializations
+///
+/// \param TPOC the partial ordering context to use to compare the function
+/// template specializations.
+///
+/// \param Loc the location where the ambiguity or no-specializations 
+/// diagnostic should occur.
+///
+/// \param NoneDiag partial diagnostic used to diagnose cases where there are
+/// no matching candidates.
+///
+/// \param AmbigDiag partial diagnostic used to diagnose an ambiguity, if one
+/// occurs.
+///
+/// \param CandidateDiag partial diagnostic used for each function template
+/// specialization that is a candidate in the ambiguous ordering. One parameter
+/// in this diagnostic should be unbound, which will correspond to the string
+/// describing the template arguments for the function template specialization.
+///
+/// \param Index if non-NULL and the result of this function is non-nULL, 
+/// receives the index corresponding to the resulting function template
+/// specialization.
+///
+/// \returns the most specialized function template specialization, if 
+/// found. Otherwise, returns NULL.
+///
+/// \todo FIXME: Consider passing in the "also-ran" candidates that failed 
+/// template argument deduction.
+FunctionDecl *Sema::getMostSpecialized(FunctionDecl **Specializations,
+                                       unsigned NumSpecializations,
+                                       TemplatePartialOrderingContext TPOC,
+                                       SourceLocation Loc,
+                                       const PartialDiagnostic &NoneDiag,
+                                       const PartialDiagnostic &AmbigDiag,
+                                       const PartialDiagnostic &CandidateDiag,
+                                       unsigned *Index) {
+  if (NumSpecializations == 0) {
+    Diag(Loc, NoneDiag);
+    return 0;
+  }
+  
+  if (NumSpecializations == 1) {
+    if (Index)
+      *Index = 0;
+    
+    return Specializations[0];
+  }
+    
+  
+  // Find the function template that is better than all of the templates it
+  // has been compared to.
+  unsigned Best = 0;
+  FunctionTemplateDecl *BestTemplate 
+    = Specializations[Best]->getPrimaryTemplate();
+  assert(BestTemplate && "Not a function template specialization?");
+  for (unsigned I = 1; I != NumSpecializations; ++I) {
+    FunctionTemplateDecl *Challenger = Specializations[I]->getPrimaryTemplate();
+    assert(Challenger && "Not a function template specialization?");
+    if (isSameTemplate(getMoreSpecializedTemplate(BestTemplate, Challenger, 
+                                                  TPOC),
+                       Challenger)) {
+      Best = I;
+      BestTemplate = Challenger;
+    }
+  }
+  
+  // Make sure that the "best" function template is more specialized than all
+  // of the others.
+  bool Ambiguous = false;
+  for (unsigned I = 0; I != NumSpecializations; ++I) {
+    FunctionTemplateDecl *Challenger = Specializations[I]->getPrimaryTemplate();
+    if (I != Best &&
+        !isSameTemplate(getMoreSpecializedTemplate(BestTemplate, Challenger, 
+                                                  TPOC),
+                        BestTemplate)) {
+      Ambiguous = true;
+      break;
+    }
+  }
+  
+  if (!Ambiguous) {
+    // We found an answer. Return it.
+    if (Index)
+      *Index = Best;
+    return Specializations[Best];
+  }
+  
+  // Diagnose the ambiguity.
+  Diag(Loc, AmbigDiag);
+  
+  // FIXME: Can we order the candidates in some sane way?
+  for (unsigned I = 0; I != NumSpecializations; ++I)
+    Diag(Specializations[I]->getLocation(), CandidateDiag)
+      << getTemplateArgumentBindingsText(
+            Specializations[I]->getPrimaryTemplate()->getTemplateParameters(),
+                         *Specializations[I]->getTemplateSpecializationArgs());
+  
+  return 0;
+}
+
 /// \brief Returns the more specialized class template partial specialization
 /// according to the rules of partial ordering of class template partial
 /// specializations (C++ [temp.class.order]).
index 94b7069afbe4b56a35958fe6f8e4dafa7482fddb..7f1ff3dc316c4aed81782888089f85336c99a61d 100644 (file)
@@ -2,7 +2,8 @@
 
 // Errors
 export class foo { };   // expected-error {{expected template}}
-template  x;            // expected-error {{C++ requires a type specifier for all declarations}}
+template  x;            // expected-error {{C++ requires a type specifier for all declarations}} \
+                        // expected-error {{does not refer}}
 export template x;      // expected-error {{expected '<' after 'template'}}
 export template<class T> class x0; // expected-note {{exported templates are unsupported}}
 template < ;            // expected-error {{parse error}} expected-error {{declaration does not declare anything}}
diff --git a/test/SemaTemplate/explicit-instantiation.cpp b/test/SemaTemplate/explicit-instantiation.cpp
new file mode 100644 (file)
index 0000000..f92c463
--- /dev/null
@@ -0,0 +1,57 @@
+// RUN: clang-cc -fsyntax-only -verify %s
+
+template void *; // expected-error{{expected unqualified-id}}
+
+template typedef void f0; // expected-error{{explicit instantiation of typedef}}
+
+int v0; // expected-note{{refers here}}
+template int v0; // expected-error{{does not refer}}
+
+template<typename T>
+struct X0 {
+  static T value;
+  
+  T f0(T x) {
+    return x + 1;  // expected-error{{invalid operands}}
+  } 
+  T* f0(T*, T*);
+  
+  template<typename U>
+  T f0(T, U);
+};
+
+template int X0<int>::value;
+
+struct NotDefaultConstructible {
+  NotDefaultConstructible(int);
+};
+
+template NotDefaultConstructible X0<NotDefaultConstructible>::value;
+
+template int X0<int>::f0(int);
+template int* X0<int>::f0(int*, int*);
+template int X0<int>::f0(int, float);
+
+template int X0<int>::f0(int) const; // expected-error{{does not refer}}
+template int* X0<int>::f0(int*, float*); // expected-error{{does not refer}}
+
+struct X1 { };
+typedef int X1::*MemPtr;
+
+template MemPtr X0<MemPtr>::f0(MemPtr); // expected-note{{requested here}}
+
+struct X2 {
+  int f0(int); // expected-note{{refers here}}
+  
+  template<typename T> T f1(T);
+  template<typename T> T* f1(T*);
+
+  template<typename T, typename U> void f2(T, U*); // expected-note{{candidate}}
+  template<typename T, typename U> void f2(T*, U); // expected-note{{candidate}}
+};
+
+template int X2::f0(int); // expected-error{{not an instantiation}}
+
+template int *X2::f1(int *); // okay
+
+template void X2::f2(int *, int *); // expected-error{{ambiguous}}