From 5adc3bbd5c9ea60c40ebd4ba85e4beb774626bd7 Mon Sep 17 00:00:00 2001 From: Richard Smith Date: Wed, 14 May 2014 23:23:27 +0000 Subject: [PATCH] PR19742: cv-qualifiers and ref-qualifiers aren't allowed on functions within pointer and reference types, even if those types are produced by template instantiation. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@208825 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/Sema/SemaType.cpp | 134 +++++++++--------- .../dcl.decl/dcl.meaning/dcl.fct/p6-0x.cpp | 15 +- test/CXX/dcl.decl/dcl.meaning/dcl.fct/p6.cpp | 13 ++ 3 files changed, 94 insertions(+), 68 deletions(-) diff --git a/lib/Sema/SemaType.cpp b/lib/Sema/SemaType.cpp index 3fabaee8ad..cb8fc24c56 100644 --- a/lib/Sema/SemaType.cpp +++ b/lib/Sema/SemaType.cpp @@ -1311,6 +1311,59 @@ static QualType inferARCLifetimeForPointee(Sema &S, QualType type, return S.Context.getQualifiedType(type, qs); } +static std::string getFunctionQualifiersAsString(const FunctionProtoType *FnTy){ + std::string Quals = + Qualifiers::fromCVRMask(FnTy->getTypeQuals()).getAsString(); + + switch (FnTy->getRefQualifier()) { + case RQ_None: + break; + + case RQ_LValue: + if (!Quals.empty()) + Quals += ' '; + Quals += '&'; + break; + + case RQ_RValue: + if (!Quals.empty()) + Quals += ' '; + Quals += "&&"; + break; + } + + return Quals; +} + +namespace { +/// Kinds of declarator that cannot contain a qualified function type. +/// +/// C++98 [dcl.fct]p4 / C++11 [dcl.fct]p6: +/// a function type with a cv-qualifier or a ref-qualifier can only appear +/// at the topmost level of a type. +/// +/// Parens and member pointers are permitted. We don't diagnose array and +/// function declarators, because they don't allow function types at all. +/// +/// The values of this enum are used in diagnostics. +enum QualifiedFunctionKind { QFK_BlockPointer, QFK_Pointer, QFK_Reference }; +} + +/// Check whether the type T is a qualified function type, and if it is, +/// diagnose that it cannot be contained within the given kind of declarator. +static bool checkQualifiedFunction(Sema &S, QualType T, SourceLocation Loc, + QualifiedFunctionKind QFK) { + // Does T refer to a function type with a cv-qualifier or a ref-qualifier? + const FunctionProtoType *FPT = T->getAs(); + if (!FPT || (FPT->getTypeQuals() == 0 && FPT->getRefQualifier() == RQ_None)) + return false; + + S.Diag(Loc, diag::err_compound_qualified_function_type) + << QFK << isa(T.IgnoreParens()) << T + << getFunctionQualifiersAsString(FPT); + return true; +} + /// \brief Build a pointer type. /// /// \param T The type to which we'll be building a pointer. @@ -1333,6 +1386,9 @@ QualType Sema::BuildPointerType(QualType T, return QualType(); } + if (checkQualifiedFunction(*this, T, Loc, QFK_Pointer)) + return QualType(); + assert(!T->isObjCObjectType() && "Should build ObjCObjectPointerType"); // In ARC, it is forbidden to build pointers to unqualified pointers. @@ -1392,6 +1448,9 @@ QualType Sema::BuildReferenceType(QualType T, bool SpelledAsLValue, return QualType(); } + if (checkQualifiedFunction(*this, T, Loc, QFK_Reference)) + return QualType(); + // In ARC, it is forbidden to build references to unqualified pointers. if (getLangOpts().ObjCAutoRefCount) T = inferARCLifetimeForPointee(*this, T, Loc, /*reference*/ true); @@ -1803,6 +1862,9 @@ QualType Sema::BuildBlockPointerType(QualType T, return QualType(); } + if (checkQualifiedFunction(*this, T, Loc, QFK_BlockPointer)) + return QualType(); + return Context.getBlockPointerType(T); } @@ -2270,66 +2332,6 @@ static QualType GetDeclSpecTypeForDeclarator(TypeProcessingState &state, return T; } -static std::string getFunctionQualifiersAsString(const FunctionProtoType *FnTy){ - std::string Quals = - Qualifiers::fromCVRMask(FnTy->getTypeQuals()).getAsString(); - - switch (FnTy->getRefQualifier()) { - case RQ_None: - break; - - case RQ_LValue: - if (!Quals.empty()) - Quals += ' '; - Quals += '&'; - break; - - case RQ_RValue: - if (!Quals.empty()) - Quals += ' '; - Quals += "&&"; - break; - } - - return Quals; -} - -/// Check that the function type T, which has a cv-qualifier or a ref-qualifier, -/// can be contained within the declarator chunk DeclType, and produce an -/// appropriate diagnostic if not. -static void checkQualifiedFunction(Sema &S, QualType T, - DeclaratorChunk &DeclType) { - // C++98 [dcl.fct]p4 / C++11 [dcl.fct]p6: a function type with a - // cv-qualifier or a ref-qualifier can only appear at the topmost level - // of a type. - int DiagKind = -1; - switch (DeclType.Kind) { - case DeclaratorChunk::Paren: - case DeclaratorChunk::MemberPointer: - // These cases are permitted. - return; - case DeclaratorChunk::Array: - case DeclaratorChunk::Function: - // These cases don't allow function types at all; no need to diagnose the - // qualifiers separately. - return; - case DeclaratorChunk::BlockPointer: - DiagKind = 0; - break; - case DeclaratorChunk::Pointer: - DiagKind = 1; - break; - case DeclaratorChunk::Reference: - DiagKind = 2; - break; - } - - assert(DiagKind != -1); - S.Diag(DeclType.Loc, diag::err_compound_qualified_function_type) - << DiagKind << isa(T.IgnoreParens()) << T - << getFunctionQualifiersAsString(T->castAs()); -} - /// Produce an appropriate diagnostic for an ambiguity between a function /// declarator and a C++ direct-initializer. static void warnAboutAmbiguousFunction(Sema &S, Declarator &D, @@ -2546,10 +2548,7 @@ static TypeSourceInfo *GetFullTypeForDeclarator(TypeProcessingState &state, unsigned chunkIndex = e - i - 1; state.setCurrentChunkIndex(chunkIndex); DeclaratorChunk &DeclType = D.getTypeObject(chunkIndex); - if (IsQualifiedFunction) { - checkQualifiedFunction(S, T, DeclType); - IsQualifiedFunction = DeclType.Kind == DeclaratorChunk::Paren; - } + IsQualifiedFunction &= DeclType.Kind == DeclaratorChunk::Paren; switch (DeclType.Kind) { case DeclaratorChunk::Paren: T = S.BuildParenType(T); @@ -3093,6 +3092,13 @@ static TypeSourceInfo *GetFullTypeForDeclarator(TypeProcessingState &state, // alias-declaration, // - the type-id in the default argument of a type-parameter, or // - the type-id of a template-argument for a type-parameter + // + // FIXME: Checking this here is insufficient. We accept-invalid on: + // + // template struct S { void f(T); }; + // S s; + // + // ... for instance. if (IsQualifiedFunction && !(!FreeFunction && D.getDeclSpec().getStorageClassSpec() != DeclSpec::SCS_static) && diff --git a/test/CXX/dcl.decl/dcl.meaning/dcl.fct/p6-0x.cpp b/test/CXX/dcl.decl/dcl.meaning/dcl.fct/p6-0x.cpp index 2ec1454100..cec747e1d6 100644 --- a/test/CXX/dcl.decl/dcl.meaning/dcl.fct/p6-0x.cpp +++ b/test/CXX/dcl.decl/dcl.meaning/dcl.fct/p6-0x.cpp @@ -22,13 +22,13 @@ typedef func_type_rvalue &func_type_rvalue_ref; // expected-error{{reference to template struct wrap { typedef T val; - typedef T *ptr; - typedef T &ref; + typedef T *ptr; // expected-error-re 2{{pointer to function type '{{.*}}' cannot have '{{&|&&}}' qualifier}} + typedef T &ref; // expected-error-re 2{{reference to function type '{{.*}}' cannot have '{{&|&&}}' qualifier}} }; -using func_type_lvalue = wrap<>::val; +using func_type_lvalue = wrap<>::val; // expected-note{{in instantiation of}} using func_type_lvalue = wrap::val; -using func_type_rvalue = wrap::val; +using func_type_rvalue = wrap::val; // expected-note{{in instantiation of}} using func_type_lvalue_ptr = wrap<>::ptr; using func_type_lvalue_ptr = wrap::ptr; @@ -51,3 +51,10 @@ void (X::*mpf2)() && = &X::f1; void (f() &&); // expected-error{{non-member function cannot have '&&' qualifier}} + +// FIXME: These are ill-formed. +template struct pass { + void f(T); +}; +pass pass0; +pass pass1; diff --git a/test/CXX/dcl.decl/dcl.meaning/dcl.fct/p6.cpp b/test/CXX/dcl.decl/dcl.meaning/dcl.fct/p6.cpp index e2d94fbf38..a035086c9a 100644 --- a/test/CXX/dcl.decl/dcl.meaning/dcl.fct/p6.cpp +++ b/test/CXX/dcl.decl/dcl.meaning/dcl.fct/p6.cpp @@ -18,3 +18,16 @@ struct Y { friend void X::f() const; friend void ::f() const; // expected-error {{non-member function cannot have 'const' qualifier}} }; + +template struct S { + typedef T F; + typedef T *P; // expected-error {{pointer to function type 'void () const' cannot have 'const' qualifier}} + typedef T &R; // expected-error {{reference to function type 'void () const' cannot have 'const' qualifier}} +}; +S s; // expected-note {{in instantiation of}} + +// FIXME: This is ill-formed. +template struct U { + void f(T); +}; +U u; -- 2.40.0