From a6f0f9d589a06737707fe914e06bd6d4bfae0997 Mon Sep 17 00:00:00 2001 From: Douglas Gregor Date: Mon, 31 Aug 2009 19:52:13 +0000 Subject: [PATCH] Support explicit C++ member operator syntax, from James Porter! git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@80608 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/Parse/Action.h | 28 ++++++++++++++++++++ lib/Parse/ParseExpr.cpp | 31 ++++++++++++++++++++-- lib/Sema/Sema.h | 15 +++++++++++ lib/Sema/SemaExprCXX.cpp | 37 +++++++++++++++++++++++++++ test/SemaCXX/invalid-member-expr.cpp | 21 +++++++++++++++ test/SemaCXX/member-operator-expr.cpp | 29 +++++++++++++++++++++ 6 files changed, 159 insertions(+), 2 deletions(-) create mode 100644 test/SemaCXX/invalid-member-expr.cpp create mode 100644 test/SemaCXX/member-operator-expr.cpp diff --git a/include/clang/Parse/Action.h b/include/clang/Parse/Action.h index 108d43d9e6..f3364352aa 100644 --- a/include/clang/Parse/Action.h +++ b/include/clang/Parse/Action.h @@ -1292,6 +1292,34 @@ public: const CXXScopeSpec *SS = 0) { return ExprEmpty(); } + + /// ActOnOverloadedOperatorReferenceExpr - Parsed an overloaded operator + /// reference, for example: + /// + /// t.operator++(); + virtual OwningExprResult + ActOnOverloadedOperatorReferenceExpr(Scope *S, ExprArg Base, + SourceLocation OpLoc, + tok::TokenKind OpKind, + SourceLocation ClassNameLoc, + OverloadedOperatorKind OverOpKind, + const CXXScopeSpec *SS = 0) { + return ExprEmpty(); + } + + /// ActOnConversionOperatorReferenceExpr - Parsed an overloaded conversion + /// function reference, for example: + /// + /// t.operator int(); + virtual OwningExprResult + ActOnConversionOperatorReferenceExpr(Scope *S, ExprArg Base, + SourceLocation OpLoc, + tok::TokenKind OpKind, + SourceLocation ClassNameLoc, + TypeTy *Ty, + const CXXScopeSpec *SS = 0) { + return ExprEmpty(); + } /// ActOnFinishFullExpr - Called whenever a full expression has been parsed. /// (C++ [intro.execution]p12). diff --git a/lib/Parse/ParseExpr.cpp b/lib/Parse/ParseExpr.cpp index 80e701e17a..d00d6d2e33 100644 --- a/lib/Parse/ParseExpr.cpp +++ b/lib/Parse/ParseExpr.cpp @@ -939,6 +939,7 @@ Parser::ParsePostfixExpressionSuffix(OwningExprResult LHS) { OpKind, Tok.getLocation(), *Tok.getIdentifierInfo(), ObjCImpDecl, &SS); + ConsumeToken(); } else if (getLang().CPlusPlus && Tok.is(tok::tilde)) { // We have a C++ pseudo-destructor. @@ -946,6 +947,8 @@ Parser::ParsePostfixExpressionSuffix(OwningExprResult LHS) { ConsumeToken(); if (!Tok.is(tok::identifier)) { + if (getLang().CPlusPlus) + Actions.ActOnCXXExitMemberScope(CurScope, MemberSS); Diag(Tok, diag::err_expected_ident); return ExprError(); } @@ -956,15 +959,39 @@ Parser::ParsePostfixExpressionSuffix(OwningExprResult LHS) { Tok.getLocation(), Tok.getIdentifierInfo(), &SS); + ConsumeToken(); + } else if (getLang().CPlusPlus && Tok.is(tok::kw_operator)) { + if (OverloadedOperatorKind Op = TryParseOperatorFunctionId()) { + if (!LHS.isInvalid()) + LHS = Actions.ActOnOverloadedOperatorReferenceExpr(CurScope, + move(LHS), OpLoc, + OpKind, + Tok.getLocation(), + Op, &SS); + // TryParseOperatorFunctionId already consumed our token, so + // don't bother + } else if (TypeTy *ConvType = ParseConversionFunctionId()) { + if (!LHS.isInvalid()) + LHS = Actions.ActOnConversionOperatorReferenceExpr(CurScope, + move(LHS), OpLoc, + OpKind, + Tok.getLocation(), + ConvType, &SS); + } else { + if (getLang().CPlusPlus) + Actions.ActOnCXXExitMemberScope(CurScope, MemberSS); + // Don't emit a diagnostic; ParseConversionFunctionId does it for us + return ExprError(); + } } else { + if (getLang().CPlusPlus) + Actions.ActOnCXXExitMemberScope(CurScope, MemberSS); Diag(Tok, diag::err_expected_ident); return ExprError(); } if (getLang().CPlusPlus) Actions.ActOnCXXExitMemberScope(CurScope, MemberSS); - - ConsumeToken(); break; } case tok::plusplus: // postfix-expression: postfix-expression '++' diff --git a/lib/Sema/Sema.h b/lib/Sema/Sema.h index 0e45fe7f15..f9c893f0a0 100644 --- a/lib/Sema/Sema.h +++ b/lib/Sema/Sema.h @@ -1956,6 +1956,21 @@ public: SourceLocation ClassNameLoc, IdentifierInfo *ClassName, const CXXScopeSpec *SS = 0); + + virtual OwningExprResult + ActOnOverloadedOperatorReferenceExpr(Scope *S, ExprArg Base, + SourceLocation OpLoc, + tok::TokenKind OpKind, + SourceLocation ClassNameLoc, + OverloadedOperatorKind OverOpKind, + const CXXScopeSpec *SS = 0); + virtual OwningExprResult + ActOnConversionOperatorReferenceExpr(Scope *S, ExprArg Base, + SourceLocation OpLoc, + tok::TokenKind OpKind, + SourceLocation ClassNameLoc, + TypeTy *Ty, + const CXXScopeSpec *SS = 0); /// MaybeCreateCXXExprWithTemporaries - If the list of temporaries is /// non-empty, will create a new CXXExprWithTemporaries expression. diff --git a/lib/Sema/SemaExprCXX.cpp b/lib/Sema/SemaExprCXX.cpp index 16e83e6514..35938260c6 100644 --- a/lib/Sema/SemaExprCXX.cpp +++ b/lib/Sema/SemaExprCXX.cpp @@ -1794,6 +1794,43 @@ Sema::ActOnDestructorReferenceExpr(Scope *S, ExprArg Base, DtorName, DeclPtrTy(), SS); } +Sema::OwningExprResult +Sema::ActOnOverloadedOperatorReferenceExpr(Scope *S, ExprArg Base, + SourceLocation OpLoc, + tok::TokenKind OpKind, + SourceLocation ClassNameLoc, + OverloadedOperatorKind OverOpKind, + const CXXScopeSpec *SS) { + if (SS && SS->isInvalid()) + return ExprError(); + + DeclarationName Name = + Context.DeclarationNames.getCXXOperatorName(OverOpKind); + + return BuildMemberReferenceExpr(S, move(Base), OpLoc, OpKind, ClassNameLoc, + Name, DeclPtrTy(), SS); +} + +Sema::OwningExprResult +Sema::ActOnConversionOperatorReferenceExpr(Scope *S, ExprArg Base, + SourceLocation OpLoc, + tok::TokenKind OpKind, + SourceLocation ClassNameLoc, + TypeTy *Ty, + const CXXScopeSpec *SS) { + if (SS && SS->isInvalid()) + return ExprError(); + + //FIXME: Preserve type source info. + QualType ConvType = GetTypeFromParser(Ty); + CanQualType ConvTypeCanon = Context.getCanonicalType(ConvType); + DeclarationName ConvName = + Context.DeclarationNames.getCXXConversionFunctionName(ConvTypeCanon); + + return BuildMemberReferenceExpr(S, move(Base), OpLoc, OpKind, ClassNameLoc, + ConvName, DeclPtrTy(), SS); +} + Sema::OwningExprResult Sema::ActOnFinishFullExpr(ExprArg Arg) { Expr *FullExpr = Arg.takeAs(); if (FullExpr) diff --git a/test/SemaCXX/invalid-member-expr.cpp b/test/SemaCXX/invalid-member-expr.cpp new file mode 100644 index 0000000000..90932ed65e --- /dev/null +++ b/test/SemaCXX/invalid-member-expr.cpp @@ -0,0 +1,21 @@ +// RUN: clang-cc -fsyntax-only -verify %s + +class X {}; + +void test() { + X x; + + x.int; // expected-error{{expected identifier}} + x.~int(); // expected-error{{expected identifier}} + x.operator; // expected-error{{missing type specifier after 'operator'}} + x.operator typedef; // expected-error{{missing type specifier after 'operator'}} +} + +void test2() { + X *x; + + x->int; // expected-error{{expected identifier}} + x->~int(); // expected-error{{expected identifier}} + x->operator; // expected-error{{missing type specifier after 'operator'}} + x->operator typedef; // expected-error{{missing type specifier after 'operator'}} +} diff --git a/test/SemaCXX/member-operator-expr.cpp b/test/SemaCXX/member-operator-expr.cpp new file mode 100644 index 0000000000..4d0e00fd60 --- /dev/null +++ b/test/SemaCXX/member-operator-expr.cpp @@ -0,0 +1,29 @@ +// RUN: clang-cc -fsyntax-only -verify %s + +class X { +public: + int operator++(); + operator int(); +}; + +void test() { + X x; + int i; + + i = x.operator++(); + i = x.operator int(); + x.operator--(); // expected-error{{no member named 'operator--'}} + x.operator float(); // expected-error{{no member named 'operator float'}} + x.operator; // expected-error{{missing type specifier after 'operator'}} +} + +void test2() { + X *x; + int i; + + i = x->operator++(); + i = x->operator int(); + x->operator--(); // expected-error{{no member named 'operator--'}} + x->operator float(); // expected-error{{no member named 'operator float'}} + x->operator; // expected-error{{missing type specifier after 'operator'}} +} -- 2.40.0