From 86a7625062abbd40fffa186f2f5d305cc89615b8 Mon Sep 17 00:00:00 2001 From: Douglas Gregor Date: Thu, 4 Feb 2010 17:21:48 +0000 Subject: [PATCH] When substituting the template argument for a pointer non-type template parameter, perform array/function decay (if needed), take the address of the argument (if needed), perform qualification conversions (if needed), and remove any top-level cv-qualifiers from the resulting expression. Fixes PR6226. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@95309 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/Sema/SemaTemplateInstantiate.cpp | 51 +++++++++++++++---- .../CXX/temp/temp.arg/temp.arg.nontype/p5.cpp | 37 ++++++++++++++ test/SemaTemplate/temp_arg_nontype.cpp | 11 +--- 3 files changed, 79 insertions(+), 20 deletions(-) diff --git a/lib/Sema/SemaTemplateInstantiate.cpp b/lib/Sema/SemaTemplateInstantiate.cpp index 3efb52e91b..b560fad25e 100644 --- a/lib/Sema/SemaTemplateInstantiate.cpp +++ b/lib/Sema/SemaTemplateInstantiate.cpp @@ -718,7 +718,8 @@ TemplateInstantiator::TransformDeclRefExpr(DeclRefExpr *E) { if (!VD) return SemaRef.ExprError(); - if (VD->getDeclContext()->isRecord()) { + if (VD->getDeclContext()->isRecord() && + (isa(VD) || isa(VD))) { // If the value is a class member, we might have a pointer-to-member. // Determine whether the non-type template template parameter is of // pointer-to-member type. If so, we need to build an appropriate @@ -746,21 +747,51 @@ TemplateInstantiator::TransformDeclRefExpr(DeclRefExpr *E) { move(RefExpr)); } } - if (NTTP->getType()->isPointerType() && - !VD->getType()->isPointerType()) { - // If the template argument is expected to be a pointer and value - // isn't inherently of pointer type, then it is specified with '&...' - // to indicate its address should be used. Build an expression to - // take the address of the argument. + if (NTTP->getType()->isPointerType()) { + // If the template argument is expected to be a pointer + // type, we may have to decay array/pointer references, take + // the address of the argument, or perform cv-qualification + // adjustments to get the type of the rvalue right. Do so. OwningExprResult RefExpr = SemaRef.BuildDeclRefExpr(VD, VD->getType().getNonReferenceType(), E->getLocation()); if (RefExpr.isInvalid()) return SemaRef.ExprError(); - return SemaRef.CreateBuiltinUnaryOp(E->getLocation(), - UnaryOperator::AddrOf, - move(RefExpr)); + // Decay functions and arrays. + Expr *RefE = (Expr *)RefExpr.get(); + SemaRef.DefaultFunctionArrayConversion(RefE); + if (RefE != RefExpr.get()) { + RefExpr.release(); + RefExpr = SemaRef.Owned(RefE); + } + + // If the unqualified types are different and a a + // qualification conversion won't fix them types, we need to + // take the address. FIXME: Should we encode these steps in + // the template argument, then replay them here, like a + // miniature InitializationSequence? + if (!SemaRef.Context.hasSameUnqualifiedType(RefE->getType(), + NTTP->getType()) && + !SemaRef.IsQualificationConversion(RefE->getType(), + NTTP->getType())) { + RefExpr = SemaRef.CreateBuiltinUnaryOp(E->getLocation(), + UnaryOperator::AddrOf, + move(RefExpr)); + if (RefExpr.isInvalid()) + return SemaRef.ExprError(); + + RefE = (Expr *)RefExpr.get(); + assert(SemaRef.IsQualificationConversion(RefE->getType(), + NTTP->getType())); + } + + // Strip top-level cv-qualifiers off the type. + RefExpr.release(); + SemaRef.ImpCastExprToType(RefE, + NTTP->getType().getUnqualifiedType(), + CastExpr::CK_NoOp); + return SemaRef.Owned(RefE); } return SemaRef.BuildDeclRefExpr(VD, VD->getType().getNonReferenceType(), diff --git a/test/CXX/temp/temp.arg/temp.arg.nontype/p5.cpp b/test/CXX/temp/temp.arg/temp.arg.nontype/p5.cpp index 6d1d39c33c..74c7a855e2 100644 --- a/test/CXX/temp/temp.arg/temp.arg.nontype/p5.cpp +++ b/test/CXX/temp/temp.arg/temp.arg.nontype/p5.cpp @@ -11,6 +11,43 @@ // qualification conversions (4.4) and the array-to-pointer conversion // (4.2) are applied; if the template-argument is of type // std::nullptr_t, the null pointer conversion (4.10) is applied. +namespace pointer_to_object_parameters { + // PR6226 + struct Str { + Str(const char *); + }; + + template + struct A { + Str get() { return s; } + }; + + char hello[6] = "Hello"; + extern const char world[6]; + const char world[6] = "world"; + void test() { + (void)A().get(); + (void)A().get(); + } + + class X { + public: + X(); + X(int, int); + operator int() const; + }; + + template struct A2; + + X *X_ptr; + X an_X; + X array_of_Xs[10]; + A2 *a12; + A2 *a13; + A2<&an_X> *a13_2; + A2<(&an_X)> *a13_3; // expected-error{{non-type template argument cannot be surrounded by parentheses}} +} + // -- 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 diff --git a/test/SemaTemplate/temp_arg_nontype.cpp b/test/SemaTemplate/temp_arg_nontype.cpp index 133b8db949..497bc6daf8 100644 --- a/test/SemaTemplate/temp_arg_nontype.cpp +++ b/test/SemaTemplate/temp_arg_nontype.cpp @@ -34,16 +34,6 @@ public: }; A *a11; // expected-error{{non-type template argument of type 'class X' must have an integral or enumeration type}} -template struct A2; - -X *X_ptr; -X an_X; -X array_of_Xs[10]; -A2 *a12; -A2 *a13; -A2<&an_X> *a13_2; -A2<(&an_X)> *a13_3; // expected-error{{non-type template argument cannot be surrounded by parentheses}} - float f(float); float g(float); @@ -67,6 +57,7 @@ struct Y { } y; volatile X * X_volatile_ptr; template struct A4; // expected-note 2{{template parameter is declared here}} +X an_X; A4 *a15_1; // okay A4<*X_volatile_ptr> *a15_2; // expected-error{{reference binding of non-type template parameter of type 'class X const &' to template argument of type 'class X volatile' ignores qualifiers}} A4 *15_3; // expected-error{{non-type template parameter of reference type 'class X const &' cannot bind to template argument of type 'struct Y'}} \