Implement support for null non-type template arguments for non-type

template parameters of pointer, pointer-to-member, or nullptr_t
type in C++11. Fixes PR9700 / <rdar://problem/11193097>.

llvm-svn: 154219
This commit is contained in:
Douglas Gregor 2012-04-06 22:40:38 +00:00
parent 4460e0f805
commit 31f55dced5
16 changed files with 230 additions and 60 deletions

View File

@ -101,7 +101,6 @@ public:
/// declaration, which is either an external declaration or a
/// template declaration.
TemplateArgument(Decl *D) : Kind(Declaration) {
// FIXME: Need to be sure we have the "canonical" declaration!
TypeOrValue = reinterpret_cast<uintptr_t>(D);
}

View File

@ -2339,6 +2339,8 @@ def err_template_arg_not_integral_or_enumeral : Error<
def err_template_arg_not_ice : Error<
"non-type template argument of type %0 is not an integral constant "
"expression">;
def err_template_arg_untyped_null_constant : Error<
"null non-type template argument must be cast to template parameter type %0">;
def err_deduced_non_type_template_arg_type_mismatch : Error<
"deduced non-type template argument does not have the same type as the "
"its corresponding template parameter (%0 vs %1)">;

View File

@ -3313,8 +3313,11 @@ ASTContext::getCanonicalTemplateArgument(const TemplateArgument &Arg) const {
case TemplateArgument::Expression:
return Arg;
case TemplateArgument::Declaration:
return TemplateArgument(Arg.getAsDecl()->getCanonicalDecl());
case TemplateArgument::Declaration: {
if (Decl *D = Arg.getAsDecl())
return TemplateArgument(D->getCanonicalDecl());
return TemplateArgument((Decl*)0);
}
case TemplateArgument::Template:
return TemplateArgument(getCanonicalTemplateName(Arg.getAsTemplate()));

View File

@ -325,6 +325,8 @@ static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context,
return IsSameValue(*Arg1.getAsIntegral(), *Arg2.getAsIntegral());
case TemplateArgument::Declaration:
if (!Arg1.getAsDecl() || !Arg2.getAsDecl())
return !Arg1.getAsDecl() && !Arg2.getAsDecl();
return Context.IsStructurallyEquivalent(Arg1.getAsDecl(), Arg2.getAsDecl());
case TemplateArgument::Template:

View File

@ -320,7 +320,8 @@ struct XMLDumper : public XMLDeclVisitor<XMLDumper>,
break;
case TemplateArgument::Declaration: {
visitDeclRef(A.getAsDecl());
if (Decl *D = A.getAsDecl())
visitDeclRef(D);
break;
}
case TemplateArgument::Integral: {

View File

@ -1743,8 +1743,10 @@ static bool HandleLValueToRValueConversion(EvalInfo &Info, const Expr *Conv,
// parameters are constant expressions even if they're non-const.
// In C, such things can also be folded, although they are not ICEs.
const VarDecl *VD = dyn_cast<VarDecl>(D);
if (const VarDecl *VDef = VD->getDefinition(Info.Ctx))
VD = VDef;
if (VD) {
if (const VarDecl *VDef = VD->getDefinition(Info.Ctx))
VD = VDef;
}
if (!VD || VD->isInvalidDecl()) {
Info.Diag(Conv);
return false;

View File

@ -3118,12 +3118,22 @@ void CXXNameMangler::mangleTemplateArg(const NamedDecl *P,
case TemplateArgument::Declaration: {
assert(P && "Missing template parameter for declaration argument");
// <expr-primary> ::= L <mangled-name> E # external name
// <expr-primary> ::= L <type> 0 E
// Clang produces AST's where pointer-to-member-function expressions
// and pointer-to-function expressions are represented as a declaration not
// an expression. We compensate for it here to produce the correct mangling.
NamedDecl *D = cast<NamedDecl>(A.getAsDecl());
const NonTypeTemplateParmDecl *Parameter = cast<NonTypeTemplateParmDecl>(P);
// Handle NULL pointer arguments.
if (!A.getAsDecl()) {
Out << "L";
mangleType(Parameter->getType());
Out << "0E";
break;
}
NamedDecl *D = cast<NamedDecl>(A.getAsDecl());
bool compensateMangling = !Parameter->getType()->isReferenceType();
if (compensateMangling) {
Out << 'X';

View File

@ -80,9 +80,13 @@ bool TemplateArgument::isDependent() const {
return true;
case Declaration:
if (DeclContext *DC = dyn_cast<DeclContext>(getAsDecl()))
return DC->isDependentContext();
return getAsDecl()->getDeclContext()->isDependentContext();
if (Decl *D = getAsDecl()) {
if (DeclContext *DC = dyn_cast<DeclContext>(D))
return DC->isDependentContext();
return D->getDeclContext()->isDependentContext();
}
return false;
case Integral:
// Never dependent
@ -118,10 +122,13 @@ bool TemplateArgument::isInstantiationDependent() const {
return true;
case Declaration:
if (DeclContext *DC = dyn_cast<DeclContext>(getAsDecl()))
return DC->isDependentContext();
return getAsDecl()->getDeclContext()->isDependentContext();
if (Decl *D = getAsDecl()) {
if (DeclContext *DC = dyn_cast<DeclContext>(D))
return DC->isDependentContext();
return D->getDeclContext()->isDependentContext();
}
return false;
case Integral:
// Never dependent
return false;
@ -322,16 +329,14 @@ void TemplateArgument::print(const PrintingPolicy &Policy,
}
case Declaration: {
bool Unnamed = true;
if (NamedDecl *ND = dyn_cast_or_null<NamedDecl>(getAsDecl())) {
if (ND->getDeclName()) {
Unnamed = false;
Out << *ND;
} else {
Out << "<anonymous>";
}
}
if (Unnamed) {
Out << "<anonymous>";
} else {
Out << "nullptr";
}
break;
}
@ -488,7 +493,9 @@ const DiagnosticBuilder &clang::operator<<(const DiagnosticBuilder &DB,
return DB << Arg.getAsType();
case TemplateArgument::Declaration:
return DB << Arg.getAsDecl();
if (Decl *D = Arg.getAsDecl())
return DB << D;
return DB << "nullptr";
case TemplateArgument::Integral:
return DB << Arg.getAsIntegral()->toString(10);

View File

@ -264,8 +264,9 @@ void TypePrinter::printRValueReference(const RValueReferenceType *T,
void TypePrinter::printMemberPointer(const MemberPointerType *T,
std::string &S) {
std::string C;
print(QualType(T->getClass(), 0), C);
PrintingPolicy InnerPolicy(Policy);
Policy.SuppressTag = true;
std::string C = QualType(T->getClass(), 0).getAsString(InnerPolicy);
C += "::*";
S = C + S;

View File

@ -10480,7 +10480,8 @@ namespace {
bool MarkReferencedDecls::TraverseTemplateArgument(
const TemplateArgument &Arg) {
if (Arg.getKind() == TemplateArgument::Declaration) {
S.MarkAnyDeclReferenced(Loc, Arg.getAsDecl());
if (Decl *D = Arg.getAsDecl())
S.MarkAnyDeclReferenced(Loc, D);
}
return Inherited::TraverseTemplateArgument(Arg);

View File

@ -3527,20 +3527,21 @@ CheckTemplateArgumentAddressOfObjectOrFunction(Sema &S,
dyn_cast<SubstNonTypeTemplateParmExpr>(Arg))
Arg = subst->getReplacement()->IgnoreImpCasts();
DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(Arg);
if (!DRE) {
S.Diag(Arg->getLocStart(), diag::err_template_arg_not_decl_ref)
<< Arg->getSourceRange();
S.Diag(Param->getLocation(), diag::note_template_param_here);
return true;
}
// Stop checking the precise nature of the argument if it is value dependent,
// it should be checked when instantiated.
if (Arg->isValueDependent()) {
Converted = TemplateArgument(ArgIn);
return false;
}
DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(Arg);
if (!DRE) {
S.Diag(Arg->getLocStart(), diag::err_template_arg_not_decl_ref)
<< Arg->getSourceRange();
S.Diag(Param->getLocation(), diag::note_template_param_here);
return true;
}
if (!isa<ValueDecl>(DRE->getDecl())) {
S.Diag(Arg->getLocStart(),
@ -4048,21 +4049,74 @@ ExprResult Sema::CheckTemplateArgument(NonTypeTemplateParmDecl *Param,
QualType ArgType = Arg->getType();
DeclAccessPair FoundResult; // temporary for ResolveOverloadedFunction
// C++0x [temp.arg.nontype]p5 bullets 2, 4 and 6 permit conversion
// from a template argument of type std::nullptr_t to a non-type
// template parameter of type pointer to object, pointer to
// function, or pointer-to-member, respectively.
if (ArgType->isNullPtrType()) {
if (ParamType->isPointerType() || ParamType->isMemberPointerType()) {
Converted = TemplateArgument((NamedDecl *)0);
return Owned(Arg);
// C++11 [temp.arg.nontype]p1:
// - a constant expression that evaluates to a null pointer value (4.10); or
// - a constant expression that evaluates to a null member pointer value
// (4.11); or
// - an address constant expression of type std::nullptr_t
if (getLangOpts().CPlusPlus0x &&
(ParamType->isPointerType() || ParamType->isMemberPointerType() ||
ParamType->isNullPtrType()) &&
!Arg->isValueDependent() && !Arg->isTypeDependent()) {
if (Expr::NullPointerConstantKind NPC
= Arg->isNullPointerConstant(Context, Expr::NPC_NeverValueDependent)){
if (NPC != Expr::NPCK_CXX0X_nullptr) {
// C++11 [temp.arg.nontype]p5b2:
// if the template-argument is of type std::nullptr_t, the null
// pointer conversion (4.10) is applied. [ Note: In particular,
// neither the null pointer conversion for a zero-valued integral
// constant expression (4.10) nor the derived-to-base conversion
// (4.10) are applied. Although 0 is a valid template-argument for a
// non-type template-parameter of integral type, it is not a valid
// template-argument for a non-type template-parameter of pointer
// type. However, both (int*)0 and nullptr are valid
// template-arguments for a non-type template-parameter of type
// "pointer to int." — end note ]
bool ObjCLifetimeConversion;
if (!Context.hasSameUnqualifiedType(ArgType, ParamType) &&
!IsQualificationConversion(ArgType, ParamType, false,
ObjCLifetimeConversion)) {
{
SemaDiagnosticBuilder DB
= Diag(Arg->getExprLoc(),
diag::err_template_arg_untyped_null_constant);
DB << ParamType;
if (ArgType->isIntegralType(Context)) {
std::string Code = "(" + ParamType.getAsString() + ")";
DB << FixItHint::CreateInsertion(Arg->getLocStart(), Code);
}
}
Diag(Param->getLocation(), diag::note_template_param_here);
}
}
Converted = TemplateArgument((Decl *)0);
return false;
}
// Check for a null (member) pointer value.
Expr::EvalResult EvalResult;
if (Arg->EvaluateAsRValue(EvalResult, Context) &&
((EvalResult.Val.isLValue() && !EvalResult.Val.getLValueBase()) ||
(EvalResult.Val.isMemberPointer() &&
!EvalResult.Val.getMemberPointerDecl()))) {
Converted = TemplateArgument((Decl *)0);
return false;
}
}
// If we haven't dealt with a null pointer-typed parameter yet, do so now.
if (ParamType->isNullPtrType()) {
if (Arg->isTypeDependent() || Arg->isValueDependent()) {
Converted = TemplateArgument(Arg);
return false;
}
if (ParamType->isNullPtrType()) {
llvm::APSInt Zero(Context.getTypeSize(Context.NullPtrTy), true);
Converted = TemplateArgument(Zero, Context.NullPtrTy);
return Owned(Arg);
}
Diag(Arg->getExprLoc(), diag::err_template_arg_not_convertible)
<< Arg->getType() << ParamType;
Diag(Param->getLocation(), diag::note_template_param_here);
return true;
}
// Handle pointer-to-function, reference-to-function, and
@ -4255,6 +4309,18 @@ Sema::BuildExpressionFromDeclTemplateArgument(const TemplateArgument &Arg,
SourceLocation Loc) {
assert(Arg.getKind() == TemplateArgument::Declaration &&
"Only declaration template arguments permitted here");
// For a NULL non-type template argument, return nullptr casted to the
// parameter's type.
if (!Arg.getAsDecl()) {
return ImpCastExprToType(
new (Context) CXXNullPtrLiteralExpr(Context.NullPtrTy, Loc),
ParamType,
ParamType->getAs<MemberPointerType>()
? CK_NullToMemberPointer
: CK_NullToPointer);
}
ValueDecl *VD = cast<ValueDecl>(Arg.getAsDecl());
if (VD->getDeclContext()->isRecord() &&

View File

@ -1586,8 +1586,7 @@ DeduceTemplateArguments(Sema &S,
case TemplateArgument::Declaration:
if (Arg.getKind() == TemplateArgument::Declaration &&
Param.getAsDecl()->getCanonicalDecl() ==
Arg.getAsDecl()->getCanonicalDecl())
isSameDeclaration(Param.getAsDecl(), Arg.getAsDecl()))
return Sema::TDK_Success;
Info.FirstArg = Param;
@ -1858,8 +1857,7 @@ static bool isSameTemplateArg(ASTContext &Context,
Context.getCanonicalType(Y.getAsType());
case TemplateArgument::Declaration:
return X.getAsDecl()->getCanonicalDecl() ==
Y.getAsDecl()->getCanonicalDecl();
return isSameDeclaration(X.getAsDecl(), Y.getAsDecl());
case TemplateArgument::Template:
case TemplateArgument::TemplateExpansion:
@ -1925,7 +1923,7 @@ getTrivialTemplateArgumentLoc(Sema &S,
case TemplateArgument::Declaration: {
Expr *E
= S.BuildExpressionFromDeclTemplateArgument(Arg, NTTPType, Loc)
.takeAs<Expr>();
.takeAs<Expr>();
return TemplateArgumentLoc(TemplateArgument(E), E);
}
@ -4410,7 +4408,7 @@ MarkUsedTemplateParameters(ASTContext &Ctx,
switch (TemplateArg.getKind()) {
case TemplateArgument::Null:
case TemplateArgument::Integral:
case TemplateArgument::Declaration:
case TemplateArgument::Declaration:
break;
case TemplateArgument::Type:

View File

@ -1113,15 +1113,21 @@ ExprResult TemplateInstantiator::transformNonTypeTemplateParmRef(
type = argExpr->getType();
} else if (arg.getKind() == TemplateArgument::Declaration) {
ValueDecl *VD = cast<ValueDecl>(arg.getAsDecl());
// Find the instantiation of the template argument. This is
// required for nested templates.
VD = cast_or_null<ValueDecl>(
getSema().FindInstantiatedDecl(loc, VD, TemplateArgs));
if (!VD)
return ExprError();
ValueDecl *VD;
if (Decl *D = arg.getAsDecl()) {
VD = cast<ValueDecl>(D);
// Find the instantiation of the template argument. This is
// required for nested templates.
VD = cast_or_null<ValueDecl>(
getSema().FindInstantiatedDecl(loc, VD, TemplateArgs));
if (!VD)
return ExprError();
} else {
// Propagate NULL template argument.
VD = 0;
}
// Derive the type we want the substituted decl to have. This had
// better be non-dependent, or these checks will have serious problems.
if (parm->isExpandedParameterPack()) {

View File

@ -0,0 +1,55 @@
// RUN: %clang_cc1 -std=c++11 %s -verify
namespace std {
typedef decltype(nullptr) nullptr_t;
}
template<int *ip> struct IP { // expected-note 2 {{template parameter is declared here}}
IP<ip> *ip2;
};
constexpr std::nullptr_t get_nullptr() { return nullptr; }
std::nullptr_t np;
IP<0> ip0; // expected-error{{null non-type template argument must be cast to template parameter type 'int *'}}
IP<(0)> ip1; // expected-error{{null non-type template argument must be cast to template parameter type 'int *'}}
IP<nullptr> ip2;
IP<get_nullptr()> ip3;
IP<(int*)0> ip4;
IP<np> ip5;
struct X { };
template<int X::*pm> struct PM { // expected-note 2 {{template parameter is declared here}}
PM<pm> *pm2;
};
PM<0> pm0; // expected-error{{null non-type template argument must be cast to template parameter type 'int X::*'}}
PM<(0)> pm1; // expected-error{{null non-type template argument must be cast to template parameter type 'int X::*'}}
PM<nullptr> pm2;
PM<get_nullptr()> pm3;
PM<(int X::*)0> pm4;
PM<np> pm5;
template<int (X::*pmf)(int)> struct PMF { // expected-note 2 {{template parameter is declared here}}
PMF<pmf> *pmf2;
};
PMF<0> pmf0; // expected-error{{null non-type template argument must be cast to template parameter type 'int (X::*)(int)'}}
PMF<(0)> pmf1; // expected-error{{null non-type template argument must be cast to template parameter type 'int (X::*)(int)'}}
PMF<nullptr> pmf2;
PMF<get_nullptr()> pmf3;
PMF<(int (X::*)(int))0> pmf4;
PMF<np> pmf5;
template<std::nullptr_t np> struct NP { // expected-note 2{{template parameter is declared here}}
NP<np> *np2;
};
NP<nullptr> np1;
NP<np> np2;
NP<get_nullptr()> np3;
NP<0> np4; // expected-error{{null non-type template argument must be cast to template parameter type 'std::nullptr_t' (aka 'nullptr_t')}}
constexpr int i = 7;
NP<i> np5; // expected-error{{non-type template argument of type 'const int' cannot be converted to a value of type 'std::nullptr_t'}}

View File

@ -0,0 +1,13 @@
// RUN: %clang_cc1 -std=c++11 -emit-llvm -o - %s | FileCheck %s
template<int *ip> struct IP {};
// CHECK: define void @_Z5test12IPILPi0EE
void test1(IP<nullptr>) {}
struct X{ };
template<int X::*pm> struct PM {};
// CHECK: define void @_Z5test22PMILM1Xi0EE
void test2(PM<nullptr>) { }

View File

@ -104,3 +104,7 @@ namespace TestMisplacedEllipsisRecovery {
template<template<typename> ...Foo, // expected-error {{template template parameter requires 'class' after the parameter list}}
template<template<template<typename>>>> // expected-error 3 {{template template parameter requires 'class' after the parameter list}}
void func();
template<int *ip> struct IP { }; // expected-note{{declared here}}
IP<0> ip0; // expected-error{{null non-type template argument must be cast to template parameter type 'int *'}}