From: Douglas Gregor Date: Mon, 10 Nov 2008 20:40:00 +0000 (+0000) Subject: Basic support for taking the address of an overloaded function X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=904eed3f6148758d39a2d3c88f3133274460d645;p=clang Basic support for taking the address of an overloaded function git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@59000 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/clang/AST/Expr.h b/include/clang/AST/Expr.h index 2c25fcd123..6023c6803d 100644 --- a/include/clang/AST/Expr.h +++ b/include/clang/AST/Expr.h @@ -198,10 +198,11 @@ public: NamedDecl *getDecl() { return D; } const NamedDecl *getDecl() const { return D; } + void setDecl(NamedDecl *NewD) { D = NewD; } + SourceLocation getLocation() const { return Loc; } virtual SourceRange getSourceRange() const { return SourceRange(Loc); } - static bool classof(const Stmt *T) { return T->getStmtClass() == DeclRefExprClass || T->getStmtClass() == CXXConditionDeclExprClass; diff --git a/include/clang/AST/Type.h b/include/clang/AST/Type.h index 7915dd056f..176d67a00e 100644 --- a/include/clang/AST/Type.h +++ b/include/clang/AST/Type.h @@ -356,6 +356,7 @@ public: bool isObjCInterfaceType() const; // NSString or NSString bool isObjCQualifiedInterfaceType() const; // NSString bool isObjCQualifiedIdType() const; // id + bool isOverloadType() const; // C++ overloaded function // Type Checking Functions: Check to see if this type is structurally the // specified type, ignoring typedefs and qualifiers, and return a pointer to @@ -1478,6 +1479,13 @@ inline bool Type::isObjCQualifiedInterfaceType() const { inline bool Type::isObjCQualifiedIdType() const { return isa(CanonicalType.getUnqualifiedType()); } +inline bool Type::isOverloadType() const { + if (const BuiltinType *BT = getAsBuiltinType()) + return BT->getKind() == BuiltinType::Overload; + else + return false; +} + } // end namespace clang #endif diff --git a/lib/Sema/Sema.h b/lib/Sema/Sema.h index d7518e0aa7..3b187f2099 100644 --- a/lib/Sema/Sema.h +++ b/lib/Sema/Sema.h @@ -438,6 +438,10 @@ public: void PrintOverloadCandidates(OverloadCandidateSet& CandidateSet, bool OnlyViable); + FunctionDecl *ResolveAddressOfOverloadedFunction(Expr *From, QualType ToType, + bool Complain); + void FixOverloadedFunctionReference(Expr *E, FunctionDecl *Fn); + /// Helpers for dealing with function parameters bool CheckParmsForFunctionDef(FunctionDecl *FD); diff --git a/lib/Sema/SemaDeclCXX.cpp b/lib/Sema/SemaDeclCXX.cpp index 3495880d22..2f43e5fa9e 100644 --- a/lib/Sema/SemaDeclCXX.cpp +++ b/lib/Sema/SemaDeclCXX.cpp @@ -1529,6 +1529,22 @@ Sema::CheckReferenceInit(Expr *&Init, QualType &DeclType, QualType T1 = DeclType->getAsReferenceType()->getPointeeType(); QualType T2 = Init->getType(); + // If the initializer is the address of an overloaded function, try + // to resolve the overloaded function. If all goes well, T2 is the + // type of the resulting function. + if (T2->isOverloadType()) { + FunctionDecl *Fn = ResolveAddressOfOverloadedFunction(Init, DeclType, + ICS != 0); + if (Fn) { + // Since we're performing this reference-initialization for + // real, update the initializer with the resulting function. + if (!ICS) + FixOverloadedFunctionReference(Init, Fn); + + T2 = Fn->getType(); + } + } + // Compute some basic properties of the types and the initializer. bool DerivedToBase = false; Expr::isLvalueResult InitLvalue = Init->isLvalue(Context); diff --git a/lib/Sema/SemaExpr.cpp b/lib/Sema/SemaExpr.cpp index 3bb7c09ce8..a45d6717b7 100644 --- a/lib/Sema/SemaExpr.cpp +++ b/lib/Sema/SemaExpr.cpp @@ -2511,6 +2511,8 @@ static NamedDecl *getPrimaryDecl(Expr *E) { /// object cannot be declared with storage class register or be a bit field. /// Note: The usual conversions are *not* applied to the operand of the & /// operator (C99 6.3.2.1p[2-4]), and its result is never an lvalue. +/// In C++, the operand might be an overloaded function name, in which case +/// we allow the '&' but retain the overloaded-function type. QualType Sema::CheckAddressOfOperand(Expr *op, SourceLocation OpLoc) { if (getLangOptions().C99) { // Implement C99-only parts of addressof rules. @@ -2554,7 +2556,9 @@ QualType Sema::CheckAddressOfOperand(Expr *op, SourceLocation OpLoc) { std::string("register variable"), op->getSourceRange()); return QualType(); } - } else + } else if (isa(dcl)) + return Context.OverloadTy; + else assert(0 && "Unknown/unexpected decl type"); } diff --git a/lib/Sema/SemaExprCXX.cpp b/lib/Sema/SemaExprCXX.cpp index 41ee8ddcae..a6a62a9e37 100644 --- a/lib/Sema/SemaExprCXX.cpp +++ b/lib/Sema/SemaExprCXX.cpp @@ -280,7 +280,16 @@ Sema::PerformImplicitConversion(Expr *&From, QualType ToType, break; case ICK_Array_To_Pointer: - FromType = Context.getArrayDecayedType(FromType); + if (FromType->isOverloadType()) { + FunctionDecl *Fn = ResolveAddressOfOverloadedFunction(From, ToType, true); + if (!Fn) + return true; + + FixOverloadedFunctionReference(From, Fn); + FromType = From->getType(); + } else { + FromType = Context.getArrayDecayedType(FromType); + } ImpCastExprToType(From, FromType); break; diff --git a/lib/Sema/SemaOverload.cpp b/lib/Sema/SemaOverload.cpp index 176701e57d..b60e55492b 100644 --- a/lib/Sema/SemaOverload.cpp +++ b/lib/Sema/SemaOverload.cpp @@ -426,7 +426,8 @@ Sema::IsStandardConversion(Expr* From, QualType ToType, // converted to an rvalue. Expr::isLvalueResult argIsLvalue = From->isLvalue(Context); if (argIsLvalue == Expr::LV_Valid && - !FromType->isFunctionType() && !FromType->isArrayType()) { + !FromType->isFunctionType() && !FromType->isArrayType() && + !FromType->isOverloadType()) { SCS.First = ICK_Lvalue_To_Rvalue; // If T is a non-class type, the type of the rvalue is the @@ -465,9 +466,20 @@ Sema::IsStandardConversion(Expr* From, QualType ToType, // type "pointer to T." The result is a pointer to the // function. (C++ 4.3p1). FromType = Context.getPointerType(FromType); - - // FIXME: Deal with overloaded functions here (C++ 4.3p2). } + // Address of overloaded function (C++ [over.over]). + else if (FunctionDecl *Fn + = ResolveAddressOfOverloadedFunction(From, ToType, false)) { + SCS.First = ICK_Function_To_Pointer; + + // We were able to resolve the address of the overloaded function, + // so we can convert to the type of that function. + FromType = Fn->getType(); + if (ToType->isReferenceType()) + FromType = Context.getReferenceType(FromType); + else + FromType = Context.getPointerType(FromType); + } // We don't require any conversions for the first step. else { SCS.First = ICK_Identity; @@ -1681,4 +1693,101 @@ Sema::PrintOverloadCandidates(OverloadCandidateSet& CandidateSet, } } +/// ResolveAddressOfOverloadedFunction - Try to resolve the address of +/// an overloaded function (C++ [over.over]), where @p From is an +/// expression with overloaded function type and @p ToType is the type +/// we're trying to resolve to. For example: +/// +/// @code +/// int f(double); +/// int f(int); +/// +/// int (*pfd)(double) = f; // selects f(double) +/// @endcode +/// +/// This routine returns the resulting FunctionDecl if it could be +/// resolved, and NULL otherwise. When @p Complain is true, this +/// routine will emit diagnostics if there is an error. +FunctionDecl * +Sema::ResolveAddressOfOverloadedFunction(Expr *From, QualType ToType, + bool Complain) { + QualType FunctionType = ToType; + if (const PointerLikeType *ToTypePtr = ToType->getAsPointerLikeType()) + FunctionType = ToTypePtr->getPointeeType(); + + // We only look at pointers or references to functions. + if (!FunctionType->isFunctionType()) + return 0; + + // Find the actual overloaded function declaration. + OverloadedFunctionDecl *Ovl = 0; + + // C++ [over.over]p1: + // [...] [Note: any redundant set of parentheses surrounding the + // overloaded function name is ignored (5.1). ] + Expr *OvlExpr = From->IgnoreParens(); + + // C++ [over.over]p1: + // [...] The overloaded function name can be preceded by the & + // operator. + if (UnaryOperator *UnOp = dyn_cast(OvlExpr)) { + if (UnOp->getOpcode() == UnaryOperator::AddrOf) + OvlExpr = UnOp->getSubExpr()->IgnoreParens(); + } + + // Try to dig out the overloaded function. + if (DeclRefExpr *DR = dyn_cast(OvlExpr)) + Ovl = dyn_cast(DR->getDecl()); + + // If there's no overloaded function declaration, we're done. + if (!Ovl) + return 0; + + // Look through all of the overloaded functions, searching for one + // whose type matches exactly. + // FIXME: When templates or using declarations come along, we'll actually + // have to deal with duplicates, partial ordering, etc. For now, we + // can just do a simple search. + FunctionType = Context.getCanonicalType(FunctionType.getUnqualifiedType()); + for (OverloadedFunctionDecl::function_iterator Fun = Ovl->function_begin(); + Fun != Ovl->function_end(); ++Fun) { + // C++ [over.over]p3: + // Non-member functions and static member functions match + // targets of type “pointer-to-function”or + // “reference-to-function.” + if (CXXMethodDecl *Method = dyn_cast(*Fun)) + if (!Method->isStatic()) + continue; + + if (FunctionType == Context.getCanonicalType((*Fun)->getType())) + return *Fun; + } + + return 0; +} + +/// 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 +/// to the function declaration Fn, so patch up the expression E to +/// refer (possibly indirectly) to Fn. +void Sema::FixOverloadedFunctionReference(Expr *E, FunctionDecl *Fn) { + if (ParenExpr *PE = dyn_cast(E)) { + FixOverloadedFunctionReference(PE->getSubExpr(), Fn); + E->setType(PE->getSubExpr()->getType()); + } else if (UnaryOperator *UnOp = dyn_cast(E)) { + assert(UnOp->getOpcode() == UnaryOperator::AddrOf && + "Can only take the address of an overloaded function"); + FixOverloadedFunctionReference(UnOp->getSubExpr(), Fn); + E->setType(Context.getPointerType(E->getType())); + } else if (DeclRefExpr *DR = dyn_cast(E)) { + assert(isa(DR->getDecl()) && + "Expected overloaded function"); + DR->setDecl(Fn); + E->setType(Fn->getType()); + } else { + assert(false && "Invalid reference to overloaded function"); + } +} + } // end namespace clang diff --git a/test/SemaCXX/addr-of-overloaded-function.cpp b/test/SemaCXX/addr-of-overloaded-function.cpp new file mode 100644 index 0000000000..2184116dd2 --- /dev/null +++ b/test/SemaCXX/addr-of-overloaded-function.cpp @@ -0,0 +1,29 @@ +// RUN: clang -fsyntax-only -verify %s +int f(double); +int f(int); + +int (*pfd)(double) = f; // selects f(double) +int (*pfd2)(double) = &f; // selects f(double) +int (*pfd3)(double) = ((&((f)))); // selects f(double) +int (*pfi)(int) = &f; // selects f(int) +// FIXME: This error message is not very good. We need to keep better +// track of what went wrong when the implicit conversion failed to +// give a better error message here. +int (*pfe)(...) = &f; // expected-error{{incompatible type initializing '', expected 'int (*)(...)'}} +int (&rfi)(int) = f; // selects f(int) +int (&rfd)(double) = f; // selects f(double) + +void g(int (*fp)(int)); // expected-note{{note: candidate function}} +void g(int (*fp)(float)); +void g(int (*fp)(double)); // expected-note{{note: candidate function}} + +int g1(int); +int g1(char); + +int g2(int); +int g2(double); + +void g_test() { + g(g1); + g(g2); // expected-error{{call to 'g' is ambiguous; candidates are:}} +} diff --git a/www/cxx_status.html b/www/cxx_status.html index e98ce87a0b..586cb2652b 100644 --- a/www/cxx_status.html +++ b/www/cxx_status.html @@ -927,7 +927,16 @@ welcome!

-  13.4 [over.over] + +   13.4 [over.over] + + + + + Error messages need some work. Without templates or using + declarations, we don't have any ambiguities, so the semantic + analysis is incomplete. +   13.5 [over.oper] @@ -940,7 +949,7 @@ welcome!

    13.5.1 [over.unary] - + @@ -948,7 +957,7 @@ welcome!

    13.5.2 [over.binary] - + @@ -956,7 +965,7 @@ welcome!

    13.5.3 [over.ass] - + @@ -964,7 +973,7 @@ welcome!

    13.5.4 [over.call] - + @@ -972,7 +981,7 @@ welcome!

    13.5.5 [over.sub] - + @@ -980,7 +989,7 @@ welcome!

    13.5.6 [over.ref] - + @@ -988,7 +997,7 @@ welcome!

    13.5.7 [over.inc] - +