Implement C++0x nullptr.
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@71405 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/include/clang/AST/ASTContext.h b/include/clang/AST/ASTContext.h
index 3afccdb..1f24dee 100644
--- a/include/clang/AST/ASTContext.h
+++ b/include/clang/AST/ASTContext.h
@@ -174,7 +174,7 @@
QualType UnsignedLongLongTy, UnsignedInt128Ty;
QualType FloatTy, DoubleTy, LongDoubleTy;
QualType FloatComplexTy, DoubleComplexTy, LongDoubleComplexTy;
- QualType VoidPtrTy;
+ QualType VoidPtrTy, NullPtrTy;
QualType OverloadTy;
QualType DependentTy;
diff --git a/include/clang/AST/ExprCXX.h b/include/clang/AST/ExprCXX.h
index 483f0e3..c34ded2 100644
--- a/include/clang/AST/ExprCXX.h
+++ b/include/clang/AST/ExprCXX.h
@@ -222,6 +222,24 @@
virtual child_iterator child_end();
};
+/// CXXNullPtrLiteralExpr - [C++0x 2.14.7] C++ Pointer Literal
+class CXXNullPtrLiteralExpr : public Expr {
+ SourceLocation Loc;
+public:
+ CXXNullPtrLiteralExpr(QualType Ty, SourceLocation l) :
+ Expr(CXXNullPtrLiteralExprClass, Ty), Loc(l) {}
+
+ virtual SourceRange getSourceRange() const { return SourceRange(Loc); }
+
+ static bool classof(const Stmt *T) {
+ return T->getStmtClass() == CXXNullPtrLiteralExprClass;
+ }
+ static bool classof(const CXXNullPtrLiteralExpr *) { return true; }
+
+ virtual child_iterator child_begin();
+ virtual child_iterator child_end();
+};
+
/// CXXTypeidExpr - A C++ @c typeid expression (C++ [expr.typeid]), which gets
/// the type_info that corresponds to the supplied type, or the (possibly
/// dynamic) type of the supplied expression.
diff --git a/include/clang/AST/StmtNodes.def b/include/clang/AST/StmtNodes.def
index 71dd77b..b1b0531 100644
--- a/include/clang/AST/StmtNodes.def
+++ b/include/clang/AST/StmtNodes.def
@@ -107,6 +107,7 @@
STMT(CXXFunctionalCastExpr , ExplicitCastExpr)
STMT(CXXTypeidExpr , Expr)
STMT(CXXBoolLiteralExpr , Expr)
+STMT(CXXNullPtrLiteralExpr , Expr)
STMT(CXXThisExpr , Expr)
STMT(CXXThrowExpr , Expr)
STMT(CXXDefaultArgExpr , Expr)
diff --git a/include/clang/AST/Type.h b/include/clang/AST/Type.h
index 1d440ac..22e36a8 100644
--- a/include/clang/AST/Type.h
+++ b/include/clang/AST/Type.h
@@ -394,6 +394,7 @@
bool isObjCQualifiedInterfaceType() const; // NSString<foo>
bool isObjCQualifiedIdType() const; // id<foo>
bool isTemplateTypeParmType() const; // C++ template type parameter
+ bool isNullPtrType() const; // C++0x nullptr_t
/// isDependentType - Whether this type is a dependent type, meaning
/// that its definition somehow depends on a template parameter
@@ -404,7 +405,7 @@
/// hasPointerRepresentation - Whether this type is represented
/// natively as a pointer; this includes pointers, references, block
/// pointers, and Objective-C interface, qualified id, and qualified
- /// interface types.
+ /// interface types, as well as nullptr_t.
bool hasPointerRepresentation() const;
/// hasObjCPointerRepresentation - Whether this type can represent
@@ -559,6 +560,8 @@
Float, Double, LongDouble,
+ NullPtr, // This is the type of C++0x 'nullptr'.
+
Overload, // This represents the type of an overloaded function declaration.
Dependent // This represents the type of a type-dependent expression.
};
@@ -1926,7 +1929,7 @@
inline bool Type::hasPointerRepresentation() const {
return (isPointerType() || isReferenceType() || isBlockPointerType() ||
isObjCInterfaceType() || isObjCQualifiedIdType() ||
- isObjCQualifiedInterfaceType());
+ isObjCQualifiedInterfaceType() || isNullPtrType());
}
inline bool Type::hasObjCPointerRepresentation() const {
diff --git a/include/clang/Frontend/PCHBitCodes.h b/include/clang/Frontend/PCHBitCodes.h
index bb5c91b..29f4083 100644
--- a/include/clang/Frontend/PCHBitCodes.h
+++ b/include/clang/Frontend/PCHBitCodes.h
@@ -319,7 +319,9 @@
/// \brief The '__uint128_t' type.
PREDEF_TYPE_UINT128_ID = 21,
/// \brief The '__int128_t' type.
- PREDEF_TYPE_INT128_ID = 22
+ PREDEF_TYPE_INT128_ID = 22,
+ /// \brief The type of 'nullptr'.
+ PREDEF_TYPE_NULLPTR_ID = 23
};
/// \brief The number of predefined type IDs that are reserved for
diff --git a/include/clang/Parse/Action.h b/include/clang/Parse/Action.h
index fdd2b2a..f70c9f0 100644
--- a/include/clang/Parse/Action.h
+++ b/include/clang/Parse/Action.h
@@ -963,7 +963,12 @@
/// ActOnCXXBoolLiteral - Parse {true,false} literals.
virtual OwningExprResult ActOnCXXBoolLiteral(SourceLocation OpLoc,
- tok::TokenKind Kind) {
+ tok::TokenKind Kind) {
+ return ExprEmpty();
+ }
+
+ /// ActOnCXXNullPtrLiteral - Parse 'nullptr'.
+ virtual OwningExprResult ActOnCXXNullPtrLiteral(SourceLocation Loc) {
return ExprEmpty();
}
diff --git a/lib/AST/ASTContext.cpp b/lib/AST/ASTContext.cpp
index 4aed59f..133e8c2 100644
--- a/lib/AST/ASTContext.cpp
+++ b/lib/AST/ASTContext.cpp
@@ -276,6 +276,9 @@
// void * type
VoidPtrTy = getPointerType(VoidTy);
+
+ // nullptr type (C++0x 2.14.7)
+ InitBuiltinType(NullPtrTy, BuiltinType::NullPtr);
}
//===----------------------------------------------------------------------===//
@@ -431,6 +434,9 @@
Width = Target.getLongDoubleWidth();
Align = Target.getLongDoubleAlign();
break;
+ case BuiltinType::NullPtr:
+ Width = Target.getPointerWidth(0); // C++ 3.9.1p11: sizeof(nullptr_t)
+ Align = Target.getPointerAlign(0); // == sizeof(void*)
}
break;
case Type::FixedWidthInt:
diff --git a/lib/AST/Expr.cpp b/lib/AST/Expr.cpp
index 77a25f6..0d38dd2 100644
--- a/lib/AST/Expr.cpp
+++ b/lib/AST/Expr.cpp
@@ -1377,6 +1377,10 @@
return true;
}
+ // C++0x nullptr_t is always a null pointer constant.
+ if (getType()->isNullPtrType())
+ return true;
+
// This expression must be an integer type.
if (!getType()->isIntegerType())
return false;
diff --git a/lib/AST/ExprCXX.cpp b/lib/AST/ExprCXX.cpp
index b05bf7d..8176db5 100644
--- a/lib/AST/ExprCXX.cpp
+++ b/lib/AST/ExprCXX.cpp
@@ -44,6 +44,14 @@
return child_iterator();
}
+// CXXNullPtrLiteralExpr
+Stmt::child_iterator CXXNullPtrLiteralExpr::child_begin() {
+ return child_iterator();
+}
+Stmt::child_iterator CXXNullPtrLiteralExpr::child_end() {
+ return child_iterator();
+}
+
// CXXThisExpr
Stmt::child_iterator CXXThisExpr::child_begin() { return child_iterator(); }
Stmt::child_iterator CXXThisExpr::child_end() { return child_iterator(); }
diff --git a/lib/AST/ExprConstant.cpp b/lib/AST/ExprConstant.cpp
index aa4920f..34b0187 100644
--- a/lib/AST/ExprConstant.cpp
+++ b/lib/AST/ExprConstant.cpp
@@ -299,7 +299,9 @@
{ return APValue((Expr*)0, 0); }
APValue VisitConditionalOperator(ConditionalOperator *E);
APValue VisitChooseExpr(ChooseExpr *E)
- { return Visit(E->getChosenSubExpr(Info.Ctx)); }
+ { return Visit(E->getChosenSubExpr(Info.Ctx)); }
+ APValue VisitCXXNullPtrLiteralExpr(CXXNullPtrLiteralExpr *E)
+ { return APValue((Expr*)0, 0); }
// FIXME: Missing: @protocol, @selector
};
} // end anonymous namespace
diff --git a/lib/AST/StmtPrinter.cpp b/lib/AST/StmtPrinter.cpp
index 0f8284a..cf1a255 100644
--- a/lib/AST/StmtPrinter.cpp
+++ b/lib/AST/StmtPrinter.cpp
@@ -996,6 +996,10 @@
OS << (Node->getValue() ? "true" : "false");
}
+void StmtPrinter::VisitCXXNullPtrLiteralExpr(CXXNullPtrLiteralExpr *Node) {
+ OS << "nullptr";
+}
+
void StmtPrinter::VisitCXXThisExpr(CXXThisExpr *Node) {
OS << "this";
}
diff --git a/lib/AST/Type.cpp b/lib/AST/Type.cpp
index d6cf4bd..b5a8840 100644
--- a/lib/AST/Type.cpp
+++ b/lib/AST/Type.cpp
@@ -889,6 +889,12 @@
return false;
}
+bool Type::isNullPtrType() const {
+ if (const BuiltinType *BT = getAsBuiltinType())
+ return BT->getKind() == BuiltinType::NullPtr;
+ return false;
+}
+
const char *BuiltinType::getName() const {
switch (getKind()) {
default: assert(0 && "Unknown builtin type!");
@@ -912,6 +918,7 @@
case Double: return "double";
case LongDouble: return "long double";
case WChar: return "wchar_t";
+ case NullPtr: return "nullptr_t";
case Overload: return "<overloaded function type>";
case Dependent: return "<dependent type>";
}
diff --git a/lib/CodeGen/Mangle.cpp b/lib/CodeGen/Mangle.cpp
index b61acbb..036389d 100644
--- a/lib/CodeGen/Mangle.cpp
+++ b/lib/CodeGen/Mangle.cpp
@@ -499,6 +499,8 @@
// UNSUPPORTED: ::= Di # char32_t
// UNSUPPORTED: ::= Ds # char16_t
// ::= u <source-name> # vendor extended type
+ // From our point of view, std::nullptr_t is a builtin, but as far as mangling
+ // is concerned, it's a type called std::nullptr_t.
switch (T->getKind()) {
case BuiltinType::Void: Out << 'v'; break;
case BuiltinType::Bool: Out << 'b'; break;
@@ -519,6 +521,7 @@
case BuiltinType::Float: Out << 'f'; break;
case BuiltinType::Double: Out << 'd'; break;
case BuiltinType::LongDouble: Out << 'e'; break;
+ case BuiltinType::NullPtr: Out << "St9nullptr_t"; break;
case BuiltinType::Overload:
case BuiltinType::Dependent:
diff --git a/lib/Frontend/PCHReader.cpp b/lib/Frontend/PCHReader.cpp
index 9323620..759865c 100644
--- a/lib/Frontend/PCHReader.cpp
+++ b/lib/Frontend/PCHReader.cpp
@@ -1676,6 +1676,7 @@
case pch::PREDEF_TYPE_LONGDOUBLE_ID: T = Context->LongDoubleTy; break;
case pch::PREDEF_TYPE_OVERLOAD_ID: T = Context->OverloadTy; break;
case pch::PREDEF_TYPE_DEPENDENT_ID: T = Context->DependentTy; break;
+ case pch::PREDEF_TYPE_NULLPTR_ID: T = Context->NullPtrTy; break;
}
assert(!T.isNull() && "Unknown predefined type");
diff --git a/lib/Frontend/PCHWriter.cpp b/lib/Frontend/PCHWriter.cpp
index 4b9ca5c..fc6aa47 100644
--- a/lib/Frontend/PCHWriter.cpp
+++ b/lib/Frontend/PCHWriter.cpp
@@ -1837,6 +1837,7 @@
case BuiltinType::Float: ID = pch::PREDEF_TYPE_FLOAT_ID; break;
case BuiltinType::Double: ID = pch::PREDEF_TYPE_DOUBLE_ID; break;
case BuiltinType::LongDouble: ID = pch::PREDEF_TYPE_LONGDOUBLE_ID; break;
+ case BuiltinType::NullPtr: ID = pch::PREDEF_TYPE_NULLPTR_ID; break;
case BuiltinType::Overload: ID = pch::PREDEF_TYPE_OVERLOAD_ID; break;
case BuiltinType::Dependent: ID = pch::PREDEF_TYPE_DEPENDENT_ID; break;
}
diff --git a/lib/Parse/ParseExpr.cpp b/lib/Parse/ParseExpr.cpp
index 509aa0f..bfbac3a 100644
--- a/lib/Parse/ParseExpr.cpp
+++ b/lib/Parse/ParseExpr.cpp
@@ -430,6 +430,7 @@
/// constant
/// string-literal
/// [C++] boolean-literal [C++ 2.13.5]
+/// [C++0x] 'nullptr' [C++0x 2.14.7]
/// '(' expression ')'
/// '__func__' [C99 6.4.2.2]
/// [GNU] '__FUNCTION__'
@@ -569,6 +570,9 @@
case tok::kw_false:
return ParseCXXBoolLiteral();
+ case tok::kw_nullptr:
+ return Actions.ActOnCXXNullPtrLiteral(ConsumeToken());
+
case tok::identifier: { // primary-expression: identifier
// unqualified-id: identifier
// constant: enumeration-constant
diff --git a/lib/Sema/Sema.h b/lib/Sema/Sema.h
index 3054028..20d1edb 100644
--- a/lib/Sema/Sema.h
+++ b/lib/Sema/Sema.h
@@ -1534,6 +1534,9 @@
virtual OwningExprResult ActOnCXXBoolLiteral(SourceLocation OpLoc,
tok::TokenKind Kind);
+ /// ActOnCXXNullPtrLiteral - Parse 'nullptr'.
+ virtual OwningExprResult ActOnCXXNullPtrLiteral(SourceLocation Loc);
+
//// ActOnCXXThrow - Parse throw expressions.
virtual OwningExprResult ActOnCXXThrow(SourceLocation OpLoc,
ExprArg expr);
diff --git a/lib/Sema/SemaExpr.cpp b/lib/Sema/SemaExpr.cpp
index 063e0ec..d2e84c1 100644
--- a/lib/Sema/SemaExpr.cpp
+++ b/lib/Sema/SemaExpr.cpp
@@ -3811,6 +3811,20 @@
ImpCastExprToType(rex, lType); // promote the pointer to pointer
return ResultTy;
}
+ // C++ allows comparison of pointers with null pointer constants.
+ if (getLangOptions().CPlusPlus) {
+ if (lType->isPointerType() && RHSIsNull) {
+ ImpCastExprToType(rex, lType);
+ return ResultTy;
+ }
+ if (rType->isPointerType() && LHSIsNull) {
+ ImpCastExprToType(lex, rType);
+ return ResultTy;
+ }
+ // And comparison of nullptr_t with itself.
+ if (lType->isNullPtrType() && rType->isNullPtrType())
+ return ResultTy;
+ }
// Handle block pointer types.
if (!isRelational && lType->isBlockPointerType() && rType->isBlockPointerType()) {
QualType lpointee = lType->getAsBlockPointerType()->getPointeeType();
diff --git a/lib/Sema/SemaExprCXX.cpp b/lib/Sema/SemaExprCXX.cpp
index 92df67a..5f2b705 100644
--- a/lib/Sema/SemaExprCXX.cpp
+++ b/lib/Sema/SemaExprCXX.cpp
@@ -85,6 +85,12 @@
Context.BoolTy, OpLoc));
}
+/// ActOnCXXNullPtrLiteral - Parse 'nullptr'.
+Action::OwningExprResult
+Sema::ActOnCXXNullPtrLiteral(SourceLocation Loc) {
+ return Owned(new (Context) CXXNullPtrLiteralExpr(Context.NullPtrTy, Loc));
+}
+
/// ActOnCXXThrow - Parse throw expressions.
Action::OwningExprResult
Sema::ActOnCXXThrow(SourceLocation OpLoc, ExprArg E) {
diff --git a/lib/Sema/SemaNamedCast.cpp b/lib/Sema/SemaNamedCast.cpp
index f5bef4a..e0b94a4 100644
--- a/lib/Sema/SemaNamedCast.cpp
+++ b/lib/Sema/SemaNamedCast.cpp
@@ -279,12 +279,26 @@
return;
}
+ // See below for the enumeral issue.
+ if (SrcType->isNullPtrType() && DestType->isIntegralType() &&
+ !DestType->isEnumeralType()) {
+ // C++0x 5.2.10p4: A pointer can be explicitly converted to any integral
+ // type large enough to hold it. A value of std::nullptr_t can be
+ // converted to an integral type; the conversion has the same meaning
+ // and validity as a conversion of (void*)0 to the integral type.
+ if (Self.Context.getTypeSize(SrcType) >
+ Self.Context.getTypeSize(DestType)) {
+ Self.Diag(OpRange.getBegin(), diag::err_bad_reinterpret_cast_small_int)
+ << OrigDestType << DestRange;
+ }
+ return;
+ }
+
bool destIsPtr = DestType->isPointerType();
bool srcIsPtr = SrcType->isPointerType();
if (!destIsPtr && !srcIsPtr) {
- // Except for std::nullptr_t->integer, which is not supported yet, and
- // lvalue->reference, which is handled above, at least one of the two
- // arguments must be a pointer.
+ // Except for std::nullptr_t->integer and lvalue->reference, which are
+ // handled above, at least one of the two arguments must be a pointer.
Self.Diag(OpRange.getBegin(), diag::err_bad_cxx_cast_generic)
<< "reinterpret_cast" << OrigDestType << OrigSrcType << OpRange;
return;
diff --git a/lib/Sema/SemaOverload.cpp b/lib/Sema/SemaOverload.cpp
index e6b8056..50330e3 100644
--- a/lib/Sema/SemaOverload.cpp
+++ b/lib/Sema/SemaOverload.cpp
@@ -621,7 +621,8 @@
FromType->isEnumeralType() ||
FromType->isPointerType() ||
FromType->isBlockPointerType() ||
- FromType->isMemberPointerType())) {
+ FromType->isMemberPointerType() ||
+ FromType->isNullPtrType())) {
SCS.Second = ICK_Boolean_Conversion;
FromType = Context.BoolTy;
}
@@ -898,6 +899,13 @@
return true;
}
+ // If the left-hand-side is nullptr_t, the right side can be a null
+ // pointer constant.
+ if (ToType->isNullPtrType() && From->isNullPointerConstant(Context)) {
+ ConvertedType = ToType;
+ return true;
+ }
+
const PointerType* ToTypePtr = ToType->getAsPointerType();
if (!ToTypePtr)
return false;
diff --git a/lib/Sema/SemaTemplate.cpp b/lib/Sema/SemaTemplate.cpp
index e62b9a2..c8fdc22 100644
--- a/lib/Sema/SemaTemplate.cpp
+++ b/lib/Sema/SemaTemplate.cpp
@@ -1198,6 +1198,10 @@
if (ImplicitCastExpr *Cast = dyn_cast<ImplicitCastExpr>(Arg))
Arg = Cast->getSubExpr();
+ // C++0x allows nullptr, and there's no further checking to be done for that.
+ if (Arg->getType()->isNullPtrType())
+ return false;
+
// C++ [temp.arg.nontype]p1:
//
// A template-argument for a non-type, non-template
@@ -1296,6 +1300,10 @@
if (ImplicitCastExpr *Cast = dyn_cast<ImplicitCastExpr>(Arg))
Arg = Cast->getSubExpr();
+ // C++0x allows nullptr, and there's no further checking to be done for that.
+ if (Arg->getType()->isNullPtrType())
+ return false;
+
// C++ [temp.arg.nontype]p1:
//
// A template-argument for a non-type, non-template
@@ -1485,6 +1493,7 @@
// applied. If the template-argument represents a set of
// overloaded functions (or a pointer to such), the matching
// function is selected from the set (13.4).
+ // In C++0x, any std::nullptr_t value can be converted.
(ParamType->isPointerType() &&
ParamType->getAsPointerType()->getPointeeType()->isFunctionType()) ||
// -- For a non-type template-parameter of type reference to
@@ -1498,12 +1507,17 @@
// template-argument represents a set of overloaded member
// functions, the matching member function is selected from
// the set (13.4).
+ // Again, C++0x allows a std::nullptr_t value.
(ParamType->isMemberPointerType() &&
ParamType->getAsMemberPointerType()->getPointeeType()
->isFunctionType())) {
if (Context.hasSameUnqualifiedType(ArgType,
ParamType.getNonReferenceType())) {
// We don't have to do anything: the types already match.
+ } else if (ArgType->isNullPtrType() && (ParamType->isPointerType() ||
+ ParamType->isMemberPointerType())) {
+ ArgType = ParamType;
+ ImpCastExprToType(Arg, ParamType);
} else if (ArgType->isFunctionType() && ParamType->isPointerType()) {
ArgType = Context.getPointerType(ArgType);
ImpCastExprToType(Arg, ArgType);
@@ -1554,14 +1568,18 @@
// -- for a non-type template-parameter of type pointer to
// object, qualification conversions (4.4) and the
// array-to-pointer conversion (4.2) are applied.
+ // C++0x also allows a value of std::nullptr_t.
assert(ParamType->getAsPointerType()->getPointeeType()->isObjectType() &&
"Only object pointers allowed here");
- if (ArgType->isArrayType()) {
+ if (ArgType->isNullPtrType()) {
+ ArgType = ParamType;
+ ImpCastExprToType(Arg, ParamType);
+ } else if (ArgType->isArrayType()) {
ArgType = Context.getArrayDecayedType(ArgType);
ImpCastExprToType(Arg, ArgType);
}
-
+
if (IsQualificationConversion(ArgType, ParamType)) {
ArgType = ParamType;
ImpCastExprToType(Arg, ParamType);
@@ -1630,10 +1648,13 @@
// -- For a non-type template-parameter of type pointer to data
// member, qualification conversions (4.4) are applied.
+ // C++0x allows std::nullptr_t values.
assert(ParamType->isMemberPointerType() && "Only pointers to members remain");
if (Context.hasSameUnqualifiedType(ParamType, ArgType)) {
// Types match exactly: nothing more to do here.
+ } else if (ArgType->isNullPtrType()) {
+ ImpCastExprToType(Arg, ParamType);
} else if (IsQualificationConversion(ArgType, ParamType)) {
ImpCastExprToType(Arg, ParamType);
} else {
diff --git a/test/SemaCXX/nullptr.cpp b/test/SemaCXX/nullptr.cpp
new file mode 100644
index 0000000..6cc5a81
--- /dev/null
+++ b/test/SemaCXX/nullptr.cpp
@@ -0,0 +1,67 @@
+// RUN: clang-cc -fsyntax-only -verify -std=c++0x %s
+#include <stdint.h>
+
+// Don't have decltype yet.
+typedef __typeof__(nullptr) nullptr_t;
+
+struct A {};
+
+int o1(char*);
+void o1(uintptr_t);
+void o2(char*); // expected-note {{candidate}}
+void o2(int A::*); // expected-note {{candidate}}
+
+nullptr_t f(nullptr_t null)
+{
+ // Implicit conversions.
+ null = nullptr;
+ void *p = nullptr;
+ p = null;
+ int *pi = nullptr;
+ pi = null;
+ null = 0;
+ int A::*pm = nullptr;
+ pm = null;
+ void (*pf)() = nullptr;
+ pf = null;
+ void (A::*pmf)() = nullptr;
+ pmf = null;
+ bool b = nullptr;
+
+ // Can't convert nullptr to integral implicitly.
+ uintptr_t i = nullptr; // expected-error {{incompatible type initializing}}
+
+ // Operators
+ (void)(null == nullptr);
+ (void)(null <= nullptr);
+ (void)(null == (void*)0);
+ (void)((void*)0 == nullptr);
+ (void)(null <= (void*)0);
+ (void)((void*)0 <= nullptr);
+ (void)(1 > nullptr); // expected-error {{invalid operands to binary expression}}
+ (void)(1 != nullptr); // expected-error {{invalid operands to binary expression}}
+ (void)(1 + nullptr); // expected-error {{invalid operands to binary expression}}
+ (void)(0 ? nullptr : 0); // expected-error {{incompatible operand types}}
+ (void)(0 ? nullptr : (void*)0);
+
+ // Overloading
+ int t = o1(nullptr);
+ t = o1(null);
+ o2(nullptr); // expected-error {{ambiguous}}
+
+ // nullptr is an rvalue, null is an lvalue
+ (void)&nullptr; // expected-error {{address expression must be an lvalue}}
+ nullptr_t *pn = &null;
+
+ // You can reinterpret_cast nullptr to an integer.
+ (void)reinterpret_cast<uintptr_t>(nullptr);
+
+ // You can throw nullptr.
+ throw nullptr;
+}
+
+// Template arguments can be nullptr.
+template <int *PI, void (*PF)(), int A::*PM, void (A::*PMF)()>
+struct T {};
+
+typedef T<nullptr, nullptr, nullptr, nullptr> NT;
diff --git a/www/cxx_status.html b/www/cxx_status.html
index b165b9b..f4b799d 100644
--- a/www/cxx_status.html
+++ b/www/cxx_status.html
@@ -1952,38 +1952,46 @@
<tr><td>E [extendid]</td><td></td><td></td><td></td><td></td><td></td></tr>
<tr>
<td colspan="6" align="center" bgcolor="#ffffcc">C++0x Features</td>
- <tr>
+</tr>
+<tr>
<td>Explicit conversion operators (<a href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2437.pdf">N2437</a>)</td>
<td class="complete" align="center">✓</td>
<td class="medium" align="center"></td>
<td class="advanced" align="center"></td>
<td class="broken"></td>
<td>No name mangling; ASTs don't contain calls to conversion operators</td>
- </tr>
- <tr>
+</tr>
+<tr>
<td>Static assertions (<a href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2004/n1720.pdf">N1720</a>)</td>
<td class="complete" align="center">✓</td>
<td class="complete" align="center">✓</td>
<td class="complete" align="center">✓</td>
<td class="na">N/A</td>
<td></td>
- </tr>
- <tr>
+</tr>
+<tr>
<td>Deleted functions (<a href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2346.htm">N2346</a>)</td>
<td class="complete" align="center">✓</td>
<td class="complete" align="center">✓</td>
<td class="complete" align="center">✓</td>
<td class="na">N/A</td>
<td></td>
- </tr>
- <tr>
+</tr>
+<tr>
<td>Rvalue references (<a href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2006/n2118.html">N2118</a> + <a href="http://www.open-std.org/JTC1/SC22/WG21/docs/papers/2009/n2831.html">N2831</a>)</td>
<td class="complete" align="center">✓</td>
<td class="complete" align="center">✓</td>
<td class="complete" align="center">✓</td>
<td class="broken"></td>
<td></td>
- </tr>
+</tr>
+<tr>
+ <td>nullptr (<a href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2431.pdf">N2431</a>)</td>
+ <td class="complete" align="center">✓</td>
+ <td class="complete" align="center">✓</td>
+ <td class="complete" align="center">✓</td>
+ <td class="broken"></td>
+ <td></td>
</tr>
</table>