]> granicus.if.org Git - clang/commitdiff
Implement the suggested resolution to core issue 547, extended to also
authorDouglas Gregor <dgregor@apple.com>
Mon, 31 Jan 2011 16:09:46 +0000 (16:09 +0000)
committerDouglas Gregor <dgregor@apple.com>
Mon, 31 Jan 2011 16:09:46 +0000 (16:09 +0000)
allow ref-qualifiers on function types used as template type
arguments. GNU actually allows cv-qualifiers on function types in many
places where it shouldn't, so we currently categorize this as a GNU
extension.

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

include/clang/Basic/DiagnosticSemaKinds.td
include/clang/Parse/Parser.h
include/clang/Sema/DeclSpec.h
lib/Parse/ParseDecl.cpp
lib/Parse/ParseTemplate.cpp
lib/Sema/SemaType.cpp
test/SemaCXX/issue547.cpp [new file with mode: 0644]

index 308bf0d0311fd8238522dcc81d0d86d4755f4516..f55120722c342742c71115ca3131c2af7a425057 100644 (file)
@@ -2475,6 +2475,10 @@ def err_invalid_qualified_function_type : Error<
 def err_invalid_ref_qualifier_function_type : Error<
   "ref-qualifier '%select{&&|&}0' is only allowed on non-static member functions,"
   " member function pointers, and typedefs of function types">;
+def ext_qualified_function_type_template_arg : ExtWarn<
+  "template argument of '%0' qualified function type is a GNU extension">, 
+  InGroup<GNU>;
+
 def err_invalid_qualified_function_pointer : Error<
   "type qualifier is not allowed on this function %select{pointer|reference}0">;
 def err_invalid_qualified_typedef_function_type_use : Error<
index e3ed3e8c8b42792a2e6273acc7c47ded5cc4e0f2..f1cee277b906b07ca91fb983c6c0d65271196c3b 100644 (file)
@@ -1464,7 +1464,9 @@ private:
   TPResult TryParseFunctionDeclarator();
   TPResult TryParseBracketDeclarator();
 
-  TypeResult ParseTypeName(SourceRange *Range = 0);
+  TypeResult ParseTypeName(SourceRange *Range = 0,
+                           Declarator::TheContext Context
+                                                 = Declarator::TypeNameContext);
   void ParseBlockId();
 
   void ProhibitAttributes(ParsedAttributesWithRange &attrs) {
index 742cfe96afae6533e2ab6e8c2b2b383e17426e9e..d95f3f78ca4bca3e58a6145b0973cc440086bab0 100644 (file)
@@ -1177,7 +1177,8 @@ public:
     ConditionContext,    // Condition declaration in a C++ if/switch/while/for.
     TemplateParamContext,// Within a template parameter list.
     CXXCatchContext,     // C++ catch exception-declaration
-    BlockLiteralContext  // Block literal declarator.
+    BlockLiteralContext,  // Block literal declarator.
+    TemplateTypeArgContext // Template type argument.
   };
 
 private:
@@ -1302,14 +1303,15 @@ public:
   bool mayOmitIdentifier() const {
     return Context == TypeNameContext || Context == PrototypeContext ||
            Context == TemplateParamContext || Context == CXXCatchContext ||
-           Context == BlockLiteralContext;
+           Context == BlockLiteralContext || Context == TemplateTypeArgContext;
   }
 
   /// mayHaveIdentifier - Return true if the identifier is either optional or
   /// required.  This is true for normal declarators and prototypes, but not
   /// typenames.
   bool mayHaveIdentifier() const {
-    return Context != TypeNameContext && Context != BlockLiteralContext;
+    return Context != TypeNameContext && Context != BlockLiteralContext &&
+           Context != TemplateTypeArgContext;
   }
 
   /// mayBeFollowedByCXXDirectInit - Return true if the declarator can be
index 24e41d073799fa775d8557e692b804bd1a2f4d2c..dfac16a082f161bc3246bfd7d1635a9ae958b0b5 100644 (file)
@@ -29,13 +29,14 @@ using namespace clang;
 ///         specifier-qualifier-list abstract-declarator[opt]
 ///
 /// Called type-id in C++.
-TypeResult Parser::ParseTypeName(SourceRange *Range) {
+TypeResult Parser::ParseTypeName(SourceRange *Range,
+                                 Declarator::TheContext Context) {
   // Parse the common declaration-specifiers piece.
   DeclSpec DS;
   ParseSpecifierQualifierList(DS);
 
   // Parse the abstract-declarator, if present.
-  Declarator DeclaratorInfo(DS, Declarator::TypeNameContext);
+  Declarator DeclaratorInfo(DS, Context);
   ParseDeclarator(DeclaratorInfo);
   if (Range)
     *Range = DeclaratorInfo.getSourceRange();
index d150260709ea40f5d39eb6d362c9d637677c7d6b..e64a933dec178ee5cdedd0b64f02af42b8a14573 100644 (file)
@@ -992,7 +992,8 @@ ParsedTemplateArgument Parser::ParseTemplateArgument() {
   // Therefore, we initially try to parse a type-id.  
   if (isCXXTypeId(TypeIdAsTemplateArgument)) {
     SourceLocation Loc = Tok.getLocation();
-    TypeResult TypeArg = ParseTypeName();
+    TypeResult TypeArg = ParseTypeName(/*Range=*/0, 
+                                       Declarator::TemplateTypeArgContext);
     if (TypeArg.isInvalid())
       return ParsedTemplateArgument();
     
index 9e1d067fda87a6a6bb43477baca588cb1d329c1b..a1908126efcb5719a1e9da0d2cdcce54c0374472 100644 (file)
@@ -1512,6 +1512,7 @@ TypeSourceInfo *Sema::GetTypeForDeclarator(Declarator &D, Scope *S,
     case Declarator::ForContext:
     case Declarator::ConditionContext:
     case Declarator::TypeNameContext:
+    case Declarator::TemplateTypeArgContext:
       break;
     }
 
@@ -1872,6 +1873,9 @@ TypeSourceInfo *Sema::GetTypeForDeclarator(Declarator &D, Scope *S,
     //   for a nonstatic member function, the function type to which a pointer
     //   to member refers, or the top-level function type of a function typedef
     //   declaration.
+    //
+    // Core issue 547 also allows cv-qualifiers on function types that are
+    // top-level template type arguments.
     bool FreeFunction;
     if (!D.getCXXScopeSpec().isSet()) {
       FreeFunction = (D.getContext() != Declarator::MemberContext ||
@@ -1887,48 +1891,81 @@ TypeSourceInfo *Sema::GetTypeForDeclarator(Declarator &D, Scope *S,
     //   member refers, or the top-level function type of a function typedef 
     //   declaration.
     if ((FnTy->getTypeQuals() != 0 || FnTy->getRefQualifier()) &&
+        !(D.getContext() == Declarator::TemplateTypeArgContext &&
+          !D.isFunctionDeclarator()) &&
         D.getDeclSpec().getStorageClassSpec() != DeclSpec::SCS_typedef &&
         (FreeFunction ||
          D.getDeclSpec().getStorageClassSpec() == DeclSpec::SCS_static)) {
-      if (FnTy->getTypeQuals() != 0) {
-        if (D.isFunctionDeclarator())
-          Diag(D.getIdentifierLoc(), diag::err_invalid_qualified_function_type);
-        else
-          Diag(D.getIdentifierLoc(),
-               diag::err_invalid_qualified_typedef_function_type_use)
-            << FreeFunction;
-      }
+      if (D.getContext() == Declarator::TemplateTypeArgContext) {
+        // Accept qualified function types as template type arguments as a GNU
+        // extension. This is also the subject of C++ core issue 547.
+        std::string Quals;
+        if (FnTy->getTypeQuals() != 0)
+          Quals = Qualifiers::fromCVRMask(FnTy->getTypeQuals()).getAsString();
+        
+        switch (FnTy->getRefQualifier()) {
+        case RQ_None:
+          break;
+            
+        case RQ_LValue:
+          if (!Quals.empty())
+            Quals += ' ';
+          Quals += '&';
+          break;
           
-      if (FnTy->getRefQualifier()) {
-        if (D.isFunctionDeclarator()) {
-          SourceLocation Loc = D.getIdentifierLoc();
-          for (unsigned I = 0, N = D.getNumTypeObjects(); I != N; ++I) {
-            const DeclaratorChunk &Chunk = D.getTypeObject(N-I-1);
-            if (Chunk.Kind == DeclaratorChunk::Function &&
-                Chunk.Fun.hasRefQualifier()) {
-              Loc = Chunk.Fun.getRefQualifierLoc();
-              break;
+        case RQ_RValue:
+          if (!Quals.empty())
+            Quals += ' ';
+          Quals += "&&";
+          break;
+        }
+        
+        Diag(D.getIdentifierLoc(), 
+             diag::ext_qualified_function_type_template_arg)
+          << Quals;
+      } else {
+        if (FnTy->getTypeQuals() != 0) {
+          if (D.isFunctionDeclarator())
+            Diag(D.getIdentifierLoc(), 
+                 diag::err_invalid_qualified_function_type);
+          else
+            Diag(D.getIdentifierLoc(),
+                 diag::err_invalid_qualified_typedef_function_type_use)
+              << FreeFunction;
+        }
+          
+        if (FnTy->getRefQualifier()) {
+          if (D.isFunctionDeclarator()) {
+            SourceLocation Loc = D.getIdentifierLoc();
+            for (unsigned I = 0, N = D.getNumTypeObjects(); I != N; ++I) {
+              const DeclaratorChunk &Chunk = D.getTypeObject(N-I-1);
+              if (Chunk.Kind == DeclaratorChunk::Function &&
+                  Chunk.Fun.hasRefQualifier()) {
+                Loc = Chunk.Fun.getRefQualifierLoc();
+                break;
+              }
             }
-          }
 
-          Diag(Loc, diag::err_invalid_ref_qualifier_function_type)
-            << (FnTy->getRefQualifier() == RQ_LValue)
-            << FixItHint::CreateRemoval(Loc);
-        } else {
-          Diag(D.getIdentifierLoc(), 
-               diag::err_invalid_ref_qualifier_typedef_function_type_use)
-            << FreeFunction
-            << (FnTy->getRefQualifier() == RQ_LValue);
+            Diag(Loc, diag::err_invalid_ref_qualifier_function_type)
+              << (FnTy->getRefQualifier() == RQ_LValue)
+              << FixItHint::CreateRemoval(Loc);
+          } else {
+            Diag(D.getIdentifierLoc(), 
+                 diag::err_invalid_ref_qualifier_typedef_function_type_use)
+              << FreeFunction
+              << (FnTy->getRefQualifier() == RQ_LValue);
+          }
         }
-      }
           
-      // Strip the cv-quals and ref-qualifier from the type.
-      FunctionProtoType::ExtProtoInfo EPI = FnTy->getExtProtoInfo();
-      EPI.TypeQuals = 0;
-      EPI.RefQualifier = RQ_None;
+        // Strip the cv-qualifiers and ref-qualifiers from the type.
+        FunctionProtoType::ExtProtoInfo EPI = FnTy->getExtProtoInfo();
+        EPI.TypeQuals = 0;
+        EPI.RefQualifier = RQ_None;
           
-      T = Context.getFunctionType(FnTy->getResultType(), FnTy->arg_type_begin(),
-                                  FnTy->getNumArgs(), EPI);
+        T = Context.getFunctionType(FnTy->getResultType(), 
+                                    FnTy->arg_type_begin(),
+                                    FnTy->getNumArgs(), EPI);
+      }
     }
   }
 
@@ -1997,6 +2034,7 @@ TypeSourceInfo *Sema::GetTypeForDeclarator(Declarator &D, Scope *S,
     case Declarator::ConditionContext:
     case Declarator::CXXCatchContext:
     case Declarator::BlockLiteralContext:
+    case Declarator::TemplateTypeArgContext:
       // FIXME: We may want to allow parameter packs in block-literal contexts
       // in the future.
       Diag(D.getEllipsisLoc(), diag::err_ellipsis_in_declarator_not_parameter);
diff --git a/test/SemaCXX/issue547.cpp b/test/SemaCXX/issue547.cpp
new file mode 100644 (file)
index 0000000..03c5b7c
--- /dev/null
@@ -0,0 +1,66 @@
+// RUN: %clang_cc1 -std=c++0x -fsyntax-only -verify %s
+
+template<typename T>
+struct classify_function {
+  static const unsigned value = 0;
+};
+
+template<typename R, typename ...Args>
+struct classify_function<R(Args...)> {
+  static const unsigned value = 1;
+};
+
+template<typename R, typename ...Args>
+struct classify_function<R(Args...) const> { // expected-warning{{template argument of 'const' qualified function type is a GNU extension}}
+  static const unsigned value = 2;
+};
+
+template<typename R, typename ...Args>
+struct classify_function<R(Args...) volatile> { // expected-warning{{template argument of 'volatile' qualified function type is a GNU extension}}
+  static const unsigned value = 3;
+};
+
+template<typename R, typename ...Args>
+struct classify_function<R(Args...) const volatile> { // expected-warning{{template argument of 'const volatile' qualified function type is a GNU extension}}
+  static const unsigned value = 4;
+};
+
+template<typename R, typename ...Args>
+struct classify_function<R(Args......)> {
+  static const unsigned value = 5;
+};
+
+template<typename R, typename ...Args>
+struct classify_function<R(Args......) const> { // expected-warning{{template argument of 'const' qualified function type is a GNU extension}}
+  static const unsigned value = 6;
+};
+
+template<typename R, typename ...Args>
+struct classify_function<R(Args......) volatile> { // expected-warning{{template argument of 'volatile' qualified function type is a GNU extension}}
+  static const unsigned value = 7;
+};
+
+template<typename R, typename ...Args>
+struct classify_function<R(Args......) const volatile> { // expected-warning{{template argument of 'const volatile' qualified function type is a GNU extension}}
+  static const unsigned value = 8;
+};
+
+template<typename R, typename ...Args>
+struct classify_function<R(Args......) &&> { // expected-warning{{template argument of '&&' qualified function type is a GNU extension}}
+  static const unsigned value = 9;
+};
+
+template<typename R, typename ...Args>
+struct classify_function<R(Args......) const &> { // expected-warning{{template argument of 'const &' qualified function type is a GNU extension}}
+  static const unsigned value = 10;
+};
+
+typedef void f0(int) const;
+typedef void f1(int, float...) const volatile;
+typedef void f2(int, double, ...) &&;
+typedef void f3(int, double, ...) const &;
+
+int check0[classify_function<f0>::value == 2? 1 : -1];
+int check1[classify_function<f1>::value == 8? 1 : -1];
+int check2[classify_function<f2>::value == 9? 1 : -1];
+int check3[classify_function<f3>::value == 10? 1 : -1];