Check for matching template-parameter-lists when merging template declarations.
authorRichard Smith <richard-llvm@metafoo.co.uk>
Mon, 24 Jun 2013 22:51:00 +0000 (22:51 +0000)
committerRichard Smith <richard-llvm@metafoo.co.uk>
Mon, 24 Jun 2013 22:51:00 +0000 (22:51 +0000)
llvm-svn: 184791

clang/lib/Serialization/ASTReaderDecl.cpp
clang/test/Modules/Inputs/cxx-templates-a.h
clang/test/Modules/Inputs/cxx-templates-b.h
clang/test/Modules/cxx-templates.cpp

index 3d64fa5..a7305c8 100644 (file)
@@ -967,6 +967,9 @@ void ASTDeclReader::VisitParmVarDecl(ParmVarDecl *PD) {
   PD->ParmVarDeclBits.HasInheritedDefaultArg = Record[Idx++];
   if (Record[Idx++]) // hasUninstantiatedDefaultArg.
     PD->setUninstantiatedDefaultArg(Reader.ReadExpr(F));
+
+  // FIXME: If this is a redeclaration of a function from another module, handle
+  // inheritance of default arguments.
 }
 
 void ASTDeclReader::VisitFileScopeAsmDecl(FileScopeAsmDecl *AD) {
@@ -1334,6 +1337,9 @@ void ASTDeclReader::VisitTemplateDecl(TemplateDecl *D) {
   TemplateParameterList* TemplateParams
       = Reader.ReadTemplateParameterList(F, Record, Idx); 
   D->init(TemplatedDecl, TemplateParams);
+
+  // FIXME: If this is a redeclaration of a template from another module, handle
+  // inheritance of default template arguments.
 }
 
 ASTDeclReader::RedeclarableResult 
@@ -1772,6 +1778,48 @@ uint64_t ASTReader::getGlobalBitOffset(ModuleFile &M, uint32_t LocalOffset) {
   return LocalOffset + M.GlobalBitOffset;
 }
 
+static bool isSameTemplateParameterList(const TemplateParameterList *X,
+                                        const TemplateParameterList *Y);
+
+/// \brief Determine whether two template parameters are similar enough
+/// that they may be used in declarations of the same template.
+static bool isSameTemplateParameter(const NamedDecl *X,
+                                    const NamedDecl *Y) {
+  if (X->getKind() != Y->getKind())
+    return false;
+
+  if (const TemplateTypeParmDecl *TX = dyn_cast<TemplateTypeParmDecl>(X)) {
+    const TemplateTypeParmDecl *TY = cast<TemplateTypeParmDecl>(Y);
+    return TX->isParameterPack() == TY->isParameterPack();
+  }
+
+  if (const NonTypeTemplateParmDecl *TX = dyn_cast<NonTypeTemplateParmDecl>(X)) {
+    const NonTypeTemplateParmDecl *TY = cast<NonTypeTemplateParmDecl>(Y);
+    return TX->isParameterPack() == TY->isParameterPack() &&
+           TX->getASTContext().hasSameType(TX->getType(), TY->getType());
+  }
+
+  const TemplateTemplateParmDecl *TX = cast<TemplateTemplateParmDecl>(X);
+  const TemplateTemplateParmDecl *TY = cast<TemplateTemplateParmDecl>(Y);
+  return TX->isParameterPack() == TY->isParameterPack() &&
+         isSameTemplateParameterList(TX->getTemplateParameters(),
+                                     TY->getTemplateParameters());
+}
+
+/// \brief Determine whether two template parameter lists are similar enough
+/// that they may be used in declarations of the same template.
+static bool isSameTemplateParameterList(const TemplateParameterList *X,
+                                        const TemplateParameterList *Y) {
+  if (X->size() != Y->size())
+    return false;
+
+  for (unsigned I = 0, N = X->size(); I != N; ++I)
+    if (!isSameTemplateParameter(X->getParam(I), Y->getParam(I)))
+      return false;
+
+  return true;
+}
+
 /// \brief Determine whether the two declarations refer to the same entity.
 static bool isSameEntity(NamedDecl *X, NamedDecl *Y) {
   assert(X->getDeclName() == Y->getDeclName() && "Declaration name mismatch!");
@@ -1841,10 +1889,11 @@ static bool isSameEntity(NamedDecl *X, NamedDecl *Y) {
   // Identical template names and kinds match if their template parameter lists
   // and patterns match.
   if (TemplateDecl *TemplateX = dyn_cast<TemplateDecl>(X)) {
-    TemplateDecl *TemplateY = dyn_cast<TemplateDecl>(Y);
-    // FIXME: Check template parameter lists.
+    TemplateDecl *TemplateY = cast<TemplateDecl>(Y);
     return isSameEntity(TemplateX->getTemplatedDecl(),
-                        TemplateY->getTemplatedDecl());
+                        TemplateY->getTemplatedDecl()) &&
+           isSameTemplateParameterList(TemplateX->getTemplateParameters(),
+                                       TemplateY->getTemplateParameters());
   }
 
   // FIXME: Many other cases to implement.
index 52bc3a2..76997c7 100644 (file)
@@ -4,3 +4,7 @@ namespace N {
   template<typename T> T f() { return T(); }
   template<typename T> T f(T);
 }
+
+template<int N> int template_param_kinds_1();
+template<template<typename T, int, int> class> int template_param_kinds_2();
+template<template<typename T, typename U, T> class> int template_param_kinds_3();
index 3cc940c..c50d705 100644 (file)
@@ -4,3 +4,7 @@ namespace N {
   template<typename T> T f();
   template<typename T> T f(T t) { return t; }
 }
+
+template<typename> int template_param_kinds_1();
+template<template<typename, int, int...> class> int template_param_kinds_2();
+template<template<typename T, typename U, U> class> int template_param_kinds_3();
index 7926812..7d500f4 100644 (file)
@@ -6,6 +6,12 @@
 @import cxx_templates_a;
 @import cxx_templates_b;
 
+template<typename, char> struct Tmpl_T_C {};
+template<typename, int, int> struct Tmpl_T_I_I {};
+
+template<typename A, typename B, A> struct Tmpl_T_T_A {};
+template<typename A, typename B, B> struct Tmpl_T_T_B {};
+
 void g() {
   f(0);
   f<double>(1.0);
@@ -20,6 +26,26 @@ void g() {
   N::f(); // expected-error {{no matching function}}
   // expected-note@Inputs/cxx-templates-a.h:4 {{couldn't infer template argument}}
   // expected-note@Inputs/cxx-templates-a.h:5 {{requires 1 argument, but 0 were provided}}
+
+  template_param_kinds_1<0>(); // ok, from cxx-templates-a.h
+  template_param_kinds_1<int>(); // ok, from cxx-templates-b.h
+
+  template_param_kinds_2<Tmpl_T_C>(); // expected-error {{no matching function}}
+  // expected-note@Inputs/cxx-templates-a.h:9 {{invalid explicitly-specified argument}}
+  // expected-note@Inputs/cxx-templates-b.h:9 {{invalid explicitly-specified argument}}
+
+  template_param_kinds_2<Tmpl_T_I_I>(); // expected-error {{ambiguous}}
+  // expected-note@Inputs/cxx-templates-a.h:9 {{candidate}}
+  // expected-note@Inputs/cxx-templates-b.h:9 {{candidate}}
+
+  // FIXME: This should be valid, but we incorrectly match the template template
+  // argument against both template template parameters.
+  template_param_kinds_3<Tmpl_T_T_A>(); // expected-error {{ambiguous}}
+  // expected-note@Inputs/cxx-templates-a.h:10 {{candidate}}
+  // expected-note@Inputs/cxx-templates-b.h:10 {{candidate}}
+  template_param_kinds_3<Tmpl_T_T_B>(); // expected-error {{ambiguous}}
+  // expected-note@Inputs/cxx-templates-a.h:10 {{candidate}}
+  // expected-note@Inputs/cxx-templates-b.h:10 {{candidate}}
 }
 
 // FIXME: There should only be two 'f's here.