forked from OSchip/llvm-project
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:
parent
1703f15516
commit
205d5e3a51
|
@ -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<
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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];
|
Loading…
Reference in New Issue