forked from OSchip/llvm-project
Implement semantic checking for C++ literal operators.
This now rejects literal operators that don't meet the requirements. Templates are not yet checked for. llvm-svn: 93315
This commit is contained in:
parent
10e2f7264e
commit
c88db06565
|
@ -1184,6 +1184,8 @@ public:
|
|||
|
||||
OverloadedOperatorKind getOverloadedOperator() const;
|
||||
|
||||
const IdentifierInfo *getLiteralIdentifier() const;
|
||||
|
||||
/// \brief If this function is an instantiation of a member function
|
||||
/// of a class template specialization, retrieves the function from
|
||||
/// which it was instantiated.
|
||||
|
|
|
@ -309,6 +309,7 @@ inline bool operator>=(DeclarationName LHS, DeclarationName RHS) {
|
|||
class DeclarationNameTable {
|
||||
void *CXXSpecialNamesImpl; // Actually a FoldingSet<CXXSpecialName> *
|
||||
CXXOperatorIdName *CXXOperatorNames; // Operator names
|
||||
void *CXXLiteralOperatorNames; // Actually a FoldingSet<...> *
|
||||
|
||||
DeclarationNameTable(const DeclarationNameTable&); // NONCOPYABLE
|
||||
DeclarationNameTable& operator=(const DeclarationNameTable&); // NONCOPYABLE
|
||||
|
|
|
@ -2341,6 +2341,13 @@ def err_operator_delete_dependent_param_type : Error<
|
|||
def err_operator_delete_param_type : Error<
|
||||
"%0 takes type %1 as first parameter">;
|
||||
|
||||
// C++ literal operators
|
||||
def err_literal_operator_outside_namespace : Error<
|
||||
"literal operator %0 must be in a namespace or global scope">;
|
||||
// FIXME: This diagnostic sucks
|
||||
def err_literal_operator_params : Error<
|
||||
"parameter declaration for literal operator %0 is not valid">;
|
||||
|
||||
// C++ conversion functions
|
||||
def err_conv_function_not_member : Error<
|
||||
"conversion function must be a non-static member function">;
|
||||
|
|
|
@ -1039,6 +1039,15 @@ OverloadedOperatorKind FunctionDecl::getOverloadedOperator() const {
|
|||
return OO_None;
|
||||
}
|
||||
|
||||
/// getLiteralIdentifier - The literal suffix identifier this function
|
||||
/// represents, if any.
|
||||
const IdentifierInfo *FunctionDecl::getLiteralIdentifier() const {
|
||||
if (getDeclName().getNameKind() == DeclarationName::CXXLiteralOperatorName)
|
||||
return getDeclName().getCXXLiteralIdentifier();
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
FunctionDecl *FunctionDecl::getInstantiatedFromMemberFunction() const {
|
||||
if (MemberSpecializationInfo *Info = getMemberSpecializationInfo())
|
||||
return cast<FunctionDecl>(Info->getInstantiatedFrom());
|
||||
|
|
|
@ -56,9 +56,14 @@ public:
|
|||
/// This identifier is stored here rather than directly in DeclarationName so as
|
||||
/// to allow Objective-C selectors, which are about a million times more common,
|
||||
/// to consume minimal memory.
|
||||
class CXXLiteralOperatorIdName : public DeclarationNameExtra {
|
||||
class CXXLiteralOperatorIdName
|
||||
: public DeclarationNameExtra, public llvm::FoldingSetNode {
|
||||
public:
|
||||
IdentifierInfo *ID;
|
||||
|
||||
void Profile(llvm::FoldingSetNodeID &FSID) {
|
||||
FSID.AddPointer(ID);
|
||||
}
|
||||
};
|
||||
|
||||
bool operator<(DeclarationName LHS, DeclarationName RHS) {
|
||||
|
@ -358,6 +363,7 @@ void DeclarationName::dump() const {
|
|||
|
||||
DeclarationNameTable::DeclarationNameTable() {
|
||||
CXXSpecialNamesImpl = new llvm::FoldingSet<CXXSpecialName>;
|
||||
CXXLiteralOperatorNames = new llvm::FoldingSet<CXXLiteralOperatorIdName>;
|
||||
|
||||
// Initialize the overloaded operator names.
|
||||
CXXOperatorNames = new CXXOperatorIdName[NUM_OVERLOADED_OPERATORS];
|
||||
|
@ -369,16 +375,30 @@ DeclarationNameTable::DeclarationNameTable() {
|
|||
}
|
||||
|
||||
DeclarationNameTable::~DeclarationNameTable() {
|
||||
llvm::FoldingSet<CXXSpecialName> *set =
|
||||
llvm::FoldingSet<CXXSpecialName> *SpecialNames =
|
||||
static_cast<llvm::FoldingSet<CXXSpecialName>*>(CXXSpecialNamesImpl);
|
||||
llvm::FoldingSetIterator<CXXSpecialName> I = set->begin(), E = set->end();
|
||||
llvm::FoldingSetIterator<CXXSpecialName>
|
||||
SI = SpecialNames->begin(), SE = SpecialNames->end();
|
||||
|
||||
while (I != E) {
|
||||
CXXSpecialName *n = &*I++;
|
||||
while (SI != SE) {
|
||||
CXXSpecialName *n = &*SI++;
|
||||
delete n;
|
||||
}
|
||||
|
||||
delete set;
|
||||
|
||||
llvm::FoldingSet<CXXLiteralOperatorIdName> *LiteralNames
|
||||
= static_cast<llvm::FoldingSet<CXXLiteralOperatorIdName>*>
|
||||
(CXXLiteralOperatorNames);
|
||||
llvm::FoldingSetIterator<CXXLiteralOperatorIdName>
|
||||
LI = LiteralNames->begin(), LE = LiteralNames->end();
|
||||
|
||||
while (LI != LE) {
|
||||
CXXLiteralOperatorIdName *n = &*LI++;
|
||||
delete n;
|
||||
}
|
||||
|
||||
delete SpecialNames;
|
||||
delete LiteralNames;
|
||||
delete [] CXXOperatorNames;
|
||||
}
|
||||
|
||||
|
@ -433,9 +453,23 @@ DeclarationNameTable::getCXXOperatorName(OverloadedOperatorKind Op) {
|
|||
|
||||
DeclarationName
|
||||
DeclarationNameTable::getCXXLiteralOperatorName(IdentifierInfo *II) {
|
||||
llvm::FoldingSet<CXXLiteralOperatorIdName> *LiteralNames
|
||||
= static_cast<llvm::FoldingSet<CXXLiteralOperatorIdName>*>
|
||||
(CXXLiteralOperatorNames);
|
||||
|
||||
llvm::FoldingSetNodeID ID;
|
||||
ID.AddPointer(II);
|
||||
|
||||
void *InsertPos = 0;
|
||||
if (CXXLiteralOperatorIdName *Name =
|
||||
LiteralNames->FindNodeOrInsertPos(ID, InsertPos))
|
||||
return DeclarationName (Name);
|
||||
|
||||
CXXLiteralOperatorIdName *LiteralName = new CXXLiteralOperatorIdName;
|
||||
LiteralName->ExtraKindOrNumArgs = DeclarationNameExtra::CXXLiteralOperator;
|
||||
LiteralName->ID = II;
|
||||
|
||||
LiteralNames->InsertNode(LiteralName, InsertPos);
|
||||
return DeclarationName(LiteralName);
|
||||
}
|
||||
|
||||
|
|
|
@ -2360,6 +2360,8 @@ public:
|
|||
|
||||
bool CheckOverloadedOperatorDeclaration(FunctionDecl *FnDecl);
|
||||
|
||||
bool CheckLiteralOperatorDeclaration(FunctionDecl *FnDecl);
|
||||
|
||||
//===--------------------------------------------------------------------===//
|
||||
// C++ Templates [C++ 14]
|
||||
//
|
||||
|
|
|
@ -3487,7 +3487,12 @@ void Sema::CheckFunctionDeclaration(Scope *S, FunctionDecl *NewFD,
|
|||
if (NewFD->isOverloadedOperator() &&
|
||||
CheckOverloadedOperatorDeclaration(NewFD))
|
||||
return NewFD->setInvalidDecl();
|
||||
|
||||
|
||||
// Extra checking for C++0x literal operators (C++0x [over.literal]).
|
||||
if (NewFD->getLiteralIdentifier() &&
|
||||
CheckLiteralOperatorDeclaration(NewFD))
|
||||
return NewFD->setInvalidDecl();
|
||||
|
||||
// In C++, check default arguments now that we have merged decls. Unless
|
||||
// the lexical context is the class, because in this case this is done
|
||||
// during delayed parsing anyway.
|
||||
|
|
|
@ -5006,6 +5006,88 @@ bool Sema::CheckOverloadedOperatorDeclaration(FunctionDecl *FnDecl) {
|
|||
return false;
|
||||
}
|
||||
|
||||
/// CheckLiteralOperatorDeclaration - Check whether the declaration
|
||||
/// of this literal operator function is well-formed. If so, returns
|
||||
/// false; otherwise, emits appropriate diagnostics and returns true.
|
||||
bool Sema::CheckLiteralOperatorDeclaration(FunctionDecl *FnDecl) {
|
||||
DeclContext *DC = FnDecl->getDeclContext();
|
||||
Decl::Kind Kind = DC->getDeclKind();
|
||||
if (Kind != Decl::TranslationUnit && Kind != Decl::Namespace &&
|
||||
Kind != Decl::LinkageSpec) {
|
||||
Diag(FnDecl->getLocation(), diag::err_literal_operator_outside_namespace)
|
||||
<< FnDecl->getDeclName();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Valid = false;
|
||||
|
||||
// FIXME: Check for the one valid template signature
|
||||
// template <char...> type operator "" name();
|
||||
|
||||
if (FunctionDecl::param_iterator Param = FnDecl->param_begin()) {
|
||||
// Check the first parameter
|
||||
QualType T = (*Param)->getType();
|
||||
|
||||
// unsigned long long int and long double are allowed, but only
|
||||
// alone.
|
||||
// We also allow any character type; their omission seems to be a bug
|
||||
// in n3000
|
||||
if (Context.hasSameType(T, Context.UnsignedLongLongTy) ||
|
||||
Context.hasSameType(T, Context.LongDoubleTy) ||
|
||||
Context.hasSameType(T, Context.CharTy) ||
|
||||
Context.hasSameType(T, Context.WCharTy) ||
|
||||
Context.hasSameType(T, Context.Char16Ty) ||
|
||||
Context.hasSameType(T, Context.Char32Ty)) {
|
||||
if (++Param == FnDecl->param_end())
|
||||
Valid = true;
|
||||
goto FinishedParams;
|
||||
}
|
||||
|
||||
// Otherwise it must be a pointer to const; let's strip those.
|
||||
const PointerType *PT = T->getAs<PointerType>();
|
||||
if (!PT)
|
||||
goto FinishedParams;
|
||||
T = PT->getPointeeType();
|
||||
if (!T.isConstQualified())
|
||||
goto FinishedParams;
|
||||
T = T.getUnqualifiedType();
|
||||
|
||||
// Move on to the second parameter;
|
||||
++Param;
|
||||
|
||||
// If there is no second parameter, the first must be a const char *
|
||||
if (Param == FnDecl->param_end()) {
|
||||
if (Context.hasSameType(T, Context.CharTy))
|
||||
Valid = true;
|
||||
goto FinishedParams;
|
||||
}
|
||||
|
||||
// const char *, const wchar_t*, const char16_t*, and const char32_t*
|
||||
// are allowed as the first parameter to a two-parameter function
|
||||
if (!(Context.hasSameType(T, Context.CharTy) ||
|
||||
Context.hasSameType(T, Context.WCharTy) ||
|
||||
Context.hasSameType(T, Context.Char16Ty) ||
|
||||
Context.hasSameType(T, Context.Char32Ty)))
|
||||
goto FinishedParams;
|
||||
|
||||
// The second and final parameter must be an std::size_t
|
||||
T = (*Param)->getType().getUnqualifiedType();
|
||||
if (Context.hasSameType(T, Context.getSizeType()) &&
|
||||
++Param == FnDecl->param_end())
|
||||
Valid = true;
|
||||
}
|
||||
|
||||
// FIXME: This diagnostic is absolutely terrible.
|
||||
FinishedParams:
|
||||
if (!Valid) {
|
||||
Diag(FnDecl->getLocation(), diag::err_literal_operator_params)
|
||||
<< FnDecl->getDeclName();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// ActOnStartLinkageSpecification - Parsed the beginning of a C++
|
||||
/// linkage specification, including the language and (if present)
|
||||
/// the '{'. ExternLoc is the location of the 'extern', LangLoc is
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
// RUN: %clang_cc1 -fsyntax-only -verify -std=c++0x %s
|
||||
|
||||
void operator "" (); // expected-error {{expected identifier}}
|
||||
void operator "k" foo(); // expected-error {{string literal after 'operator' must be '""'}}
|
||||
void operator "" tester (int);
|
||||
void operator "" (const char *); // expected-error {{expected identifier}}
|
||||
void operator "k" foo(const char *); // expected-error {{string literal after 'operator' must be '""'}}
|
||||
void operator "" tester (const char *);
|
||||
|
|
|
@ -0,0 +1,43 @@
|
|||
// RUN: %clang_cc1 -fsyntax-only -verify -std=c++0x %s
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
struct tag {
|
||||
void operator "" tag_bad (const char *); // expected-error {{literal operator 'operator "" tag_bad' must be in a namespace or global scope}}
|
||||
friend void operator "" tag_good (const char *);
|
||||
};
|
||||
|
||||
namespace ns { void operator "" ns_good (const char *); }
|
||||
|
||||
// Check extern "C++" declarations
|
||||
extern "C++" void operator "" extern_good (const char *);
|
||||
extern "C++" { void operator "" extern_good (const char *); }
|
||||
|
||||
void fn () { void operator "" fn_bad (const char *); } // expected-error {{literal operator 'operator "" fn_bad' must be in a namespace or global scope}}
|
||||
|
||||
// One-param declarations (const char * was already checked)
|
||||
void operator "" good (char);
|
||||
void operator "" good (wchar_t);
|
||||
void operator "" good (char16_t);
|
||||
void operator "" good (char32_t);
|
||||
void operator "" good (unsigned long long);
|
||||
void operator "" good (long double);
|
||||
|
||||
// Two-param declarations
|
||||
void operator "" good (const char *, size_t);
|
||||
void operator "" good (const wchar_t *, size_t);
|
||||
void operator "" good (const char16_t *, size_t);
|
||||
void operator "" good (const char32_t *, size_t);
|
||||
|
||||
// Check typedef and array equivalences
|
||||
void operator "" good (const char[]);
|
||||
typedef const char c;
|
||||
void operator "" good (c*);
|
||||
|
||||
// Check extra cv-qualifiers
|
||||
void operator "" cv_good (volatile const char *, const size_t);
|
||||
|
||||
// Template delcaration (not implemented yet)
|
||||
// template <char...> void operator "" good ();
|
||||
|
||||
// FIXME: Test some invalid decls that might crop up.
|
Loading…
Reference in New Issue