From 505ef81422b96a1e3ff87b942ff1e1a45f3d0f00 Mon Sep 17 00:00:00 2001 From: Richard Smith Date: Wed, 21 Dec 2016 01:57:02 +0000 Subject: [PATCH] Fix defaulted-functions-in-C++98 extension to give the functions the same effect they would have in C++11. In particular, they do not prevent value-initialization from performing zero-initialization, nor do they prevent a struct from being an aggregate. llvm-svn: 290229 --- clang/lib/AST/DeclCXX.cpp | 11 +++------ clang/lib/Sema/SemaInit.cpp | 32 ++++++++++++------------- clang/test/CodeGenCXX/global-init.cpp | 2 ++ clang/test/CodeGenCXX/value-init.cpp | 10 +++++++- clang/test/SemaCXX/aggregate-init-cxx98.cpp | 7 ++++++ clang/test/SemaCXX/aggregate-initialization.cpp | 2 -- 6 files changed, 36 insertions(+), 28 deletions(-) create mode 100644 clang/test/SemaCXX/aggregate-init-cxx98.cpp diff --git a/clang/lib/AST/DeclCXX.cpp b/clang/lib/AST/DeclCXX.cpp index 1860b94..a9db65a 100644 --- a/clang/lib/AST/DeclCXX.cpp +++ b/clang/lib/AST/DeclCXX.cpp @@ -534,15 +534,10 @@ void CXXRecordDecl::addedMember(Decl *D) { SMKind |= SMF_MoveConstructor; } - // C++ [dcl.init.aggr]p1: - // An aggregate is an array or a class with no user-declared - // constructors [...]. // C++11 [dcl.init.aggr]p1: DR1518 - // An aggregate is an array or a class with no user-provided, explicit, or - // inherited constructors - if (getASTContext().getLangOpts().CPlusPlus11 - ? (Constructor->isUserProvided() || Constructor->isExplicit()) - : !Constructor->isImplicit()) + // An aggregate is an array or a class with no user-provided, explicit, or + // inherited constructors + if (Constructor->isUserProvided() || Constructor->isExplicit()) data().Aggregate = false; } diff --git a/clang/lib/Sema/SemaInit.cpp b/clang/lib/Sema/SemaInit.cpp index 515a667..5bb5518 100644 --- a/clang/lib/Sema/SemaInit.cpp +++ b/clang/lib/Sema/SemaInit.cpp @@ -4539,23 +4539,21 @@ static void TryValueInitialization(Sema &S, if (const RecordType *RT = T->getAs()) { if (CXXRecordDecl *ClassDecl = dyn_cast(RT->getDecl())) { bool NeedZeroInitialization = true; - if (!S.getLangOpts().CPlusPlus11) { - // C++98: - // -- if T is a class type (clause 9) with a user-declared constructor - // (12.1), then the default constructor for T is called (and the - // initialization is ill-formed if T has no accessible default - // constructor); - if (ClassDecl->hasUserDeclaredConstructor()) - NeedZeroInitialization = false; - } else { - // C++11: - // -- if T is a class type (clause 9) with either no default constructor - // (12.1 [class.ctor]) or a default constructor that is user-provided - // or deleted, then the object is default-initialized; - CXXConstructorDecl *CD = S.LookupDefaultConstructor(ClassDecl); - if (!CD || !CD->getCanonicalDecl()->isDefaulted() || CD->isDeleted()) - NeedZeroInitialization = false; - } + // C++98: + // -- if T is a class type (clause 9) with a user-declared constructor + // (12.1), then the default constructor for T is called (and the + // initialization is ill-formed if T has no accessible default + // constructor); + // C++11: + // -- if T is a class type (clause 9) with either no default constructor + // (12.1 [class.ctor]) or a default constructor that is user-provided + // or deleted, then the object is default-initialized; + // + // Note that the C++11 rule is the same as the C++98 rule if there are no + // defaulted or deleted constructors, so we just use it unconditionally. + CXXConstructorDecl *CD = S.LookupDefaultConstructor(ClassDecl); + if (!CD || !CD->getCanonicalDecl()->isDefaulted() || CD->isDeleted()) + NeedZeroInitialization = false; // -- if T is a (possibly cv-qualified) non-union class type without a // user-provided or deleted default constructor, then the object is diff --git a/clang/test/CodeGenCXX/global-init.cpp b/clang/test/CodeGenCXX/global-init.cpp index e806af9..8144a97 100644 --- a/clang/test/CodeGenCXX/global-init.cpp +++ b/clang/test/CodeGenCXX/global-init.cpp @@ -171,6 +171,8 @@ namespace test7 { const int b3 = B().n; // CHECK-NOT: @_ZN5test7L2c1E + // CHECK: call void @llvm.memset{{.*}} @_ZN5test7L2c1E + // CHECK-NOT: @_ZN5test7L2c1E // CHECK: @_ZN5test7L2c2E // CHECK-NOT: @_ZN5test7L2c3E // CHECK: @_ZN5test7L2c4E diff --git a/clang/test/CodeGenCXX/value-init.cpp b/clang/test/CodeGenCXX/value-init.cpp index 735dc61..fc4e0d3 100644 --- a/clang/test/CodeGenCXX/value-init.cpp +++ b/clang/test/CodeGenCXX/value-init.cpp @@ -1,4 +1,4 @@ -// RUN: %clang_cc1 %s -triple x86_64-apple-darwin10 -emit-llvm -o - | FileCheck %s +// RUN: %clang_cc1 -std=c++98 %s -triple x86_64-apple-darwin10 -emit-llvm -o - | FileCheck %s struct A { virtual ~A(); @@ -315,6 +315,14 @@ namespace PR20256 { // CHECK: } } +// CHECK-LABEL: define {{.*}}@_Z20explicitly_defaultedv +int explicitly_defaulted() { + struct A { A() = default; int n; }; + // CHECK: call void @llvm.memset + A a = A(); + return a.n; +} // CHECK-LABEL: } + // CHECK-LABEL: define linkonce_odr void @_ZN8zeroinit2X3IiEC2Ev(%"struct.zeroinit::X3"* %this) unnamed_addr // CHECK: call void @llvm.memset.p0i8.i64 // CHECK-NEXT: call void @_ZN8zeroinit2X2IiEC2Ev diff --git a/clang/test/SemaCXX/aggregate-init-cxx98.cpp b/clang/test/SemaCXX/aggregate-init-cxx98.cpp new file mode 100644 index 0000000..332801f --- /dev/null +++ b/clang/test/SemaCXX/aggregate-init-cxx98.cpp @@ -0,0 +1,7 @@ +// RUN: %clang_cc1 -std=c++98 -verify %s + +struct A { + A() = default; // expected-warning {{C++11}} + int n; +}; +A a = {0}; diff --git a/clang/test/SemaCXX/aggregate-initialization.cpp b/clang/test/SemaCXX/aggregate-initialization.cpp index b416963..7b6abd2 100644 --- a/clang/test/SemaCXX/aggregate-initialization.cpp +++ b/clang/test/SemaCXX/aggregate-initialization.cpp @@ -4,8 +4,6 @@ // Verify that using an initializer list for a non-aggregate looks for // constructors.. -// Note that due to a (likely) standard bug, this is technically an aggregate, -// but we do not treat it as one. struct NonAggr1 { // expected-note 2 {{candidate constructor}} NonAggr1(int, int) { } // expected-note {{candidate constructor}} -- 2.7.4