PR11684, core issue 1417:

o Correct the handling of the restrictions on usage of cv-qualified and
  ref-qualified function types.
o Fix a bug where such types were rejected in template type parameter default
  arguments, due to such arguments not being treated as a template type arg
  context.
o Remove the ExtWarn for usage of such types as template arguments; that was
  a standard defect, not a GCC extension.
o Improve the wording and unify the code for diagnosing cv-qualifiers with the
  code for diagnosing ref-qualifiers.

llvm-svn: 150244
This commit is contained in:
Richard Smith 2012-02-10 11:05:11 +00:00
parent d98937b011
commit 63168c7533
14 changed files with 197 additions and 182 deletions

View File

@ -3720,22 +3720,11 @@ def err_invalid_this_use : Error<
def err_invalid_member_use_in_static_method : Error<
"invalid use of member %0 in static member function">;
def err_invalid_qualified_function_type : Error<
"type qualifier is not allowed on this function">;
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<
"a qualified function type cannot be used to declare a "
"%select{static member|nonmember}0 function">;
def err_invalid_ref_qualifier_typedef_function_type_use : Error<
"%select{static member|nonmember}0 function cannot have a ref-qualifier "
"'%select{&&|&}1'">;
"%select{static |non-}0member function %select{of type %2 |}1"
"cannot have '%3' qualifier">;
def err_compound_qualified_function_type : Error<
"%select{block pointer|pointer|reference}0 to function type %select{%2 |}1"
"cannot have '%3' qualifier">;
def err_ref_qualifier_overload : Error<
"cannot overload a member function %select{without a ref-qualifier|with "

View File

@ -498,9 +498,10 @@ Decl *Parser::ParseTypeParameter(unsigned Depth, unsigned Position) {
ParsedType DefaultArg;
if (Tok.is(tok::equal)) {
EqualLoc = ConsumeToken();
DefaultArg = ParseTypeName().get();
DefaultArg = ParseTypeName(/*Range=*/0,
Declarator::TemplateTypeArgContext).get();
}
return Actions.ActOnTypeParameter(getCurScope(), TypenameKeyword, Ellipsis,
EllipsisLoc, KeyLoc, ParamName, NameLoc,
Depth, Position, EqualLoc, DefaultArg);

View File

@ -4341,24 +4341,6 @@ bool Sema::CheckVariableDeclaration(VarDecl *NewVD,
return false;
}
// Function pointers and references cannot have qualified function type, only
// function pointer-to-members can do that.
QualType Pointee;
unsigned PtrOrRef = 0;
if (const PointerType *Ptr = T->getAs<PointerType>())
Pointee = Ptr->getPointeeType();
else if (const ReferenceType *Ref = T->getAs<ReferenceType>()) {
Pointee = Ref->getPointeeType();
PtrOrRef = 1;
}
if (!Pointee.isNull() && Pointee->isFunctionProtoType() &&
Pointee->getAs<FunctionProtoType>()->getTypeQuals() != 0) {
Diag(NewVD->getLocation(), diag::err_invalid_qualified_function_pointer)
<< PtrOrRef;
NewVD->setInvalidDecl();
return false;
}
if (!Previous.empty()) {
MergeVarDecl(NewVD, Previous);
return true;

View File

@ -1789,8 +1789,8 @@ static QualType GetDeclSpecTypeForDeclarator(TypeProcessingState &state,
if (D.getAttributes())
distributeTypeAttrsFromDeclarator(state, T);
// C++0x [dcl.spec.auto]p5: reject 'auto' if it is not in an allowed context.
// In C++0x, a function declarator using 'auto' must have a trailing return
// C++11 [dcl.spec.auto]p5: reject 'auto' if it is not in an allowed context.
// In C++11, a function declarator using 'auto' must have a trailing return
// type (this is checked later) and we can skip this. In other languages
// using auto, we need to check regardless.
if (D.getDeclSpec().getTypeSpecType() == DeclSpec::TST_auto &&
@ -1852,7 +1852,7 @@ static QualType GetDeclSpecTypeForDeclarator(TypeProcessingState &state,
if (D.isFunctionDeclarator())
Error = 10;
// C++0x [dcl.spec.auto]p2: 'auto' is always fine if the declarator
// C++11 [dcl.spec.auto]p2: 'auto' is always fine if the declarator
// contains a trailing return type. That is only legal at the outermost
// level. Check all declarator chunks (outermost first) anyway, to give
// better diagnostics.
@ -1893,7 +1893,7 @@ static QualType GetDeclSpecTypeForDeclarator(TypeProcessingState &state,
case Declarator::ForContext:
case Declarator::BlockLiteralContext:
case Declarator::LambdaExprContext:
// C++0x [dcl.type]p3:
// C++11 [dcl.type]p3:
// A type-specifier-seq shall not define a class or enumeration unless
// it appears in the type-id of an alias-declaration (7.1.3) that is not
// the declaration of a template-declaration.
@ -1937,6 +1937,66 @@ static QualType GetDeclSpecTypeForDeclarator(TypeProcessingState &state,
return T;
}
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<FunctionType>(T.IgnoreParens()) << T
<< getFunctionQualifiersAsString(T->castAs<FunctionProtoType>());
}
static TypeSourceInfo *GetFullTypeForDeclarator(TypeProcessingState &state,
QualType declSpecType,
TypeSourceInfo *TInfo) {
@ -1968,6 +2028,11 @@ static TypeSourceInfo *GetFullTypeForDeclarator(TypeProcessingState &state,
D.getContext() == Declarator::AliasDeclContext ||
D.getContext() == Declarator::AliasTemplateContext;
// Does T refer to a function type with a cv-qualifier or a ref-qualifier?
bool IsQualifiedFunction = T->isFunctionProtoType() &&
(T->castAs<FunctionProtoType>()->getTypeQuals() != 0 ||
T->castAs<FunctionProtoType>()->getRefQualifier() != RQ_None);
// Walk the DeclTypeInfo, building the recursive type as we go.
// DeclTypeInfos are ordered from the identifier out, which is
// opposite of what we want :).
@ -1975,6 +2040,10 @@ 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;
}
switch (DeclType.Kind) {
case DeclaratorChunk::Paren:
T = S.BuildParenType(T);
@ -2056,6 +2125,7 @@ static TypeSourceInfo *GetFullTypeForDeclarator(TypeProcessingState &state,
// does not have a K&R-style identifier list), then the arguments are part
// of the type, otherwise the argument list is ().
const DeclaratorChunk::FunctionTypeInfo &FTI = DeclType.Fun;
IsQualifiedFunction = FTI.TypeQuals || FTI.hasRefQualifier();
// Check for auto functions and trailing return type and adjust the
// return type accordingly.
@ -2407,113 +2477,57 @@ static TypeSourceInfo *GetFullTypeForDeclarator(TypeProcessingState &state,
FnTy->getNumArgs(), EPI);
}
// C++0x [dcl.fct]p6:
// A ref-qualifier shall only be part of the function type for a
// non-static member function, the function type to which a pointer to
// 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()) && !IsTypedefName &&
(FreeFunction ||
D.getDeclSpec().getStorageClassSpec() == DeclSpec::SCS_static)) {
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;
case RQ_RValue:
if (!Quals.empty())
Quals += ' ';
Quals += "&&";
break;
// C++11 [dcl.fct]p6 (w/DR1417):
// An attempt to specify a function type with a cv-qualifier-seq or a
// ref-qualifier (including by typedef-name) is ill-formed unless it is:
// - the function type for a non-static member function,
// - the function type to which a pointer to member refers,
// - the top-level function type of a function typedef declaration or
// 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
if (IsQualifiedFunction &&
!(!FreeFunction &&
D.getDeclSpec().getStorageClassSpec() != DeclSpec::SCS_static) &&
!IsTypedefName &&
D.getContext() != Declarator::TemplateTypeArgContext) {
SourceLocation Loc = D.getSourceRange().getBegin();
SourceRange RemovalRange;
unsigned I;
if (D.isFunctionDeclarator(I)) {
SmallVector<SourceLocation, 4> RemovalLocs;
const DeclaratorChunk &Chunk = D.getTypeObject(I);
assert(Chunk.Kind == DeclaratorChunk::Function);
if (Chunk.Fun.hasRefQualifier())
RemovalLocs.push_back(Chunk.Fun.getRefQualifierLoc());
if (Chunk.Fun.TypeQuals & Qualifiers::Const)
RemovalLocs.push_back(Chunk.Fun.getConstQualifierLoc());
if (Chunk.Fun.TypeQuals & Qualifiers::Volatile)
RemovalLocs.push_back(Chunk.Fun.getVolatileQualifierLoc());
// FIXME: We do not track the location of the __restrict qualifier.
//if (Chunk.Fun.TypeQuals & Qualifiers::Restrict)
// RemovalLocs.push_back(Chunk.Fun.getRestrictQualifierLoc());
if (!RemovalLocs.empty()) {
std::sort(RemovalLocs.begin(), RemovalLocs.end(),
SourceManager::LocBeforeThanCompare(S.getSourceManager()));
RemovalRange = SourceRange(RemovalLocs.front(), RemovalLocs.back());
Loc = RemovalLocs.front();
}
S.Diag(D.getIdentifierLoc(),
diag::ext_qualified_function_type_template_arg)
<< Quals;
} else {
if (FnTy->getTypeQuals() != 0) {
if (D.isFunctionDeclarator()) {
SourceRange Range = 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.TypeQuals != 0) {
switch (Chunk.Fun.TypeQuals) {
case Qualifiers::Const:
Range = Chunk.Fun.getConstQualifierLoc();
break;
case Qualifiers::Volatile:
Range = Chunk.Fun.getVolatileQualifierLoc();
break;
case Qualifiers::Const | Qualifiers::Volatile: {
SourceLocation CLoc = Chunk.Fun.getConstQualifierLoc();
SourceLocation VLoc = Chunk.Fun.getVolatileQualifierLoc();
if (S.getSourceManager()
.isBeforeInTranslationUnit(CLoc, VLoc)) {
Range = SourceRange(CLoc, VLoc);
} else {
Range = SourceRange(VLoc, CLoc);
}
}
break;
}
break;
}
}
S.Diag(Range.getBegin(), diag::err_invalid_qualified_function_type)
<< FixItHint::CreateRemoval(Range);
} else
S.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;
}
}
S.Diag(Loc, diag::err_invalid_ref_qualifier_function_type)
<< (FnTy->getRefQualifier() == RQ_LValue)
<< FixItHint::CreateRemoval(Loc);
} else {
S.Diag(D.getIdentifierLoc(),
diag::err_invalid_ref_qualifier_typedef_function_type_use)
<< FreeFunction
<< (FnTy->getRefQualifier() == RQ_LValue);
}
}
// 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);
}
S.Diag(Loc, diag::err_invalid_qualified_function_type)
<< FreeFunction << D.isFunctionDeclarator() << T
<< getFunctionQualifiersAsString(FnTy)
<< FixItHint::CreateRemoval(RemovalRange);
// 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);
}
}

View File

@ -58,7 +58,7 @@ class A {
friend A operator|(const A& r); // expected-error {{overloaded 'operator|' must be a binary operator (has 1 parameter)}}
friend operator bool() const; // expected-error {{must use a qualified name when declaring a conversion operator as a friend}} \
// expected-error{{type qualifier is not allowed on this function}}
// expected-error{{non-member function cannot have 'const' qualifier}}
typedef void ftypedef();
friend ftypedef typedeffed_function; // okay (because it's not declared as a member)

View File

@ -60,7 +60,7 @@ int ints[] = {1, 2, 3};
template <const auto (*a)[3] = &ints> class D { }; // expected-error{{'auto' not allowed in template parameter}}
enum E : auto {}; // expected-error{{'auto' not allowed here}}
struct F : auto {}; // expected-error{{expected class name}}
template<typename T = auto> struct G { }; // expected-error{{'auto' not allowed here}}
template<typename T = auto> struct G { }; // expected-error{{'auto' not allowed in template argument}}
using A = auto; // expected-error{{'auto' not allowed in type alias}}

View File

@ -15,8 +15,7 @@ namespace IllegalTypeIds {
using C = virtual void(int n); // expected-error {{type name does not allow function specifier}}
using D = explicit void(int n); // expected-error {{type name does not allow function specifier}}
using E = void(int n) throw(); // expected-error {{exception specifications are not allowed in type aliases}}
// FIXME: this is illegal; we incorrectly accept it for typedefs too.
using F = void(*)(int n) &&; // expected-err
using F = void(*)(int n) &&; // expected-error {{pointer to function type cannot have '&&' qualifier}}
using G = __thread void(int n); // expected-error {{type name does not allow storage class to be specified}}
using H = void(int n); // ok

View File

@ -1,20 +1,45 @@
// RUN: %clang_cc1 -std=c++11 -fsyntax-only -verify %s
void f0() &; // expected-error{{ref-qualifier '&' is only allowed on non-static member functions, member function pointers, and typedefs of function types}}
void f1() &&; // expected-error{{ref-qualifier '&&' is only allowed on non-static member functions, member function pointers, and typedefs of function types}}
void f0() &; // expected-error {{non-member function cannot have '&' qualifier}}
void f1() &&; // expected-error {{non-member function cannot have '&&' qualifier}}
void f2() const volatile &&; // expected-error {{non-member function cannot have 'const volatile &&' qualifier}}
struct X {
void f0() &;
void f0() &;
void f1() &&;
static void f2() &; // expected-error{{ref-qualifier '&' is only allowed on non-static member functions, member function pointers, and typedefs of function types}}
static void f3() &&; // expected-error{{ref-qualifier '&&' is only allowed on non-static member functions, member function pointers, and typedefs of function types}}
static void f2() &; // expected-error{{static member function cannot have '&' qualifier}}
static void f3() &&; // expected-error{{static member function cannot have '&&' qualifier}}
};
typedef void func_type_lvalue() &;
typedef void func_type_rvalue() &&;
func_type_lvalue f2; // expected-error{{nonmember function cannot have a ref-qualifier '&'}}
func_type_rvalue f3; // expected-error{{nonmember function cannot have a ref-qualifier '&&'}}
typedef func_type_lvalue *func_type_lvalue_ptr; // expected-error{{pointer to function type 'func_type_lvalue' (aka 'void () &') cannot have '&' qualifier}}
typedef func_type_rvalue *func_type_rvalue_ptr; // expected-error{{pointer to function type 'func_type_rvalue' (aka 'void () &&') cannot have '&&' qualifier}}
typedef func_type_lvalue &func_type_lvalue_ref; // expected-error{{reference to function type 'func_type_lvalue' (aka 'void () &') cannot have '&' qualifier}}
typedef func_type_rvalue &func_type_rvalue_ref; // expected-error{{reference to function type 'func_type_rvalue' (aka 'void () &&') cannot have '&&' qualifier}}
template<typename T = func_type_lvalue> struct wrap {
typedef T val;
typedef T *ptr;
typedef T &ref;
};
using func_type_lvalue = wrap<>::val;
using func_type_lvalue = wrap<func_type_lvalue>::val;
using func_type_rvalue = wrap<func_type_rvalue>::val;
using func_type_lvalue_ptr = wrap<>::ptr;
using func_type_lvalue_ptr = wrap<func_type_lvalue>::ptr;
using func_type_rvalue_ptr = wrap<func_type_rvalue>::ptr;
using func_type_lvalue_ref = wrap<>::ref;
using func_type_lvalue_ref = wrap<func_type_lvalue>::ref;
using func_type_rvalue_ref = wrap<func_type_rvalue>::ref;
func_type_lvalue f2; // expected-error{{non-member function of type 'func_type_lvalue' (aka 'void () &') cannot have '&' qualifier}}
func_type_rvalue f3; // expected-error{{non-member function of type 'func_type_rvalue' (aka 'void () &&') cannot have '&&' qualifier}}
struct Y {
func_type_lvalue f0;
@ -25,4 +50,4 @@ void (X::*mpf1)() & = &X::f0;
void (X::*mpf2)() && = &X::f1;
void (f() &&); // expected-error{{ref-qualifier '&&' is only allowed on non-static member functions, member function pointers, and typedefs of function types}}
void (f() &&); // expected-error{{non-member function cannot have '&&' qualifier}}

View File

@ -1,14 +1,20 @@
// RUN: %clang_cc1 -fsyntax-only -verify %s
void f() const; // expected-error{{type qualifier is not allowed on this function}}
typedef void F() const;
void f() const; // expected-error {{non-member function cannot have 'const' qualifier}}
F g; // expected-error {{non-member function of type 'F' (aka 'void () const') cannot have 'const' qualifier}}
struct X {
void f() const;
friend void g() const; // expected-error{{type qualifier is not allowed on this function}}
static void h() const; // expected-error{{type qualifier is not allowed on this function}}
friend void g() const; // expected-error {{non-member function cannot have 'const' qualifier}}
static void h() const; // expected-error {{static member function cannot have 'const' qualifier}}
F i; // ok
friend F j; // expected-error {{non-member function of type 'F' (aka 'void () const') cannot have 'const' qualifier}}
static F k; // expected-error {{static member function of type 'F' (aka 'void () const') cannot have 'const' qualifier}}
};
struct Y {
friend void X::f() const;
friend void ::f() const; // expected-error{{type qualifier is not allowed on this function}}
friend void ::f() const; // expected-error {{non-member function cannot have 'const' qualifier}}
};

View File

@ -162,9 +162,9 @@ void test (BD &br) {
aPtr = br; // expected-error {{assigning to 'AD *' from incompatible type 'BD'; take the address with &}}
}
void foo1() const {} // expected-error {{type qualifier is not allowed on this function}}
void foo2() volatile {} // expected-error {{type qualifier is not allowed on this function}}
void foo3() const volatile {} // expected-error {{type qualifier is not allowed on this function}}
void foo1() const {} // expected-error {{non-member function cannot have 'const' qualifier}}
void foo2() volatile {} // expected-error {{non-member function cannot have 'volatile' qualifier}}
void foo3() const volatile {} // expected-error {{non-member function cannot have 'const volatile' qualifier}}
struct S { void f(int, char); };
int itsAComma,

View File

@ -12,8 +12,7 @@ namespace IllegalTypeIds {
template<typename U> using C = virtual void(int n); // expected-error {{type name does not allow function specifier}}
template<typename U> using D = explicit void(int n); // expected-error {{type name does not allow function specifier}}
template<typename U> using E = void(int n) throw(); // expected-error {{exception specifications are not allowed in type aliases}}
// FIXME: this is illegal; we incorrectly accept it for typedefs too.
template<typename U> using F = void(*)(int n) &&; // expected-err
template<typename U> using F = void(*)(int n) &&; // expected-error {{pointer to function type cannot have '&&' qualifier}}
template<typename U> using G = __thread void(int n); // expected-error {{type name does not allow storage class to be specified}}
template<typename U> using H = void(int n); // ok

View File

@ -16,7 +16,7 @@ public:
struct D {
static void ~D(int, ...) const { } // \
// expected-error{{type qualifier is not allowed on this function}} \
// expected-error{{static member function cannot have 'const' qualifier}} \
// expected-error{{destructor cannot be declared 'static'}} \
// expected-error{{destructor cannot have any parameters}} \
// expected-error{{destructor cannot be variadic}} \

View File

@ -1,17 +1,17 @@
// RUN: %clang_cc1 -fsyntax-only -verify %s
void f() const; // expected-error {{type qualifier is not allowed on this function}}
void (*pf)() const; // expected-error {{type qualifier is not allowed on this function pointer}}
void (&rf)() const = f; // expected-error {{type qualifier is not allowed on this function reference}}
void f() const; // expected-error {{non-member function cannot have 'const' qualifier}}
void (*pf)() const; // expected-error {{pointer to function type cannot have 'const' qualifier}}
extern void (&rf)() const; // expected-error {{reference to function type cannot have 'const' qualifier}}
typedef void cfn() const;
cfn f2; // expected-error {{a qualified function type cannot be used to declare a nonmember function}}
typedef void cfn() const;
cfn f2; // expected-error {{non-member function of type 'cfn' (aka 'void () const') cannot have 'const' qualifier}}
class C {
void f() const;
cfn f2;
static void f3() const; // expected-error {{type qualifier is not allowed on this function}}
static cfn f4; // expected-error {{a qualified function type cannot be used to declare a static member function}}
static void f3() const; // expected-error {{static member function cannot have 'const' qualifier}}
static cfn f4; // expected-error {{static member function of type 'cfn' (aka 'void () const') cannot have 'const' qualifier}}
void m1() {
x = 0;

View File

@ -11,17 +11,17 @@ struct classify_function<R(Args...)> {
};
template<typename R, typename ...Args>
struct classify_function<R(Args...) const> { // expected-warning{{template argument of 'const' qualified function type is a GNU extension}}
struct classify_function<R(Args...) const> {
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}}
struct classify_function<R(Args...) volatile> {
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}}
struct classify_function<R(Args...) const volatile> {
static const unsigned value = 4;
};
@ -31,27 +31,27 @@ struct classify_function<R(Args......)> {
};
template<typename R, typename ...Args>
struct classify_function<R(Args......) const> { // expected-warning{{template argument of 'const' qualified function type is a GNU extension}}
struct classify_function<R(Args......) const> {
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}}
struct classify_function<R(Args......) volatile> {
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}}
struct classify_function<R(Args......) const volatile> {
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}}
struct classify_function<R(Args......) &&> {
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}}
struct classify_function<R(Args......) const &> {
static const unsigned value = 10;
};