[cfe-commits] r84154 - in /cfe/trunk: include/clang/Basic/DiagnosticSemaKinds.td lib/Sema/SemaTemplate.cpp test/CXX/temp/temp.spec/temp.explicit/p2.cpp test/SemaTemplate/explicit-instantiation.cpp test/SemaTemplate/temp_explicit.cpp

Douglas Gregor dgregor at apple.com
Wed Oct 14 16:41:35 PDT 2009


Author: dgregor
Date: Wed Oct 14 18:41:34 2009
New Revision: 84154

URL: http://llvm.org/viewvc/llvm-project?rev=84154&view=rev
Log:
Additional semantic checking for explicit template instantiations,
focusing on the scope- and qualifier-related semantic requirements in
C++ [temp.explicit]p2.

Added:
    cfe/trunk/test/CXX/temp/temp.spec/temp.explicit/p2.cpp
Modified:
    cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td
    cfe/trunk/lib/Sema/SemaTemplate.cpp
    cfe/trunk/test/SemaTemplate/explicit-instantiation.cpp
    cfe/trunk/test/SemaTemplate/temp_explicit.cpp

Modified: cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td?rev=84154&r1=84153&r2=84154&view=diff

==============================================================================
--- cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td (original)
+++ cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td Wed Oct 14 18:41:34 2009
@@ -1085,6 +1085,9 @@
     "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_must_be_global : Error<
+  "explicit instantiation of %0 must occur at global scope">;
+  
 def err_explicit_instantiation_requires_name : Error<
   "explicit instantiation declaration requires a name">;
 def err_explicit_instantiation_of_typedef : Error<
@@ -1106,6 +1109,12 @@
   "explicit instantiation candidate function template here %0">;
 def err_explicit_instantiation_inline : Error<
   "explicit instantiation cannot be 'inline'">;
+def err_explicit_instantiation_without_qualified_id : Error<
+  "qualifier in explicit instantiation of %q0 requires a template-id">;
+def err_explicit_instantiation_without_qualified_id_quals : Error<
+  "qualifier in explicit instantiation of '%0%1' requires a template-id">;
+def err_explicit_instantiation_unqualified_wrong_namespace : Error<
+  "explicit instantiation of %q0 must occur in %1">;
 
 // C++ typename-specifiers
 def err_typename_nested_not_found : Error<"no type named %0 in %1">;

Modified: cfe/trunk/lib/Sema/SemaTemplate.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaTemplate.cpp?rev=84154&r1=84153&r2=84154&view=diff

==============================================================================
--- cfe/trunk/lib/Sema/SemaTemplate.cpp (original)
+++ cfe/trunk/lib/Sema/SemaTemplate.cpp Wed Oct 14 18:41:34 2009
@@ -2421,7 +2421,7 @@
 /// \param IsPartialSpecialization whether this is a partial specialization of
 /// a class template.
 ///
-/// \param TSK the kind of specialization or implicit instantiation being 
+/// \param TSK the kind of specialization or explicit instantiation being 
 /// performed.
 ///
 /// \returns true if there was an error that we cannot recover from, false
@@ -3325,6 +3325,68 @@
   return false;
 }
 
+/// \brief Check the scope of an explicit instantiation.
+static void CheckExplicitInstantiationScope(Sema &S, NamedDecl *D,
+                                            SourceLocation InstLoc,
+                                            bool WasQualifiedName) {
+  DeclContext *ExpectedContext
+    = D->getDeclContext()->getEnclosingNamespaceContext()->getLookupContext();
+  DeclContext *CurContext = S.CurContext->getLookupContext();
+  
+  // C++0x [temp.explicit]p2:
+  //   An explicit instantiation shall appear in an enclosing namespace of its 
+  //   template.
+  //
+  // This is DR275, which we do not retroactively apply to C++98/03.
+  if (S.getLangOptions().CPlusPlus0x && 
+      !CurContext->Encloses(ExpectedContext)) {
+    if (NamespaceDecl *NS = dyn_cast<NamespaceDecl>(ExpectedContext))
+      S.Diag(InstLoc, diag::err_explicit_instantiation_out_of_scope)
+        << D << NS;
+    else
+      S.Diag(InstLoc, diag::err_explicit_instantiation_must_be_global)
+        << D;
+    S.Diag(D->getLocation(), diag::note_explicit_instantiation_here);
+    return;
+  }
+  
+  // C++0x [temp.explicit]p2:
+  //   If the name declared in the explicit instantiation is an unqualified 
+  //   name, the explicit instantiation shall appear in the namespace where 
+  //   its template is declared or, if that namespace is inline (7.3.1), any
+  //   namespace from its enclosing namespace set.
+  if (WasQualifiedName)
+    return;
+  
+  if (CurContext->Equals(ExpectedContext))
+    return;
+  
+  S.Diag(InstLoc, diag::err_explicit_instantiation_unqualified_wrong_namespace)
+    << D << ExpectedContext;
+  S.Diag(D->getLocation(), diag::note_explicit_instantiation_here);
+}
+
+/// \brief Determine whether the given scope specifier has a template-id in it.
+static bool ScopeSpecifierHasTemplateId(const CXXScopeSpec &SS) {
+  if (!SS.isSet())
+    return false;
+  
+  // C++0x [temp.explicit]p2:
+  //   If the explicit instantiation is for a member function, a member class 
+  //   or a static data member of a class template specialization, the name of
+  //   the class template specialization in the qualified-id for the member
+  //   name shall be a simple-template-id.
+  //
+  // C++98 has the same restriction, just worded differently.
+  for (NestedNameSpecifier *NNS = (NestedNameSpecifier *)SS.getScopeRep();
+       NNS; NNS = NNS->getPrefix())
+    if (Type *T = NNS->getAsType())
+      if (isa<TemplateSpecializationType>(T))
+        return true;
+
+  return false;
+}
+
 // Explicit instantiation of a class template specialization
 // FIXME: Implement extern template semantics
 Sema::DeclResult
@@ -3367,6 +3429,10 @@
     Kind = ClassTemplate->getTemplatedDecl()->getTagKind();
   }
 
+  // C++0x [temp.explicit]p2:
+  //   There are two forms of explicit instantiation: an explicit instantiation
+  //   definition and an explicit instantiation declaration. An explicit 
+  //   instantiation declaration begins with the extern keyword. [...]  
   TemplateSpecializationKind TSK
     = ExternLoc.isInvalid()? TSK_ExplicitInstantiationDefinition
                            : TSK_ExplicitInstantiationDeclaration;
@@ -3404,10 +3470,8 @@
   //   namespace of its template. [...]
   //
   // This is C++ DR 275.
-  if (CheckTemplateSpecializationScope(*this, ClassTemplate, PrevDecl,
-                                       TemplateNameLoc, false, 
-                                       TSK))
-    return true;
+  CheckExplicitInstantiationScope(*this, ClassTemplate, TemplateNameLoc,
+                                  SS.isSet());
   
   ClassTemplateSpecializationDecl *Specialization = 0;
 
@@ -3563,7 +3627,7 @@
 
   if (Tag->isInvalidDecl())
     return true;
-
+    
   CXXRecordDecl *Record = cast<CXXRecordDecl>(Tag);
   CXXRecordDecl *Pattern = Record->getInstantiatedFromMemberClass();
   if (!Pattern) {
@@ -3573,7 +3637,20 @@
     return true;
   }
 
-  // What kind of explicit instantiation? (for C++0x, GNU extern templates).
+  // C++0x [temp.explicit]p2:
+  //   If the explicit instantiation is for a class or member class, the 
+  //   elaborated-type-specifier in the declaration shall include a 
+  //   simple-template-id.
+  //
+  // C++98 has the same restriction, just worded differently.
+  if (!ScopeSpecifierHasTemplateId(SS))
+    Diag(TemplateLoc, diag::err_explicit_instantiation_without_qualified_id)
+      << Record << SS.getRange();
+           
+  // C++0x [temp.explicit]p2:
+  //   There are two forms of explicit instantiation: an explicit instantiation
+  //   definition and an explicit instantiation declaration. An explicit 
+  //   instantiation declaration begins with the extern keyword. [...]
   TemplateSpecializationKind TSK
     = ExternLoc.isInvalid()? TSK_ExplicitInstantiationDefinition
                            : TSK_ExplicitInstantiationDeclaration;
@@ -3583,11 +3660,7 @@
   //   namespace of its template. [...]
   //
   // This is C++ DR 275.
-  if (CheckTemplateSpecializationScope(*this, Record, 
-                                       Record->getPreviousDeclaration(),
-                                       NameLoc, false, 
-                                       TSK))
-    return true;  
+  CheckExplicitInstantiationScope(*this, Record, NameLoc, true);
 
   if (!Record->getDefinition(Context)) {
     // If the class has a definition, instantiate it (and all of its
@@ -3655,11 +3728,14 @@
   
   // FIXME: check for constexpr specifier.
   
-  // Determine what kind of explicit instantiation we have.
+  // C++0x [temp.explicit]p2:
+  //   There are two forms of explicit instantiation: an explicit instantiation
+  //   definition and an explicit instantiation declaration. An explicit 
+  //   instantiation declaration begins with the extern keyword. [...]  
   TemplateSpecializationKind TSK
     = ExternLoc.isInvalid()? TSK_ExplicitInstantiationDefinition
                            : TSK_ExplicitInstantiationDeclaration;
-  
+    
   LookupResult Previous;
   LookupParsedName(Previous, S, &D.getCXXScopeSpec(),
                    Name, LookupOrdinaryName);
@@ -3696,6 +3772,21 @@
       return true;
     }
     
+    // C++0x [temp.explicit]p2:
+    //   If the explicit instantiation is for a member function, a member class 
+    //   or a static data member of a class template specialization, the name of
+    //   the class template specialization in the qualified-id for the member
+    //   name shall be a simple-template-id.
+    //
+    // C++98 has the same restriction, just worded differently.
+    if (!ScopeSpecifierHasTemplateId(D.getCXXScopeSpec()))
+      Diag(D.getIdentifierLoc(), 
+           diag::err_explicit_instantiation_without_qualified_id)
+        << Prev << D.getCXXScopeSpec().getRange();
+    
+    // Check the scope of this explicit instantiation.
+    CheckExplicitInstantiationScope(*this, Prev, D.getIdentifierLoc(), true);
+    
     // Instantiate static data member.
     // FIXME: Check for prior specializations and such.
     Prev->setTemplateSpecializationKind(TSK);
@@ -3806,6 +3897,29 @@
     break;
   }
 
+  // Check the scope of this explicit instantiation.
+  FunctionTemplateDecl *FunTmpl = Specialization->getPrimaryTemplate();
+  
+  // C++0x [temp.explicit]p2:
+  //   If the explicit instantiation is for a member function, a member class 
+  //   or a static data member of a class template specialization, the name of
+  //   the class template specialization in the qualified-id for the member
+  //   name shall be a simple-template-id.
+  //
+  // C++98 has the same restriction, just worded differently.
+  if (D.getKind() != Declarator::DK_TemplateId && !FunTmpl &&
+      D.getCXXScopeSpec().isSet() && 
+      !ScopeSpecifierHasTemplateId(D.getCXXScopeSpec()))
+    Diag(D.getIdentifierLoc(), 
+         diag::err_explicit_instantiation_without_qualified_id)
+    << Specialization << D.getCXXScopeSpec().getRange();
+  
+  CheckExplicitInstantiationScope(*this,
+                   FunTmpl? (NamedDecl *)FunTmpl 
+                          : Specialization->getInstantiatedFromMemberFunction(),
+                                  D.getIdentifierLoc(), 
+                                  D.getCXXScopeSpec().isSet());
+  
   // FIXME: Create some kind of ExplicitInstantiationDecl here.
   return DeclPtrTy();
 }

Added: cfe/trunk/test/CXX/temp/temp.spec/temp.explicit/p2.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CXX/temp/temp.spec/temp.explicit/p2.cpp?rev=84154&view=auto

==============================================================================
--- cfe/trunk/test/CXX/temp/temp.spec/temp.explicit/p2.cpp (added)
+++ cfe/trunk/test/CXX/temp/temp.spec/temp.explicit/p2.cpp Wed Oct 14 18:41:34 2009
@@ -0,0 +1,43 @@
+// RUN: clang-cc -fsyntax-only -verify %s
+
+// Example from the standard
+template<class T> class Array { void mf() { } }; 
+
+template class Array<char>; 
+template void Array<int>::mf();
+template<class T> void sort(Array<T>& v) { /* ... */ }
+template void sort(Array<char>&);
+namespace N { 
+  template<class T> void f(T&) { }
+} 
+template void N::f<int>(int&);
+
+
+template<typename T>
+struct X0 {
+  struct Inner {};
+  void f() { }
+  static T value;
+};
+
+template<typename T>
+T X0<T>::value = 17;
+
+typedef X0<int> XInt;
+
+template struct XInt::Inner; // expected-error{{template-id}}
+template void XInt::f(); // expected-error{{template-id}}
+template int XInt::value; // expected-error{{template-id}}
+
+namespace N {
+  template<typename T>
+  struct X1 { // expected-note{{explicit instantiation refers here}}
+  };
+  
+  template<typename T>
+  void f1(T); // expected-note{{explicit instantiation refers here}}
+}
+using namespace N;
+
+template struct X1<int>; // expected-error{{must occur in}}
+template void f1(int); // expected-error{{must occur in}}

Modified: cfe/trunk/test/SemaTemplate/explicit-instantiation.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaTemplate/explicit-instantiation.cpp?rev=84154&r1=84153&r2=84154&view=diff

==============================================================================
--- cfe/trunk/test/SemaTemplate/explicit-instantiation.cpp (original)
+++ cfe/trunk/test/SemaTemplate/explicit-instantiation.cpp Wed Oct 14 18:41:34 2009
@@ -14,19 +14,22 @@
   T f0(T x) {
     return x + 1;  // expected-error{{invalid operands}}
   } 
-  T* f0(T*, T*);
+  T* f0(T*, T*) { return T(); }
   
   template<typename U>
-  T f0(T, U);
+  T f0(T, U) { return T(); }
 };
 
+template<typename T>
+T X0<T>::value; // expected-error{{no matching constructor}}
+
 template int X0<int>::value;
 
 struct NotDefaultConstructible {
   NotDefaultConstructible(int);
 };
 
-template NotDefaultConstructible X0<NotDefaultConstructible>::value;
+template NotDefaultConstructible X0<NotDefaultConstructible>::value; // expected-note{{instantiation}}
 
 template int X0<int>::f0(int);
 template int* X0<int>::f0(int*, int*);
@@ -43,11 +46,11 @@
 struct X2 {
   int f0(int); // expected-note{{refers here}}
   
-  template<typename T> T f1(T);
-  template<typename T> T* f1(T*);
+  template<typename T> T f1(T) { return T(); }
+  template<typename T> T* f1(T*) { return 0; }
 
-  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<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}}
@@ -57,12 +60,12 @@
 template void X2::f2(int *, int *); // expected-error{{ambiguous}}
 
 
-template<typename T> void print_type();
+template<typename T> void print_type() { }
 
 template void print_type<int>();
 template void print_type<float>();
 
-template<typename T> void print_type(T*);
+template<typename T> void print_type(T*) { }
 
 template void print_type(int*);
 template void print_type<int>(float*); // expected-error{{does not refer}}

Modified: cfe/trunk/test/SemaTemplate/temp_explicit.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaTemplate/temp_explicit.cpp?rev=84154&r1=84153&r2=84154&view=diff

==============================================================================
--- cfe/trunk/test/SemaTemplate/temp_explicit.cpp (original)
+++ cfe/trunk/test/SemaTemplate/temp_explicit.cpp Wed Oct 14 18:41:34 2009
@@ -15,7 +15,6 @@
 template class ::N::X1<int, float>;
 
 using namespace N;
-template class X1<float>;
 
 // Check for some bogus syntax that probably means that the user
 // wanted to write an explicit specialization, but forgot the '<>'





More information about the cfe-commits mailing list