[cfe-commits] r81866 - in /cfe/trunk: include/clang/AST/DeclTemplate.h include/clang/Basic/DiagnosticSemaKinds.td lib/AST/DeclTemplate.cpp lib/Sema/Sema.h lib/Sema/SemaTemplate.cpp lib/Sema/SemaTemplateDeduction.cpp lib/Sema/SemaTemplateInstantiate.cpp lib/Sema/SemaTemplateInstantiateDecl.cpp test/SemaTemplate/temp_class_order.cpp test/SemaTemplate/temp_class_spec.cpp

Douglas Gregor dgregor at apple.com
Tue Sep 15 09:23:52 PDT 2009


Author: dgregor
Date: Tue Sep 15 11:23:51 2009
New Revision: 81866

URL: http://llvm.org/viewvc/llvm-project?rev=81866&view=rev
Log:
Implement partial ordering of class template partial specializations 
(C++ [temp.class.order]).

Added:
    cfe/trunk/test/SemaTemplate/temp_class_order.cpp   (with props)
Modified:
    cfe/trunk/include/clang/AST/DeclTemplate.h
    cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td
    cfe/trunk/lib/AST/DeclTemplate.cpp
    cfe/trunk/lib/Sema/Sema.h
    cfe/trunk/lib/Sema/SemaTemplate.cpp
    cfe/trunk/lib/Sema/SemaTemplateDeduction.cpp
    cfe/trunk/lib/Sema/SemaTemplateInstantiate.cpp
    cfe/trunk/lib/Sema/SemaTemplateInstantiateDecl.cpp
    cfe/trunk/test/SemaTemplate/temp_class_spec.cpp

Modified: cfe/trunk/include/clang/AST/DeclTemplate.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/AST/DeclTemplate.h?rev=81866&r1=81865&r2=81866&view=diff

==============================================================================
--- cfe/trunk/include/clang/AST/DeclTemplate.h (original)
+++ cfe/trunk/include/clang/AST/DeclTemplate.h Tue Sep 15 11:23:51 2009
@@ -49,38 +49,38 @@
   unsigned NumParams;
 
   TemplateParameterList(SourceLocation TemplateLoc, SourceLocation LAngleLoc,
-                        Decl **Params, unsigned NumParams,
+                        NamedDecl **Params, unsigned NumParams,
                         SourceLocation RAngleLoc);
 
 public:
   static TemplateParameterList *Create(ASTContext &C,
                                        SourceLocation TemplateLoc,
                                        SourceLocation LAngleLoc,
-                                       Decl **Params,
+                                       NamedDecl **Params,
                                        unsigned NumParams,
                                        SourceLocation RAngleLoc);
 
   /// iterator - Iterates through the template parameters in this list.
-  typedef Decl** iterator;
+  typedef NamedDecl** iterator;
 
   /// const_iterator - Iterates through the template parameters in this list.
-  typedef Decl* const* const_iterator;
+  typedef NamedDecl* const* const_iterator;
 
-  iterator begin() { return reinterpret_cast<Decl **>(this + 1); }
+  iterator begin() { return reinterpret_cast<NamedDecl **>(this + 1); }
   const_iterator begin() const {
-    return reinterpret_cast<Decl * const *>(this + 1);
+    return reinterpret_cast<NamedDecl * const *>(this + 1);
   }
   iterator end() { return begin() + NumParams; }
   const_iterator end() const { return begin() + NumParams; }
 
   unsigned size() const { return NumParams; }
 
-  Decl* getParam(unsigned Idx) {
+  NamedDecl* getParam(unsigned Idx) {
     assert(Idx < size() && "Template parameter index out-of-range");
     return begin()[Idx];
   }
 
-  const Decl* getParam(unsigned Idx) const {
+  const NamedDecl* getParam(unsigned Idx) const {
     assert(Idx < size() && "Template parameter index out-of-range");
     return begin()[Idx];
   }

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

==============================================================================
--- cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td (original)
+++ cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td Tue Sep 15 11:23:51 2009
@@ -955,9 +955,9 @@
     "deduced; this partial specialization will never be used">;
 def note_partial_spec_unused_parameter : Note<
     "non-deducible template parameter %0">;
-def unsup_template_partial_spec_ordering : Error<
-  "partial ordering of class template partial specializations is not yet "
-  "supported">;
+def err_partial_spec_ordering_ambiguous : Error<
+    "ambiguous partial specializations of %0">;
+def note_partial_spec_match : Note<"partial specialization matches %0">;
 
 // C++ Template Instantiation
 def err_template_recursion_depth_exceeded : Error<

Modified: cfe/trunk/lib/AST/DeclTemplate.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/DeclTemplate.cpp?rev=81866&r1=81865&r2=81866&view=diff

==============================================================================
--- cfe/trunk/lib/AST/DeclTemplate.cpp (original)
+++ cfe/trunk/lib/AST/DeclTemplate.cpp Tue Sep 15 11:23:51 2009
@@ -25,7 +25,7 @@
 
 TemplateParameterList::TemplateParameterList(SourceLocation TemplateLoc,
                                              SourceLocation LAngleLoc,
-                                             Decl **Params, unsigned NumParams,
+                                             NamedDecl **Params, unsigned NumParams,
                                              SourceLocation RAngleLoc)
   : TemplateLoc(TemplateLoc), LAngleLoc(LAngleLoc), RAngleLoc(RAngleLoc),
     NumParams(NumParams) {
@@ -35,9 +35,10 @@
 
 TemplateParameterList *
 TemplateParameterList::Create(ASTContext &C, SourceLocation TemplateLoc,
-                              SourceLocation LAngleLoc, Decl **Params,
+                              SourceLocation LAngleLoc, NamedDecl **Params,
                               unsigned NumParams, SourceLocation RAngleLoc) {
-  unsigned Size = sizeof(TemplateParameterList) + sizeof(Decl *) * NumParams;
+  unsigned Size = sizeof(TemplateParameterList) 
+                + sizeof(NamedDecl *) * NumParams;
   unsigned Align = llvm::AlignOf<TemplateParameterList>::Alignment;
   void *Mem = C.Allocate(Size, Align);
   return new (Mem) TemplateParameterList(TemplateLoc, LAngleLoc, Params,

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

==============================================================================
--- cfe/trunk/lib/Sema/Sema.h (original)
+++ cfe/trunk/lib/Sema/Sema.h Tue Sep 15 11:23:51 2009
@@ -2568,6 +2568,10 @@
   QualType RebuildTypeInCurrentInstantiation(QualType T, SourceLocation Loc,
                                              DeclarationName Name);
 
+  std::string
+  getTemplateArgumentBindingsText(const TemplateParameterList *Params,
+                                  const TemplateArgumentList &Args);
+  
   /// \brief Describes the result of template argument deduction.
   ///
   /// The TemplateDeductionResult enumeration describes the result of
@@ -2732,7 +2736,11 @@
   FunctionTemplateDecl *getMoreSpecializedTemplate(FunctionTemplateDecl *FT1,
                                                    FunctionTemplateDecl *FT2,
                                            TemplatePartialOrderingContext TPOC);
-
+  ClassTemplatePartialSpecializationDecl *
+  getMoreSpecializedPartialSpecialization(
+                                  ClassTemplatePartialSpecializationDecl *PS1,
+                                  ClassTemplatePartialSpecializationDecl *PS2);
+  
   void MarkUsedTemplateParameters(const TemplateArgumentList &TemplateArgs,
                                   bool OnlyDeduced,
                                   llvm::SmallVectorImpl<bool> &Deduced);

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

==============================================================================
--- cfe/trunk/lib/Sema/SemaTemplate.cpp (original)
+++ cfe/trunk/lib/Sema/SemaTemplate.cpp Tue Sep 15 11:23:51 2009
@@ -18,7 +18,7 @@
 #include "clang/Parse/DeclSpec.h"
 #include "clang/Basic/LangOptions.h"
 #include "llvm/Support/Compiler.h"
-
+#include "llvm/ADT/StringExtras.h"
 using namespace clang;
 
 /// \brief Determine whether the declaration found is acceptable as the name
@@ -537,7 +537,8 @@
     Diag(ExportLoc, diag::note_template_export_unsupported);
 
   return TemplateParameterList::Create(Context, TemplateLoc, LAngleLoc,
-                                       (Decl**)Params, NumParams, RAngleLoc);
+                                       (NamedDecl**)Params, NumParams, 
+                                       RAngleLoc);
 }
 
 Sema::DeclResult
@@ -3423,3 +3424,78 @@
   CurrentInstantiationRebuilder Rebuilder(*this, Loc, Name);
   return Rebuilder.TransformType(T);
 }
+
+/// \brief Produces a formatted string that describes the binding of
+/// template parameters to template arguments.
+std::string
+Sema::getTemplateArgumentBindingsText(const TemplateParameterList *Params,
+                                      const TemplateArgumentList &Args) {
+  std::string Result;
+
+  if (!Params || Params->size() == 0)
+    return Result;
+  
+  for (unsigned I = 0, N = Params->size(); I != N; ++I) {
+    if (I == 0)
+      Result += "[with ";
+    else
+      Result += ", ";
+    
+    if (const IdentifierInfo *Id = Params->getParam(I)->getIdentifier()) {
+      Result += Id->getName();
+    } else {
+      Result += '$';
+      Result += llvm::utostr(I);
+    }
+    
+    Result += " = ";
+    
+    switch (Args[I].getKind()) {
+      case TemplateArgument::Null:
+        Result += "<no value>";
+        break;
+        
+      case TemplateArgument::Type: {
+        std::string TypeStr;
+        Args[I].getAsType().getAsStringInternal(TypeStr, 
+                                                Context.PrintingPolicy);
+        Result += TypeStr;
+        break;
+      }
+        
+      case TemplateArgument::Declaration: {
+        bool Unnamed = true;
+        if (NamedDecl *ND = dyn_cast_or_null<NamedDecl>(Args[I].getAsDecl())) {
+          if (ND->getDeclName()) {
+            Unnamed = false;
+            Result += ND->getNameAsString();
+          }
+        }
+        
+        if (Unnamed) {
+          Result += "<anonymous>";
+        }
+        break;
+      }
+        
+      case TemplateArgument::Integral: {
+        Result += Args[I].getAsIntegral()->toString(10);
+        break;
+      }
+        
+      case TemplateArgument::Expression: {
+        assert(false && "No expressions in deduced template arguments!");
+        Result += "<expression>";
+        break;
+      }
+        
+      case TemplateArgument::Pack:
+        // FIXME: Format template argument packs
+        Result += "<template argument pack>";
+        break;        
+    }
+  }
+  
+  Result += ']';
+  return Result;
+}

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

==============================================================================
--- cfe/trunk/lib/Sema/SemaTemplateDeduction.cpp (original)
+++ cfe/trunk/lib/Sema/SemaTemplateDeduction.cpp Tue Sep 15 11:23:51 2009
@@ -949,7 +949,8 @@
   for (unsigned I = 0, N = Deduced.size(); I != N; ++I) {
     if (Deduced[I].isNull()) {
       Decl *Param
-        = const_cast<Decl *>(Partial->getTemplateParameters()->getParam(I));
+        = const_cast<NamedDecl *>(
+                                Partial->getTemplateParameters()->getParam(I));
       if (TemplateTypeParmDecl *TTP = dyn_cast<TemplateTypeParmDecl>(Param))
         Info.Param = TTP;
       else if (NonTypeTemplateParmDecl *NTTP
@@ -976,7 +977,7 @@
   ClassTemplateDecl *ClassTemplate = Partial->getSpecializedTemplate();
   const TemplateArgumentList &PartialTemplateArgs = Partial->getTemplateArgs();
   for (unsigned I = 0, N = PartialTemplateArgs.flat_size(); I != N; ++I) {
-    Decl *Param = const_cast<Decl *>(
+    Decl *Param = const_cast<NamedDecl *>(
                     ClassTemplate->getTemplateParameters()->getParam(I));
     TemplateArgument InstArg
       = Subst(PartialTemplateArgs[I],
@@ -1199,7 +1200,7 @@
   for (unsigned I = 0, N = Deduced.size(); I != N; ++I) {
     if (Deduced[I].isNull()) {
       Info.Param = makeTemplateParameter(
-                            const_cast<Decl *>(TemplateParams->getParam(I)));
+                            const_cast<NamedDecl *>(TemplateParams->getParam(I)));
       return TDK_Incomplete;
     }
 
@@ -1796,7 +1797,7 @@
 }
                                     
                                      
-/// \brief Returns the more specialization function template according
+/// \brief Returns the more specialized function template according
 /// to the rules of function template partial ordering (C++ [temp.func.order]).
 ///
 /// \param FT1 the first function template
@@ -1806,13 +1807,12 @@
 /// \param TPOC the context in which we are performing partial ordering of
 /// function templates.
 ///
-/// \returns the more specialization function template. If neither
+/// \returns the more specialized function template. If neither
 /// template is more specialized, returns NULL.
 FunctionTemplateDecl *
 Sema::getMoreSpecializedTemplate(FunctionTemplateDecl *FT1,
                                  FunctionTemplateDecl *FT2,
                                  TemplatePartialOrderingContext TPOC) {
-  // FIXME: Implement this
   llvm::SmallVector<DeductionQualifierComparison, 4> QualifierComparisons;
   bool Better1 = isAtLeastAsSpecializedAs(*this, FT1, FT2, TPOC, 0);
   bool Better2 = isAtLeastAsSpecializedAs(*this, FT2, FT1, TPOC, 
@@ -1869,6 +1869,70 @@
     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]).
+///
+/// \param PS1 the first class template partial specialization
+///
+/// \param PS2 the second class template partial specialization
+///
+/// \returns the more specialized class template partial specialization. If
+/// neither partial specialization is more specialized, returns NULL.
+ClassTemplatePartialSpecializationDecl *
+Sema::getMoreSpecializedPartialSpecialization(
+                                  ClassTemplatePartialSpecializationDecl *PS1,
+                                  ClassTemplatePartialSpecializationDecl *PS2) {
+  // C++ [temp.class.order]p1:
+  //   For two class template partial specializations, the first is at least as
+  //   specialized as the second if, given the following rewrite to two 
+  //   function templates, the first function template is at least as 
+  //   specialized as the second according to the ordering rules for function 
+  //   templates (14.6.6.2):
+  //     - the first function template has the same template parameters as the
+  //       first partial specialization and has a single function parameter 
+  //       whose type is a class template specialization with the template 
+  //       arguments of the first partial specialization, and
+  //     - the second function template has the same template parameters as the
+  //       second partial specialization and has a single function parameter 
+  //       whose type is a class template specialization with the template 
+  //       arguments of the second partial specialization.
+  //
+  // Rather than synthesize function templates, we merely perform the 
+  // equivalent partial ordering by performing deduction directly on the
+  // template arguments of the class template partial specializations. This
+  // computation is slightly simpler than the general problem of function
+  // template partial ordering, because class template partial specializations
+  // are more constrained. We know that every template parameter is deduc
+  llvm::SmallVector<TemplateArgument, 4> Deduced;
+  Sema::TemplateDeductionInfo Info(Context);
+  
+  // Determine whether PS1 is at least as specialized as PS2
+  Deduced.resize(PS2->getTemplateParameters()->size());
+  bool Better1 = !DeduceTemplateArgumentsDuringPartialOrdering(Context,
+                                                  PS2->getTemplateParameters(),
+                                                  Context.getTypeDeclType(PS2),
+                                                  Context.getTypeDeclType(PS1),
+                                                               Info,
+                                                               Deduced,
+                                                               0);
+
+  // Determine whether PS2 is at least as specialized as PS1
+  Deduced.resize(PS1->getTemplateParameters()->size());
+  bool Better2 = !DeduceTemplateArgumentsDuringPartialOrdering(Context,
+                                                  PS1->getTemplateParameters(),
+                                                  Context.getTypeDeclType(PS1),
+                                                  Context.getTypeDeclType(PS2),
+                                                               Info,
+                                                               Deduced,
+                                                               0);
+  
+  if (Better1 == Better2)
+    return 0;
+  
+  return Better1? PS1 : PS2;
+}
+
 static void
 MarkUsedTemplateParameters(Sema &SemaRef,
                            const TemplateArgument &TemplateArg,

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

==============================================================================
--- cfe/trunk/lib/Sema/SemaTemplateInstantiate.cpp (original)
+++ cfe/trunk/lib/Sema/SemaTemplateInstantiate.cpp Tue Sep 15 11:23:51 2009
@@ -888,17 +888,50 @@
     //      specialized than all of the other matching
     //      specializations, then the use of the class template is
     //      ambiguous and the program is ill-formed.
-    // FIXME: Implement partial ordering of class template partial
-    // specializations.
-    Diag(ClassTemplateSpec->getLocation(),
-         diag::unsup_template_partial_spec_ordering);
-
-    // FIXME: Temporary hack to fall back to the primary template
-    ClassTemplateDecl *OrigTemplate = Template;
-    while (OrigTemplate->getInstantiatedFromMemberTemplate())
-      OrigTemplate = OrigTemplate->getInstantiatedFromMemberTemplate();
+    llvm::SmallVector<MatchResult, 4>::iterator Best = Matched.begin();
+    for (llvm::SmallVector<MatchResult, 4>::iterator P = Best + 1,
+                                                  PEnd = Matched.end();
+         P != PEnd; ++P) {
+      if (getMoreSpecializedPartialSpecialization(P->first, Best->first) 
+            == P->first)
+        Best = P;
+    }
+    
+    // Determine if the best partial specialization is more specialized than
+    // the others.
+    bool Ambiguous = false;
+    for (llvm::SmallVector<MatchResult, 4>::iterator P = Matched.begin(),
+                                                  PEnd = Matched.end();
+         P != PEnd; ++P) {
+      if (P != Best &&
+          getMoreSpecializedPartialSpecialization(P->first, Best->first)
+            != Best->first) {
+        Ambiguous = true;
+        break;
+      }
+    }
+     
+    if (Ambiguous) {
+      // Partial ordering did not produce a clear winner. Complain.
+      ClassTemplateSpec->setInvalidDecl();
+      Diag(ClassTemplateSpec->getPointOfInstantiation(),
+           diag::err_partial_spec_ordering_ambiguous)
+        << ClassTemplateSpec;
+      
+      // Print the matching partial specializations.
+      for (llvm::SmallVector<MatchResult, 4>::iterator P = Matched.begin(),
+                                                    PEnd = Matched.end();
+           P != PEnd; ++P)
+        Diag(P->first->getLocation(), diag::note_partial_spec_match)
+          << getTemplateArgumentBindingsText(P->first->getTemplateParameters(),
+                                             *P->second);
 
-    Pattern = OrigTemplate->getTemplatedDecl();
+      return true;
+    }
+    
+    // Instantiate using the best class template partial specialization.
+    Pattern = Best->first;
+    ClassTemplateSpec->setInstantiationOf(Best->first, Best->second);
   } else {
     //   -- If no matches are found, the instantiation is generated
     //      from the primary template.

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

==============================================================================
--- cfe/trunk/lib/Sema/SemaTemplateInstantiateDecl.cpp (original)
+++ cfe/trunk/lib/Sema/SemaTemplateInstantiateDecl.cpp Tue Sep 15 11:23:51 2009
@@ -797,12 +797,12 @@
   bool Invalid = false;
 
   unsigned N = L->size();
-  typedef llvm::SmallVector<Decl*,8> ParamVector;
+  typedef llvm::SmallVector<NamedDecl *, 8> ParamVector;
   ParamVector Params;
   Params.reserve(N);
   for (TemplateParameterList::iterator PI = L->begin(), PE = L->end();
        PI != PE; ++PI) {
-    Decl *D = Visit(*PI);
+    NamedDecl *D = cast_or_null<NamedDecl>(Visit(*PI));
     Params.push_back(D);
     Invalid = Invalid || !D;
   }

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

==============================================================================
--- cfe/trunk/test/SemaTemplate/temp_class_order.cpp (added)
+++ cfe/trunk/test/SemaTemplate/temp_class_order.cpp Tue Sep 15 11:23:51 2009
@@ -0,0 +1,42 @@
+// RUN: clang-cc -fsyntax-only -verify %s
+
+template<typename T, typename U>
+struct X1 {
+  static const int value = 0;
+};
+
+template<typename T, typename U>
+struct X1<T*, U*> {
+  static const int value = 1;
+};
+
+template<typename T>
+struct X1<T*, T*> {
+  static const int value = 2;
+};
+
+template<typename T>
+struct X1<const T*, const T*> {
+  static const int value = 3;
+};
+
+int array0[X1<int, int>::value == 0? 1 : -1];
+int array1[X1<int*, float*>::value == 1? 1 : -1];
+int array2[X1<int*, int*>::value == 2? 1 : -1];
+typedef const int* CIP;
+int array3[X1<const int*, CIP>::value == 3? 1 : -1];
+
+template<typename T, typename U>
+struct X2 { };
+
+template<typename T, typename U>
+struct X2<T*, U> { }; // expected-note{{matches}}
+
+template<typename T, typename U>
+struct X2<T, U*> { }; // expected-note{{matches}}
+
+template<typename T, typename U>
+struct X2<const T*, const U*> { };
+
+X2<int*, int*> x2a; // expected-error{{ambiguous}}
+X2<const int*, const int*> x2b;

Propchange: cfe/trunk/test/SemaTemplate/temp_class_order.cpp

------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: cfe/trunk/test/SemaTemplate/temp_class_order.cpp

------------------------------------------------------------------------------
    svn:keywords = Id

Propchange: cfe/trunk/test/SemaTemplate/temp_class_order.cpp

------------------------------------------------------------------------------
    svn:mime-type = text/plain

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

==============================================================================
--- cfe/trunk/test/SemaTemplate/temp_class_spec.cpp (original)
+++ cfe/trunk/test/SemaTemplate/temp_class_spec.cpp Tue Sep 15 11:23:51 2009
@@ -1,6 +1,6 @@
 // RUN: clang-cc -fsyntax-only -verify %s
 template<typename T>
-struct is_pointer { // expected-error{{partial ordering}}
+struct is_pointer {
   static const bool value = false;
 };
 
@@ -16,7 +16,7 @@
 
 int array0[is_pointer<int>::value? -1 : 1];
 int array1[is_pointer<int*>::value? 1 : -1];
-int array2[is_pointer<const int*>::value? 1 : -1]; // expected-error{{negative}}
+int array2[is_pointer<const int*>::value? 1 : -1];
 
 template<typename T>
 struct is_lvalue_reference {





More information about the cfe-commits mailing list