forked from OSchip/llvm-project
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:
parent
ae612d22ac
commit
eca8f5a49c
|
@ -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(),
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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'}} \
|
||||
|
|
Loading…
Reference in New Issue