aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDouglas Gregor <dgregor@apple.com>2008-11-19 22:57:39 +0000
committerDouglas Gregor <dgregor@apple.com>2008-11-19 22:57:39 +0000
commit106c6eb3f1380bfe243a62d8f0f8bce6a7c8d5c6 (patch)
tree1958b9b22212d642b3ba84732a56ef3333db2594
parent9f1384f7db76e24068f6c9d7d881714addb6c029 (diff)
Implement the rest of C++ [over.call.object], which permits the object
being called to be converted to a reference-to-function, pointer-to-function, or reference-to-pointer-to-function. This is done through "surrogate" candidate functions that model the conversions from the object to the function (reference/pointer) and the conversions in the arguments. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@59674 91177308-0d34-0410-b5e6-96231b3b80d8
-rw-r--r--include/clang/Basic/DiagnosticKinds.def2
-rw-r--r--lib/Sema/Sema.h4
-rw-r--r--lib/Sema/SemaOverload.cpp161
-rw-r--r--lib/Sema/SemaOverload.h15
-rw-r--r--test/SemaCXX/overloaded-operator.cpp14
-rw-r--r--www/cxx_status.html4
6 files changed, 188 insertions, 12 deletions
diff --git a/include/clang/Basic/DiagnosticKinds.def b/include/clang/Basic/DiagnosticKinds.def
index 4ee5a2f748..2b15f5d1f5 100644
--- a/include/clang/Basic/DiagnosticKinds.def
+++ b/include/clang/Basic/DiagnosticKinds.def
@@ -896,6 +896,8 @@ 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_ovl_surrogate_cand, NOTE,
+ "conversion candidate of type '%0'")
DIAG(err_unexpected_typedef, ERROR,
"unexpected type name '%0': expected expression")
diff --git a/lib/Sema/Sema.h b/lib/Sema/Sema.h
index 805fa118ee..ded90e8103 100644
--- a/lib/Sema/Sema.h
+++ b/lib/Sema/Sema.h
@@ -429,6 +429,10 @@ public:
void AddConversionCandidate(CXXConversionDecl *Conversion,
Expr *From, QualType ToType,
OverloadCandidateSet& CandidateSet);
+ void AddSurrogateCandidate(CXXConversionDecl *Conversion,
+ const FunctionTypeProto *Proto,
+ Expr *Object, Expr **Args, unsigned NumArgs,
+ OverloadCandidateSet& CandidateSet);
void AddOperatorCandidates(OverloadedOperatorKind Op, Scope *S,
Expr **Args, unsigned NumArgs,
OverloadCandidateSet& CandidateSet);
diff --git a/lib/Sema/SemaOverload.cpp b/lib/Sema/SemaOverload.cpp
index cadda5b440..77546fd246 100644
--- a/lib/Sema/SemaOverload.cpp
+++ b/lib/Sema/SemaOverload.cpp
@@ -1514,6 +1514,7 @@ Sema::AddOverloadCandidate(FunctionDecl *Function,
CandidateSet.push_back(OverloadCandidate());
OverloadCandidate& Candidate = CandidateSet.back();
Candidate.Function = Function;
+ Candidate.IsSurrogate = false;
unsigned NumArgsInProto = Proto->getNumArgs();
@@ -1589,6 +1590,7 @@ Sema::AddMethodCandidate(CXXMethodDecl *Method, Expr *Object,
CandidateSet.push_back(OverloadCandidate());
OverloadCandidate& Candidate = CandidateSet.back();
Candidate.Function = Method;
+ Candidate.IsSurrogate = false;
unsigned NumArgsInProto = Proto->getNumArgs();
@@ -1665,6 +1667,7 @@ Sema::AddConversionCandidate(CXXConversionDecl *Conversion,
CandidateSet.push_back(OverloadCandidate());
OverloadCandidate& Candidate = CandidateSet.back();
Candidate.Function = Conversion;
+ Candidate.IsSurrogate = false;
Candidate.FinalConversion.setAsIdentityConversion();
Candidate.FinalConversion.FromTypePtr
= Conversion->getConversionType().getAsOpaquePtr();
@@ -1713,6 +1716,89 @@ Sema::AddConversionCandidate(CXXConversionDecl *Conversion,
}
}
+/// AddSurrogateCandidate - Adds a "surrogate" candidate function that
+/// converts the given @c Object to a function pointer via the
+/// conversion function @c Conversion, and then attempts to call it
+/// with the given arguments (C++ [over.call.object]p2-4). Proto is
+/// the type of function that we'll eventually be calling.
+void Sema::AddSurrogateCandidate(CXXConversionDecl *Conversion,
+ const FunctionTypeProto *Proto,
+ Expr *Object, Expr **Args, unsigned NumArgs,
+ OverloadCandidateSet& CandidateSet) {
+ CandidateSet.push_back(OverloadCandidate());
+ OverloadCandidate& Candidate = CandidateSet.back();
+ Candidate.Function = 0;
+ Candidate.Surrogate = Conversion;
+ Candidate.Viable = true;
+ Candidate.IsSurrogate = true;
+ Candidate.Conversions.resize(NumArgs + 1);
+
+ // Determine the implicit conversion sequence for the implicit
+ // object parameter.
+ ImplicitConversionSequence ObjectInit
+ = TryObjectArgumentInitialization(Object, Conversion);
+ if (ObjectInit.ConversionKind == ImplicitConversionSequence::BadConversion) {
+ Candidate.Viable = false;
+ return;
+ }
+
+ // The first conversion is actually a user-defined conversion whose
+ // first conversion is ObjectInit's standard conversion (which is
+ // effectively a reference binding). Record it as such.
+ Candidate.Conversions[0].ConversionKind
+ = ImplicitConversionSequence::UserDefinedConversion;
+ Candidate.Conversions[0].UserDefined.Before = ObjectInit.Standard;
+ Candidate.Conversions[0].UserDefined.ConversionFunction = Conversion;
+ Candidate.Conversions[0].UserDefined.After
+ = Candidate.Conversions[0].UserDefined.Before;
+ Candidate.Conversions[0].UserDefined.After.setAsIdentityConversion();
+
+ // Find the
+ unsigned NumArgsInProto = Proto->getNumArgs();
+
+ // (C++ 13.3.2p2): A candidate function having fewer than m
+ // parameters is viable only if it has an ellipsis in its parameter
+ // list (8.3.5).
+ if (NumArgs > NumArgsInProto && !Proto->isVariadic()) {
+ Candidate.Viable = false;
+ return;
+ }
+
+ // Function types don't have any default arguments, so just check if
+ // we have enough arguments.
+ if (NumArgs < NumArgsInProto) {
+ // Not enough arguments.
+ Candidate.Viable = false;
+ return;
+ }
+
+ // Determine the implicit conversion sequences for each of the
+ // arguments.
+ for (unsigned ArgIdx = 0; ArgIdx < NumArgs; ++ArgIdx) {
+ if (ArgIdx < NumArgsInProto) {
+ // (C++ 13.3.2p3): for F to be a viable function, there shall
+ // exist for each argument an implicit conversion sequence
+ // (13.3.3.1) that converts that argument to the corresponding
+ // parameter of F.
+ QualType ParamType = Proto->getArgType(ArgIdx);
+ Candidate.Conversions[ArgIdx + 1]
+ = TryCopyInitialization(Args[ArgIdx], ParamType,
+ /*SuppressUserConversions=*/false);
+ if (Candidate.Conversions[ArgIdx + 1].ConversionKind
+ == ImplicitConversionSequence::BadConversion) {
+ Candidate.Viable = false;
+ break;
+ }
+ } else {
+ // (C++ 13.3.2p2): For the purposes of overload resolution, any
+ // argument for which there is no corresponding parameter is
+ // considered to ""match the ellipsis" (C+ 13.3.3.1.3).
+ Candidate.Conversions[ArgIdx + 1].ConversionKind
+ = ImplicitConversionSequence::EllipsisConversion;
+ }
+ }
+}
+
/// IsAcceptableNonMemberOperatorCandidate - Determine whether Fn is
/// an acceptable non-member overloaded operator for a call whose
/// arguments have types T1 (and, if non-empty, T2). This routine
@@ -2742,8 +2828,10 @@ Sema::BestViableFunction(OverloadCandidateSet& CandidateSet,
Cand != CandidateSet.end(); ++Cand) {
if (Cand->Viable &&
Cand != Best &&
- !isBetterOverloadCandidate(*Best, *Cand))
+ !isBetterOverloadCandidate(*Best, *Cand)) {
+ Best = CandidateSet.end();
return OR_Ambiguous;
+ }
}
// Best is the best viable function.
@@ -2764,6 +2852,10 @@ Sema::PrintOverloadCandidates(OverloadCandidateSet& CandidateSet,
if (Cand->Function) {
// Normal function
Diag(Cand->Function->getLocation(), diag::err_ovl_candidate);
+ } else if (Cand->IsSurrogate) {
+ Diag(Cand->Surrogate->getLocation(), diag::err_ovl_surrogate_cand)
+ << Context.getCanonicalType(Cand->Surrogate->getConversionType())
+ .getAsString();
} else {
// FIXME: We need to get the identifier in here
// FIXME: Do we want the error message to point at the
@@ -2893,14 +2985,48 @@ Sema::BuildCallToObjectOfClassType(Expr *Object, SourceLocation LParenLoc,
}
}
- CXXMethodDecl *Method = 0;
+ // C++ [over.call.object]p2:
+ // In addition, for each conversion function declared in T of the
+ // form
+ //
+ // operator conversion-type-id () cv-qualifier;
+ //
+ // where cv-qualifier is the same cv-qualification as, or a
+ // greater cv-qualification than, cv, and where conversion-type-id
+ // denotes the type “pointer to function of (P1,...,Pn) returning
+ // R”, or the type “reference to pointer to function of
+ // (P1,...,Pn) returning R”, or the type “reference to function
+ // of (P1,...,Pn) returning R”, a surrogate call function [...]
+ // is also considered as a candidate function. Similarly,
+ // surrogate call functions are added to the set of candidate
+ // functions for each conversion function declared in an
+ // accessible base class provided the function is not hidden
+ // within T by another intervening declaration.
+ //
+ // FIXME: Look in base classes for more conversion operators!
+ OverloadedFunctionDecl *Conversions
+ = cast<CXXRecordDecl>(Record->getDecl())->getConversionFunctions();
+ for (OverloadedFunctionDecl::function_iterator Func
+ = Conversions->function_begin();
+ Func != Conversions->function_end(); ++Func) {
+ CXXConversionDecl *Conv = cast<CXXConversionDecl>(*Func);
+
+ // Strip the reference type (if any) and then the pointer type (if
+ // any) to get down to what might be a function type.
+ QualType ConvType = Conv->getConversionType().getNonReferenceType();
+ if (const PointerType *ConvPtrType = ConvType->getAsPointerType())
+ ConvType = ConvPtrType->getPointeeType();
+
+ if (const FunctionTypeProto *Proto = ConvType->getAsFunctionTypeProto())
+ AddSurrogateCandidate(Conv, Proto, Object, Args, NumArgs, CandidateSet);
+ }
// 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);
+ // Overload resolution succeeded; we'll build the appropriate call
+ // below.
break;
case OR_No_Viable_Function:
@@ -2924,7 +3050,7 @@ Sema::BuildCallToObjectOfClassType(Expr *Object, SourceLocation LParenLoc,
break;
}
- if (!Method) {
+ if (Best == CandidateSet.end()) {
// We had an error; delete all of the subexpressions and return
// the error.
delete Object;
@@ -2933,9 +3059,28 @@ Sema::BuildCallToObjectOfClassType(Expr *Object, SourceLocation LParenLoc,
return true;
}
- // Build a CXXOperatorCallExpr that calls this method, using Object for
- // the implicit object parameter and passing along the remaining
- // arguments.
+ if (Best->Function == 0) {
+ // Since there is no function declaration, this is one of the
+ // surrogate candidates. Dig out the conversion function.
+ CXXConversionDecl *Conv
+ = cast<CXXConversionDecl>(
+ Best->Conversions[0].UserDefined.ConversionFunction);
+
+ // We selected one of the surrogate functions that converts the
+ // object parameter to a function pointer. Perform the conversion
+ // on the object argument, then let ActOnCallExpr finish the job.
+ // FIXME: Represent the user-defined conversion in the AST!
+ ImpCastExprToType(Object,
+ Conv->getConversionType().getNonReferenceType(),
+ Conv->getConversionType()->isReferenceType());
+ return ActOnCallExpr((ExprTy*)Object, LParenLoc, (ExprTy**)Args, NumArgs,
+ CommaLocs, RParenLoc);
+ }
+
+ // We found an overloaded operator(). Build a CXXOperatorCallExpr
+ // that calls this method, using Object for the implicit object
+ // parameter and passing along the remaining arguments.
+ CXXMethodDecl *Method = cast<CXXMethodDecl>(Best->Function);
const FunctionTypeProto *Proto = Method->getType()->getAsFunctionTypeProto();
unsigned NumArgsInProto = Proto->getNumArgs();
diff --git a/lib/Sema/SemaOverload.h b/lib/Sema/SemaOverload.h
index 24b796b211..13be00ad7f 100644
--- a/lib/Sema/SemaOverload.h
+++ b/lib/Sema/SemaOverload.h
@@ -199,15 +199,21 @@ namespace clang {
/// OverloadCandidate - A single candidate in an overload set (C++ 13.3).
struct OverloadCandidate {
/// Function - The actual function that this candidate
- /// represents. When NULL, this is a built-in candidate.
+ /// represents. When NULL, this is a built-in candidate
+ /// (C++ [over.oper]) or a surrogate for a conversion to a
+ /// function pointer or reference (C++ [over.call.object]).
FunctionDecl *Function;
-
+
// BuiltinTypes - Provides the return and parameter types of a
// built-in overload candidate. Only valid when Function is NULL.
struct {
QualType ResultTy;
QualType ParamTypes[3];
} BuiltinTypes;
+
+ /// Surrogate - The conversion function for which this candidate
+ /// is a surrogate, but only if IsSurrogate is true.
+ CXXConversionDecl *Surrogate;
/// Conversions - The conversion sequences used to convert the
/// function arguments to the function parameters.
@@ -216,6 +222,11 @@ namespace clang {
/// Viable - True to indicate that this overload candidate is viable.
bool Viable;
+ /// IsSurrogate - True to indicate that this candidate is a
+ /// surrogate for a conversion to a function pointer or reference
+ /// (C++ [over.call.object]).
+ bool IsSurrogate;
+
/// FinalConversion - For a conversion function (where Function is
/// a CXXConversionDecl), the standard conversion that occurs
/// after the call to the overload candidate to convert the result
diff --git a/test/SemaCXX/overloaded-operator.cpp b/test/SemaCXX/overloaded-operator.cpp
index 751c4afd4b..cc3f3158d0 100644
--- a/test/SemaCXX/overloaded-operator.cpp
+++ b/test/SemaCXX/overloaded-operator.cpp
@@ -134,3 +134,17 @@ void test_callable(Callable c) {
c(); // expected-error{{no matching function for call to object of type 'struct Callable'; candidates are:}}
}
+
+typedef int& Func1(float, double);
+typedef float& Func2(int, double);
+
+struct ConvertToFunc {
+ operator Func1*(); // expected-note{{conversion candidate of type 'int &(*)(float, double)'}}
+ operator Func2&(); // expected-note{{conversion candidate of type 'float &(&)(int, double)'}}
+};
+
+void test_funcptr_call(ConvertToFunc ctf) {
+ int &i1 = ctf(1.0f, 2.0);
+ float &f2 = ctf((short int)1, 1.0f);
+ ctf((long int)17, 2.0); // expected-error{{error: call to object of type 'struct ConvertToFunc' is ambiguous; candidates are:}}
+}
diff --git a/www/cxx_status.html b/www/cxx_status.html
index 01f29b6b34..59f8c72ff0 100644
--- a/www/cxx_status.html
+++ b/www/cxx_status.html
@@ -818,10 +818,10 @@ welcome!</p>
<tr>
<td>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;13.3.1.1.2 [over.call.object]</td>
<td class="complete" align="center">&#x2713;</td>
+ <td class="medium" align="center"></td>
<td class="advanced" align="center"></td>
- <td class="basic" align="center"></td>
<td class="broken" align="center"></td>
- <td></td>
+ <td>Missing AST representation for the implicit conversion to a function reference/pointer</td>
</tr>
<tr>
<td>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;13.3.1.2 [over.match.oper]</td>