[cfe-commits] r71689 - in /cfe/trunk: include/clang/Basic/DiagnosticSemaKinds.td lib/Sema/Sema.h lib/Sema/SemaTemplate.cpp lib/Sema/SemaTemplateInstantiate.cpp test/SemaTemplate/temp_explicit.cpp test/SemaTemplate/temp_explicit_cxx0x.cpp

Douglas Gregor dgregor at apple.com
Wed May 13 11:28:31 PDT 2009


Author: dgregor
Date: Wed May 13 13:28:20 2009
New Revision: 71689

URL: http://llvm.org/viewvc/llvm-project?rev=71689&view=rev
Log:
Improve the semantic checking for explicit instantiations of
templates. In particular:
  - An explicit instantiation can follow an implicit instantiation (we
  were improperly diagnosing this as an error, previously).
  - In C++0x, an explicit instantiation that follows an explicit
  specialization of the same template specialization is ignored. In
  C++98, we just emit an extension warning.
  - In C++0x, an explicit instantiation must be in a namespace
  enclosing the original template. C++98 has no such requirement.

Also, fixed a longstanding FIXME regarding the integral type that is
used for the size of a constant array type when it is being instantiated.


Added:
    cfe/trunk/test/SemaTemplate/temp_explicit_cxx0x.cpp
Modified:
    cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td
    cfe/trunk/lib/Sema/Sema.h
    cfe/trunk/lib/Sema/SemaTemplate.cpp
    cfe/trunk/lib/Sema/SemaTemplateInstantiate.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=71689&r1=71688&r2=71689&view=diff

==============================================================================
--- cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td (original)
+++ cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td Wed May 13 13:28:20 2009
@@ -694,11 +694,14 @@
 def err_template_spec_decl_out_of_scope : Error<
   "class template specialization of %0 not in namespace %1">;
 def err_template_spec_decl_function_scope : Error<
-  "class template specialization of %0 in function scope">;
+  "%select{class template specialization|explicit instantiation}0 of %1 "
+  "in function scope">;
 def err_template_spec_redecl_out_of_scope : Error<
-  "class template specialization of %0 not in a namespace enclosing %1">;
+  "%select{class template specialization|explicit instantiation}0 of %1 "
+  "not in a namespace enclosing %2">;
 def err_template_spec_redecl_global_scope : Error<
-  "class template specialization of %0 must occur in at global scope">;
+  "%select{class template specialization|explicit instantiation}0 of %1 must "
+  "occur at global scope">;
 
 // C++ Template Instantiation
 def err_template_recursion_depth_exceeded : Error<
@@ -722,13 +725,15 @@
   "type %0 cannot be used prior to '::' because it has no members">;
 
 // C++ Explicit Instantiation
-def err_explicit_instantiation_redef : Error<
-    "explicit instantiation of %0 occurs after "
-    "%select{|explicit specialization|implicit instantiation|explicit "
-    "instantiation}1">;
-def note_previous_instantiation : Note<
-    "previous %select{|explicit specialization|implicit instantiation|explicit "
-    "instantiation}0 is here">;
+def err_explicit_instantiation_duplicate : Error<
+    "duplicate explicit instantiation of %0">;
+def note_previous_explicit_instantiation : Note<
+    "previous explicit instantiation is here">;
+def ext_explicit_instantiation_after_specialization : Extension<
+    "explicit instantiation of %0 that occurs after an explicit "
+    "specialization will be ignored (C++0x extension)">;
+def note_previous_template_specialization : Note<
+    "previous template specialization is here">;
 
 // C++ typename-specifiers
 def err_typename_nested_not_found : Error<"no type named %0 in %1">;

Modified: cfe/trunk/lib/Sema/Sema.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/Sema.h?rev=71689&r1=71688&r2=71689&view=diff

==============================================================================
--- cfe/trunk/lib/Sema/Sema.h (original)
+++ cfe/trunk/lib/Sema/Sema.h Wed May 13 13:28:20 2009
@@ -1882,7 +1882,8 @@
   bool CheckClassTemplateSpecializationScope(ClassTemplateDecl *ClassTemplate,
                                     ClassTemplateSpecializationDecl *PrevDecl,
                                              SourceLocation TemplateNameLoc,
-                                             SourceRange ScopeSpecifierRange);
+                                             SourceRange ScopeSpecifierRange,
+                                             bool ExplicitInstantiation);
 
   virtual DeclResult
   ActOnClassTemplateSpecialization(Scope *S, unsigned TagSpec, TagKind TK,

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

==============================================================================
--- cfe/trunk/lib/Sema/SemaTemplate.cpp (original)
+++ cfe/trunk/lib/Sema/SemaTemplate.cpp Wed May 13 13:28:20 2009
@@ -1892,19 +1892,20 @@
     << TemplateRange;
 }
 
-/// \brief Check whether a class template specialization in the
-/// current context is well-formed.
+/// \brief Check whether a class template specialization or explicit
+/// instantiation in the current context is well-formed.
 ///
-/// This routine determines whether a class template specialization
-/// can be declared in the current context (C++ [temp.expl.spec]p2)
-/// and emits appropriate diagnostics if there was an error. It
-/// returns true if there was an error that we cannot recover from,
-/// and false otherwise.
+/// This routine determines whether a class template specialization or
+/// explicit instantiation can be declared in the current context 
+/// (C++ [temp.expl.spec]p2, C++0x [temp.explicit]p2) and emits 
+/// appropriate diagnostics if there was an error. It returns true if 
+// there was an error that we cannot recover from, and false otherwise.
 bool 
 Sema::CheckClassTemplateSpecializationScope(ClassTemplateDecl *ClassTemplate,
                                    ClassTemplateSpecializationDecl *PrevDecl,
                                             SourceLocation TemplateNameLoc,
-                                            SourceRange ScopeSpecifierRange) {
+                                            SourceRange ScopeSpecifierRange,
+                                            bool ExplicitInstantiation) {
   // C++ [temp.expl.spec]p2:
   //   An explicit specialization shall be declared in the namespace
   //   of which the template is a member, or, for member templates, in
@@ -1920,14 +1921,15 @@
   //   declared.
   if (CurContext->getLookupContext()->isFunctionOrMethod()) {
     Diag(TemplateNameLoc, diag::err_template_spec_decl_function_scope)
-      << ClassTemplate;
+      << ExplicitInstantiation << ClassTemplate;
     return true;
   }
 
   DeclContext *DC = CurContext->getEnclosingNamespaceContext();
   DeclContext *TemplateContext 
     = ClassTemplate->getDeclContext()->getEnclosingNamespaceContext();
-  if (!PrevDecl || PrevDecl->getSpecializationKind() == TSK_Undeclared) {
+  if ((!PrevDecl || PrevDecl->getSpecializationKind() == TSK_Undeclared) &&
+      !ExplicitInstantiation) {
     // There is no prior declaration of this entity, so this
     // specialization must be in the same context as the template
     // itself.
@@ -1949,15 +1951,26 @@
   // We have a previous declaration of this entity. Make sure that
   // this redeclaration (or definition) occurs in an enclosing namespace.
   if (!CurContext->Encloses(TemplateContext)) {
-    if (isa<TranslationUnitDecl>(TemplateContext))
-      Diag(TemplateNameLoc, diag::err_template_spec_redecl_global_scope)
-        << ClassTemplate << ScopeSpecifierRange;
-    else if (isa<NamespaceDecl>(TemplateContext))
-      Diag(TemplateNameLoc, diag::err_template_spec_redecl_out_of_scope)
-        << ClassTemplate << cast<NamedDecl>(TemplateContext) 
-        << ScopeSpecifierRange;
+    // FIXME: In C++98, we would like to turn these errors into
+    // warnings, dependent on a -Wc++0x flag.
+    bool SuppressedDiag = false;
+    if (isa<TranslationUnitDecl>(TemplateContext)) {
+      if (!ExplicitInstantiation || getLangOptions().CPlusPlus0x)
+        Diag(TemplateNameLoc, diag::err_template_spec_redecl_global_scope)
+          << ExplicitInstantiation << ClassTemplate << ScopeSpecifierRange;
+      else
+        SuppressedDiag = true;
+    } else if (isa<NamespaceDecl>(TemplateContext)) {
+      if (!ExplicitInstantiation || getLangOptions().CPlusPlus0x)
+        Diag(TemplateNameLoc, diag::err_template_spec_redecl_out_of_scope)
+          << ExplicitInstantiation << ClassTemplate
+          << cast<NamedDecl>(TemplateContext) << ScopeSpecifierRange;
+      else 
+        SuppressedDiag = true;
+    }
     
-    Diag(ClassTemplate->getLocation(), diag::note_template_decl_here);
+    if (!SuppressedDiag)
+      Diag(ClassTemplate->getLocation(), diag::note_template_decl_here);
   }
 
   return false;
@@ -2056,7 +2069,8 @@
   // the current scope.
   if (CheckClassTemplateSpecializationScope(ClassTemplate, PrevDecl,
                                             TemplateNameLoc, 
-                                            SS.getRange()))
+                                            SS.getRange(),
+                                            /*ExplicitInstantiation=*/false))
     return true;
 
   if (PrevDecl && PrevDecl->getSpecializationKind() == TSK_Undeclared) {
@@ -2179,6 +2193,17 @@
     Kind = ClassTemplate->getTemplatedDecl()->getTagKind();
   }
 
+  // C++0x [temp.explicit]p2:
+  //   [...] An explicit instantiation shall appear in an enclosing
+  //   namespace of its template. [...]
+  //
+  // This is C++ DR 275.
+  if (CheckClassTemplateSpecializationScope(ClassTemplate, 0,
+                                            TemplateNameLoc, 
+                                            SS.getRange(),
+                                            /*ExplicitInstantiation=*/true))
+    return true;
+
   // Translate the parser's template argument list in our AST format.
   llvm::SmallVector<TemplateArgument, 16> TemplateArgs;
   translateTemplateArguments(TemplateArgsIn, TemplateArgLocs, TemplateArgs);
@@ -2206,18 +2231,55 @@
 
   ClassTemplateSpecializationDecl *Specialization = 0;
 
+  bool SpecializationRequiresInstantiation = true;
   if (PrevDecl) {
-    if (PrevDecl->getSpecializationKind() != TSK_Undeclared) {
+    if (PrevDecl->getSpecializationKind() == TSK_ExplicitInstantiation) {
       // This particular specialization has already been declared or
       // instantiated. We cannot explicitly instantiate it.
-      Diag(TemplateNameLoc, diag::err_explicit_instantiation_redef)
-        << Context.getTypeDeclType(PrevDecl)
-        << (int)PrevDecl->getSpecializationKind();
-      Diag(PrevDecl->getLocation(), diag::note_previous_instantiation)
-        << (int)PrevDecl->getSpecializationKind();
+      Diag(TemplateNameLoc, diag::err_explicit_instantiation_duplicate)
+        << Context.getTypeDeclType(PrevDecl);
+      Diag(PrevDecl->getLocation(), 
+           diag::note_previous_explicit_instantiation);
       return DeclPtrTy::make(PrevDecl);
     }
 
+    if (PrevDecl->getSpecializationKind() == TSK_ExplicitSpecialization) {
+      // C++0x [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.
+      if (!getLangOptions().CPlusPlus0x) {
+        Diag(TemplateNameLoc, 
+             diag::ext_explicit_instantiation_after_specialization)
+          << Context.getTypeDeclType(PrevDecl);
+        Diag(PrevDecl->getLocation(), 
+             diag::note_previous_template_specialization);
+      }
+
+      // Create a new class template specialization declaration node
+      // for this explicit specialization. This node is only used to
+      // record the existence of this explicit instantiation for
+      // accurate reproduction of the source code; we don't actually
+      // use it for anything, since it is semantically irrelevant.
+      Specialization
+        = ClassTemplateSpecializationDecl::Create(Context, 
+                                             ClassTemplate->getDeclContext(),
+                                                  TemplateNameLoc,
+                                                  ClassTemplate,
+                                                  &ConvertedTemplateArgs[0],
+                                                  ConvertedTemplateArgs.size(),
+                                                  0);
+      Specialization->setLexicalDeclContext(CurContext);
+      CurContext->addDecl(Context, Specialization);
+      return DeclPtrTy::make(Specialization);
+    }
+
+    // If we have already (implicitly) instantiated this
+    // specialization, there is less work to do.
+    if (PrevDecl->getSpecializationKind() == TSK_ImplicitInstantiation)
+      SpecializationRequiresInstantiation = false;
+
     // Since the only prior class template specialization with these
     // arguments was referenced but not declared, reuse that
     // declaration node as our own, updating its source location to
@@ -2263,16 +2325,19 @@
   CurContext->addDecl(Context, Specialization);
 
   // C++ [temp.explicit]p3:
-  //
   //   A definition of a class template or class member template
   //   shall be in scope at the point of the explicit instantiation of
   //   the class template or class member template.
   //
   // This check comes when we actually try to perform the
   // instantiation.
-  if (InstantiateClassTemplateSpecialization(Specialization, true))
+  if (SpecializationRequiresInstantiation &&
+      InstantiateClassTemplateSpecialization(Specialization, true))
     return true;
 
+  // FIXME: Instantiate all of the members of the template (that
+  // haven't already been instantiated!).
+
   return DeclPtrTy::make(Specialization);
 }
 

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

==============================================================================
--- cfe/trunk/lib/Sema/SemaTemplateInstantiate.cpp (original)
+++ cfe/trunk/lib/Sema/SemaTemplateInstantiate.cpp Wed May 13 13:28:20 2009
@@ -247,10 +247,26 @@
   // Build a temporary integer literal to specify the size for
   // BuildArrayType. Since we have already checked the size as part of
   // creating the dependent array type in the first place, we know
-  // there aren't any errors.
-  // FIXME: Is IntTy big enough? Maybe not, but LongLongTy causes
-  // problems that I have yet to investigate.
-  IntegerLiteral ArraySize(T->getSize(), SemaRef.Context.IntTy, Loc);
+  // there aren't any errors. However, we do need to determine what
+  // C++ type to give the size expression.
+  llvm::APInt Size = T->getSize();
+  QualType Types[] = { 
+    SemaRef.Context.UnsignedCharTy, SemaRef.Context.UnsignedShortTy, 
+    SemaRef.Context.UnsignedIntTy, SemaRef.Context.UnsignedLongTy, 
+    SemaRef.Context.UnsignedLongLongTy, SemaRef.Context.UnsignedInt128Ty 
+  };
+  const unsigned NumTypes = sizeof(Types) / sizeof(QualType);
+  QualType SizeType;
+  for (unsigned I = 0; I != NumTypes; ++I)
+    if (Size.getBitWidth() == SemaRef.Context.getIntWidth(Types[I])) {
+      SizeType = Types[I];
+      break;
+    }
+
+  if (SizeType.isNull())
+    SizeType = SemaRef.Context.getFixedWidthIntType(Size.getBitWidth(), false);
+
+  IntegerLiteral ArraySize(Size, SizeType, Loc);
   return SemaRef.BuildArrayType(ElementType, T->getSizeModifier(), 
                                 &ArraySize, T->getIndexTypeQualifier(), 
                                 Loc, Entity);

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

==============================================================================
--- cfe/trunk/test/SemaTemplate/temp_explicit.cpp (original)
+++ cfe/trunk/test/SemaTemplate/temp_explicit.cpp Wed May 13 13:28:20 2009
@@ -1,4 +1,4 @@
-// RUN: clang-cc -fsyntax-only -verify %s
+// RUN: clang-cc -fsyntax-only -verify -pedantic %s
 //
 // Tests explicit instantiation of templates.
 template<typename T, typename U = T> class X0 { };
@@ -24,13 +24,13 @@
 
 // Check for explicit instantiations that come after other kinds of
 // instantiations or declarations.
-template class X0<int, int>; // expected-error{{after}}
+template class X0<int, int>; // expected-error{{duplicate}}
 
 template<> class X0<char> { }; // expected-note{{previous}}
-template class X0<char>; // expected-error{{after}}
+template class X0<char>; // expected-warning{{ignored}}
 
-void foo(X0<short>) { } // expected-note{{previous}}
-template class X0<short>;  // expected-error{{after}}
+void foo(X0<short>) { }
+template class X0<short>;
 
 // Check that explicit instantiations actually produce definitions. We
 // determine whether this happens by placing semantic errors in the

Added: cfe/trunk/test/SemaTemplate/temp_explicit_cxx0x.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaTemplate/temp_explicit_cxx0x.cpp?rev=71689&view=auto

==============================================================================
--- cfe/trunk/test/SemaTemplate/temp_explicit_cxx0x.cpp (added)
+++ cfe/trunk/test/SemaTemplate/temp_explicit_cxx0x.cpp Wed May 13 13:28:20 2009
@@ -0,0 +1,24 @@
+// RUN: clang-cc -fsyntax-only -std=c++0x -verify %s
+namespace N1 {
+
+  template<typename T> struct X0 { }; // expected-note{{here}}
+
+  namespace Inner {
+    template<typename T> struct X1 { };
+  }
+
+  template struct X0<int>;
+  template struct Inner::X1<int>;
+}
+
+template<typename T> struct X2 { }; // expected-note{{here}}
+
+template struct ::N1::Inner::X1<float>;
+
+namespace N2 {
+  using namespace N1;
+
+  template struct X0<double>; // expected-error{{not in a namespace enclosing}}
+
+  template struct X2<float>; // expected-error{{at global scope}}
+}





More information about the cfe-commits mailing list