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:
Alexis Hunt 2010-01-13 09:01:02 +00:00
parent 10e2f7264e
commit c88db06565
10 changed files with 195 additions and 10 deletions

View File

@ -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.

View File

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

View File

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

View File

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

View File

@ -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);
}

View File

@ -2360,6 +2360,8 @@ public:
bool CheckOverloadedOperatorDeclaration(FunctionDecl *FnDecl);
bool CheckLiteralOperatorDeclaration(FunctionDecl *FnDecl);
//===--------------------------------------------------------------------===//
// C++ Templates [C++ 14]
//

View File

@ -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.

View File

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

View File

@ -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 *);

View File

@ -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.