forked from OSchip/llvm-project
Audit and finish the implementation of C++0x nullptr, fixing two
minor issues along the way: - Non-type template parameters of type 'std::nullptr_t' were not permitted. - We didn't properly introduce built-in operators for nullptr ==, !=, <, <=, >=, or > as candidate functions . To my knowledge, there's only one (minor but annoying) part of nullptr that hasn't been implemented: catching a thrown 'nullptr' as a pointer or pointer-to-member, per C++0x [except.handle]p4. llvm-svn: 131813
This commit is contained in:
parent
babca9aee9
commit
80af31397a
|
@ -449,8 +449,7 @@ is enabled. clang does not currently implement this feature.</p>
|
||||||
|
|
||||||
<p>Use <tt>__has_feature(cxx_nullptr)</tt> or
|
<p>Use <tt>__has_feature(cxx_nullptr)</tt> or
|
||||||
<tt>__has_extension(cxx_nullptr)</tt> to determine if support for
|
<tt>__has_extension(cxx_nullptr)</tt> to determine if support for
|
||||||
<tt>nullptr</tt> is enabled. clang does not yet fully implement this
|
<tt>nullptr</tt> is enabled.</p>
|
||||||
feature.</p>
|
|
||||||
|
|
||||||
<h4 id="cxx_override_control">C++0x <tt>override control</tt></h3>
|
<h4 id="cxx_override_control">C++0x <tt>override control</tt></h3>
|
||||||
|
|
||||||
|
|
|
@ -571,7 +571,7 @@ static bool HasFeature(const Preprocessor &PP, const IdentifierInfo *II) {
|
||||||
.Case("cxx_inline_namespaces", LangOpts.CPlusPlus0x)
|
.Case("cxx_inline_namespaces", LangOpts.CPlusPlus0x)
|
||||||
//.Case("cxx_lambdas", false)
|
//.Case("cxx_lambdas", false)
|
||||||
.Case("cxx_noexcept", LangOpts.CPlusPlus0x)
|
.Case("cxx_noexcept", LangOpts.CPlusPlus0x)
|
||||||
//.Case("cxx_nullptr", false)
|
.Case("cxx_nullptr", LangOpts.CPlusPlus0x)
|
||||||
.Case("cxx_override_control", LangOpts.CPlusPlus0x)
|
.Case("cxx_override_control", LangOpts.CPlusPlus0x)
|
||||||
.Case("cxx_range_for", LangOpts.CPlusPlus0x)
|
.Case("cxx_range_for", LangOpts.CPlusPlus0x)
|
||||||
.Case("cxx_reference_qualified_functions", LangOpts.CPlusPlus0x)
|
.Case("cxx_reference_qualified_functions", LangOpts.CPlusPlus0x)
|
||||||
|
|
|
@ -4705,6 +4705,10 @@ class BuiltinCandidateTypeSet {
|
||||||
/// were present in the candidate set.
|
/// were present in the candidate set.
|
||||||
bool HasArithmeticOrEnumeralTypes;
|
bool HasArithmeticOrEnumeralTypes;
|
||||||
|
|
||||||
|
/// \brief A flag indicating whether the nullptr type was present in the
|
||||||
|
/// candidate set.
|
||||||
|
bool HasNullPtrType;
|
||||||
|
|
||||||
/// Sema - The semantic analysis instance where we are building the
|
/// Sema - The semantic analysis instance where we are building the
|
||||||
/// candidate type set.
|
/// candidate type set.
|
||||||
Sema &SemaRef;
|
Sema &SemaRef;
|
||||||
|
@ -4723,6 +4727,7 @@ public:
|
||||||
BuiltinCandidateTypeSet(Sema &SemaRef)
|
BuiltinCandidateTypeSet(Sema &SemaRef)
|
||||||
: HasNonRecordTypes(false),
|
: HasNonRecordTypes(false),
|
||||||
HasArithmeticOrEnumeralTypes(false),
|
HasArithmeticOrEnumeralTypes(false),
|
||||||
|
HasNullPtrType(false),
|
||||||
SemaRef(SemaRef),
|
SemaRef(SemaRef),
|
||||||
Context(SemaRef.Context) { }
|
Context(SemaRef.Context) { }
|
||||||
|
|
||||||
|
@ -4755,6 +4760,7 @@ public:
|
||||||
|
|
||||||
bool hasNonRecordTypes() { return HasNonRecordTypes; }
|
bool hasNonRecordTypes() { return HasNonRecordTypes; }
|
||||||
bool hasArithmeticOrEnumeralTypes() { return HasArithmeticOrEnumeralTypes; }
|
bool hasArithmeticOrEnumeralTypes() { return HasArithmeticOrEnumeralTypes; }
|
||||||
|
bool hasNullPtrType() const { return HasNullPtrType; }
|
||||||
};
|
};
|
||||||
|
|
||||||
/// AddPointerWithMoreQualifiedTypeVariants - Add the pointer type @p Ty to
|
/// AddPointerWithMoreQualifiedTypeVariants - Add the pointer type @p Ty to
|
||||||
|
@ -4915,6 +4921,8 @@ BuiltinCandidateTypeSet::AddTypesConvertedFrom(QualType Ty,
|
||||||
// extension.
|
// extension.
|
||||||
HasArithmeticOrEnumeralTypes = true;
|
HasArithmeticOrEnumeralTypes = true;
|
||||||
VectorTypes.insert(Ty);
|
VectorTypes.insert(Ty);
|
||||||
|
} else if (Ty->isNullPtrType()) {
|
||||||
|
HasNullPtrType = true;
|
||||||
} else if (AllowUserConversions && TyRec) {
|
} else if (AllowUserConversions && TyRec) {
|
||||||
// No conversion functions in incomplete types.
|
// No conversion functions in incomplete types.
|
||||||
if (SemaRef.RequireCompleteType(Loc, Ty, 0))
|
if (SemaRef.RequireCompleteType(Loc, Ty, 0))
|
||||||
|
@ -5374,8 +5382,8 @@ public:
|
||||||
|
|
||||||
// C++ [over.built]p15:
|
// C++ [over.built]p15:
|
||||||
//
|
//
|
||||||
// For every pointer or enumeration type T, there exist
|
// For every T, where T is an enumeration type, a pointer type, or
|
||||||
// candidate operator functions of the form
|
// std::nullptr_t, there exist candidate operator functions of the form
|
||||||
//
|
//
|
||||||
// bool operator<(T, T);
|
// bool operator<(T, T);
|
||||||
// bool operator>(T, T);
|
// bool operator>(T, T);
|
||||||
|
@ -5460,6 +5468,17 @@ public:
|
||||||
S.AddBuiltinCandidate(S.Context.BoolTy, ParamTypes, Args, 2,
|
S.AddBuiltinCandidate(S.Context.BoolTy, ParamTypes, Args, 2,
|
||||||
CandidateSet);
|
CandidateSet);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (CandidateTypes[ArgIdx].hasNullPtrType()) {
|
||||||
|
CanQualType NullPtrTy = S.Context.getCanonicalType(S.Context.NullPtrTy);
|
||||||
|
if (AddedTypes.insert(NullPtrTy) &&
|
||||||
|
!UserDefinedBinaryOperators.count(std::make_pair(NullPtrTy,
|
||||||
|
NullPtrTy))) {
|
||||||
|
QualType ParamTypes[2] = { NullPtrTy, NullPtrTy };
|
||||||
|
S.AddBuiltinCandidate(S.Context.BoolTy, ParamTypes, Args, 2,
|
||||||
|
CandidateSet);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -602,8 +602,10 @@ Sema::CheckNonTypeTemplateParameterType(QualType T, SourceLocation Loc) {
|
||||||
T->isPointerType() ||
|
T->isPointerType() ||
|
||||||
// -- reference to object or reference to function,
|
// -- reference to object or reference to function,
|
||||||
T->isReferenceType() ||
|
T->isReferenceType() ||
|
||||||
// -- pointer to member.
|
// -- pointer to member,
|
||||||
T->isMemberPointerType() ||
|
T->isMemberPointerType() ||
|
||||||
|
// -- std::nullptr_t.
|
||||||
|
T->isNullPtrType() ||
|
||||||
// If T is a dependent type, we can't do the check now, so we
|
// If T is a dependent type, we can't do the check now, so we
|
||||||
// assume that it is well-formed.
|
// assume that it is well-formed.
|
||||||
T->isDependentType())
|
T->isDependentType())
|
||||||
|
@ -3756,10 +3758,17 @@ ExprResult Sema::CheckTemplateArgument(NonTypeTemplateParmDecl *Param,
|
||||||
// from a template argument of type std::nullptr_t to a non-type
|
// from a template argument of type std::nullptr_t to a non-type
|
||||||
// template parameter of type pointer to object, pointer to
|
// template parameter of type pointer to object, pointer to
|
||||||
// function, or pointer-to-member, respectively.
|
// function, or pointer-to-member, respectively.
|
||||||
if (ArgType->isNullPtrType() &&
|
if (ArgType->isNullPtrType()) {
|
||||||
(ParamType->isPointerType() || ParamType->isMemberPointerType())) {
|
if (ParamType->isPointerType() || ParamType->isMemberPointerType()) {
|
||||||
Converted = TemplateArgument((NamedDecl *)0);
|
Converted = TemplateArgument((NamedDecl *)0);
|
||||||
return Owned(Arg);
|
return Owned(Arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ParamType->isNullPtrType()) {
|
||||||
|
llvm::APSInt Zero(Context.getTypeSize(Context.NullPtrTy), true);
|
||||||
|
Converted = TemplateArgument(Zero, Context.NullPtrTy);
|
||||||
|
return Owned(Arg);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle pointer-to-function, reference-to-function, and
|
// Handle pointer-to-function, reference-to-function, and
|
||||||
|
@ -4053,6 +4062,9 @@ Sema::BuildExpressionFromIntegralTemplateArgument(const TemplateArgument &Arg,
|
||||||
Arg.getAsIntegral()->getBoolValue(),
|
Arg.getAsIntegral()->getBoolValue(),
|
||||||
T, Loc));
|
T, Loc));
|
||||||
|
|
||||||
|
if (T->isNullPtrType())
|
||||||
|
return Owned(new (Context) CXXNullPtrLiteralExpr(Context.NullPtrTy, Loc));
|
||||||
|
|
||||||
// If this is an enum type that we're instantiating, we need to use an integer
|
// If this is an enum type that we're instantiating, we need to use an integer
|
||||||
// type the same size as the enumerator. We don't want to build an
|
// type the same size as the enumerator. We don't want to build an
|
||||||
// IntegerLiteral with enum type.
|
// IntegerLiteral with enum type.
|
||||||
|
|
|
@ -17,7 +17,7 @@ int has_nullptr();
|
||||||
int no_nullptr();
|
int no_nullptr();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// CHECK-0X: no_nullptr
|
// CHECK-0X: has_nullptr
|
||||||
// CHECK-NO-0X: no_nullptr
|
// CHECK-NO-0X: no_nullptr
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -60,6 +60,10 @@ nullptr_t f(nullptr_t null)
|
||||||
|
|
||||||
// You can reinterpret_cast nullptr to an integer.
|
// You can reinterpret_cast nullptr to an integer.
|
||||||
(void)reinterpret_cast<uintptr_t>(nullptr);
|
(void)reinterpret_cast<uintptr_t>(nullptr);
|
||||||
|
(void)reinterpret_cast<uintptr_t>(*pn);
|
||||||
|
|
||||||
|
int *ip = *pn;
|
||||||
|
if (*pn) { }
|
||||||
|
|
||||||
// You can throw nullptr.
|
// You can throw nullptr.
|
||||||
throw nullptr;
|
throw nullptr;
|
||||||
|
@ -104,3 +108,56 @@ namespace test3 {
|
||||||
f("%p", nullptr);
|
f("%p", nullptr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int array0[__is_scalar(nullptr_t)? 1 : -1];
|
||||||
|
int array1[__is_pod(nullptr_t)? 1 : -1];
|
||||||
|
int array2[sizeof(nullptr_t) == sizeof(void*)? 1 : -1];
|
||||||
|
|
||||||
|
// FIXME: when we implement constexpr, this will be testable.
|
||||||
|
#if 0
|
||||||
|
int relational0[nullptr < nullptr? -1 : 1];
|
||||||
|
int relational1[nullptr > nullptr? -1 : 1];
|
||||||
|
int relational2[nullptr <= nullptr? 1 : -1];
|
||||||
|
int relational3[nullptr >= nullptr? 1 : -1];
|
||||||
|
int equality[nullptr == nullptr? 1 : -1];
|
||||||
|
int inequality[nullptr != nullptr? -1 : 1];
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace overloading {
|
||||||
|
int &f1(int*);
|
||||||
|
float &f1(bool);
|
||||||
|
|
||||||
|
void test_f1() {
|
||||||
|
int &ir = (f1)(nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ConvertsToNullPtr {
|
||||||
|
operator nullptr_t() const;
|
||||||
|
};
|
||||||
|
|
||||||
|
void test_conversion(ConvertsToNullPtr ctn) {
|
||||||
|
(void)(ctn == ctn);
|
||||||
|
(void)(ctn != ctn);
|
||||||
|
(void)(ctn <= ctn);
|
||||||
|
(void)(ctn >= ctn);
|
||||||
|
(void)(ctn < ctn);
|
||||||
|
(void)(ctn > ctn);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace templates {
|
||||||
|
template<typename T, nullptr_t Value>
|
||||||
|
struct X {
|
||||||
|
X() { ptr = Value; }
|
||||||
|
|
||||||
|
T *ptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
X<int, nullptr> x;
|
||||||
|
|
||||||
|
|
||||||
|
template<int (*fp)(int), int* p, int A::* pmd, int (A::*pmf)(int)>
|
||||||
|
struct X2 {};
|
||||||
|
|
||||||
|
X2<nullptr, nullptr, nullptr, nullptr> x2;
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue