diff --git a/clang/include/clang/AST/CXXRecordDeclDefinitionBits.def b/clang/include/clang/AST/CXXRecordDeclDefinitionBits.def --- a/clang/include/clang/AST/CXXRecordDeclDefinitionBits.def +++ b/clang/include/clang/AST/CXXRecordDeclDefinitionBits.def @@ -214,6 +214,10 @@ /// member or base class of non-literal or volatile type. FIELD(HasNonLiteralTypeFieldsOrBases, 1, NO_MERGE) +/// True when this class contains at least one non-static data +/// member of non-volatile literal type. +FIELD(HasNonVolatileLiteralTypeFields, 1, NO_MERGE) + /// True if this class is a structural type, assuming it is a literal type. FIELD(StructuralIfLiteral, 1, NO_MERGE) diff --git a/clang/include/clang/AST/DeclCXX.h b/clang/include/clang/AST/DeclCXX.h --- a/clang/include/clang/AST/DeclCXX.h +++ b/clang/include/clang/AST/DeclCXX.h @@ -1354,6 +1354,12 @@ return data().HasNonLiteralTypeFieldsOrBases; } + /// Determine whether this class has a non-volatile literal type + /// non-static data member. + bool hasNonVolatileLiteralTypeFields() const { + return data().HasNonVolatileLiteralTypeFields; + } + /// Determine whether this class has a using-declaration that names /// a user-declared base class constructor. bool hasInheritedConstructor() const { @@ -1396,12 +1402,19 @@ /// treating types with trivial default constructors as literal types. /// /// Only in C++17 and beyond, are lambdas literal types. + /// A union is a literal type if at least one of its non-static data members + /// is of non-volatile literal type. bool isLiteral() const { const LangOptions &LangOpts = getLangOpts(); + + bool FieldsOk = !hasNonLiteralTypeFieldsOrBases(); + if (LangOpts.CPlusPlus17 && isUnion()) { + FieldsOk = !hasDirectFields() || hasNonVolatileLiteralTypeFields(); + } + return (LangOpts.CPlusPlus20 ? hasConstexprDestructor() - : hasTrivialDestructor()) && - (!isLambda() || LangOpts.CPlusPlus17) && - !hasNonLiteralTypeFieldsOrBases() && + : hasTrivialDestructor()) && + (!isLambda() || LangOpts.CPlusPlus17) && FieldsOk && (isAggregate() || isLambda() || hasConstexprNonCopyMoveConstructor() || hasTrivialDefaultConstructor()); diff --git a/clang/lib/AST/DeclCXX.cpp b/clang/lib/AST/DeclCXX.cpp --- a/clang/lib/AST/DeclCXX.cpp +++ b/clang/lib/AST/DeclCXX.cpp @@ -81,8 +81,7 @@ HasPublicFields(false), HasMutableFields(false), HasVariantMembers(false), HasOnlyCMembers(true), HasInClassInitializer(false), HasUninitializedReferenceMember(false), HasUninitializedFields(false), - HasInheritedConstructor(false), - HasInheritedDefaultConstructor(false), + HasInheritedConstructor(false), HasInheritedDefaultConstructor(false), HasInheritedAssignment(false), NeedOverloadResolutionForCopyConstructor(false), NeedOverloadResolutionForMoveConstructor(false), @@ -102,7 +101,8 @@ DefaultedDefaultConstructorIsConstexpr(true), HasConstexprDefaultConstructor(false), DefaultedDestructorIsConstexpr(true), - HasNonLiteralTypeFieldsOrBases(false), StructuralIfLiteral(true), + HasNonLiteralTypeFieldsOrBases(false), + HasNonVolatileLiteralTypeFields(false), StructuralIfLiteral(true), UserProvidedDefaultConstructor(false), DeclaredSpecialMembers(0), ImplicitCopyConstructorCanHaveConstParamForVBase(true), ImplicitCopyConstructorCanHaveConstParamForNonVBase(true), @@ -1103,8 +1103,12 @@ } } + const bool IsNonVolatileLiteralTypeField = + T->isLiteralType(Context) && !T.isVolatileQualified(); // Record if this field is the first non-literal or volatile field or base. - if (!T->isLiteralType(Context) || T.isVolatileQualified()) + if (IsNonVolatileLiteralTypeField) + data().HasNonVolatileLiteralTypeFields = true; + else data().HasNonLiteralTypeFieldsOrBases = true; if (Field->hasInClassInitializer() || diff --git a/clang/test/CXX/basic/basic.types/p10.cpp b/clang/test/CXX/basic/basic.types/p10.cpp --- a/clang/test/CXX/basic/basic.types/p10.cpp +++ b/clang/test/CXX/basic/basic.types/p10.cpp @@ -1,5 +1,6 @@ -// RUN: %clang_cc1 -fsyntax-only -verify -std=c++11 %s -// RUN: %clang_cc1 -fsyntax-only -verify -std=c++1y %s -DCXX1Y +// RUN: %clang_cc1 -fsyntax-only -verify -std=c++11 %s -DCXX11 -DCXX11_OR_LATER +// RUN: %clang_cc1 -fsyntax-only -verify -std=c++1y %s -DCXX1Y -DCXX11_OR_LATER -DCXX1Y_OR_LATER +// RUN: %clang_cc1 -fsyntax-only -verify -std=c++17 %s -DCXX17 -DCXX11_OR_LATER -DCXX1Y_OR_LATER -DCXX17_OR_LATER struct NonLiteral { NonLiteral(); }; @@ -7,7 +8,7 @@ // [C++1y] - void constexpr void f() {} -#ifndef CXX1Y +#ifdef CXX11 // expected-error@-2 {{'void' is not a literal type}} #endif @@ -20,7 +21,7 @@ struct BeingDefined; extern BeingDefined beingdefined; -struct BeingDefined { +struct BeingDefined { static constexpr BeingDefined& t = beingdefined; }; @@ -64,8 +65,14 @@ }; constexpr int f(TrivDefaultedDtor) { return 0; } -// - it is an aggregate type or has at least one constexpr constructor or +// - it is [a closure type [C++17]], an aggregate type or has at least one constexpr constructor or // constexpr constructor template that is not a copy or move constructor +#ifndef CXX17_OR_LATER +// expected-error@+4 {{not a literal type}} +// expected-note@+2 {{lambda closure types are non-literal types before C++17}} +#endif +auto closure = [] {}; +constexpr int f(decltype(closure)) { return 0; } struct Agg { int a; char *b; @@ -101,7 +108,8 @@ template constexpr DerivedFromVBase::DerivedFromVBase() : T() {} constexpr int nVBase = (DerivedFromVBase(), 0); // expected-error {{constant expression}} expected-note {{cannot construct object of type 'DerivedFromVBase' with virtual base class in a constant expression}} -// - it has all non-static data members and base classes of literal types +// - [C++17] if it is a union, at least one of its non-static data members is of non-volatile literal type, and +// - [if it is not a union [C++17]] it has all non-static data members and base classes of literal types struct NonLitMember { S s; // expected-note {{has data member 's' of non-literal type 'S'}} }; @@ -122,6 +130,19 @@ constexpr int f(MemberType) { return 0; } constexpr int f(MemberType) { return 0; } // expected-error {{not a literal type}} +template +union UnionType { + char c; + T t; + constexpr UnionType() : c{} {} +}; +constexpr int f(UnionType) { return 0; } +#ifndef CXX17_OR_LATER +// expected-error@+3 {{not a literal type}} +// expected-note@-6 {{'UnionType' is not literal because it has data member 't' of non-literal type 'NonLiteral'}} +#endif +constexpr int f(UnionType) { return 0; } + // - an array of literal type [C++1y] other than an array of runtime bound struct ArrGood { Agg agg[24];