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.

llvm-svn: 124584
This commit is contained in:
Douglas Gregor 2011-01-31 16:09:46 +00:00
parent 1703f15516
commit 205d5e3a51
7 changed files with 157 additions and 43 deletions

View 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<

View 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) {

View 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

View 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();

View 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();

View 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;
}
}
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);
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);
}
}
// 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);
}
// Strip the cv-quals and ref-qualifier 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);
}
}
@ -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);

View File

@ -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];