Fix nothrow trait with multiple default constructors

Check all default ctors, not just the first one we see. This brings
__has_nothrow_constructor() in line with the other unary type traits.

A C++ class can have multiple default constructors but clang was only checking
the first one written, presumably due to ambiguity in the GNU specification.

MSVC has the same bug, while g++ has the correct implementation which we now
match.

llvm-svn: 199618
This commit is contained in:
Alp Toker 2014-01-20 00:23:47 +00:00
parent f600441a04
commit b4bca41491
2 changed files with 18 additions and 3 deletions

View File

@ -3516,7 +3516,7 @@ static bool EvaluateUnaryTypeTrait(Sema &Self, TypeTrait UTT,
} }
return false; return false;
case UTT_HasNothrowConstructor: case UTT_HasNothrowConstructor:
// http://gcc.gnu.org/onlinedocs/gcc/Type-Traits.html: // http://gcc.gnu.org/onlinedocs/gcc/Type-Traits.html
// If __has_trivial_constructor (type) is true then the trait is // If __has_trivial_constructor (type) is true then the trait is
// true, else if type is a cv class or union type (or array // true, else if type is a cv class or union type (or array
// thereof) with a default constructor that is known not to // thereof) with a default constructor that is known not to
@ -3528,6 +3528,7 @@ static bool EvaluateUnaryTypeTrait(Sema &Self, TypeTrait UTT,
!RD->hasNonTrivialDefaultConstructor()) !RD->hasNonTrivialDefaultConstructor())
return true; return true;
bool FoundConstructor = false;
DeclContext::lookup_const_result R = Self.LookupConstructors(RD); DeclContext::lookup_const_result R = Self.LookupConstructors(RD);
for (DeclContext::lookup_const_iterator Con = R.begin(), for (DeclContext::lookup_const_iterator Con = R.begin(),
ConEnd = R.end(); Con != ConEnd; ++Con) { ConEnd = R.end(); Con != ConEnd; ++Con) {
@ -3536,16 +3537,19 @@ static bool EvaluateUnaryTypeTrait(Sema &Self, TypeTrait UTT,
continue; continue;
CXXConstructorDecl *Constructor = cast<CXXConstructorDecl>(*Con); CXXConstructorDecl *Constructor = cast<CXXConstructorDecl>(*Con);
if (Constructor->isDefaultConstructor()) { if (Constructor->isDefaultConstructor()) {
FoundConstructor = true;
const FunctionProtoType *CPT const FunctionProtoType *CPT
= Constructor->getType()->getAs<FunctionProtoType>(); = Constructor->getType()->getAs<FunctionProtoType>();
CPT = Self.ResolveExceptionSpec(KeyLoc, CPT); CPT = Self.ResolveExceptionSpec(KeyLoc, CPT);
if (!CPT) if (!CPT)
return false; return false;
// TODO: check whether evaluating default arguments can throw. // FIXME: check whether evaluating default arguments can throw.
// For now, we'll be conservative and assume that they can throw. // For now, we'll be conservative and assume that they can throw.
return CPT->isNothrow(Self.Context) && CPT->getNumArgs() == 0; if (!CPT->isNothrow(Self.Context) || CPT->getNumArgs() > 0)
return false;
} }
} }
return FoundConstructor;
} }
return false; return false;
case UTT_HasVirtualDestructor: case UTT_HasVirtualDestructor:

View File

@ -112,6 +112,14 @@ struct HasNoThrowConstructor { HasNoThrowConstructor() throw(); };
struct HasNoThrowConstructorWithArgs { struct HasNoThrowConstructorWithArgs {
HasNoThrowConstructorWithArgs(HasCons i = HasCons(0)) throw(); HasNoThrowConstructorWithArgs(HasCons i = HasCons(0)) throw();
}; };
struct HasMultipleDefaultConstructor1 {
HasMultipleDefaultConstructor1() throw();
HasMultipleDefaultConstructor1(int i = 0);
};
struct HasMultipleDefaultConstructor2 {
HasMultipleDefaultConstructor2(int i = 0);
HasMultipleDefaultConstructor2() throw();
};
struct HasNoThrowCopy { HasNoThrowCopy(const HasNoThrowCopy&) throw(); }; struct HasNoThrowCopy { HasNoThrowCopy(const HasNoThrowCopy&) throw(); };
struct HasMultipleCopy { struct HasMultipleCopy {
@ -1562,6 +1570,9 @@ void has_nothrow_constructor() {
{ int arr[F(__has_nothrow_constructor(void))]; } { int arr[F(__has_nothrow_constructor(void))]; }
{ int arr[F(__has_nothrow_constructor(cvoid))]; } { int arr[F(__has_nothrow_constructor(cvoid))]; }
{ int arr[F(__has_nothrow_constructor(HasTemplateCons))]; } { int arr[F(__has_nothrow_constructor(HasTemplateCons))]; }
{ int arr[F(__has_nothrow_constructor(HasMultipleDefaultConstructor1))]; }
{ int arr[F(__has_nothrow_constructor(HasMultipleDefaultConstructor2))]; }
} }
void has_virtual_destructor() { void has_virtual_destructor() {