From 683a81f4373cf1fa9d41a751dca6f7c36125b058 Mon Sep 17 00:00:00 2001 From: Douglas Gregor Date: Mon, 31 Jan 2011 16:09:46 +0000 Subject: [PATCH] Implement the suggested resolution to core issue 547, extended to also 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 | 4 + include/clang/Parse/Parser.h | 4 +- include/clang/Sema/DeclSpec.h | 8 +- lib/Parse/ParseDecl.cpp | 5 +- lib/Parse/ParseTemplate.cpp | 3 +- lib/Sema/SemaType.cpp | 104 ++++++++++++++------- test/SemaCXX/issue547.cpp | 66 +++++++++++++ 7 files changed, 154 insertions(+), 40 deletions(-) create mode 100644 test/SemaCXX/issue547.cpp diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td index 308bf0d031..f55120722c 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.td +++ b/include/clang/Basic/DiagnosticSemaKinds.td @@ -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; + 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< diff --git a/include/clang/Parse/Parser.h b/include/clang/Parse/Parser.h index e3ed3e8c8b..f1cee277b9 100644 --- a/include/clang/Parse/Parser.h +++ b/include/clang/Parse/Parser.h @@ -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) { diff --git a/include/clang/Sema/DeclSpec.h b/include/clang/Sema/DeclSpec.h index 742cfe96af..d95f3f78ca 100644 --- a/include/clang/Sema/DeclSpec.h +++ b/include/clang/Sema/DeclSpec.h @@ -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 diff --git a/lib/Parse/ParseDecl.cpp b/lib/Parse/ParseDecl.cpp index 24e41d0737..dfac16a082 100644 --- a/lib/Parse/ParseDecl.cpp +++ b/lib/Parse/ParseDecl.cpp @@ -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(); diff --git a/lib/Parse/ParseTemplate.cpp b/lib/Parse/ParseTemplate.cpp index d150260709..e64a933dec 100644 --- a/lib/Parse/ParseTemplate.cpp +++ b/lib/Parse/ParseTemplate.cpp @@ -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(); diff --git a/lib/Sema/SemaType.cpp b/lib/Sema/SemaType.cpp index 9e1d067fda..a1908126ef 100644 --- a/lib/Sema/SemaType.cpp +++ b/lib/Sema/SemaType.cpp @@ -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 index 0000000000..03c5b7c978 --- /dev/null +++ b/test/SemaCXX/issue547.cpp @@ -0,0 +1,66 @@ +// RUN: %clang_cc1 -std=c++0x -fsyntax-only -verify %s + +template +struct classify_function { + static const unsigned value = 0; +}; + +template +struct classify_function { + static const unsigned value = 1; +}; + +template +struct classify_function { // expected-warning{{template argument of 'const' qualified function type is a GNU extension}} + static const unsigned value = 2; +}; + +template +struct classify_function { // expected-warning{{template argument of 'volatile' qualified function type is a GNU extension}} + static const unsigned value = 3; +}; + +template +struct classify_function { // expected-warning{{template argument of 'const volatile' qualified function type is a GNU extension}} + static const unsigned value = 4; +}; + +template +struct classify_function { + static const unsigned value = 5; +}; + +template +struct classify_function { // expected-warning{{template argument of 'const' qualified function type is a GNU extension}} + static const unsigned value = 6; +}; + +template +struct classify_function { // expected-warning{{template argument of 'volatile' qualified function type is a GNU extension}} + static const unsigned value = 7; +}; + +template +struct classify_function { // expected-warning{{template argument of 'const volatile' qualified function type is a GNU extension}} + static const unsigned value = 8; +}; + +template +struct classify_function { // expected-warning{{template argument of '&&' qualified function type is a GNU extension}} + static const unsigned value = 9; +}; + +template +struct classify_function { // 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::value == 2? 1 : -1]; +int check1[classify_function::value == 8? 1 : -1]; +int check2[classify_function::value == 9? 1 : -1]; +int check3[classify_function::value == 10? 1 : -1]; -- 2.40.0