diff --git a/clang/include/clang/AST/Decl.h b/clang/include/clang/AST/Decl.h index d0d94aafb8d0..21b59099138a 100644 --- a/clang/include/clang/AST/Decl.h +++ b/clang/include/clang/AST/Decl.h @@ -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. diff --git a/clang/include/clang/AST/DeclarationName.h b/clang/include/clang/AST/DeclarationName.h index e6f50d539244..59d7e439e570 100644 --- a/clang/include/clang/AST/DeclarationName.h +++ b/clang/include/clang/AST/DeclarationName.h @@ -309,6 +309,7 @@ inline bool operator>=(DeclarationName LHS, DeclarationName RHS) { class DeclarationNameTable { void *CXXSpecialNamesImpl; // Actually a FoldingSet * CXXOperatorIdName *CXXOperatorNames; // Operator names + void *CXXLiteralOperatorNames; // Actually a FoldingSet<...> * DeclarationNameTable(const DeclarationNameTable&); // NONCOPYABLE DeclarationNameTable& operator=(const DeclarationNameTable&); // NONCOPYABLE diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 33f1b98182b6..1173863b910d 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -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">; diff --git a/clang/lib/AST/Decl.cpp b/clang/lib/AST/Decl.cpp index 49b9c6fc8b54..e77661a9fbd4 100644 --- a/clang/lib/AST/Decl.cpp +++ b/clang/lib/AST/Decl.cpp @@ -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(Info->getInstantiatedFrom()); diff --git a/clang/lib/AST/DeclarationName.cpp b/clang/lib/AST/DeclarationName.cpp index 1fa201078641..ff810735bc81 100644 --- a/clang/lib/AST/DeclarationName.cpp +++ b/clang/lib/AST/DeclarationName.cpp @@ -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; + CXXLiteralOperatorNames = new llvm::FoldingSet; // Initialize the overloaded operator names. CXXOperatorNames = new CXXOperatorIdName[NUM_OVERLOADED_OPERATORS]; @@ -369,16 +375,30 @@ DeclarationNameTable::DeclarationNameTable() { } DeclarationNameTable::~DeclarationNameTable() { - llvm::FoldingSet *set = + llvm::FoldingSet *SpecialNames = static_cast*>(CXXSpecialNamesImpl); - llvm::FoldingSetIterator I = set->begin(), E = set->end(); + llvm::FoldingSetIterator + SI = SpecialNames->begin(), SE = SpecialNames->end(); - while (I != E) { - CXXSpecialName *n = &*I++; + while (SI != SE) { + CXXSpecialName *n = &*SI++; delete n; } - delete set; + + llvm::FoldingSet *LiteralNames + = static_cast*> + (CXXLiteralOperatorNames); + llvm::FoldingSetIterator + 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 *LiteralNames + = static_cast*> + (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); } diff --git a/clang/lib/Sema/Sema.h b/clang/lib/Sema/Sema.h index 38657638b7dd..956776cf29e6 100644 --- a/clang/lib/Sema/Sema.h +++ b/clang/lib/Sema/Sema.h @@ -2360,6 +2360,8 @@ public: bool CheckOverloadedOperatorDeclaration(FunctionDecl *FnDecl); + bool CheckLiteralOperatorDeclaration(FunctionDecl *FnDecl); + //===--------------------------------------------------------------------===// // C++ Templates [C++ 14] // diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index 59cb447ecd6d..e53f141c960d 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -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. diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp index ffd10c89f0f8..e10398af85fe 100644 --- a/clang/lib/Sema/SemaDeclCXX.cpp +++ b/clang/lib/Sema/SemaDeclCXX.cpp @@ -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 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(); + 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 diff --git a/clang/test/Parser/cxx0x-literal-operators.cpp b/clang/test/Parser/cxx0x-literal-operators.cpp index 830754e56ae0..30b290382cb4 100644 --- a/clang/test/Parser/cxx0x-literal-operators.cpp +++ b/clang/test/Parser/cxx0x-literal-operators.cpp @@ -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 *); diff --git a/clang/test/SemaCXX/literal-operators.cpp b/clang/test/SemaCXX/literal-operators.cpp new file mode 100644 index 000000000000..ec585a61da9f --- /dev/null +++ b/clang/test/SemaCXX/literal-operators.cpp @@ -0,0 +1,43 @@ +// RUN: %clang_cc1 -fsyntax-only -verify -std=c++0x %s + +#include + +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 void operator "" good (); + +// FIXME: Test some invalid decls that might crop up.