mirror of
https://github.com/llvm/llvm-project.git
synced 2025-04-28 17:16:07 +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
|
/// declaration, which is either an external declaration or a
|
||||||
/// template declaration.
|
/// template declaration.
|
||||||
TemplateArgument(Decl *D) : Kind(Declaration) {
|
TemplateArgument(Decl *D) : Kind(Declaration) {
|
||||||
// FIXME: Need to be sure we have the "canonical" declaration!
|
|
||||||
TypeOrValue = reinterpret_cast<uintptr_t>(D);
|
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<
|
def err_template_arg_not_ice : Error<
|
||||||
"non-type template argument of type %0 is not an integral constant "
|
"non-type template argument of type %0 is not an integral constant "
|
||||||
"expression">;
|
"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<
|
def err_deduced_non_type_template_arg_type_mismatch : Error<
|
||||||
"deduced non-type template argument does not have the same type as the "
|
"deduced non-type template argument does not have the same type as the "
|
||||||
"its corresponding template parameter (%0 vs %1)">;
|
"its corresponding template parameter (%0 vs %1)">;
|
||||||
|
@ -3313,8 +3313,11 @@ ASTContext::getCanonicalTemplateArgument(const TemplateArgument &Arg) const {
|
|||||||
case TemplateArgument::Expression:
|
case TemplateArgument::Expression:
|
||||||
return Arg;
|
return Arg;
|
||||||
|
|
||||||
case TemplateArgument::Declaration:
|
case TemplateArgument::Declaration: {
|
||||||
return TemplateArgument(Arg.getAsDecl()->getCanonicalDecl());
|
if (Decl *D = Arg.getAsDecl())
|
||||||
|
return TemplateArgument(D->getCanonicalDecl());
|
||||||
|
return TemplateArgument((Decl*)0);
|
||||||
|
}
|
||||||
|
|
||||||
case TemplateArgument::Template:
|
case TemplateArgument::Template:
|
||||||
return TemplateArgument(getCanonicalTemplateName(Arg.getAsTemplate()));
|
return TemplateArgument(getCanonicalTemplateName(Arg.getAsTemplate()));
|
||||||
|
@ -325,6 +325,8 @@ static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context,
|
|||||||
return IsSameValue(*Arg1.getAsIntegral(), *Arg2.getAsIntegral());
|
return IsSameValue(*Arg1.getAsIntegral(), *Arg2.getAsIntegral());
|
||||||
|
|
||||||
case TemplateArgument::Declaration:
|
case TemplateArgument::Declaration:
|
||||||
|
if (!Arg1.getAsDecl() || !Arg2.getAsDecl())
|
||||||
|
return !Arg1.getAsDecl() && !Arg2.getAsDecl();
|
||||||
return Context.IsStructurallyEquivalent(Arg1.getAsDecl(), Arg2.getAsDecl());
|
return Context.IsStructurallyEquivalent(Arg1.getAsDecl(), Arg2.getAsDecl());
|
||||||
|
|
||||||
case TemplateArgument::Template:
|
case TemplateArgument::Template:
|
||||||
|
@ -320,7 +320,8 @@ struct XMLDumper : public XMLDeclVisitor<XMLDumper>,
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case TemplateArgument::Declaration: {
|
case TemplateArgument::Declaration: {
|
||||||
visitDeclRef(A.getAsDecl());
|
if (Decl *D = A.getAsDecl())
|
||||||
|
visitDeclRef(D);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case TemplateArgument::Integral: {
|
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.
|
// parameters are constant expressions even if they're non-const.
|
||||||
// In C, such things can also be folded, although they are not ICEs.
|
// In C, such things can also be folded, although they are not ICEs.
|
||||||
const VarDecl *VD = dyn_cast<VarDecl>(D);
|
const VarDecl *VD = dyn_cast<VarDecl>(D);
|
||||||
|
if (VD) {
|
||||||
if (const VarDecl *VDef = VD->getDefinition(Info.Ctx))
|
if (const VarDecl *VDef = VD->getDefinition(Info.Ctx))
|
||||||
VD = VDef;
|
VD = VDef;
|
||||||
|
}
|
||||||
if (!VD || VD->isInvalidDecl()) {
|
if (!VD || VD->isInvalidDecl()) {
|
||||||
Info.Diag(Conv);
|
Info.Diag(Conv);
|
||||||
return false;
|
return false;
|
||||||
|
@ -3118,12 +3118,22 @@ void CXXNameMangler::mangleTemplateArg(const NamedDecl *P,
|
|||||||
case TemplateArgument::Declaration: {
|
case TemplateArgument::Declaration: {
|
||||||
assert(P && "Missing template parameter for declaration argument");
|
assert(P && "Missing template parameter for declaration argument");
|
||||||
// <expr-primary> ::= L <mangled-name> E # external name
|
// <expr-primary> ::= L <mangled-name> E # external name
|
||||||
|
// <expr-primary> ::= L <type> 0 E
|
||||||
// Clang produces AST's where pointer-to-member-function expressions
|
// Clang produces AST's where pointer-to-member-function expressions
|
||||||
// and pointer-to-function expressions are represented as a declaration not
|
// and pointer-to-function expressions are represented as a declaration not
|
||||||
// an expression. We compensate for it here to produce the correct mangling.
|
// an expression. We compensate for it here to produce the correct mangling.
|
||||||
NamedDecl *D = cast<NamedDecl>(A.getAsDecl());
|
|
||||||
const NonTypeTemplateParmDecl *Parameter = cast<NonTypeTemplateParmDecl>(P);
|
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();
|
bool compensateMangling = !Parameter->getType()->isReferenceType();
|
||||||
if (compensateMangling) {
|
if (compensateMangling) {
|
||||||
Out << 'X';
|
Out << 'X';
|
||||||
|
@ -80,9 +80,13 @@ bool TemplateArgument::isDependent() const {
|
|||||||
return true;
|
return true;
|
||||||
|
|
||||||
case Declaration:
|
case Declaration:
|
||||||
if (DeclContext *DC = dyn_cast<DeclContext>(getAsDecl()))
|
if (Decl *D = getAsDecl()) {
|
||||||
|
if (DeclContext *DC = dyn_cast<DeclContext>(D))
|
||||||
return DC->isDependentContext();
|
return DC->isDependentContext();
|
||||||
return getAsDecl()->getDeclContext()->isDependentContext();
|
return D->getDeclContext()->isDependentContext();
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
|
||||||
case Integral:
|
case Integral:
|
||||||
// Never dependent
|
// Never dependent
|
||||||
@ -118,9 +122,12 @@ bool TemplateArgument::isInstantiationDependent() const {
|
|||||||
return true;
|
return true;
|
||||||
|
|
||||||
case Declaration:
|
case Declaration:
|
||||||
if (DeclContext *DC = dyn_cast<DeclContext>(getAsDecl()))
|
if (Decl *D = getAsDecl()) {
|
||||||
|
if (DeclContext *DC = dyn_cast<DeclContext>(D))
|
||||||
return DC->isDependentContext();
|
return DC->isDependentContext();
|
||||||
return getAsDecl()->getDeclContext()->isDependentContext();
|
return D->getDeclContext()->isDependentContext();
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
|
||||||
case Integral:
|
case Integral:
|
||||||
// Never dependent
|
// Never dependent
|
||||||
@ -322,17 +329,15 @@ void TemplateArgument::print(const PrintingPolicy &Policy,
|
|||||||
}
|
}
|
||||||
|
|
||||||
case Declaration: {
|
case Declaration: {
|
||||||
bool Unnamed = true;
|
|
||||||
if (NamedDecl *ND = dyn_cast_or_null<NamedDecl>(getAsDecl())) {
|
if (NamedDecl *ND = dyn_cast_or_null<NamedDecl>(getAsDecl())) {
|
||||||
if (ND->getDeclName()) {
|
if (ND->getDeclName()) {
|
||||||
Unnamed = false;
|
|
||||||
Out << *ND;
|
Out << *ND;
|
||||||
}
|
} else {
|
||||||
}
|
|
||||||
|
|
||||||
if (Unnamed) {
|
|
||||||
Out << "<anonymous>";
|
Out << "<anonymous>";
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
Out << "nullptr";
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -488,7 +493,9 @@ const DiagnosticBuilder &clang::operator<<(const DiagnosticBuilder &DB,
|
|||||||
return DB << Arg.getAsType();
|
return DB << Arg.getAsType();
|
||||||
|
|
||||||
case TemplateArgument::Declaration:
|
case TemplateArgument::Declaration:
|
||||||
return DB << Arg.getAsDecl();
|
if (Decl *D = Arg.getAsDecl())
|
||||||
|
return DB << D;
|
||||||
|
return DB << "nullptr";
|
||||||
|
|
||||||
case TemplateArgument::Integral:
|
case TemplateArgument::Integral:
|
||||||
return DB << Arg.getAsIntegral()->toString(10);
|
return DB << Arg.getAsIntegral()->toString(10);
|
||||||
|
@ -264,8 +264,9 @@ void TypePrinter::printRValueReference(const RValueReferenceType *T,
|
|||||||
|
|
||||||
void TypePrinter::printMemberPointer(const MemberPointerType *T,
|
void TypePrinter::printMemberPointer(const MemberPointerType *T,
|
||||||
std::string &S) {
|
std::string &S) {
|
||||||
std::string C;
|
PrintingPolicy InnerPolicy(Policy);
|
||||||
print(QualType(T->getClass(), 0), C);
|
Policy.SuppressTag = true;
|
||||||
|
std::string C = QualType(T->getClass(), 0).getAsString(InnerPolicy);
|
||||||
C += "::*";
|
C += "::*";
|
||||||
S = C + S;
|
S = C + S;
|
||||||
|
|
||||||
|
@ -10480,7 +10480,8 @@ namespace {
|
|||||||
bool MarkReferencedDecls::TraverseTemplateArgument(
|
bool MarkReferencedDecls::TraverseTemplateArgument(
|
||||||
const TemplateArgument &Arg) {
|
const TemplateArgument &Arg) {
|
||||||
if (Arg.getKind() == TemplateArgument::Declaration) {
|
if (Arg.getKind() == TemplateArgument::Declaration) {
|
||||||
S.MarkAnyDeclReferenced(Loc, Arg.getAsDecl());
|
if (Decl *D = Arg.getAsDecl())
|
||||||
|
S.MarkAnyDeclReferenced(Loc, D);
|
||||||
}
|
}
|
||||||
|
|
||||||
return Inherited::TraverseTemplateArgument(Arg);
|
return Inherited::TraverseTemplateArgument(Arg);
|
||||||
|
@ -3527,6 +3527,13 @@ CheckTemplateArgumentAddressOfObjectOrFunction(Sema &S,
|
|||||||
dyn_cast<SubstNonTypeTemplateParmExpr>(Arg))
|
dyn_cast<SubstNonTypeTemplateParmExpr>(Arg))
|
||||||
Arg = subst->getReplacement()->IgnoreImpCasts();
|
Arg = subst->getReplacement()->IgnoreImpCasts();
|
||||||
|
|
||||||
|
// 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);
|
DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(Arg);
|
||||||
if (!DRE) {
|
if (!DRE) {
|
||||||
S.Diag(Arg->getLocStart(), diag::err_template_arg_not_decl_ref)
|
S.Diag(Arg->getLocStart(), diag::err_template_arg_not_decl_ref)
|
||||||
@ -3535,12 +3542,6 @@ CheckTemplateArgumentAddressOfObjectOrFunction(Sema &S,
|
|||||||
return true;
|
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!isa<ValueDecl>(DRE->getDecl())) {
|
if (!isa<ValueDecl>(DRE->getDecl())) {
|
||||||
S.Diag(Arg->getLocStart(),
|
S.Diag(Arg->getLocStart(),
|
||||||
@ -4048,21 +4049,74 @@ ExprResult Sema::CheckTemplateArgument(NonTypeTemplateParmDecl *Param,
|
|||||||
QualType ArgType = Arg->getType();
|
QualType ArgType = Arg->getType();
|
||||||
DeclAccessPair FoundResult; // temporary for ResolveOverloadedFunction
|
DeclAccessPair FoundResult; // temporary for ResolveOverloadedFunction
|
||||||
|
|
||||||
// C++0x [temp.arg.nontype]p5 bullets 2, 4 and 6 permit conversion
|
// C++11 [temp.arg.nontype]p1:
|
||||||
// from a template argument of type std::nullptr_t to a non-type
|
// - a constant expression that evaluates to a null pointer value (4.10); or
|
||||||
// template parameter of type pointer to object, pointer to
|
// - a constant expression that evaluates to a null member pointer value
|
||||||
// function, or pointer-to-member, respectively.
|
// (4.11); or
|
||||||
if (ArgType->isNullPtrType()) {
|
// - an address constant expression of type std::nullptr_t
|
||||||
if (ParamType->isPointerType() || ParamType->isMemberPointerType()) {
|
if (getLangOpts().CPlusPlus0x &&
|
||||||
Converted = TemplateArgument((NamedDecl *)0);
|
(ParamType->isPointerType() || ParamType->isMemberPointerType() ||
|
||||||
return Owned(Arg);
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ParamType->isNullPtrType()) {
|
Converted = TemplateArgument((Decl *)0);
|
||||||
llvm::APSInt Zero(Context.getTypeSize(Context.NullPtrTy), true);
|
return false;
|
||||||
Converted = TemplateArgument(Zero, Context.NullPtrTy);
|
|
||||||
return Owned(Arg);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
// Handle pointer-to-function, reference-to-function, and
|
||||||
@ -4255,6 +4309,18 @@ Sema::BuildExpressionFromDeclTemplateArgument(const TemplateArgument &Arg,
|
|||||||
SourceLocation Loc) {
|
SourceLocation Loc) {
|
||||||
assert(Arg.getKind() == TemplateArgument::Declaration &&
|
assert(Arg.getKind() == TemplateArgument::Declaration &&
|
||||||
"Only declaration template arguments permitted here");
|
"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());
|
ValueDecl *VD = cast<ValueDecl>(Arg.getAsDecl());
|
||||||
|
|
||||||
if (VD->getDeclContext()->isRecord() &&
|
if (VD->getDeclContext()->isRecord() &&
|
||||||
|
@ -1586,8 +1586,7 @@ DeduceTemplateArguments(Sema &S,
|
|||||||
|
|
||||||
case TemplateArgument::Declaration:
|
case TemplateArgument::Declaration:
|
||||||
if (Arg.getKind() == TemplateArgument::Declaration &&
|
if (Arg.getKind() == TemplateArgument::Declaration &&
|
||||||
Param.getAsDecl()->getCanonicalDecl() ==
|
isSameDeclaration(Param.getAsDecl(), Arg.getAsDecl()))
|
||||||
Arg.getAsDecl()->getCanonicalDecl())
|
|
||||||
return Sema::TDK_Success;
|
return Sema::TDK_Success;
|
||||||
|
|
||||||
Info.FirstArg = Param;
|
Info.FirstArg = Param;
|
||||||
@ -1858,8 +1857,7 @@ static bool isSameTemplateArg(ASTContext &Context,
|
|||||||
Context.getCanonicalType(Y.getAsType());
|
Context.getCanonicalType(Y.getAsType());
|
||||||
|
|
||||||
case TemplateArgument::Declaration:
|
case TemplateArgument::Declaration:
|
||||||
return X.getAsDecl()->getCanonicalDecl() ==
|
return isSameDeclaration(X.getAsDecl(), Y.getAsDecl());
|
||||||
Y.getAsDecl()->getCanonicalDecl();
|
|
||||||
|
|
||||||
case TemplateArgument::Template:
|
case TemplateArgument::Template:
|
||||||
case TemplateArgument::TemplateExpansion:
|
case TemplateArgument::TemplateExpansion:
|
||||||
|
@ -1113,7 +1113,9 @@ ExprResult TemplateInstantiator::transformNonTypeTemplateParmRef(
|
|||||||
type = argExpr->getType();
|
type = argExpr->getType();
|
||||||
|
|
||||||
} else if (arg.getKind() == TemplateArgument::Declaration) {
|
} else if (arg.getKind() == TemplateArgument::Declaration) {
|
||||||
ValueDecl *VD = cast<ValueDecl>(arg.getAsDecl());
|
ValueDecl *VD;
|
||||||
|
if (Decl *D = arg.getAsDecl()) {
|
||||||
|
VD = cast<ValueDecl>(D);
|
||||||
|
|
||||||
// Find the instantiation of the template argument. This is
|
// Find the instantiation of the template argument. This is
|
||||||
// required for nested templates.
|
// required for nested templates.
|
||||||
@ -1121,6 +1123,10 @@ ExprResult TemplateInstantiator::transformNonTypeTemplateParmRef(
|
|||||||
getSema().FindInstantiatedDecl(loc, VD, TemplateArgs));
|
getSema().FindInstantiatedDecl(loc, VD, TemplateArgs));
|
||||||
if (!VD)
|
if (!VD)
|
||||||
return ExprError();
|
return ExprError();
|
||||||
|
} else {
|
||||||
|
// Propagate NULL template argument.
|
||||||
|
VD = 0;
|
||||||
|
}
|
||||||
|
|
||||||
// Derive the type we want the substituted decl to have. This had
|
// Derive the type we want the substituted decl to have. This had
|
||||||
// better be non-dependent, or these checks will have serious problems.
|
// better be non-dependent, or these checks will have serious problems.
|
||||||
|
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<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}}
|
template<template<template<typename>>>> // expected-error 3 {{template template parameter requires 'class' after the parameter list}}
|
||||||
void func();
|
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