diff options
author | Douglas Gregor <dgregor@apple.com> | 2008-11-19 21:05:33 +0000 |
---|---|---|
committer | Douglas Gregor <dgregor@apple.com> | 2008-11-19 21:05:33 +0000 |
commit | f9eb905197e44ed5634205512074993f6f40470d (patch) | |
tree | 1c6723e7af4df7df7fbd67fe5157365968ae1733 | |
parent | 487a75ab300552e42afa45b8199133f838a40e5f (diff) |
Support for calling overloaded function call operators (operator())
with function call syntax, e.g.,
Functor f;
f(x, y);
This is the easy part of handling calls to objects of class type
(C++ [over.call.object]). The hard part (coping with conversions from
f to function pointer or reference types) will come later. Nobody uses
that stuff anyway, right? :)
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@59663 91177308-0d34-0410-b5e6-96231b3b80d8
-rw-r--r-- | include/clang/Basic/DiagnosticKinds.def | 6 | ||||
-rw-r--r-- | lib/Sema/Sema.h | 5 | ||||
-rw-r--r-- | lib/Sema/SemaExpr.cpp | 6 | ||||
-rw-r--r-- | lib/Sema/SemaOverload.cpp | 150 | ||||
-rw-r--r-- | test/SemaCXX/overloaded-operator.cpp | 13 | ||||
-rw-r--r-- | www/cxx_status.html | 8 |
6 files changed, 183 insertions, 5 deletions
diff --git a/include/clang/Basic/DiagnosticKinds.def b/include/clang/Basic/DiagnosticKinds.def index c98ad4c58b..4ee5a2f748 100644 --- a/include/clang/Basic/DiagnosticKinds.def +++ b/include/clang/Basic/DiagnosticKinds.def @@ -890,6 +890,12 @@ 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_object_call, ERROR, + "no matching function for call to object of type '%0'") +DIAG(err_ovl_no_viable_object_call_with_cands, ERROR, + "no matching function for call to object of type '%0'; candidates are:") +DIAG(err_ovl_ambiguous_object_call, ERROR, + "call to object of type '%0' is ambiguous; candidates are:") DIAG(err_unexpected_typedef, ERROR, "unexpected type name '%0': expected expression") diff --git a/lib/Sema/Sema.h b/lib/Sema/Sema.h index 85acc3eb8f..805fa118ee 100644 --- a/lib/Sema/Sema.h +++ b/lib/Sema/Sema.h @@ -453,6 +453,11 @@ public: bool Complain); void FixOverloadedFunctionReference(Expr *E, FunctionDecl *Fn); + ExprResult + BuildCallToObjectOfClassType(Expr *Object, SourceLocation LParenLoc, + Expr **Args, unsigned NumArgs, + SourceLocation *CommaLocs, + SourceLocation RParenLoc); /// Helpers for dealing with function parameters bool CheckParmsForFunctionDef(FunctionDecl *FD); diff --git a/lib/Sema/SemaExpr.cpp b/lib/Sema/SemaExpr.cpp index a780012235..147b4c8323 100644 --- a/lib/Sema/SemaExpr.cpp +++ b/lib/Sema/SemaExpr.cpp @@ -1291,8 +1291,8 @@ ActOnCallExpr(ExprTy *fn, SourceLocation LParenLoc, // resolution to pick the function. if (Ovl) { OverloadCandidateSet CandidateSet; - OverloadCandidateSet::iterator Best; AddOverloadCandidates(Ovl, Args, NumArgs, CandidateSet); + OverloadCandidateSet::iterator Best; switch (BestViableFunction(CandidateSet, Best)) { case OR_Success: { @@ -1327,6 +1327,10 @@ ActOnCallExpr(ExprTy *fn, SourceLocation LParenLoc, } } + if (getLangOptions().CPlusPlus && Fn->getType()->isRecordType()) + return BuildCallToObjectOfClassType(Fn, LParenLoc, Args, NumArgs, + CommaLocs, RParenLoc); + // Promote the function operand. UsualUnaryConversions(Fn); diff --git a/lib/Sema/SemaOverload.cpp b/lib/Sema/SemaOverload.cpp index 869fcc9352..cadda5b440 100644 --- a/lib/Sema/SemaOverload.cpp +++ b/lib/Sema/SemaOverload.cpp @@ -17,6 +17,7 @@ #include "clang/Lex/Preprocessor.h" #include "clang/AST/ASTContext.h" #include "clang/AST/Expr.h" +#include "clang/AST/ExprCXX.h" #include "clang/AST/TypeOrdering.h" #include "llvm/ADT/SmallPtrSet.h" #include "llvm/Support/Compiler.h" @@ -2853,6 +2854,155 @@ Sema::ResolveAddressOfOverloadedFunction(Expr *From, QualType ToType, return 0; } +/// BuildCallToObjectOfClassType - Build a call to an object of class +/// type (C++ [over.call.object]), which can end up invoking an +/// overloaded function call operator (@c operator()) or performing a +/// user-defined conversion on the object argument. +Action::ExprResult +Sema::BuildCallToObjectOfClassType(Expr *Object, SourceLocation LParenLoc, + Expr **Args, unsigned NumArgs, + SourceLocation *CommaLocs, + SourceLocation RParenLoc) { + assert(Object->getType()->isRecordType() && "Requires object type argument"); + const RecordType *Record = Object->getType()->getAsRecordType(); + + // C++ [over.call.object]p1: + // If the primary-expression E in the function call syntax + // evaluates to a class object of type “cv T”, then the set of + // candidate functions includes at least the function call + // operators of T. The function call operators of T are obtained by + // ordinary lookup of the name operator() in the context of + // (E).operator(). + OverloadCandidateSet CandidateSet; + IdentifierResolver::iterator I + = IdResolver.begin(Context.DeclarationNames.getCXXOperatorName(OO_Call), + cast<CXXRecordType>(Record)->getDecl(), + /*LookInParentCtx=*/false); + NamedDecl *MemberOps = (I == IdResolver.end())? 0 : *I; + if (CXXMethodDecl *Method = dyn_cast_or_null<CXXMethodDecl>(MemberOps)) + AddMethodCandidate(Method, Object, Args, NumArgs, CandidateSet, + /*SuppressUserConversions=*/false); + else if (OverloadedFunctionDecl *Ovl + = dyn_cast_or_null<OverloadedFunctionDecl>(MemberOps)) { + for (OverloadedFunctionDecl::function_iterator F = Ovl->function_begin(), + FEnd = Ovl->function_end(); + F != FEnd; ++F) { + if (CXXMethodDecl *Method = dyn_cast<CXXMethodDecl>(*F)) + AddMethodCandidate(Method, Object, Args, NumArgs, CandidateSet, + /*SuppressUserConversions=*/false); + } + } + + CXXMethodDecl *Method = 0; + + // Perform overload resolution. + OverloadCandidateSet::iterator Best; + switch (BestViableFunction(CandidateSet, Best)) { + case OR_Success: + // We found a method. We'll build a call to it below. + Method = cast<CXXMethodDecl>(Best->Function); + break; + + case OR_No_Viable_Function: + if (CandidateSet.empty()) + Diag(Object->getSourceRange().getBegin(), + diag::err_ovl_no_viable_object_call) + << Object->getType().getAsString() << Object->getSourceRange(); + else { + Diag(Object->getSourceRange().getBegin(), + diag::err_ovl_no_viable_object_call_with_cands) + << Object->getType().getAsString() << Object->getSourceRange(); + PrintOverloadCandidates(CandidateSet, /*OnlyViable=*/false); + } + break; + + case OR_Ambiguous: + Diag(Object->getSourceRange().getBegin(), + diag::err_ovl_ambiguous_object_call) + << Object->getType().getAsString() << Object->getSourceRange(); + PrintOverloadCandidates(CandidateSet, /*OnlyViable=*/true); + break; + } + + if (!Method) { + // We had an error; delete all of the subexpressions and return + // the error. + delete Object; + for (unsigned ArgIdx = 0; ArgIdx < NumArgs; ++ArgIdx) + delete Args[ArgIdx]; + return true; + } + + // Build a CXXOperatorCallExpr that calls this method, using Object for + // the implicit object parameter and passing along the remaining + // arguments. + const FunctionTypeProto *Proto = Method->getType()->getAsFunctionTypeProto(); + + unsigned NumArgsInProto = Proto->getNumArgs(); + unsigned NumArgsToCheck = NumArgs; + + // Build the full argument list for the method call (the + // implicit object parameter is placed at the beginning of the + // list). + Expr **MethodArgs; + if (NumArgs < NumArgsInProto) { + NumArgsToCheck = NumArgsInProto; + MethodArgs = new Expr*[NumArgsInProto + 1]; + } else { + MethodArgs = new Expr*[NumArgs + 1]; + } + MethodArgs[0] = Object; + for (unsigned ArgIdx = 0; ArgIdx < NumArgs; ++ArgIdx) + MethodArgs[ArgIdx + 1] = Args[ArgIdx]; + + Expr *NewFn = new DeclRefExpr(Method, Method->getType(), + SourceLocation()); + UsualUnaryConversions(NewFn); + + // Once we've built TheCall, all of the expressions are properly + // owned. + QualType ResultTy = Method->getResultType().getNonReferenceType(); + llvm::OwningPtr<CXXOperatorCallExpr> + TheCall(new CXXOperatorCallExpr(NewFn, MethodArgs, NumArgs + 1, + ResultTy, RParenLoc)); + delete [] MethodArgs; + + // Initialize the implicit object parameter. + if (!PerformObjectArgumentInitialization(Object, Method)) + return true; + TheCall->setArg(0, Object); + + // Check the argument types. + for (unsigned i = 0; i != NumArgsToCheck; i++) { + QualType ProtoArgType = Proto->getArgType(i); + + Expr *Arg; + if (i < NumArgs) + Arg = Args[i]; + else + Arg = new CXXDefaultArgExpr(Method->getParamDecl(i)); + QualType ArgType = Arg->getType(); + + // Pass the argument. + if (PerformCopyInitialization(Arg, ProtoArgType, "passing")) + return true; + + TheCall->setArg(i + 1, Arg); + } + + // If this is a variadic call, handle args passed through "...". + if (Proto->isVariadic()) { + // Promote the arguments (C99 6.5.2.2p7). + for (unsigned i = NumArgsInProto; i != NumArgs; i++) { + Expr *Arg = Args[i]; + DefaultArgumentPromotion(Arg); + TheCall->setArg(i + 1, Arg); + } + } + + return CheckFunctionCall(Method, TheCall.take()); +} + /// 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 1eb86bd1aa..751c4afd4b 100644 --- a/test/SemaCXX/overloaded-operator.cpp +++ b/test/SemaCXX/overloaded-operator.cpp @@ -121,3 +121,16 @@ void test_comma(X x, Y y) { bool& b1 = (x, y); X& xr = (x, x); } + + +struct Callable { + int& operator()(int, double = 2.71828); // expected-note{{candidate function}} + float& operator()(int, double, long, ...); // expected-note{{candidate function}} +}; + +void test_callable(Callable c) { + int &ir = c(1); + float &fr = c(1, 3.14159, 17, 42); + + c(); // expected-error{{no matching function for call to object of type 'struct Callable'; candidates are:}} +} diff --git a/www/cxx_status.html b/www/cxx_status.html index 6db2840b7a..01f29b6b34 100644 --- a/www/cxx_status.html +++ b/www/cxx_status.html @@ -818,8 +818,8 @@ welcome!</p> <tr>
<td> 13.3.1.1.2 [over.call.object]</td>
<td class="complete" align="center">✓</td>
- <td class="broken" align="center"></td>
- <td class="broken" align="center"></td>
+ <td class="advanced" align="center"></td>
+ <td class="basic" align="center"></td>
<td class="broken" align="center"></td>
<td></td>
</tr>
@@ -973,7 +973,7 @@ welcome!</p> <td> 13.5.4 [over.call]</td>
<td class="na" align="center">N/A</td>
<td class="advanced" align="center"></td>
- <td class="basic" align="center"></td>
+ <td class="advanced" align="center"></td>
<td class="broken" align="center"></td>
<td></td>
</tr>
@@ -989,7 +989,7 @@ welcome!</p> <td> 13.5.6 [over.ref]</td>
<td class="na" align="center">N/A</td>
<td class="advanced" align="center"></td>
- <td class="advanced" align="center"></td>
+ <td class="basic" align="center"></td>
<td class="broken" align="center"></td>
<td></td>
</tr>
|