From: Richard Smith Date: Wed, 26 Sep 2018 19:00:16 +0000 (+0000) Subject: P1008R1 Classes with user-declared constructors are never aggregates in X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=de8e63fd00e24e58a5ca05007cf7954916a6e6b9;p=clang P1008R1 Classes with user-declared constructors are never aggregates in C++20. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@343131 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td index 6f0014ce18..5ec3e5ceae 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.td +++ b/include/clang/Basic/DiagnosticSemaKinds.td @@ -1872,6 +1872,9 @@ def err_reference_bind_init_list : Error< def err_init_list_bad_dest_type : Error< "%select{|non-aggregate }0type %1 cannot be initialized with an initializer " "list">; +def warn_cxx2a_compat_aggregate_init_with_ctors : Warning< + "aggregate initialization of type %0 with user-declared constructors " + "is incompatible with C++2a">, DefaultIgnore, InGroup; def err_reference_bind_to_bitfield : Error< "%select{non-const|volatile}0 reference cannot bind to " diff --git a/lib/AST/DeclCXX.cpp b/lib/AST/DeclCXX.cpp index 5c70869491..00ae7c9cda 100644 --- a/lib/AST/DeclCXX.cpp +++ b/lib/AST/DeclCXX.cpp @@ -731,9 +731,14 @@ void CXXRecordDecl::addedMember(Decl *D) { } // C++11 [dcl.init.aggr]p1: DR1518 - // An aggregate is an array or a class with no user-provided, explicit, or - // inherited constructors - if (Constructor->isUserProvided() || Constructor->isExplicit()) + // An aggregate is an array or a class with no user-provided [or] + // explicit [...] constructors + // C++20 [dcl.init.aggr]p1: + // An aggregate is an array or a class with no user-declared [...] + // constructors + if (getASTContext().getLangOpts().CPlusPlus2a + ? !Constructor->isImplicit() + : (Constructor->isUserProvided() || Constructor->isExplicit())) data().Aggregate = false; } diff --git a/lib/Sema/SemaInit.cpp b/lib/Sema/SemaInit.cpp index a25818f0fd..a678a31a5a 100644 --- a/lib/Sema/SemaInit.cpp +++ b/lib/Sema/SemaInit.cpp @@ -964,6 +964,14 @@ void InitListChecker::CheckImplicitInitList(const InitializedEntity &Entity, StructuredSubobjectInitList->getEndLoc()), "}"); } + + // Warn if this type won't be an aggregate in future versions of C++. + auto *CXXRD = T->getAsCXXRecordDecl(); + if (CXXRD && CXXRD->hasUserDeclaredConstructor()) { + SemaRef.Diag(StructuredSubobjectInitList->getBeginLoc(), + diag::warn_cxx2a_compat_aggregate_init_with_ctors) + << StructuredSubobjectInitList->getSourceRange() << T; + } } } @@ -1106,9 +1114,30 @@ void InitListChecker::CheckExplicitInitList(const InitializedEntity &Entity, } } - if (!VerifyOnly && T->isScalarType() && - IList->getNumInits() == 1 && !isa(IList->getInit(0))) - warnBracedScalarInit(SemaRef, Entity, IList->getSourceRange()); + if (!VerifyOnly) { + if (T->isScalarType() && IList->getNumInits() == 1 && + !isa(IList->getInit(0))) + warnBracedScalarInit(SemaRef, Entity, IList->getSourceRange()); + + // Warn if this is a class type that won't be an aggregate in future + // versions of C++. + auto *CXXRD = T->getAsCXXRecordDecl(); + if (CXXRD && CXXRD->hasUserDeclaredConstructor()) { + // Don't warn if there's an equivalent default constructor that would be + // used instead. + bool HasEquivCtor = false; + if (IList->getNumInits() == 0) { + auto *CD = SemaRef.LookupDefaultConstructor(CXXRD); + HasEquivCtor = CD && !CD->isDeleted(); + } + + if (!HasEquivCtor) { + SemaRef.Diag(IList->getBeginLoc(), + diag::warn_cxx2a_compat_aggregate_init_with_ctors) + << IList->getSourceRange() << T; + } + } + } } void InitListChecker::CheckListElementTypes(const InitializedEntity &Entity, diff --git a/test/SemaCXX/cxx2a-compat.cpp b/test/SemaCXX/cxx2a-compat.cpp new file mode 100644 index 0000000000..53043d67fb --- /dev/null +++ b/test/SemaCXX/cxx2a-compat.cpp @@ -0,0 +1,23 @@ +// RUN: %clang_cc1 -fsyntax-only -std=c++17 -Wc++2a-compat-pedantic -verify %s +// RUN: %clang_cc1 -fsyntax-only -std=c++2a -pedantic -verify %s + +struct A { // expected-note 0+{{candidate}} + A() = default; // expected-note 0+{{candidate}} + int x, y; +}; +A a1 = {1, 2}; +#if __cplusplus <= 201703L + // expected-warning@-2 {{aggregate initialization of type 'A' with user-declared constructors is incompatible with C++2a}} +#else + // expected-error@-4 {{no matching constructor}} +#endif +A a2 = {}; + +struct B : A { A a; }; +B b1 = {{}, {}}; // ok +B b2 = {1, 2, 3, 4}; +#if __cplusplus <= 201703L + // expected-warning@-2 2{{aggregate initialization of type 'A' with user-declared constructors is incompatible with C++2a}} +#else + // expected-error@-4 2{{no viable conversion from 'int' to 'A'}} +#endif diff --git a/test/SemaCXX/cxx2a-initializer-aggregates.cpp b/test/SemaCXX/cxx2a-initializer-aggregates.cpp new file mode 100644 index 0000000000..9c438d399b --- /dev/null +++ b/test/SemaCXX/cxx2a-initializer-aggregates.cpp @@ -0,0 +1,23 @@ +// RUN: %clang_cc1 -std=c++2a %s -verify + +namespace class_with_ctor { + struct A { // expected-note 6{{candidate}} + A() = default; // expected-note 3{{candidate}} + int x; + int y; + }; + A a = {1, 2}; // expected-error {{no matching constructor}} + + struct B { + int x; + int y; + }; + B b1 = B(); // trigger declaration of implicit ctors + B b2 = {1, 2}; // ok + + struct C : A { + A a; + }; + C c1 = {{}, {}}; // ok, call default ctor twice + C c2 = {{1, 2}, {3, 4}}; // expected-error 2{{no matching constructor}} +} diff --git a/www/cxx_status.html b/www/cxx_status.html index 0d0add1584..3ba8c9c7d0 100755 --- a/www/cxx_status.html +++ b/www/cxx_status.html @@ -952,7 +952,7 @@ as the draft C++2a standard evolves. Prohibit aggregates with user-declared constructors P1008R1 - No + SVN Contracts