From: Douglas Gregor Date: Wed, 11 Feb 2009 01:18:59 +0000 (+0000) Subject: Add semantic checking for template arguments that correspond to X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=b86b0579c5805c8ecaedd2d676e06bf8c2bf7f79;p=clang Add semantic checking for template arguments that correspond to non-type template parameters that are references to functions or pointers to member functions. Did a little bit of refactoring so that these two cases, along with the handling of non-type template parameters that are pointers to functions, are handled by the same path. Also, tweaked FixOverloadedFunctionReference to cope with member function pointers. This is a necessary step for getting all of the fun member pointer conversions working outside of template arguments, too. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@64277 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/lib/Sema/SemaOverload.cpp b/lib/Sema/SemaOverload.cpp index 64c61e17ad..e40455b5c6 100644 --- a/lib/Sema/SemaOverload.cpp +++ b/lib/Sema/SemaOverload.cpp @@ -3952,6 +3952,25 @@ void Sema::FixOverloadedFunctionReference(Expr *E, FunctionDecl *Fn) { } else if (UnaryOperator *UnOp = dyn_cast(E)) { assert(UnOp->getOpcode() == UnaryOperator::AddrOf && "Can only take the address of an overloaded function"); + if (CXXMethodDecl *Method = dyn_cast(Fn)) { + if (Method->isStatic()) { + // Do nothing: static member functions aren't any different + // from non-member functions. + } + else if (QualifiedDeclRefExpr *DRE + = dyn_cast(UnOp->getSubExpr())) { + // We have taken the address of a pointer to member + // function. Perform the computation here so that we get the + // appropriate pointer to member type. + DRE->setDecl(Fn); + DRE->setType(Fn->getType()); + QualType ClassType + = Context.getTypeDeclType(cast(Method->getDeclContext())); + E->setType(Context.getMemberPointerType(Fn->getType(), + ClassType.getTypePtr())); + return; + } + } FixOverloadedFunctionReference(UnOp->getSubExpr(), Fn); E->setType(Context.getPointerType(UnOp->getSubExpr()->getType())); } else if (DeclRefExpr *DR = dyn_cast(E)) { diff --git a/lib/Sema/SemaTemplate.cpp b/lib/Sema/SemaTemplate.cpp index d4ba0c5edb..eb7db25d6e 100644 --- a/lib/Sema/SemaTemplate.cpp +++ b/lib/Sema/SemaTemplate.cpp @@ -896,58 +896,45 @@ bool Sema::CheckTemplateArgument(NonTypeTemplateParmDecl *Param, return false; } - if (const PointerType *ParamPtrType = ParamType->getAsPointerType()) { - if (ParamPtrType->getPointeeType()->isObjectType()) { - // -- for a non-type template-parameter of type pointer to - // object, qualification conversions (4.4) and the - // array-to-pointer conversion (4.2) are applied. - if (ArgType->isArrayType()) { - ArgType = Context.getArrayDecayedType(ArgType); - ImpCastExprToType(Arg, ArgType); - } - - if (IsQualificationConversion(ArgType, ParamType)) { - ArgType = ParamType; - ImpCastExprToType(Arg, ParamType); - } - - if (!hasSameUnqualifiedType(ArgType, ParamType)) { - // We can't perform this conversion. - Diag(Arg->getSourceRange().getBegin(), - diag::err_template_arg_not_convertible) - << Arg->getType() << Param->getType() << Arg->getSourceRange(); - Diag(Param->getLocation(), diag::note_template_param_here); - return true; - } - - // FIXME: Check the restrictions in p1! - return false; - } - - assert(ParamPtrType->getPointeeType()->isFunctionType() && - "Only function pointers remain"); - // -- For a non-type template-parameter of type pointer to - // function, only the function-to-pointer conversion (4.3) is - // applied. If the template-argument represents a set of - // overloaded functions (or a pointer to such), the matching - // function is selected from the set (13.4). - if (hasSameUnqualifiedType(ArgType, ParamType)) { + // Handle pointer-to-function, reference-to-function, and + // pointer-to-member-function all in (roughly) the same way. + if (// -- For a non-type template-parameter of type pointer to + // function, only the function-to-pointer conversion (4.3) is + // applied. If the template-argument represents a set of + // overloaded functions (or a pointer to such), the matching + // function is selected from the set (13.4). + (ParamType->isPointerType() && + ParamType->getAsPointerType()->getPointeeType()->isFunctionType()) || + // -- For a non-type template-parameter of type reference to + // function, no conversions apply. If the template-argument + // represents a set of overloaded functions, the matching + // function is selected from the set (13.4). + (ParamType->isReferenceType() && + ParamType->getAsReferenceType()->getPointeeType()->isFunctionType()) || + // -- For a non-type template-parameter of type pointer to + // member function, no conversions apply. If the + // template-argument represents a set of overloaded member + // functions, the matching member function is selected from + // the set (13.4). + (ParamType->isMemberPointerType() && + ParamType->getAsMemberPointerType()->getPointeeType() + ->isFunctionType())) { + if (hasSameUnqualifiedType(ArgType, ParamType.getNonReferenceType())) { // We don't have to do anything: the types already match. - } - else if (ArgType->isFunctionType()) { + } else if (ArgType->isFunctionType() && ParamType->isPointerType()) { ArgType = Context.getPointerType(ArgType); ImpCastExprToType(Arg, ArgType); } else if (FunctionDecl *Fn = ResolveAddressOfOverloadedFunction(Arg, ParamType, true)) { FixOverloadedFunctionReference(Arg, Fn); ArgType = Arg->getType(); - if (ArgType->isFunctionType()) { + if (ArgType->isFunctionType() && ParamType->isPointerType()) { ArgType = Context.getPointerType(Arg->getType()); ImpCastExprToType(Arg, ArgType); } } - if (!hasSameUnqualifiedType(ArgType, ParamType)) { + if (!hasSameUnqualifiedType(ArgType, ParamType.getNonReferenceType())) { // We can't perform this conversion. Diag(Arg->getSourceRange().getBegin(), diag::err_template_arg_not_convertible) @@ -960,43 +947,73 @@ bool Sema::CheckTemplateArgument(NonTypeTemplateParmDecl *Param, return false; } + if (const PointerType *ParamPtrType = ParamType->getAsPointerType()) { + // -- for a non-type template-parameter of type pointer to + // object, qualification conversions (4.4) and the + // array-to-pointer conversion (4.2) are applied. + assert(ParamPtrType->getPointeeType()->isObjectType() && + "Only object pointers allowed here"); + + if (ArgType->isArrayType()) { + ArgType = Context.getArrayDecayedType(ArgType); + ImpCastExprToType(Arg, ArgType); + } + + if (IsQualificationConversion(ArgType, ParamType)) { + ArgType = ParamType; + ImpCastExprToType(Arg, ParamType); + } + + if (!hasSameUnqualifiedType(ArgType, ParamType)) { + // We can't perform this conversion. + Diag(Arg->getSourceRange().getBegin(), + diag::err_template_arg_not_convertible) + << Arg->getType() << Param->getType() << Arg->getSourceRange(); + Diag(Param->getLocation(), diag::note_template_param_here); + return true; + } + + // FIXME: Check the restrictions in p1! + return false; + } + if (const ReferenceType *ParamRefType = ParamType->getAsReferenceType()) { - if (ParamRefType->getPointeeType()->isObjectType()) { - // -- For a non-type template-parameter of type reference to - // object, no conversions apply. The type referred to by the - // reference may be more cv-qualified than the (otherwise - // identical) type of the template-argument. The - // template-parameter is bound directly to the - // template-argument, which must be an lvalue. - if (!hasSameUnqualifiedType(ParamRefType->getPointeeType(), ArgType)) { - Diag(Arg->getSourceRange().getBegin(), - diag::err_template_arg_no_ref_bind) - << Param->getType() << Arg->getType() - << Arg->getSourceRange(); - Diag(Param->getLocation(), diag::note_template_param_here); - return true; - } - - unsigned ParamQuals - = Context.getCanonicalType(ParamType).getCVRQualifiers(); - unsigned ArgQuals = Context.getCanonicalType(ArgType).getCVRQualifiers(); - - if ((ParamQuals | ArgQuals) != ParamQuals) { - Diag(Arg->getSourceRange().getBegin(), - diag::err_template_arg_ref_bind_ignores_quals) - << Param->getType() << Arg->getType() - << Arg->getSourceRange(); - Diag(Param->getLocation(), diag::note_template_param_here); - return true; - } + // -- For a non-type template-parameter of type reference to + // object, no conversions apply. The type referred to by the + // reference may be more cv-qualified than the (otherwise + // identical) type of the template-argument. The + // template-parameter is bound directly to the + // template-argument, which must be an lvalue. + assert(ParamRefType->getPointeeType()->isObjectType() && + "Only object references allowed here"); + + if (!hasSameUnqualifiedType(ParamRefType->getPointeeType(), ArgType)) { + Diag(Arg->getSourceRange().getBegin(), + diag::err_template_arg_no_ref_bind) + << Param->getType() << Arg->getType() + << Arg->getSourceRange(); + Diag(Param->getLocation(), diag::note_template_param_here); + return true; + } - // FIXME: Check the restrictions in p1! - // CheckAddressConstantExpression(Lvalue) can be modified to do - // this. - return false; + unsigned ParamQuals + = Context.getCanonicalType(ParamType).getCVRQualifiers(); + unsigned ArgQuals = Context.getCanonicalType(ArgType).getCVRQualifiers(); + + if ((ParamQuals | ArgQuals) != ParamQuals) { + Diag(Arg->getSourceRange().getBegin(), + diag::err_template_arg_ref_bind_ignores_quals) + << Param->getType() << Arg->getType() + << Arg->getSourceRange(); + Diag(Param->getLocation(), diag::note_template_param_here); + return true; } + + // FIXME: Check the restrictions in p1! + // CheckAddressConstantExpression(Lvalue) can be modified to do + // this. + return false; } - // FIXME: p5 has a lot more checks to perform! return false; diff --git a/test/SemaTemplate/temp_arg_nontype.cpp b/test/SemaTemplate/temp_arg_nontype.cpp index 26ebb551e0..f069accb63 100644 --- a/test/SemaTemplate/temp_arg_nontype.cpp +++ b/test/SemaTemplate/temp_arg_nontype.cpp @@ -79,3 +79,26 @@ A4<*X_volatile_ptr> *a15_2; // expected-error{{reference binding of non-type tem A4 *15_3; // expected-error{{non-type template parameter of reference type 'class X const &' cannot bind to template argument of type 'struct Y'}}\ // FIXME: expected-error{{expected unqualified-id}} +template struct A5; // expected-note 2{{template parameter is declared here}} +A5 *a16_1; +A5<(h)> *a16_2; +A5 *a16_3; +A5<(f)> *a16_4; +A5

*a16_6; // expected-error{{non-type template argument of type 'float (float)' cannot be converted to a value of type 'int (&)(int)'}} \ +// FIXME: expected-error{{expected unqualified-id}} +A5 *a14_7; // expected-error{{non-type template argument of type '' cannot be converted to a value of type 'int (&)(int)'}}\ +// FIXME: expected-error{{expected unqualified-id}} +// FIXME: the first error includes the string , which makes Doug slightly unhappy. + +struct Z { + int foo(int); + float bar(float); + int bar(int); + double baz(double); +}; +template struct A6; // expected-note{{template parameter is declared here}} +A6<&Z::foo> *a17_1; +A6<&Z::bar> *a17_2; +A6<&Z::baz> *a17_3; // expected-error{{non-type template argument of type 'double (struct Z::*)(double)' cannot be converted to a value of type 'int (struct Z::*)(int)'}} \ +// FIXME: expected-error{{expected unqualified-id}}