From: Douglas Gregor Date: Thu, 20 Nov 2008 16:27:02 +0000 (+0000) Subject: Add support for overloaded operator-> when used in a member access X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=8ba10745f525467e91bbaec21044bf4d9017a988;p=clang Add support for overloaded operator-> when used in a member access expression (smart_ptr->mem). git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@59732 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/clang/Basic/DiagnosticKinds.def b/include/clang/Basic/DiagnosticKinds.def index 0f7282799f..e8dd9499e2 100644 --- a/include/clang/Basic/DiagnosticKinds.def +++ b/include/clang/Basic/DiagnosticKinds.def @@ -892,6 +892,8 @@ DIAG(err_ovl_ambiguous_init, ERROR, "call to constructor of '%0' is ambiguous; candidates are:") DIAG(err_ovl_ambiguous_oper, ERROR, "use of overloaded operator '%0' is ambiguous; candidates are:") +DIAG(err_ovl_no_viable_oper, ERROR, + "no viable overloaded '%0'; candidates are:") DIAG(err_ovl_no_viable_object_call, ERROR, "no matching function for call to object of type '%0'") DIAG(err_ovl_no_viable_object_call_with_cands, ERROR, diff --git a/lib/Sema/Sema.h b/lib/Sema/Sema.h index 3e5a511d33..ba647cae89 100644 --- a/lib/Sema/Sema.h +++ b/lib/Sema/Sema.h @@ -444,6 +444,10 @@ public: SourceLocation *CommaLocs, SourceLocation RParenLoc); + ExprResult BuildOverloadedArrowExpr(Expr *Base, SourceLocation OpLoc, + SourceLocation MemberLoc, + IdentifierInfo &Member); + /// Helpers for dealing with function parameters bool CheckParmsForFunctionDef(FunctionDecl *FD); void CheckCXXDefaultArguments(FunctionDecl *FD); diff --git a/lib/Sema/SemaExpr.cpp b/lib/Sema/SemaExpr.cpp index 50025b284e..cb06b0fe66 100644 --- a/lib/Sema/SemaExpr.cpp +++ b/lib/Sema/SemaExpr.cpp @@ -1122,6 +1122,8 @@ ActOnMemberReferenceExpr(ExprTy *Base, SourceLocation OpLoc, if (OpKind == tok::arrow) { if (const PointerType *PT = BaseType->getAsPointerType()) BaseType = PT->getPointeeType(); + else if (getLangOptions().CPlusPlus && BaseType->isRecordType()) + return BuildOverloadedArrowExpr(BaseExpr, OpLoc, MemberLoc, Member); else return Diag(MemberLoc, diag::err_typecheck_member_reference_arrow) << BaseType.getAsString() << BaseExpr->getSourceRange(); diff --git a/lib/Sema/SemaOverload.cpp b/lib/Sema/SemaOverload.cpp index 36f2def2f1..677696aa55 100644 --- a/lib/Sema/SemaOverload.cpp +++ b/lib/Sema/SemaOverload.cpp @@ -3148,6 +3148,86 @@ Sema::BuildCallToObjectOfClassType(Expr *Object, SourceLocation LParenLoc, return CheckFunctionCall(Method, TheCall.take()); } +/// BuildOverloadedArrowExpr - Build a call to an overloaded @c operator-> +/// (if one exists), where @c Base is an expression of class type and +/// @c Member is the name of the member we're trying to find. +Action::ExprResult +Sema::BuildOverloadedArrowExpr(Expr *Base, SourceLocation OpLoc, + SourceLocation MemberLoc, + IdentifierInfo &Member) { + assert(Base->getType()->isRecordType() && "left-hand side must have class type"); + + // C++ [over.ref]p1: + // + // [...] An expression x->m is interpreted as (x.operator->())->m + // for a class object x of type T if T::operator->() exists and if + // the operator is selected as the best match function by the + // overload resolution mechanism (13.3). + // FIXME: look in base classes. + DeclarationName OpName = Context.DeclarationNames.getCXXOperatorName(OO_Arrow); + OverloadCandidateSet CandidateSet; + const RecordType *BaseRecord = Base->getType()->getAsRecordType(); + IdentifierResolver::iterator I + = IdResolver.begin(OpName, cast(BaseRecord)->getDecl(), + /*LookInParentCtx=*/false); + NamedDecl *MemberOps = (I == IdResolver.end())? 0 : *I; + if (CXXMethodDecl *Method = dyn_cast_or_null(MemberOps)) + AddMethodCandidate(Method, Base, 0, 0, CandidateSet, + /*SuppressUserConversions=*/false); + else if (OverloadedFunctionDecl *Ovl + = dyn_cast_or_null(MemberOps)) { + for (OverloadedFunctionDecl::function_iterator F = Ovl->function_begin(), + FEnd = Ovl->function_end(); + F != FEnd; ++F) { + if (CXXMethodDecl *Method = dyn_cast(*F)) + AddMethodCandidate(Method, Base, 0, 0, CandidateSet, + /*SuppressUserConversions=*/false); + } + } + + // Perform overload resolution. + OverloadCandidateSet::iterator Best; + switch (BestViableFunction(CandidateSet, Best)) { + case OR_Success: + // Overload resolution succeeded; we'll build the call below. + break; + + case OR_No_Viable_Function: + if (CandidateSet.empty()) + Diag(OpLoc, diag::err_typecheck_member_reference_arrow) + << Base->getType().getAsString() << Base->getSourceRange(); + else + Diag(OpLoc, diag::err_ovl_no_viable_oper) + << "operator->" << Base->getSourceRange(); + PrintOverloadCandidates(CandidateSet, /*OnlyViable=*/false); + delete Base; + return true; + + case OR_Ambiguous: + Diag(OpLoc, diag::err_ovl_ambiguous_oper) + << "operator->" + << Base->getSourceRange(); + PrintOverloadCandidates(CandidateSet, /*OnlyViable=*/true); + delete Base; + return true; + } + + // Convert the object parameter. + CXXMethodDecl *Method = cast(Best->Function); + if (PerformObjectArgumentInitialization(Base, Method)) { + delete Base; + return true; + } + + // Build the operator call. + Expr *FnExpr = new DeclRefExpr(Method, Method->getType(), SourceLocation()); + UsualUnaryConversions(FnExpr); + Base = new CXXOperatorCallExpr(FnExpr, &Base, 1, + Method->getResultType().getNonReferenceType(), + OpLoc); + return ActOnMemberReferenceExpr(Base, OpLoc, tok::arrow, MemberLoc, Member); +} + /// FixOverloadedFunctionReference - E is an expression that refers to /// a C++ overloaded function (possibly with some parentheses and /// perhaps a '&' around it). We have resolved the overloaded function diff --git a/test/SemaCXX/overloaded-operator.cpp b/test/SemaCXX/overloaded-operator.cpp index cf8af68b57..f03e285dbd 100644 --- a/test/SemaCXX/overloaded-operator.cpp +++ b/test/SemaCXX/overloaded-operator.cpp @@ -150,3 +150,21 @@ void test_funcptr_call(ConvertToFunc ctf) { ctf((long int)17, 2.0); // expected-error{{error: call to object of type 'struct ConvertToFunc' is ambiguous; candidates are:}} ctf(); } + +struct HasMember { + int m; +}; + +struct Arrow1 { + HasMember* operator->(); +}; + +struct Arrow2 { + Arrow1 operator->(); // expected-note{{candidate function}} +}; + +void test_arrow(Arrow1 a1, Arrow2 a2, const Arrow2 a3) { + int &i1 = a1->m; + int &i2 = a2->m; + a3->m; // expected-error{{no viable overloaded 'operator->'; candidates are}} +}