]> granicus.if.org Git - clang/commitdiff
Diagnose the declaration of explicit specializations after an implicit
authorDouglas Gregor <dgregor@apple.com>
Mon, 12 Oct 2009 20:18:28 +0000 (20:18 +0000)
committerDouglas Gregor <dgregor@apple.com>
Mon, 12 Oct 2009 20:18:28 +0000 (20:18 +0000)
instantiation has already been required. To do so, keep track of the
point of instantiation for anything that can be instantiated.

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

include/clang/AST/Decl.h
include/clang/AST/DeclCXX.h
include/clang/AST/DeclTemplate.h
include/clang/Basic/DiagnosticSemaKinds.td
lib/AST/Decl.cpp
lib/AST/DeclCXX.cpp
lib/Sema/SemaDecl.cpp
lib/Sema/SemaExpr.cpp
lib/Sema/SemaTemplate.cpp
lib/Sema/SemaType.cpp
test/SemaTemplate/class-template-spec.cpp

index 0e91511dbd997ec7b0a9742d446835d12ca0e224..7c326dee338c5db363c4c118087a361077459a8c 100644 (file)
@@ -593,6 +593,11 @@ public:
   /// template specialization or instantiation this is.
   TemplateSpecializationKind getTemplateSpecializationKind();
   
+  /// \brief If this variable is an instantiation of a static data member of a
+  /// class template specialization, retrieves the member specialization
+  /// information.
+  MemberSpecializationInfo *getMemberSpecializationInfo();
+  
   /// \brief For a static data member that was instantiated from a static
   /// data member of a class template, set the template specialiation kind.
   void setTemplateSpecializationKind(TemplateSpecializationKind TSK);
@@ -1075,6 +1080,11 @@ public:
   /// declaration returned by getInstantiatedFromMemberFunction().
   FunctionDecl *getInstantiatedFromMemberFunction() const;
 
+  /// \brief If this function is an instantiation of a member function of a
+  /// class template specialization, retrieves the member specialization
+  /// information.
+  MemberSpecializationInfo *getMemberSpecializationInfo() const;
+                       
   /// \brief Specify that this record is an instantiation of the
   /// member function FD.
   void setInstantiationOfMemberFunction(FunctionDecl *FD,
@@ -1105,6 +1115,14 @@ public:
   bool isFunctionTemplateSpecialization() const {
     return getPrimaryTemplate() != 0;
   }
+       
+  /// \brief If this function is actually a function template specialization,
+  /// retrieve information about this function template specialization. 
+  /// Otherwise, returns NULL.
+  FunctionTemplateSpecializationInfo *getTemplateSpecializationInfo() const {
+    return TemplateOrSpecialization.
+             dyn_cast<FunctionTemplateSpecializationInfo*>();
+  }
                        
   /// \brief Retrieve the primary template that this function template
   /// specialization either specializes or was instantiated from.
index 7c651b2cc06a84a97f24ce5b0876a4bfe509abb9..c858c5c0df787a3825db84e5ae91ae2014fe414f 100644 (file)
@@ -706,6 +706,11 @@ public:
   /// declaration returned by getInstantiatedFromMemberClass().
   CXXRecordDecl *getInstantiatedFromMemberClass() const;
   
+  /// \brief If this class is an instantiation of a member class of a
+  /// class template specialization, retrieves the member specialization
+  /// information.
+  MemberSpecializationInfo *getMemberSpecializationInfo() const;
+  
   /// \brief Specify that this record is an instantiation of the
   /// member class RD.
   void setInstantiationOfMemberClass(CXXRecordDecl *RD,
index f49aeccb46eca76f0d7ca7459f9420011a641a71..e21849c4539225c150a00c198cb280115f0532de 100644 (file)
@@ -518,6 +518,10 @@ public:
   /// specialization from the function template.
   const TemplateArgumentList *TemplateArguments;
 
+  /// \brief The point at which this function template specialization was
+  /// first instantiated. 
+  SourceLocation PointOfInstantiation;
+  
   /// \brief Retrieve the template from which this function was specialized.
   FunctionTemplateDecl *getTemplate() const { return Template.getPointer(); }
 
@@ -533,6 +537,21 @@ public:
     Template.setInt(TSK - 1);
   }
 
+  /// \brief Retrieve the first point of instantiation of this function
+  /// template specialization.
+  ///
+  /// The point of instantiation may be an invalid source location if this
+  /// function has yet to be instantiated.
+  SourceLocation getPointOfInstantiation() const { 
+    return PointOfInstantiation; 
+  }
+  
+  /// \brief Set the (first) point of instantiation of this function template
+  /// specialization.
+  void setPointOfInstantiation(SourceLocation POI) {
+    PointOfInstantiation = POI;
+  }
+  
   void Profile(llvm::FoldingSetNodeID &ID) {
     Profile(ID, TemplateArguments->getFlatArgumentList(),
             TemplateArguments->flat_size(),
@@ -556,10 +575,13 @@ class MemberSpecializationInfo {
   // manner in which the instantiation occurred (in the lower two bits).
   llvm::PointerIntPair<NamedDecl *, 2> MemberAndTSK;
   
+  // The point at which this member was first instantiated.
+  SourceLocation PointOfInstantiation;
+  
 public:
   explicit 
   MemberSpecializationInfo(NamedDecl *IF, TemplateSpecializationKind TSK)
-    : MemberAndTSK(IF, TSK - 1) {
+    : MemberAndTSK(IF, TSK - 1), PointOfInstantiation() {
     assert(TSK != TSK_Undeclared && 
            "Cannot encode undeclared template specializations for members");
   }
@@ -579,6 +601,18 @@ public:
            "Cannot encode undeclared template specializations for members");
     MemberAndTSK.setInt(TSK - 1);
   }
+  
+  /// \brief Retrieve the first point of instantiation of this member. 
+  /// If the point of instantiation is an invalid location, then this member
+  /// has not yet been instantiated.
+  SourceLocation getPointOfInstantiation() const { 
+    return PointOfInstantiation; 
+  }
+  
+  /// \brief Set the first point of instantiation.
+  void setPointOfInstantiation(SourceLocation POI) {
+    PointOfInstantiation = POI;
+  }
 };
   
 /// Declaration of a template function.
index 252cfd2718918f596b6b6ccb8ebd6ee1c6995f6b..cdc121f3423b51ae1b240716c233e6d41fb440f8 100644 (file)
@@ -967,6 +967,10 @@ def err_template_spec_redecl_global_scope : Error<
 def err_spec_member_not_instantiated : Error<
   "specialization of member %q0 does not specialize an instantiated member">;
 def note_specialized_decl : Note<"attempt to specialize declaration here">;
+def err_specialization_after_instantiation : Error<
+  "explicit specialization of %0 after instantiation">;
+def note_instantiation_required_here : Note<
+  "%select{implicit|explicit}0 instantiation first required here">;
 
 // C++ class template specializations and out-of-line definitions
 def err_template_spec_needs_header : Error<
index 86c5719bb204a71b94996dcdbb8dc04fea0add22..638d1cfd46ed4a1020aef83cbe96b7a212ff0649 100644 (file)
@@ -374,8 +374,7 @@ SourceRange VarDecl::getSourceRange() const {
 }
 
 VarDecl *VarDecl::getInstantiatedFromStaticDataMember() {
-  if (MemberSpecializationInfo *MSI
-        = getASTContext().getInstantiatedFromStaticDataMember(this))
+  if (MemberSpecializationInfo *MSI = getMemberSpecializationInfo())
     return cast<VarDecl>(MSI->getInstantiatedFrom());
   
   return 0;
@@ -389,9 +388,12 @@ TemplateSpecializationKind VarDecl::getTemplateSpecializationKind() {
   return TSK_Undeclared;
 }
 
+MemberSpecializationInfo *VarDecl::getMemberSpecializationInfo() {
+  return getASTContext().getInstantiatedFromStaticDataMember(this);
+}
+
 void VarDecl::setTemplateSpecializationKind(TemplateSpecializationKind TSK) {
-  MemberSpecializationInfo *MSI
-    = getASTContext().getInstantiatedFromStaticDataMember(this);
+  MemberSpecializationInfo *MSI = getMemberSpecializationInfo();
   assert(MSI && "Not an instantiated static data member?");
   MSI->setTemplateSpecializationKind(TSK);
 }
@@ -703,13 +705,16 @@ OverloadedOperatorKind FunctionDecl::getOverloadedOperator() const {
 }
 
 FunctionDecl *FunctionDecl::getInstantiatedFromMemberFunction() const {
-  if (MemberSpecializationInfo *Info 
-        = TemplateOrSpecialization.dyn_cast<MemberSpecializationInfo*>())
+  if (MemberSpecializationInfo *Info = getMemberSpecializationInfo())
     return cast<FunctionDecl>(Info->getInstantiatedFrom());
   
   return 0;
 }
 
+MemberSpecializationInfo *FunctionDecl::getMemberSpecializationInfo() const {
+  return TemplateOrSpecialization.dyn_cast<MemberSpecializationInfo*>();
+}
+
 void 
 FunctionDecl::setInstantiationOfMemberFunction(FunctionDecl *FD,
                                                TemplateSpecializationKind TSK) {
index 64e800094e09ba6b4cbb330f1c4689a3f1f13a80..b9a87aedd7612adcb9c2932f15a5c342f49b657e 100644 (file)
@@ -434,13 +434,16 @@ void CXXRecordDecl::addConversionFunction(FunctionTemplateDecl *ConvDecl) {
 }
 
 CXXRecordDecl *CXXRecordDecl::getInstantiatedFromMemberClass() const {
-  if (MemberSpecializationInfo *MSInfo
-        = TemplateOrInstantiation.dyn_cast<MemberSpecializationInfo *>())
+  if (MemberSpecializationInfo *MSInfo = getMemberSpecializationInfo())
     return cast<CXXRecordDecl>(MSInfo->getInstantiatedFrom());
   
   return 0;
 }
 
+MemberSpecializationInfo *CXXRecordDecl::getMemberSpecializationInfo() const {
+  return TemplateOrInstantiation.dyn_cast<MemberSpecializationInfo *>();
+}
+
 void 
 CXXRecordDecl::setInstantiationOfMemberClass(CXXRecordDecl *RD,
                                              TemplateSpecializationKind TSK) {
@@ -456,8 +459,7 @@ TemplateSpecializationKind CXXRecordDecl::getTemplateSpecializationKind() {
         = dyn_cast<ClassTemplateSpecializationDecl>(this))
     return Spec->getSpecializationKind();
   
-  if (MemberSpecializationInfo *MSInfo
-      = TemplateOrInstantiation.dyn_cast<MemberSpecializationInfo *>())
+  if (MemberSpecializationInfo *MSInfo = getMemberSpecializationInfo())
     return MSInfo->getTemplateSpecializationKind();
   
   return TSK_Undeclared;
@@ -471,8 +473,7 @@ CXXRecordDecl::setTemplateSpecializationKind(TemplateSpecializationKind TSK) {
     return;
   }
   
-  if (MemberSpecializationInfo *MSInfo
-        = TemplateOrInstantiation.dyn_cast<MemberSpecializationInfo *>()) {
+  if (MemberSpecializationInfo *MSInfo = getMemberSpecializationInfo()) {
     MSInfo->setTemplateSpecializationKind(TSK);
     return;
   }
index f12f2b9985989cccd8a53215d552629a5f041e52..d29b84b308281bf2ec5e9b6849b887a68b3b34f8 100644 (file)
@@ -4279,14 +4279,22 @@ Sema::DeclPtrTy Sema::ActOnTag(Scope *S, unsigned TagSpec, TagUseKind TUK,
           // Diagnose attempts to redefine a tag.
           if (TUK == TUK_Definition) {
             if (TagDecl *Def = PrevTagDecl->getDefinition(Context)) {
-              Diag(NameLoc, diag::err_redefinition) << Name;
-              Diag(Def->getLocation(), diag::note_previous_definition);
-              // If this is a redefinition, recover by making this
-              // struct be anonymous, which will make any later
-              // references get the previous definition.
-              Name = 0;
-              PrevDecl = 0;
-              Invalid = true;
+              // If we're defining a specialization and the previous definition
+              // is from an implicit instantiation, don't emit an error
+              // here; we'll catch this in the general case below.
+              if (!isExplicitSpecialization ||
+                  !isa<CXXRecordDecl>(Def) ||
+                  cast<CXXRecordDecl>(Def)->getTemplateSpecializationKind() 
+                                               == TSK_ExplicitSpecialization) {
+                Diag(NameLoc, diag::err_redefinition) << Name;
+                Diag(Def->getLocation(), diag::note_previous_definition);
+                // If this is a redefinition, recover by making this
+                // struct be anonymous, which will make any later
+                // references get the previous definition.
+                Name = 0;
+                PrevDecl = 0;
+                Invalid = true;
+              }
             } else {
               // If the type is currently being defined, complain
               // about a nested redefinition.
index 9831fba3190c7c5cebfc287d4ef4c886d9aa2006..3dc7d8f4efe721c204a77d428c732c6eeb3f457a 100644 (file)
@@ -6199,10 +6199,28 @@ void Sema::MarkDeclarationReferenced(SourceLocation Loc, Decl *D) {
   if (FunctionDecl *Function = dyn_cast<FunctionDecl>(D)) {
     // Implicit instantiation of function templates and member functions of
     // class templates.
-    if (!Function->getBody() && 
-        Function->getTemplateSpecializationKind() == TSK_ImplicitInstantiation)
-      PendingImplicitInstantiations.push_back(std::make_pair(Function, Loc));
-
+    if (!Function->getBody() &&
+        Function->getTemplateSpecializationKind() 
+                                                == TSK_ImplicitInstantiation) {
+      bool AlreadyInstantiated = false;
+      if (FunctionTemplateSpecializationInfo *SpecInfo
+                                = Function->getTemplateSpecializationInfo()) {
+        if (SpecInfo->getPointOfInstantiation().isInvalid())
+          SpecInfo->setPointOfInstantiation(Loc);
+        else
+          AlreadyInstantiated = true;
+      } else if (MemberSpecializationInfo *MSInfo 
+                                  = Function->getMemberSpecializationInfo()) {
+        if (MSInfo->getPointOfInstantiation().isInvalid())
+          MSInfo->setPointOfInstantiation(Loc);
+        else
+          AlreadyInstantiated = true;
+      }
+      
+      if (!AlreadyInstantiated)
+        PendingImplicitInstantiations.push_back(std::make_pair(Function, Loc));
+    }
+    
     // FIXME: keep track of references to static functions
     Function->setUsed(true);
     return;
@@ -6211,9 +6229,15 @@ void Sema::MarkDeclarationReferenced(SourceLocation Loc, Decl *D) {
   if (VarDecl *Var = dyn_cast<VarDecl>(D)) {
     // Implicit instantiation of static data members of class templates.
     if (Var->isStaticDataMember() &&
-        Var->getInstantiatedFromStaticDataMember() &&
-        Var->getTemplateSpecializationKind() == TSK_ImplicitInstantiation)
-      PendingImplicitInstantiations.push_back(std::make_pair(Var, Loc));
+        Var->getInstantiatedFromStaticDataMember()) {
+      MemberSpecializationInfo *MSInfo = Var->getMemberSpecializationInfo();
+      assert(MSInfo && "Missing member specialization information?");
+      if (MSInfo->getPointOfInstantiation().isInvalid() &&
+          MSInfo->getTemplateSpecializationKind()== TSK_ImplicitInstantiation) {
+        MSInfo->setPointOfInstantiation(Loc);
+        PendingImplicitInstantiations.push_back(std::make_pair(Var, Loc));
+      }
+    }
 
     // FIXME: keep track of references to static data?
 
index 8a12ac11684d85e7d883ec1dfab955ff426979fe..8ca9089c5e34f9fe3139f00ed7680a7b4a19f754 100644 (file)
@@ -2820,7 +2820,7 @@ Sema::ActOnClassTemplateSpecialization(Scope *S, unsigned TagSpec,
                                        TemplateNameLoc, isPartialSpecialization,
                                        TSK_ExplicitSpecialization))
     return true;
-
+  
   // The canonical type
   QualType CanonType;
   if (PrevDecl && 
@@ -2918,6 +2918,24 @@ Sema::ActOnClassTemplateSpecialization(Scope *S, unsigned TagSpec,
     CanonType = Context.getTypeDeclType(Specialization);
   }
 
+  // C++ [temp.expl.spec]p6:
+  //   If a template, a member template or the member of a class template is
+  //   explicitly specialized then that specialization shall be declared 
+  //   before the first use of that specialization that would cause an implicit
+  //   instantiation to take place, in every translation unit in which such a 
+  //   use occurs; no diagnostic is required.
+  if (PrevDecl && PrevDecl->getPointOfInstantiation().isValid()) {
+    SourceRange Range(TemplateNameLoc, RAngleLoc);
+    Diag(TemplateNameLoc, diag::err_specialization_after_instantiation)
+      << Context.getTypeDeclType(Specialization) << Range;
+
+    Diag(PrevDecl->getPointOfInstantiation(), 
+         diag::note_instantiation_required_here)
+      << (PrevDecl->getTemplateSpecializationKind() 
+                                                != TSK_ImplicitInstantiation);
+    return true;
+  }
+  
   // If this is not a friend, note that this is an explicit specialization.
   if (TUK != TUK_Friend)
     Specialization->setSpecializationKind(TSK_ExplicitSpecialization);
@@ -2925,8 +2943,6 @@ Sema::ActOnClassTemplateSpecialization(Scope *S, unsigned TagSpec,
   // Check that this isn't a redefinition of this specialization.
   if (TUK == TUK_Definition) {
     if (RecordDecl *Def = Specialization->getDefinition(Context)) {
-      // FIXME: Should also handle explicit specialization after implicit
-      // instantiation with a special diagnostic.
       SourceRange Range(TemplateNameLoc, RAngleLoc);
       Diag(TemplateNameLoc, diag::err_redefinition)
         << Context.getTypeDeclType(Specialization) << Range;
@@ -3106,7 +3122,7 @@ Sema::CheckFunctionTemplateSpecialization(FunctionDecl *FD,
     return true;
   
   // FIXME: Check if the prior specialization has a point of instantiation.
-  // If so, we have run afoul of C++ [temp.expl.spec]p6.
+  // If so, we have run afoul of .
   
   // Check the scope of this explicit specialization.
   if (CheckTemplateSpecializationScope(*this, 
@@ -3114,11 +3130,29 @@ Sema::CheckFunctionTemplateSpecialization(FunctionDecl *FD,
                                        Specialization, FD->getLocation(), 
                                        false, TSK_ExplicitSpecialization))
     return true;
+
+  // C++ [temp.expl.spec]p6:
+  //   If a template, a member template or the member of a class template is
+  //   explicitly specialized then that spe- cialization shall be declared 
+  //   before the first use of that specialization that would cause an implicit
+  //   instantiation to take place, in every translation unit in which such a 
+  //   use occurs; no diagnostic is required.
+  FunctionTemplateSpecializationInfo *SpecInfo
+    = Specialization->getTemplateSpecializationInfo();
+  assert(SpecInfo && "Function template specialization info missing?");
+  if (SpecInfo->getPointOfInstantiation().isValid()) {
+    Diag(FD->getLocation(), diag::err_specialization_after_instantiation)
+      << FD;
+    Diag(SpecInfo->getPointOfInstantiation(), 
+         diag::note_instantiation_required_here)
+      << (Specialization->getTemplateSpecializationKind() 
+                                                != TSK_ImplicitInstantiation);
+    return true;
+  }
   
   // Mark the prior declaration as an explicit specialization, so that later
   // clients know that this is an explicit specialization.
-  // FIXME: Check for prior explicit instantiations?
-  Specialization->setTemplateSpecializationKind(TSK_ExplicitSpecialization);
+  SpecInfo->setTemplateSpecializationKind(TSK_ExplicitSpecialization);
   
   // Turn the given function declaration into a function template
   // specialization, with the template arguments from the previous
@@ -3156,6 +3190,8 @@ Sema::CheckMemberSpecialization(NamedDecl *Member, NamedDecl *&PrevDecl) {
   // Try to find the member we are instantiating.
   NamedDecl *Instantiation = 0;
   NamedDecl *InstantiatedFrom = 0;
+  MemberSpecializationInfo *MSInfo = 0;
+
   if (!PrevDecl) {
     // Nowhere to look anyway.
   } else if (FunctionDecl *Function = dyn_cast<FunctionDecl>(Member)) {
@@ -3164,6 +3200,7 @@ Sema::CheckMemberSpecialization(NamedDecl *Member, NamedDecl *&PrevDecl) {
         if (Context.hasSameType(Function->getType(), Method->getType())) {
           Instantiation = Method;
           InstantiatedFrom = Method->getInstantiatedFromMemberFunction();
+          MSInfo = Method->getMemberSpecializationInfo();
           break;
         }
       }
@@ -3173,11 +3210,13 @@ Sema::CheckMemberSpecialization(NamedDecl *Member, NamedDecl *&PrevDecl) {
       if (PrevVar->isStaticDataMember()) {
         Instantiation = PrevDecl;
         InstantiatedFrom = PrevVar->getInstantiatedFromStaticDataMember();
+        MSInfo = PrevVar->getMemberSpecializationInfo();
       }
   } else if (isa<RecordDecl>(Member)) {
     if (CXXRecordDecl *PrevRecord = dyn_cast<CXXRecordDecl>(PrevDecl)) {
       Instantiation = PrevDecl;
       InstantiatedFrom = PrevRecord->getInstantiatedFromMemberClass();
+      MSInfo = PrevRecord->getMemberSpecializationInfo();
     }
   }
   
@@ -3188,9 +3227,6 @@ Sema::CheckMemberSpecialization(NamedDecl *Member, NamedDecl *&PrevDecl) {
     return false;
   }
   
-  // FIXME: Check if the prior declaration has a point of instantiation.
-  // If so, we have run afoul of C++ [temp.expl.spec]p6.
-  
   // Make sure that this is a specialization of a member.
   if (!InstantiatedFrom) {
     Diag(Member->getLocation(), diag::err_spec_member_not_instantiated)
@@ -3199,6 +3235,22 @@ Sema::CheckMemberSpecialization(NamedDecl *Member, NamedDecl *&PrevDecl) {
     return true;
   }
   
+  // C++ [temp.expl.spec]p6:
+  //   If a template, a member template or the member of a class template is
+  //   explicitly specialized then that spe- cialization shall be declared 
+  //   before the first use of that specialization that would cause an implicit
+  //   instantiation to take place, in every translation unit in which such a 
+  //   use occurs; no diagnostic is required.
+  assert(MSInfo && "Member specialization info missing?");
+  if (MSInfo->getPointOfInstantiation().isValid()) {
+    Diag(Member->getLocation(), diag::err_specialization_after_instantiation)
+      << Member;
+    Diag(MSInfo->getPointOfInstantiation(), 
+         diag::note_instantiation_required_here)
+      << (MSInfo->getTemplateSpecializationKind() != TSK_ImplicitInstantiation);
+    return true;
+  }
+  
   // Check the scope of this explicit specialization.
   if (CheckTemplateSpecializationScope(*this, 
                                        InstantiatedFrom,
@@ -3206,8 +3258,6 @@ Sema::CheckMemberSpecialization(NamedDecl *Member, NamedDecl *&PrevDecl) {
                                        false, TSK_ExplicitSpecialization))
     return true;
 
-  // FIXME: Check for specialization-after-instantiation errors and such.
-  
   // Note that this is an explicit instantiation of a member.
   // the original declaration to note that it is an explicit specialization
   // (if it was previously an implicit instantiation). This latter step
index b82e5ec9b11e2b89e46a7f90ef69d362b41722c8..3cdf6154232bd34ada28167e84fad04439849f26 100644 (file)
@@ -1677,12 +1677,17 @@ bool Sema::RequireCompleteType(SourceLocation Loc, QualType T,
     } else if (CXXRecordDecl *Rec
                  = dyn_cast<CXXRecordDecl>(Record->getDecl())) {
       if (CXXRecordDecl *Pattern = Rec->getInstantiatedFromMemberClass()) {
+        MemberSpecializationInfo *MSInfo = Rec->getMemberSpecializationInfo();
+        assert(MSInfo && "Missing member specialization information?");
         // This record was instantiated from a class within a template.
-        if (Rec->getTemplateSpecializationKind() != TSK_ExplicitSpecialization)
+        if (MSInfo->getTemplateSpecializationKind() 
+                                               != TSK_ExplicitSpecialization) {
+          MSInfo->setPointOfInstantiation(Loc);
           return InstantiateClass(Loc, Rec, Pattern,
                                   getTemplateInstantiationArgs(Rec),
                                   TSK_ImplicitInstantiation,
                                   /*Complain=*/diag != 0);
+        }
       }
     }
   }
index 05ddb05325971f3826226b7c9a9bab8982661111..e44115c748c3949be269b5ef216980d1f2d8e891 100644 (file)
@@ -20,7 +20,8 @@ int test_incomplete_specs(A<double, double> *a1,
                           A<double> *a2)
 {
   (void)a1->x; // expected-error{{incomplete definition of type 'A<double, double>'}}
-  (void)a2->x; // expected-error{{implicit instantiation of undefined template 'struct A<double, int>'}}
+  (void)a2->x; // expected-error{{implicit instantiation of undefined template 'struct A<double, int>'}} \
+               // expected-note{{first required here}}
 }
 
 typedef float FLOAT;
@@ -70,7 +71,8 @@ namespace N {
 }
 
 // Diagnose specialization errors
-struct A<double> { }; // expected-error{{template specialization requires 'template<>'}}
+struct A<double> { }; // expected-error{{template specialization requires 'template<>'}} \
+                      // expected-error{{after instantiation}}
 
 template<> struct ::A<double>;