From cf1b10a63b988bdc4c2c58457b096599577537c2 Mon Sep 17 00:00:00 2001 From: Richard Smith Date: Thu, 5 Dec 2013 00:58:33 +0000 Subject: [PATCH] Reject template-ids containing literal-operator-ids that have a dependent nested-name-specifier, rather than crashing. (In fact, reject all literal-operator-ids that have a non-namespace nested-name-specifier). The grammar doesn't allow these in some cases, and in other cases does allow them but instantiation will always fail. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@196443 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/Basic/DiagnosticSemaKinds.td | 2 ++ include/clang/Sema/Sema.h | 1 + lib/Parse/ParseExprCXX.cpp | 15 +++++++---- lib/Sema/SemaExprCXX.cpp | 28 ++++++++++++++++++++ lib/Sema/SemaTemplate.cpp | 3 +-- test/SemaCXX/cxx11-user-defined-literals.cpp | 24 +++++++++++++++++ 6 files changed, 66 insertions(+), 7 deletions(-) diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td index d80e4f2856..e1ca10fc83 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.td +++ b/include/clang/Basic/DiagnosticSemaKinds.td @@ -6009,6 +6009,8 @@ def err_operator_delete_param_type : Error< // C++ literal operators def err_literal_operator_outside_namespace : Error< "literal operator %0 must be in a namespace or global scope">; +def err_literal_operator_id_outside_namespace : Error< + "non-namespace scope '%0' cannot have a literal operator member">; def err_literal_operator_default_argument : Error< "literal operator cannot have a default argument">; // FIXME: This diagnostic sucks diff --git a/include/clang/Sema/Sema.h b/include/clang/Sema/Sema.h index 3fbe2b7827..1ad3194a79 100644 --- a/include/clang/Sema/Sema.h +++ b/include/clang/Sema/Sema.h @@ -2478,6 +2478,7 @@ public: bool RValueThis, unsigned ThisQuals); CXXDestructorDecl *LookupDestructor(CXXRecordDecl *Class); + bool checkLiteralOperatorId(const CXXScopeSpec &SS, const UnqualifiedId &Id); LiteralOperatorLookupResult LookupLiteralOperator(Scope *S, LookupResult &R, ArrayRef ArgTys, bool AllowRaw, diff --git a/lib/Parse/ParseExprCXX.cpp b/lib/Parse/ParseExprCXX.cpp index f07428a0f3..9489b4787e 100644 --- a/lib/Parse/ParseExprCXX.cpp +++ b/lib/Parse/ParseExprCXX.cpp @@ -287,19 +287,23 @@ bool Parser::ParseOptionalCXXScopeSpecifier(CXXScopeSpec &SS, TentativeParsingAction TPA(*this); SourceLocation TemplateKWLoc = ConsumeToken(); - + UnqualifiedId TemplateName; if (Tok.is(tok::identifier)) { // Consume the identifier. TemplateName.setIdentifier(Tok.getIdentifierInfo(), Tok.getLocation()); ConsumeToken(); } else if (Tok.is(tok::kw_operator)) { - if (ParseUnqualifiedIdOperator(SS, EnteringContext, ObjectType, + // We don't need to actually parse the unqualified-id in this case, + // because a simple-template-id cannot start with 'operator', but + // go ahead and parse it anyway for consistency with the case where + // we already annotated the template-id. + if (ParseUnqualifiedIdOperator(SS, EnteringContext, ObjectType, TemplateName)) { TPA.Commit(); break; } - + if (TemplateName.getKind() != UnqualifiedId::IK_OperatorFunctionId && TemplateName.getKind() != UnqualifiedId::IK_LiteralOperatorId) { Diag(TemplateName.getSourceRange().getBegin(), @@ -2131,9 +2135,10 @@ bool Parser::ParseUnqualifiedIdOperator(CXXScopeSpec &SS, bool EnteringContext, } Result.setLiteralOperatorId(II, KeywordLoc, SuffixLoc); - return false; + + return Actions.checkLiteralOperatorId(SS, Result); } - + // Parse a conversion-function-id. // // conversion-function-id: [C++ 12.3.2] diff --git a/lib/Sema/SemaExprCXX.cpp b/lib/Sema/SemaExprCXX.cpp index 6ad042991c..6273ce19f5 100644 --- a/lib/Sema/SemaExprCXX.cpp +++ b/lib/Sema/SemaExprCXX.cpp @@ -333,6 +333,34 @@ ParsedType Sema::getDestructorType(const DeclSpec& DS, ParsedType ObjectType) { return ParsedType(); } +bool Sema::checkLiteralOperatorId(const CXXScopeSpec &SS, + const UnqualifiedId &Name) { + assert(Name.getKind() == UnqualifiedId::IK_LiteralOperatorId); + + if (!SS.isValid()) + return false; + + switch (SS.getScopeRep()->getKind()) { + case NestedNameSpecifier::Identifier: + case NestedNameSpecifier::TypeSpec: + case NestedNameSpecifier::TypeSpecWithTemplate: + // Per C++11 [over.literal]p2, literal operators can only be declared at + // namespace scope. Therefore, this unqualified-id cannot name anything. + // Reject it early, because we have no AST representation for this in the + // case where the scope is dependent. + Diag(Name.getLocStart(), diag::err_literal_operator_id_outside_namespace) + << SS.getScopeRep(); + return true; + + case NestedNameSpecifier::Global: + case NestedNameSpecifier::Namespace: + case NestedNameSpecifier::NamespaceAlias: + return false; + } + + llvm_unreachable("unknown nested name specifier kind"); +} + /// \brief Build a C++ typeid expression with a type operand. ExprResult Sema::BuildCXXTypeId(QualType TypeInfoType, SourceLocation TypeidLoc, diff --git a/lib/Sema/SemaTemplate.cpp b/lib/Sema/SemaTemplate.cpp index 3e55ec90ad..b5b2f0dbed 100644 --- a/lib/Sema/SemaTemplate.cpp +++ b/lib/Sema/SemaTemplate.cpp @@ -2914,8 +2914,7 @@ TemplateNameKind Sema::ActOnDependentTemplateName(Scope *S, return TNK_Function_template; case UnqualifiedId::IK_LiteralOperatorId: - llvm_unreachable( - "We don't support these; Parse shouldn't have allowed propagation"); + llvm_unreachable("literal operator id cannot have a dependent scope"); default: break; diff --git a/test/SemaCXX/cxx11-user-defined-literals.cpp b/test/SemaCXX/cxx11-user-defined-literals.cpp index f8bbcd960f..cb7796418e 100644 --- a/test/SemaCXX/cxx11-user-defined-literals.cpp +++ b/test/SemaCXX/cxx11-user-defined-literals.cpp @@ -141,3 +141,27 @@ namespace PR14950 { int operator"" _b(); // expected-error {{no function template matches function template specialization}} int main() { return 0_b; } // expected-error {{no matching literal operator for call to 'operator "" _b'}} } + +namespace bad_names { + template int operator""_x(); + + template void f() { + class T:: // expected-error {{anonymous class}} expected-warning {{does not declare anything}} + operator // expected-error {{expected identifier}} + ""_q<'a'>; + + T::template operator""_q<'a'>(); // expected-error {{non-namespace scope 'T::' cannot have a literal operator member}} expected-error +{{}} + T::template operator""_q<'a'>::X; // expected-error {{non-namespace scope 'T::' cannot have a literal operator member}} expected-error +{{}} + T::operator""_q<'a'>(); // expected-error {{non-namespace scope 'T::' cannot have a literal operator member}} expected-error +{{}} + typename T::template operator""_q<'a'> a; // expected-error {{non-namespace scope 'T::' cannot have a literal operator member}} expected-error +{{}} + typename T::operator""_q(""); // expected-error +{{}} expected-note {{to match}} + T::operator""_q(""); // expected-error {{non-namespace scope 'T::' cannot have a literal operator member}} + + bad_names::operator""_x<'a', 'b', 'c'>(); + }; + + struct S {}; + void g() { + S::operator""_q(); // expected-error {{non-namespace scope 'S::' cannot have a literal operator member}} + } +} -- 2.40.0