mirror of
https://github.com/llvm/llvm-project.git
synced 2025-04-27 20:06:06 +00:00
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:
parent
4460e0f805
commit
31f55dced5
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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)">;
|
||||
|
@ -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()));
|
||||
|
@ -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:
|
||||
|
@ -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: {
|
||||
|
@ -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;
|
||||
|
@ -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';
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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() &&
|
||||
|
@ -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:
|
||||
|
@ -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()) {
|
||||
|
55
clang/test/CXX/temp/temp.arg/temp.arg.nontype/p1-11.cpp
Normal file
55
clang/test/CXX/temp/temp.arg/temp.arg.nontype/p1-11.cpp
Normal 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'}}
|
13
clang/test/CodeGenCXX/mangle-nullptr-arg.cpp
Normal file
13
clang/test/CodeGenCXX/mangle-nullptr-arg.cpp
Normal 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>) { }
|
||||
|
@ -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 *'}}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user