Fix rejects-valid caused by r261297.

r261297 called hasUserProvidedDefaultConstructor() to check if defining a
const object is ok.  This is incorrect for this example:

  struct X { template<typename ...T> X(T...); int n; };
  const X x; // formerly OK, now bogus error

Instead, track if a class has a defaulted default constructor, and disallow
a const object for classes that either have defaulted default constructors or
if they need an implicit constructor.

Bug report and fix approach by Richard Smith, thanks!

llvm-svn: 261770
This commit is contained in:
Nico Weber 2016-02-24 20:58:14 +00:00
parent 57ca270b7a
commit 72c57f49c4
6 changed files with 47 additions and 1 deletions

View File

@ -421,6 +421,10 @@ class CXXRecordDecl : public RecordDecl {
/// constructor which is neither the copy nor move constructor.
bool HasConstexprNonCopyMoveConstructor : 1;
/// \brief True if this class has a (possibly implicit) defaulted default
/// constructor.
bool HasDefaultedDefaultConstructor : 1;
/// \brief True if a defaulted default constructor for this class would
/// be constexpr.
bool DefaultedDefaultConstructorIsConstexpr : 1;
@ -1278,7 +1282,8 @@ public:
/// per core issue 253.
bool allowConstDefaultInit() const {
return !data().HasUninitializedFields ||
hasUserProvidedDefaultConstructor();
!(data().HasDefaultedDefaultConstructor ||
needsImplicitDefaultConstructor());
}
/// \brief Determine whether this class has a destructor which has no

View File

@ -2040,6 +2040,8 @@ bool ASTNodeImporter::ImportDefinition(RecordDecl *From, RecordDecl *To,
ToData.HasIrrelevantDestructor = FromData.HasIrrelevantDestructor;
ToData.HasConstexprNonCopyMoveConstructor
= FromData.HasConstexprNonCopyMoveConstructor;
ToData.HasDefaultedDefaultConstructor
= FromData.HasDefaultedDefaultConstructor;
ToData.DefaultedDefaultConstructorIsConstexpr
= FromData.DefaultedDefaultConstructorIsConstexpr;
ToData.HasConstexprDefaultConstructor

View File

@ -61,6 +61,7 @@ CXXRecordDecl::DefinitionData::DefinitionData(CXXRecordDecl *D)
DefaultedDestructorIsDeleted(false), HasTrivialSpecialMembers(SMF_All),
DeclaredNonTrivialSpecialMembers(0), HasIrrelevantDestructor(true),
HasConstexprNonCopyMoveConstructor(false),
HasDefaultedDefaultConstructor(false),
DefaultedDefaultConstructorIsConstexpr(true),
HasConstexprDefaultConstructor(false),
HasNonLiteralTypeFieldsOrBases(false), ComputedVisibleConversions(false),
@ -497,6 +498,8 @@ void CXXRecordDecl::addedMember(Decl *D) {
data().UserProvidedDefaultConstructor = true;
if (Constructor->isConstexpr())
data().HasConstexprDefaultConstructor = true;
if (Constructor->isDefaulted())
data().HasDefaultedDefaultConstructor = true;
}
if (!FunTmpl) {

View File

@ -1423,6 +1423,7 @@ void ASTDeclReader::ReadCXXDefinitionData(
Data.DeclaredNonTrivialSpecialMembers = Record[Idx++];
Data.HasIrrelevantDestructor = Record[Idx++];
Data.HasConstexprNonCopyMoveConstructor = Record[Idx++];
Data.HasDefaultedDefaultConstructor = Record[Idx++];
Data.DefaultedDefaultConstructorIsConstexpr = Record[Idx++];
Data.HasConstexprDefaultConstructor = Record[Idx++];
Data.HasNonLiteralTypeFieldsOrBases = Record[Idx++];
@ -1548,6 +1549,7 @@ void ASTDeclReader::MergeDefinitionData(
OR_FIELD(DeclaredNonTrivialSpecialMembers)
MATCH_FIELD(HasIrrelevantDestructor)
OR_FIELD(HasConstexprNonCopyMoveConstructor)
OR_FIELD(HasDefaultedDefaultConstructor)
MATCH_FIELD(DefaultedDefaultConstructorIsConstexpr)
OR_FIELD(HasConstexprDefaultConstructor)
MATCH_FIELD(HasNonLiteralTypeFieldsOrBases)

View File

@ -5558,6 +5558,7 @@ void ASTWriter::AddCXXDefinitionData(const CXXRecordDecl *D, RecordDataImpl &Rec
Record.push_back(Data.DeclaredNonTrivialSpecialMembers);
Record.push_back(Data.HasIrrelevantDestructor);
Record.push_back(Data.HasConstexprNonCopyMoveConstructor);
Record.push_back(Data.HasDefaultedDefaultConstructor);
Record.push_back(Data.DefaultedDefaultConstructorIsConstexpr);
Record.push_back(Data.HasConstexprDefaultConstructor);
Record.push_back(Data.HasNonLiteralTypeFieldsOrBases);

View File

@ -74,6 +74,33 @@ struct some_init_container_ctor {
struct no_fields_container {
no_fields nf;
};
struct param_pack_ctor {
template <typename... T>
param_pack_ctor(T...);
int n;
};
struct param_pack_ctor_field {
param_pack_ctor ndc;
};
struct multi_param_pack_ctor {
template <typename... T, typename... U>
multi_param_pack_ctor(T..., U..., int f = 0);
int n;
};
struct ignored_template_ctor_and_def {
template <class T> ignored_template_ctor_and_def(T* f = nullptr);
ignored_template_ctor_and_def() = default;
int field;
};
template<bool, typename = void> struct enable_if {};
template<typename T> struct enable_if<true, T> { typedef T type; };
struct multi_param_pack_and_defaulted {
template <typename... T,
typename enable_if<sizeof...(T) != 0>::type* = nullptr>
multi_param_pack_and_defaulted(T...);
multi_param_pack_and_defaulted() = default;
int n;
};
void constobjs() {
const no_fields nf; // ok
@ -88,6 +115,12 @@ void constobjs() {
const some_init_container sicon; // expected-error {{default initialization of an object of const type 'const some_init_container' without a user-provided default constructor}}
const some_init_container_ctor siconc; // ok
const no_fields_container nfc; // ok
const param_pack_ctor ppc; // ok
const param_pack_ctor_field ppcf; // ok
const multi_param_pack_ctor mppc; // ok
const multi_param_pack_and_defaulted mppad; // expected-error {{default initialization of an object of const type 'const multi_param_pack_and_defaulted' without a user-provided default constructor}}
const ignored_template_ctor_and_def itcad; // expected-error {{default initialization of an object of const type 'const ignored_template_ctor_and_def' without a user-provided default constructor}}
}
struct non_const_derived : non_const_copy {