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.

llvm-svn: 95309
This commit is contained in:
Douglas Gregor 2010-02-04 17:21:48 +00:00
parent ae612d22ac
commit eca8f5a49c
3 changed files with 79 additions and 20 deletions

View File

@ -718,7 +718,8 @@ TemplateInstantiator::TransformDeclRefExpr(DeclRefExpr *E) {
if (!VD)
return SemaRef.ExprError();
if (VD->getDeclContext()->isRecord()) {
if (VD->getDeclContext()->isRecord() &&
(isa<CXXMethodDecl>(VD) || isa<FieldDecl>(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(),

View File

@ -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<const char *s>
struct A {
Str get() { return s; }
};
char hello[6] = "Hello";
extern const char world[6];
const char world[6] = "world";
void test() {
(void)A<hello>().get();
(void)A<world>().get();
}
class X {
public:
X();
X(int, int);
operator int() const;
};
template<X const *Ptr> struct A2;
X *X_ptr;
X an_X;
X array_of_Xs[10];
A2<X_ptr> *a12;
A2<array_of_Xs> *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

View File

@ -34,16 +34,6 @@ public:
};
A<X(17, 42)> *a11; // expected-error{{non-type template argument of type 'class X' must have an integral or enumeration type}}
template<X const *Ptr> struct A2;
X *X_ptr;
X an_X;
X array_of_Xs[10];
A2<X_ptr> *a12;
A2<array_of_Xs> *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<X const &AnX> struct A4; // expected-note 2{{template parameter is declared here}}
X an_X;
A4<an_X> *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<y> *15_3; // expected-error{{non-type template parameter of reference type 'class X const &' cannot bind to template argument of type 'struct Y'}} \