llvm-project/clang/lib/AST/DeclCXX.cpp
Akira Hatanaka a3283a92ae
[PAC] Add support for __ptrauth type qualifier (#100830)
The qualifier allows programmer to directly control how pointers are
signed when they are stored in a particular variable.

The qualifier takes three arguments: the signing key, a flag specifying
whether address discrimination should be used, and a non-negative
integer that is used for additional discrimination.

```
typedef void (*my_callback)(const void*);
my_callback __ptrauth(ptrauth_key_process_dependent_code, 1, 0xe27a) callback;
```

Co-Authored-By: John McCall rjmccall@apple.com
2025-04-15 12:54:25 -07:00

3789 lines
147 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//===- DeclCXX.cpp - C++ Declaration AST Node Implementation --------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// This file implements the C++ related Decl classes.
//
//===----------------------------------------------------------------------===//
#include "clang/AST/DeclCXX.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/ASTLambda.h"
#include "clang/AST/ASTMutationListener.h"
#include "clang/AST/ASTUnresolvedSet.h"
#include "clang/AST/Attr.h"
#include "clang/AST/CXXInheritance.h"
#include "clang/AST/DeclBase.h"
#include "clang/AST/DeclTemplate.h"
#include "clang/AST/DeclarationName.h"
#include "clang/AST/Expr.h"
#include "clang/AST/ExprCXX.h"
#include "clang/AST/LambdaCapture.h"
#include "clang/AST/NestedNameSpecifier.h"
#include "clang/AST/ODRHash.h"
#include "clang/AST/Type.h"
#include "clang/AST/TypeLoc.h"
#include "clang/AST/UnresolvedSet.h"
#include "clang/Basic/Diagnostic.h"
#include "clang/Basic/DiagnosticAST.h"
#include "clang/Basic/IdentifierTable.h"
#include "clang/Basic/LLVM.h"
#include "clang/Basic/LangOptions.h"
#include "clang/Basic/OperatorKinds.h"
#include "clang/Basic/SourceLocation.h"
#include "clang/Basic/Specifiers.h"
#include "clang/Basic/TargetInfo.h"
#include "llvm/ADT/SmallPtrSet.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/iterator_range.h"
#include "llvm/Support/Casting.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/Format.h"
#include "llvm/Support/raw_ostream.h"
#include <algorithm>
#include <cassert>
#include <cstddef>
#include <cstdint>
using namespace clang;
//===----------------------------------------------------------------------===//
// Decl Allocation/Deallocation Method Implementations
//===----------------------------------------------------------------------===//
void AccessSpecDecl::anchor() {}
AccessSpecDecl *AccessSpecDecl::CreateDeserialized(ASTContext &C,
GlobalDeclID ID) {
return new (C, ID) AccessSpecDecl(EmptyShell());
}
void LazyASTUnresolvedSet::getFromExternalSource(ASTContext &C) const {
ExternalASTSource *Source = C.getExternalSource();
assert(Impl.Decls.isLazy() && "getFromExternalSource for non-lazy set");
assert(Source && "getFromExternalSource with no external source");
for (ASTUnresolvedSet::iterator I = Impl.begin(); I != Impl.end(); ++I)
I.setDecl(
cast<NamedDecl>(Source->GetExternalDecl(GlobalDeclID(I.getDeclID()))));
Impl.Decls.setLazy(false);
}
CXXRecordDecl::DefinitionData::DefinitionData(CXXRecordDecl *D)
: UserDeclaredConstructor(false), UserDeclaredSpecialMembers(0),
Aggregate(true), PlainOldData(true), Empty(true), Polymorphic(false),
Abstract(false), IsStandardLayout(true), IsCXX11StandardLayout(true),
HasBasesWithFields(false), HasBasesWithNonStaticDataMembers(false),
HasPrivateFields(false), HasProtectedFields(false),
HasPublicFields(false), HasMutableFields(false), HasVariantMembers(false),
HasOnlyCMembers(true), HasInitMethod(false), HasInClassInitializer(false),
HasUninitializedReferenceMember(false), HasUninitializedFields(false),
HasInheritedConstructor(false), HasInheritedDefaultConstructor(false),
HasInheritedAssignment(false),
NeedOverloadResolutionForCopyConstructor(false),
NeedOverloadResolutionForMoveConstructor(false),
NeedOverloadResolutionForCopyAssignment(false),
NeedOverloadResolutionForMoveAssignment(false),
NeedOverloadResolutionForDestructor(false),
DefaultedCopyConstructorIsDeleted(false),
DefaultedMoveConstructorIsDeleted(false),
DefaultedCopyAssignmentIsDeleted(false),
DefaultedMoveAssignmentIsDeleted(false),
DefaultedDestructorIsDeleted(false), HasTrivialSpecialMembers(SMF_All),
HasTrivialSpecialMembersForCall(SMF_All),
DeclaredNonTrivialSpecialMembers(0),
DeclaredNonTrivialSpecialMembersForCall(0), HasIrrelevantDestructor(true),
HasConstexprNonCopyMoveConstructor(false),
HasDefaultedDefaultConstructor(false),
DefaultedDefaultConstructorIsConstexpr(true),
HasConstexprDefaultConstructor(false),
DefaultedDestructorIsConstexpr(true),
HasNonLiteralTypeFieldsOrBases(false), StructuralIfLiteral(true),
UserProvidedDefaultConstructor(false), DeclaredSpecialMembers(0),
ImplicitCopyConstructorCanHaveConstParamForVBase(true),
ImplicitCopyConstructorCanHaveConstParamForNonVBase(true),
ImplicitCopyAssignmentHasConstParam(true),
HasDeclaredCopyConstructorWithConstParam(false),
HasDeclaredCopyAssignmentWithConstParam(false),
IsAnyDestructorNoReturn(false), IsHLSLIntangible(false), IsLambda(false),
IsParsingBaseSpecifiers(false), ComputedVisibleConversions(false),
HasODRHash(false), Definition(D) {}
CXXBaseSpecifier *CXXRecordDecl::DefinitionData::getBasesSlowCase() const {
return Bases.get(Definition->getASTContext().getExternalSource());
}
CXXBaseSpecifier *CXXRecordDecl::DefinitionData::getVBasesSlowCase() const {
return VBases.get(Definition->getASTContext().getExternalSource());
}
CXXRecordDecl::CXXRecordDecl(Kind K, TagKind TK, const ASTContext &C,
DeclContext *DC, SourceLocation StartLoc,
SourceLocation IdLoc, IdentifierInfo *Id,
CXXRecordDecl *PrevDecl)
: RecordDecl(K, TK, C, DC, StartLoc, IdLoc, Id, PrevDecl),
DefinitionData(PrevDecl ? PrevDecl->DefinitionData
: nullptr) {}
CXXRecordDecl *CXXRecordDecl::Create(const ASTContext &C, TagKind TK,
DeclContext *DC, SourceLocation StartLoc,
SourceLocation IdLoc, IdentifierInfo *Id,
CXXRecordDecl *PrevDecl,
bool DelayTypeCreation) {
auto *R = new (C, DC) CXXRecordDecl(CXXRecord, TK, C, DC, StartLoc, IdLoc, Id,
PrevDecl);
R->setMayHaveOutOfDateDef(C.getLangOpts().Modules);
// FIXME: DelayTypeCreation seems like such a hack
if (!DelayTypeCreation)
C.getTypeDeclType(R, PrevDecl);
return R;
}
CXXRecordDecl *
CXXRecordDecl::CreateLambda(const ASTContext &C, DeclContext *DC,
TypeSourceInfo *Info, SourceLocation Loc,
unsigned DependencyKind, bool IsGeneric,
LambdaCaptureDefault CaptureDefault) {
auto *R = new (C, DC) CXXRecordDecl(CXXRecord, TagTypeKind::Class, C, DC, Loc,
Loc, nullptr, nullptr);
R->setBeingDefined(true);
R->DefinitionData = new (C) struct LambdaDefinitionData(
R, Info, DependencyKind, IsGeneric, CaptureDefault);
R->setMayHaveOutOfDateDef(false);
R->setImplicit(true);
C.getTypeDeclType(R, /*PrevDecl=*/nullptr);
return R;
}
CXXRecordDecl *CXXRecordDecl::CreateDeserialized(const ASTContext &C,
GlobalDeclID ID) {
auto *R = new (C, ID)
CXXRecordDecl(CXXRecord, TagTypeKind::Struct, C, nullptr,
SourceLocation(), SourceLocation(), nullptr, nullptr);
R->setMayHaveOutOfDateDef(false);
return R;
}
/// Determine whether a class has a repeated base class. This is intended for
/// use when determining if a class is standard-layout, so makes no attempt to
/// handle virtual bases.
static bool hasRepeatedBaseClass(const CXXRecordDecl *StartRD) {
llvm::SmallPtrSet<const CXXRecordDecl*, 8> SeenBaseTypes;
SmallVector<const CXXRecordDecl*, 8> WorkList = {StartRD};
while (!WorkList.empty()) {
const CXXRecordDecl *RD = WorkList.pop_back_val();
if (RD->getTypeForDecl()->isDependentType())
continue;
for (const CXXBaseSpecifier &BaseSpec : RD->bases()) {
if (const CXXRecordDecl *B = BaseSpec.getType()->getAsCXXRecordDecl()) {
if (!SeenBaseTypes.insert(B).second)
return true;
WorkList.push_back(B);
}
}
}
return false;
}
void
CXXRecordDecl::setBases(CXXBaseSpecifier const * const *Bases,
unsigned NumBases) {
ASTContext &C = getASTContext();
if (!data().Bases.isOffset() && data().NumBases > 0)
C.Deallocate(data().getBases());
if (NumBases) {
if (!C.getLangOpts().CPlusPlus17) {
// C++ [dcl.init.aggr]p1:
// An aggregate is [...] a class with [...] no base classes [...].
data().Aggregate = false;
}
// C++ [class]p4:
// A POD-struct is an aggregate class...
data().PlainOldData = false;
}
// The set of seen virtual base types.
llvm::SmallPtrSet<CanQualType, 8> SeenVBaseTypes;
// The virtual bases of this class.
SmallVector<const CXXBaseSpecifier *, 8> VBases;
data().Bases = new(C) CXXBaseSpecifier [NumBases];
data().NumBases = NumBases;
for (unsigned i = 0; i < NumBases; ++i) {
data().getBases()[i] = *Bases[i];
// Keep track of inherited vbases for this base class.
const CXXBaseSpecifier *Base = Bases[i];
QualType BaseType = Base->getType();
// Skip dependent types; we can't do any checking on them now.
if (BaseType->isDependentType())
continue;
auto *BaseClassDecl =
cast<CXXRecordDecl>(BaseType->castAs<RecordType>()->getDecl());
// C++2a [class]p7:
// A standard-layout class is a class that:
// [...]
// -- has all non-static data members and bit-fields in the class and
// its base classes first declared in the same class
if (BaseClassDecl->data().HasBasesWithFields ||
!BaseClassDecl->field_empty()) {
if (data().HasBasesWithFields)
// Two bases have members or bit-fields: not standard-layout.
data().IsStandardLayout = false;
data().HasBasesWithFields = true;
}
// C++11 [class]p7:
// A standard-layout class is a class that:
// -- [...] has [...] at most one base class with non-static data
// members
if (BaseClassDecl->data().HasBasesWithNonStaticDataMembers ||
BaseClassDecl->hasDirectFields()) {
if (data().HasBasesWithNonStaticDataMembers)
data().IsCXX11StandardLayout = false;
data().HasBasesWithNonStaticDataMembers = true;
}
if (!BaseClassDecl->isEmpty()) {
// C++14 [meta.unary.prop]p4:
// T is a class type [...] with [...] no base class B for which
// is_empty<B>::value is false.
data().Empty = false;
}
// C++1z [dcl.init.agg]p1:
// An aggregate is a class with [...] no private or protected base classes
if (Base->getAccessSpecifier() != AS_public) {
data().Aggregate = false;
// C++20 [temp.param]p7:
// A structural type is [...] a literal class type with [...] all base
// classes [...] public
data().StructuralIfLiteral = false;
}
// C++ [class.virtual]p1:
// A class that declares or inherits a virtual function is called a
// polymorphic class.
if (BaseClassDecl->isPolymorphic()) {
data().Polymorphic = true;
// An aggregate is a class with [...] no virtual functions.
data().Aggregate = false;
}
// C++0x [class]p7:
// A standard-layout class is a class that: [...]
// -- has no non-standard-layout base classes
if (!BaseClassDecl->isStandardLayout())
data().IsStandardLayout = false;
if (!BaseClassDecl->isCXX11StandardLayout())
data().IsCXX11StandardLayout = false;
// Record if this base is the first non-literal field or base.
if (!hasNonLiteralTypeFieldsOrBases() && !BaseType->isLiteralType(C))
data().HasNonLiteralTypeFieldsOrBases = true;
// Now go through all virtual bases of this base and add them.
for (const auto &VBase : BaseClassDecl->vbases()) {
// Add this base if it's not already in the list.
if (SeenVBaseTypes.insert(C.getCanonicalType(VBase.getType())).second) {
VBases.push_back(&VBase);
// C++11 [class.copy]p8:
// The implicitly-declared copy constructor for a class X will have
// the form 'X::X(const X&)' if each [...] virtual base class B of X
// has a copy constructor whose first parameter is of type
// 'const B&' or 'const volatile B&' [...]
if (CXXRecordDecl *VBaseDecl = VBase.getType()->getAsCXXRecordDecl())
if (!VBaseDecl->hasCopyConstructorWithConstParam())
data().ImplicitCopyConstructorCanHaveConstParamForVBase = false;
// C++1z [dcl.init.agg]p1:
// An aggregate is a class with [...] no virtual base classes
data().Aggregate = false;
}
}
if (Base->isVirtual()) {
// Add this base if it's not already in the list.
if (SeenVBaseTypes.insert(C.getCanonicalType(BaseType)).second)
VBases.push_back(Base);
// C++14 [meta.unary.prop] is_empty:
// T is a class type, but not a union type, with ... no virtual base
// classes
data().Empty = false;
// C++1z [dcl.init.agg]p1:
// An aggregate is a class with [...] no virtual base classes
data().Aggregate = false;
// C++11 [class.ctor]p5, C++11 [class.copy]p12, C++11 [class.copy]p25:
// A [default constructor, copy/move constructor, or copy/move assignment
// operator for a class X] is trivial [...] if:
// -- class X has [...] no virtual base classes
data().HasTrivialSpecialMembers &= SMF_Destructor;
data().HasTrivialSpecialMembersForCall &= SMF_Destructor;
// C++0x [class]p7:
// A standard-layout class is a class that: [...]
// -- has [...] no virtual base classes
data().IsStandardLayout = false;
data().IsCXX11StandardLayout = false;
// C++20 [dcl.constexpr]p3:
// In the definition of a constexpr function [...]
// -- if the function is a constructor or destructor,
// its class shall not have any virtual base classes
data().DefaultedDefaultConstructorIsConstexpr = false;
data().DefaultedDestructorIsConstexpr = false;
// C++1z [class.copy]p8:
// The implicitly-declared copy constructor for a class X will have
// the form 'X::X(const X&)' if each potentially constructed subobject
// has a copy constructor whose first parameter is of type
// 'const B&' or 'const volatile B&' [...]
if (!BaseClassDecl->hasCopyConstructorWithConstParam())
data().ImplicitCopyConstructorCanHaveConstParamForVBase = false;
} else {
// C++ [class.ctor]p5:
// A default constructor is trivial [...] if:
// -- all the direct base classes of its class have trivial default
// constructors.
if (!BaseClassDecl->hasTrivialDefaultConstructor())
data().HasTrivialSpecialMembers &= ~SMF_DefaultConstructor;
// C++0x [class.copy]p13:
// A copy/move constructor for class X is trivial if [...]
// [...]
// -- the constructor selected to copy/move each direct base class
// subobject is trivial, and
if (!BaseClassDecl->hasTrivialCopyConstructor())
data().HasTrivialSpecialMembers &= ~SMF_CopyConstructor;
if (!BaseClassDecl->hasTrivialCopyConstructorForCall())
data().HasTrivialSpecialMembersForCall &= ~SMF_CopyConstructor;
// If the base class doesn't have a simple move constructor, we'll eagerly
// declare it and perform overload resolution to determine which function
// it actually calls. If it does have a simple move constructor, this
// check is correct.
if (!BaseClassDecl->hasTrivialMoveConstructor())
data().HasTrivialSpecialMembers &= ~SMF_MoveConstructor;
if (!BaseClassDecl->hasTrivialMoveConstructorForCall())
data().HasTrivialSpecialMembersForCall &= ~SMF_MoveConstructor;
// C++0x [class.copy]p27:
// A copy/move assignment operator for class X is trivial if [...]
// [...]
// -- the assignment operator selected to copy/move each direct base
// class subobject is trivial, and
if (!BaseClassDecl->hasTrivialCopyAssignment())
data().HasTrivialSpecialMembers &= ~SMF_CopyAssignment;
// If the base class doesn't have a simple move assignment, we'll eagerly
// declare it and perform overload resolution to determine which function
// it actually calls. If it does have a simple move assignment, this
// check is correct.
if (!BaseClassDecl->hasTrivialMoveAssignment())
data().HasTrivialSpecialMembers &= ~SMF_MoveAssignment;
// C++11 [class.ctor]p6:
// If that user-written default constructor would satisfy the
// requirements of a constexpr constructor/function(C++23), the
// implicitly-defined default constructor is constexpr.
if (!BaseClassDecl->hasConstexprDefaultConstructor())
data().DefaultedDefaultConstructorIsConstexpr =
C.getLangOpts().CPlusPlus23;
// C++1z [class.copy]p8:
// The implicitly-declared copy constructor for a class X will have
// the form 'X::X(const X&)' if each potentially constructed subobject
// has a copy constructor whose first parameter is of type
// 'const B&' or 'const volatile B&' [...]
if (!BaseClassDecl->hasCopyConstructorWithConstParam())
data().ImplicitCopyConstructorCanHaveConstParamForNonVBase = false;
}
// C++ [class.ctor]p3:
// A destructor is trivial if all the direct base classes of its class
// have trivial destructors.
if (!BaseClassDecl->hasTrivialDestructor())
data().HasTrivialSpecialMembers &= ~SMF_Destructor;
if (!BaseClassDecl->hasTrivialDestructorForCall())
data().HasTrivialSpecialMembersForCall &= ~SMF_Destructor;
if (!BaseClassDecl->hasIrrelevantDestructor())
data().HasIrrelevantDestructor = false;
if (BaseClassDecl->isAnyDestructorNoReturn())
data().IsAnyDestructorNoReturn = true;
if (BaseClassDecl->isHLSLIntangible())
data().IsHLSLIntangible = true;
// C++11 [class.copy]p18:
// The implicitly-declared copy assignment operator for a class X will
// have the form 'X& X::operator=(const X&)' if each direct base class B
// of X has a copy assignment operator whose parameter is of type 'const
// B&', 'const volatile B&', or 'B' [...]
if (!BaseClassDecl->hasCopyAssignmentWithConstParam())
data().ImplicitCopyAssignmentHasConstParam = false;
// A class has an Objective-C object member if... or any of its bases
// has an Objective-C object member.
if (BaseClassDecl->hasObjectMember())
setHasObjectMember(true);
if (BaseClassDecl->hasVolatileMember())
setHasVolatileMember(true);
if (BaseClassDecl->getArgPassingRestrictions() ==
RecordArgPassingKind::CanNeverPassInRegs)
setArgPassingRestrictions(RecordArgPassingKind::CanNeverPassInRegs);
// Keep track of the presence of mutable fields.
if (BaseClassDecl->hasMutableFields())
data().HasMutableFields = true;
if (BaseClassDecl->hasUninitializedExplicitInitFields() &&
BaseClassDecl->isAggregate())
setHasUninitializedExplicitInitFields(true);
if (BaseClassDecl->hasUninitializedReferenceMember())
data().HasUninitializedReferenceMember = true;
if (!BaseClassDecl->allowConstDefaultInit())
data().HasUninitializedFields = true;
addedClassSubobject(BaseClassDecl);
}
// C++2a [class]p7:
// A class S is a standard-layout class if it:
// -- has at most one base class subobject of any given type
//
// Note that we only need to check this for classes with more than one base
// class. If there's only one base class, and it's standard layout, then
// we know there are no repeated base classes.
if (data().IsStandardLayout && NumBases > 1 && hasRepeatedBaseClass(this))
data().IsStandardLayout = false;
if (VBases.empty()) {
data().IsParsingBaseSpecifiers = false;
return;
}
// Create base specifier for any direct or indirect virtual bases.
data().VBases = new (C) CXXBaseSpecifier[VBases.size()];
data().NumVBases = VBases.size();
for (int I = 0, E = VBases.size(); I != E; ++I) {
QualType Type = VBases[I]->getType();
if (!Type->isDependentType())
addedClassSubobject(Type->getAsCXXRecordDecl());
data().getVBases()[I] = *VBases[I];
}
data().IsParsingBaseSpecifiers = false;
}
unsigned CXXRecordDecl::getODRHash() const {
assert(hasDefinition() && "ODRHash only for records with definitions");
// Previously calculated hash is stored in DefinitionData.
if (DefinitionData->HasODRHash)
return DefinitionData->ODRHash;
// Only calculate hash on first call of getODRHash per record.
ODRHash Hash;
Hash.AddCXXRecordDecl(getDefinition());
DefinitionData->HasODRHash = true;
DefinitionData->ODRHash = Hash.CalculateHash();
return DefinitionData->ODRHash;
}
void CXXRecordDecl::addedClassSubobject(CXXRecordDecl *Subobj) {
// C++11 [class.copy]p11:
// A defaulted copy/move constructor for a class X is defined as
// deleted if X has:
// -- a direct or virtual base class B that cannot be copied/moved [...]
// -- a non-static data member of class type M (or array thereof)
// that cannot be copied or moved [...]
if (!Subobj->hasSimpleCopyConstructor())
data().NeedOverloadResolutionForCopyConstructor = true;
if (!Subobj->hasSimpleMoveConstructor())
data().NeedOverloadResolutionForMoveConstructor = true;
// C++11 [class.copy]p23:
// A defaulted copy/move assignment operator for a class X is defined as
// deleted if X has:
// -- a direct or virtual base class B that cannot be copied/moved [...]
// -- a non-static data member of class type M (or array thereof)
// that cannot be copied or moved [...]
if (!Subobj->hasSimpleCopyAssignment())
data().NeedOverloadResolutionForCopyAssignment = true;
if (!Subobj->hasSimpleMoveAssignment())
data().NeedOverloadResolutionForMoveAssignment = true;
// C++11 [class.ctor]p5, C++11 [class.copy]p11, C++11 [class.dtor]p5:
// A defaulted [ctor or dtor] for a class X is defined as
// deleted if X has:
// -- any direct or virtual base class [...] has a type with a destructor
// that is deleted or inaccessible from the defaulted [ctor or dtor].
// -- any non-static data member has a type with a destructor
// that is deleted or inaccessible from the defaulted [ctor or dtor].
if (!Subobj->hasSimpleDestructor()) {
data().NeedOverloadResolutionForCopyConstructor = true;
data().NeedOverloadResolutionForMoveConstructor = true;
data().NeedOverloadResolutionForDestructor = true;
}
// C++20 [dcl.constexpr]p5:
// The definition of a constexpr destructor whose function-body is not
// = delete shall additionally satisfy the following requirement:
// -- for every subobject of class type or (possibly multi-dimensional)
// array thereof, that class type shall have a constexpr destructor
if (!Subobj->hasConstexprDestructor())
data().DefaultedDestructorIsConstexpr =
getASTContext().getLangOpts().CPlusPlus23;
// C++20 [temp.param]p7:
// A structural type is [...] a literal class type [for which] the types
// of all base classes and non-static data members are structural types or
// (possibly multi-dimensional) array thereof
if (!Subobj->data().StructuralIfLiteral)
data().StructuralIfLiteral = false;
}
const CXXRecordDecl *CXXRecordDecl::getStandardLayoutBaseWithFields() const {
assert(
isStandardLayout() &&
"getStandardLayoutBaseWithFields called on a non-standard-layout type");
#ifdef EXPENSIVE_CHECKS
{
unsigned NumberOfBasesWithFields = 0;
if (!field_empty())
++NumberOfBasesWithFields;
llvm::SmallPtrSet<const CXXRecordDecl *, 8> UniqueBases;
forallBases([&](const CXXRecordDecl *Base) -> bool {
if (!Base->field_empty())
++NumberOfBasesWithFields;
assert(
UniqueBases.insert(Base->getCanonicalDecl()).second &&
"Standard layout struct has multiple base classes of the same type");
return true;
});
assert(NumberOfBasesWithFields <= 1 &&
"Standard layout struct has fields declared in more than one class");
}
#endif
if (!field_empty())
return this;
const CXXRecordDecl *Result = this;
forallBases([&](const CXXRecordDecl *Base) -> bool {
if (!Base->field_empty()) {
// This is the base where the fields are declared; return early
Result = Base;
return false;
}
return true;
});
return Result;
}
bool CXXRecordDecl::hasConstexprDestructor() const {
auto *Dtor = getDestructor();
return Dtor ? Dtor->isConstexpr() : defaultedDestructorIsConstexpr();
}
bool CXXRecordDecl::hasAnyDependentBases() const {
if (!isDependentContext())
return false;
return !forallBases([](const CXXRecordDecl *) { return true; });
}
bool CXXRecordDecl::isTriviallyCopyable() const {
// C++0x [class]p5:
// A trivially copyable class is a class that:
// -- has no non-trivial copy constructors,
if (hasNonTrivialCopyConstructor()) return false;
// -- has no non-trivial move constructors,
if (hasNonTrivialMoveConstructor()) return false;
// -- has no non-trivial copy assignment operators,
if (hasNonTrivialCopyAssignment()) return false;
// -- has no non-trivial move assignment operators, and
if (hasNonTrivialMoveAssignment()) return false;
// -- has a trivial destructor.
if (!hasTrivialDestructor()) return false;
return true;
}
bool CXXRecordDecl::isTriviallyCopyConstructible() const {
// A trivially copy constructible class is a class that:
// -- has no non-trivial copy constructors,
if (hasNonTrivialCopyConstructor())
return false;
// -- has a trivial destructor.
if (!hasTrivialDestructor())
return false;
return true;
}
void CXXRecordDecl::markedVirtualFunctionPure() {
// C++ [class.abstract]p2:
// A class is abstract if it has at least one pure virtual function.
data().Abstract = true;
}
bool CXXRecordDecl::hasSubobjectAtOffsetZeroOfEmptyBaseType(
ASTContext &Ctx, const CXXRecordDecl *XFirst) {
if (!getNumBases())
return false;
llvm::SmallPtrSet<const CXXRecordDecl*, 8> Bases;
llvm::SmallPtrSet<const CXXRecordDecl*, 8> M;
SmallVector<const CXXRecordDecl*, 8> WorkList;
// Visit a type that we have determined is an element of M(S).
auto Visit = [&](const CXXRecordDecl *RD) -> bool {
RD = RD->getCanonicalDecl();
// C++2a [class]p8:
// A class S is a standard-layout class if it [...] has no element of the
// set M(S) of types as a base class.
//
// If we find a subobject of an empty type, it might also be a base class,
// so we'll need to walk the base classes to check.
if (!RD->data().HasBasesWithFields) {
// Walk the bases the first time, stopping if we find the type. Build a
// set of them so we don't need to walk them again.
if (Bases.empty()) {
bool RDIsBase = !forallBases([&](const CXXRecordDecl *Base) -> bool {
Base = Base->getCanonicalDecl();
if (RD == Base)
return false;
Bases.insert(Base);
return true;
});
if (RDIsBase)
return true;
} else {
if (Bases.count(RD))
return true;
}
}
if (M.insert(RD).second)
WorkList.push_back(RD);
return false;
};
if (Visit(XFirst))
return true;
while (!WorkList.empty()) {
const CXXRecordDecl *X = WorkList.pop_back_val();
// FIXME: We don't check the bases of X. That matches the standard, but
// that sure looks like a wording bug.
// -- If X is a non-union class type with a non-static data member
// [recurse to each field] that is either of zero size or is the
// first non-static data member of X
// -- If X is a union type, [recurse to union members]
bool IsFirstField = true;
for (auto *FD : X->fields()) {
// FIXME: Should we really care about the type of the first non-static
// data member of a non-union if there are preceding unnamed bit-fields?
if (FD->isUnnamedBitField())
continue;
if (!IsFirstField && !FD->isZeroSize(Ctx))
continue;
if (FD->isInvalidDecl())
continue;
// -- If X is n array type, [visit the element type]
QualType T = Ctx.getBaseElementType(FD->getType());
if (auto *RD = T->getAsCXXRecordDecl())
if (Visit(RD))
return true;
if (!X->isUnion())
IsFirstField = false;
}
}
return false;
}
bool CXXRecordDecl::lambdaIsDefaultConstructibleAndAssignable() const {
assert(isLambda() && "not a lambda");
// C++2a [expr.prim.lambda.capture]p11:
// The closure type associated with a lambda-expression has no default
// constructor if the lambda-expression has a lambda-capture and a
// defaulted default constructor otherwise. It has a deleted copy
// assignment operator if the lambda-expression has a lambda-capture and
// defaulted copy and move assignment operators otherwise.
//
// C++17 [expr.prim.lambda]p21:
// The closure type associated with a lambda-expression has no default
// constructor and a deleted copy assignment operator.
if (!isCapturelessLambda())
return false;
return getASTContext().getLangOpts().CPlusPlus20;
}
void CXXRecordDecl::addedMember(Decl *D) {
if (!D->isImplicit() && !isa<FieldDecl>(D) && !isa<IndirectFieldDecl>(D) &&
(!isa<TagDecl>(D) ||
cast<TagDecl>(D)->getTagKind() == TagTypeKind::Class ||
cast<TagDecl>(D)->getTagKind() == TagTypeKind::Interface))
data().HasOnlyCMembers = false;
// Ignore friends and invalid declarations.
if (D->getFriendObjectKind() || D->isInvalidDecl())
return;
auto *FunTmpl = dyn_cast<FunctionTemplateDecl>(D);
if (FunTmpl)
D = FunTmpl->getTemplatedDecl();
// FIXME: Pass NamedDecl* to addedMember?
Decl *DUnderlying = D;
if (auto *ND = dyn_cast<NamedDecl>(DUnderlying)) {
DUnderlying = ND->getUnderlyingDecl();
if (auto *UnderlyingFunTmpl = dyn_cast<FunctionTemplateDecl>(DUnderlying))
DUnderlying = UnderlyingFunTmpl->getTemplatedDecl();
}
if (const auto *Method = dyn_cast<CXXMethodDecl>(D)) {
if (Method->isVirtual()) {
// C++ [dcl.init.aggr]p1:
// An aggregate is an array or a class with [...] no virtual functions.
data().Aggregate = false;
// C++ [class]p4:
// A POD-struct is an aggregate class...
data().PlainOldData = false;
// C++14 [meta.unary.prop]p4:
// T is a class type [...] with [...] no virtual member functions...
data().Empty = false;
// C++ [class.virtual]p1:
// A class that declares or inherits a virtual function is called a
// polymorphic class.
data().Polymorphic = true;
// C++11 [class.ctor]p5, C++11 [class.copy]p12, C++11 [class.copy]p25:
// A [default constructor, copy/move constructor, or copy/move
// assignment operator for a class X] is trivial [...] if:
// -- class X has no virtual functions [...]
data().HasTrivialSpecialMembers &= SMF_Destructor;
data().HasTrivialSpecialMembersForCall &= SMF_Destructor;
// C++0x [class]p7:
// A standard-layout class is a class that: [...]
// -- has no virtual functions
data().IsStandardLayout = false;
data().IsCXX11StandardLayout = false;
}
}
// Notify the listener if an implicit member was added after the definition
// was completed.
if (!isBeingDefined() && D->isImplicit())
if (ASTMutationListener *L = getASTMutationListener())
L->AddedCXXImplicitMember(data().Definition, D);
// The kind of special member this declaration is, if any.
unsigned SMKind = 0;
// Handle constructors.
if (const auto *Constructor = dyn_cast<CXXConstructorDecl>(D)) {
if (Constructor->isInheritingConstructor()) {
// Ignore constructor shadow declarations. They are lazily created and
// so shouldn't affect any properties of the class.
} else {
if (!Constructor->isImplicit()) {
// Note that we have a user-declared constructor.
data().UserDeclaredConstructor = true;
const TargetInfo &TI = getASTContext().getTargetInfo();
if ((!Constructor->isDeleted() && !Constructor->isDefaulted()) ||
!TI.areDefaultedSMFStillPOD(getLangOpts())) {
// C++ [class]p4:
// A POD-struct is an aggregate class [...]
// Since the POD bit is meant to be C++03 POD-ness, clear it even if
// the type is technically an aggregate in C++0x since it wouldn't be
// in 03.
data().PlainOldData = false;
}
}
if (Constructor->isDefaultConstructor()) {
SMKind |= SMF_DefaultConstructor;
if (Constructor->isUserProvided())
data().UserProvidedDefaultConstructor = true;
if (Constructor->isConstexpr())
data().HasConstexprDefaultConstructor = true;
if (Constructor->isDefaulted())
data().HasDefaultedDefaultConstructor = true;
}
if (!FunTmpl) {
unsigned Quals;
if (Constructor->isCopyConstructor(Quals)) {
SMKind |= SMF_CopyConstructor;
if (Quals & Qualifiers::Const)
data().HasDeclaredCopyConstructorWithConstParam = true;
} else if (Constructor->isMoveConstructor())
SMKind |= SMF_MoveConstructor;
}
// C++11 [dcl.init.aggr]p1: DR1518
// 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().CPlusPlus20
? !Constructor->isImplicit()
: (Constructor->isUserProvided() || Constructor->isExplicit()))
data().Aggregate = false;
}
}
// Handle constructors, including those inherited from base classes.
if (const auto *Constructor = dyn_cast<CXXConstructorDecl>(DUnderlying)) {
// Record if we see any constexpr constructors which are neither copy
// nor move constructors.
// C++1z [basic.types]p10:
// [...] has at least one constexpr constructor or constructor template
// (possibly inherited from a base class) that is not a copy or move
// constructor [...]
if (Constructor->isConstexpr() && !Constructor->isCopyOrMoveConstructor())
data().HasConstexprNonCopyMoveConstructor = true;
if (!isa<CXXConstructorDecl>(D) && Constructor->isDefaultConstructor())
data().HasInheritedDefaultConstructor = true;
}
// Handle member functions.
if (const auto *Method = dyn_cast<CXXMethodDecl>(D)) {
if (isa<CXXDestructorDecl>(D))
SMKind |= SMF_Destructor;
if (Method->isCopyAssignmentOperator()) {
SMKind |= SMF_CopyAssignment;
const auto *ParamTy =
Method->getNonObjectParameter(0)->getType()->getAs<ReferenceType>();
if (!ParamTy || ParamTy->getPointeeType().isConstQualified())
data().HasDeclaredCopyAssignmentWithConstParam = true;
}
if (Method->isMoveAssignmentOperator())
SMKind |= SMF_MoveAssignment;
// Keep the list of conversion functions up-to-date.
if (auto *Conversion = dyn_cast<CXXConversionDecl>(D)) {
// FIXME: We use the 'unsafe' accessor for the access specifier here,
// because Sema may not have set it yet. That's really just a misdesign
// in Sema. However, LLDB *will* have set the access specifier correctly,
// and adds declarations after the class is technically completed,
// so completeDefinition()'s overriding of the access specifiers doesn't
// work.
AccessSpecifier AS = Conversion->getAccessUnsafe();
if (Conversion->getPrimaryTemplate()) {
// We don't record specializations.
} else {
ASTContext &Ctx = getASTContext();
ASTUnresolvedSet &Conversions = data().Conversions.get(Ctx);
NamedDecl *Primary =
FunTmpl ? cast<NamedDecl>(FunTmpl) : cast<NamedDecl>(Conversion);
if (Primary->getPreviousDecl())
Conversions.replace(cast<NamedDecl>(Primary->getPreviousDecl()),
Primary, AS);
else
Conversions.addDecl(Ctx, Primary, AS);
}
}
if (SMKind) {
// If this is the first declaration of a special member, we no longer have
// an implicit trivial special member.
data().HasTrivialSpecialMembers &=
data().DeclaredSpecialMembers | ~SMKind;
data().HasTrivialSpecialMembersForCall &=
data().DeclaredSpecialMembers | ~SMKind;
// Note when we have declared a declared special member, and suppress the
// implicit declaration of this special member.
data().DeclaredSpecialMembers |= SMKind;
if (!Method->isImplicit()) {
data().UserDeclaredSpecialMembers |= SMKind;
const TargetInfo &TI = getASTContext().getTargetInfo();
if ((!Method->isDeleted() && !Method->isDefaulted() &&
SMKind != SMF_MoveAssignment) ||
!TI.areDefaultedSMFStillPOD(getLangOpts())) {
// C++03 [class]p4:
// A POD-struct is an aggregate class that has [...] no user-defined
// copy assignment operator and no user-defined destructor.
//
// Since the POD bit is meant to be C++03 POD-ness, and in C++03,
// aggregates could not have any constructors, clear it even for an
// explicitly defaulted or deleted constructor.
// type is technically an aggregate in C++0x since it wouldn't be in
// 03.
//
// Also, a user-declared move assignment operator makes a class
// non-POD. This is an extension in C++03.
data().PlainOldData = false;
}
}
// When instantiating a class, we delay updating the destructor and
// triviality properties of the class until selecting a destructor and
// computing the eligibility of its special member functions. This is
// because there might be function constraints that we need to evaluate
// and compare later in the instantiation.
if (!Method->isIneligibleOrNotSelected()) {
addedEligibleSpecialMemberFunction(Method, SMKind);
}
}
return;
}
// Handle non-static data members.
if (const auto *Field = dyn_cast<FieldDecl>(D)) {
ASTContext &Context = getASTContext();
// C++2a [class]p7:
// A standard-layout class is a class that:
// [...]
// -- has all non-static data members and bit-fields in the class and
// its base classes first declared in the same class
if (data().HasBasesWithFields)
data().IsStandardLayout = false;
// C++ [class.bit]p2:
// A declaration for a bit-field that omits the identifier declares an
// unnamed bit-field. Unnamed bit-fields are not members and cannot be
// initialized.
if (Field->isUnnamedBitField()) {
// C++ [meta.unary.prop]p4: [LWG2358]
// T is a class type [...] with [...] no unnamed bit-fields of non-zero
// length
if (data().Empty && !Field->isZeroLengthBitField() &&
Context.getLangOpts().getClangABICompat() >
LangOptions::ClangABI::Ver6)
data().Empty = false;
return;
}
// C++11 [class]p7:
// A standard-layout class is a class that:
// -- either has no non-static data members in the most derived class
// [...] or has no base classes with non-static data members
if (data().HasBasesWithNonStaticDataMembers)
data().IsCXX11StandardLayout = false;
// C++ [dcl.init.aggr]p1:
// An aggregate is an array or a class (clause 9) with [...] no
// private or protected non-static data members (clause 11).
//
// A POD must be an aggregate.
if (D->getAccess() == AS_private || D->getAccess() == AS_protected) {
data().Aggregate = false;
data().PlainOldData = false;
// C++20 [temp.param]p7:
// A structural type is [...] a literal class type [for which] all
// non-static data members are public
data().StructuralIfLiteral = false;
}
// Track whether this is the first field. We use this when checking
// whether the class is standard-layout below.
bool IsFirstField = !data().HasPrivateFields &&
!data().HasProtectedFields && !data().HasPublicFields;
// C++0x [class]p7:
// A standard-layout class is a class that:
// [...]
// -- has the same access control for all non-static data members,
switch (D->getAccess()) {
case AS_private: data().HasPrivateFields = true; break;
case AS_protected: data().HasProtectedFields = true; break;
case AS_public: data().HasPublicFields = true; break;
case AS_none: llvm_unreachable("Invalid access specifier");
};
if ((data().HasPrivateFields + data().HasProtectedFields +
data().HasPublicFields) > 1) {
data().IsStandardLayout = false;
data().IsCXX11StandardLayout = false;
}
// Keep track of the presence of mutable fields.
if (Field->isMutable()) {
data().HasMutableFields = true;
// C++20 [temp.param]p7:
// A structural type is [...] a literal class type [for which] all
// non-static data members are public
data().StructuralIfLiteral = false;
}
// C++11 [class.union]p8, DR1460:
// If X is a union, a non-static data member of X that is not an anonymous
// union is a variant member of X.
if (isUnion() && !Field->isAnonymousStructOrUnion())
data().HasVariantMembers = true;
if (isUnion() && IsFirstField)
data().HasUninitializedFields = true;
// C++0x [class]p9:
// A POD struct is a class that is both a trivial class and a
// standard-layout class, and has no non-static data members of type
// non-POD struct, non-POD union (or array of such types).
//
// Automatic Reference Counting: the presence of a member of Objective-C pointer type
// that does not explicitly have no lifetime makes the class a non-POD.
QualType T = Context.getBaseElementType(Field->getType());
if (T->isObjCRetainableType() || T.isObjCGCStrong()) {
if (T.hasNonTrivialObjCLifetime()) {
// Objective-C Automatic Reference Counting:
// If a class has a non-static data member of Objective-C pointer
// type (or array thereof), it is a non-POD type and its
// default constructor (if any), copy constructor, move constructor,
// copy assignment operator, move assignment operator, and destructor are
// non-trivial.
setHasObjectMember(true);
struct DefinitionData &Data = data();
Data.PlainOldData = false;
Data.HasTrivialSpecialMembers = 0;
// __strong or __weak fields do not make special functions non-trivial
// for the purpose of calls.
Qualifiers::ObjCLifetime LT = T.getQualifiers().getObjCLifetime();
if (LT != Qualifiers::OCL_Strong && LT != Qualifiers::OCL_Weak)
data().HasTrivialSpecialMembersForCall = 0;
// Structs with __weak fields should never be passed directly.
if (LT == Qualifiers::OCL_Weak)
setArgPassingRestrictions(RecordArgPassingKind::CanNeverPassInRegs);
Data.HasIrrelevantDestructor = false;
if (isUnion()) {
data().DefaultedCopyConstructorIsDeleted = true;
data().DefaultedMoveConstructorIsDeleted = true;
data().DefaultedCopyAssignmentIsDeleted = true;
data().DefaultedMoveAssignmentIsDeleted = true;
data().DefaultedDestructorIsDeleted = true;
data().NeedOverloadResolutionForCopyConstructor = true;
data().NeedOverloadResolutionForMoveConstructor = true;
data().NeedOverloadResolutionForCopyAssignment = true;
data().NeedOverloadResolutionForMoveAssignment = true;
data().NeedOverloadResolutionForDestructor = true;
}
} else if (!Context.getLangOpts().ObjCAutoRefCount) {
setHasObjectMember(true);
}
} else if (!T.isCXX98PODType(Context))
data().PlainOldData = false;
// If a class has an address-discriminated signed pointer member, it is a
// non-POD type and its copy constructor, move constructor, copy assignment
// operator, move assignment operator are non-trivial.
if (PointerAuthQualifier Q = T.getPointerAuth()) {
if (Q.isAddressDiscriminated()) {
struct DefinitionData &Data = data();
Data.PlainOldData = false;
Data.HasTrivialSpecialMembers &=
~(SMF_CopyConstructor | SMF_MoveConstructor | SMF_CopyAssignment |
SMF_MoveAssignment);
setArgPassingRestrictions(RecordArgPassingKind::CanNeverPassInRegs);
// Copy/move constructors/assignment operators of a union are deleted by
// default if it has an address-discriminated ptrauth field.
if (isUnion()) {
data().DefaultedCopyConstructorIsDeleted = true;
data().DefaultedMoveConstructorIsDeleted = true;
data().DefaultedCopyAssignmentIsDeleted = true;
data().DefaultedMoveAssignmentIsDeleted = true;
data().NeedOverloadResolutionForCopyConstructor = true;
data().NeedOverloadResolutionForMoveConstructor = true;
data().NeedOverloadResolutionForCopyAssignment = true;
data().NeedOverloadResolutionForMoveAssignment = true;
}
}
}
if (Field->hasAttr<ExplicitInitAttr>())
setHasUninitializedExplicitInitFields(true);
if (T->isReferenceType()) {
if (!Field->hasInClassInitializer())
data().HasUninitializedReferenceMember = true;
// C++0x [class]p7:
// A standard-layout class is a class that:
// -- has no non-static data members of type [...] reference,
data().IsStandardLayout = false;
data().IsCXX11StandardLayout = false;
// C++1z [class.copy.ctor]p10:
// A defaulted copy constructor for a class X is defined as deleted if X has:
// -- a non-static data member of rvalue reference type
if (T->isRValueReferenceType())
data().DefaultedCopyConstructorIsDeleted = true;
}
if (isUnion() && !Field->isMutable()) {
if (Field->hasInClassInitializer())
data().HasUninitializedFields = false;
} else if (!Field->hasInClassInitializer() && !Field->isMutable()) {
if (CXXRecordDecl *FieldType = T->getAsCXXRecordDecl()) {
if (FieldType->hasDefinition() && !FieldType->allowConstDefaultInit())
data().HasUninitializedFields = true;
} else {
data().HasUninitializedFields = true;
}
}
// Record if this field is the first non-literal or volatile field or base.
if (!T->isLiteralType(Context) || T.isVolatileQualified())
data().HasNonLiteralTypeFieldsOrBases = true;
if (Field->hasInClassInitializer() ||
(Field->isAnonymousStructOrUnion() &&
Field->getType()->getAsCXXRecordDecl()->hasInClassInitializer())) {
data().HasInClassInitializer = true;
// C++11 [class]p5:
// A default constructor is trivial if [...] no non-static data member
// of its class has a brace-or-equal-initializer.
data().HasTrivialSpecialMembers &= ~SMF_DefaultConstructor;
// C++11 [dcl.init.aggr]p1:
// An aggregate is a [...] class with [...] no
// brace-or-equal-initializers for non-static data members.
//
// This rule was removed in C++14.
if (!getASTContext().getLangOpts().CPlusPlus14)
data().Aggregate = false;
// C++11 [class]p10:
// A POD struct is [...] a trivial class.
data().PlainOldData = false;
}
// C++11 [class.copy]p23:
// A defaulted copy/move assignment operator for a class X is defined
// as deleted if X has:
// -- a non-static data member of reference type
if (T->isReferenceType()) {
data().DefaultedCopyAssignmentIsDeleted = true;
data().DefaultedMoveAssignmentIsDeleted = true;
}
// Bitfields of length 0 are also zero-sized, but we already bailed out for
// those because they are always unnamed.
bool IsZeroSize = Field->isZeroSize(Context);
if (const auto *RecordTy = T->getAs<RecordType>()) {
auto *FieldRec = cast<CXXRecordDecl>(RecordTy->getDecl());
if (FieldRec->getDefinition()) {
addedClassSubobject(FieldRec);
// We may need to perform overload resolution to determine whether a
// field can be moved if it's const or volatile qualified.
if (T.getCVRQualifiers() & (Qualifiers::Const | Qualifiers::Volatile)) {
// We need to care about 'const' for the copy constructor because an
// implicit copy constructor might be declared with a non-const
// parameter.
data().NeedOverloadResolutionForCopyConstructor = true;
data().NeedOverloadResolutionForMoveConstructor = true;
data().NeedOverloadResolutionForCopyAssignment = true;
data().NeedOverloadResolutionForMoveAssignment = true;
}
// C++11 [class.ctor]p5, C++11 [class.copy]p11:
// A defaulted [special member] for a class X is defined as
// deleted if:
// -- X is a union-like class that has a variant member with a
// non-trivial [corresponding special member]
if (isUnion()) {
if (FieldRec->hasNonTrivialCopyConstructor())
data().DefaultedCopyConstructorIsDeleted = true;
if (FieldRec->hasNonTrivialMoveConstructor())
data().DefaultedMoveConstructorIsDeleted = true;
if (FieldRec->hasNonTrivialCopyAssignment())
data().DefaultedCopyAssignmentIsDeleted = true;
if (FieldRec->hasNonTrivialMoveAssignment())
data().DefaultedMoveAssignmentIsDeleted = true;
if (FieldRec->hasNonTrivialDestructor()) {
data().DefaultedDestructorIsDeleted = true;
// C++20 [dcl.constexpr]p5:
// The definition of a constexpr destructor whose function-body is
// not = delete shall additionally satisfy...
data().DefaultedDestructorIsConstexpr = true;
}
}
// For an anonymous union member, our overload resolution will perform
// overload resolution for its members.
if (Field->isAnonymousStructOrUnion()) {
data().NeedOverloadResolutionForCopyConstructor |=
FieldRec->data().NeedOverloadResolutionForCopyConstructor;
data().NeedOverloadResolutionForMoveConstructor |=
FieldRec->data().NeedOverloadResolutionForMoveConstructor;
data().NeedOverloadResolutionForCopyAssignment |=
FieldRec->data().NeedOverloadResolutionForCopyAssignment;
data().NeedOverloadResolutionForMoveAssignment |=
FieldRec->data().NeedOverloadResolutionForMoveAssignment;
data().NeedOverloadResolutionForDestructor |=
FieldRec->data().NeedOverloadResolutionForDestructor;
}
// C++0x [class.ctor]p5:
// A default constructor is trivial [...] if:
// -- for all the non-static data members of its class that are of
// class type (or array thereof), each such class has a trivial
// default constructor.
if (!FieldRec->hasTrivialDefaultConstructor())
data().HasTrivialSpecialMembers &= ~SMF_DefaultConstructor;
// C++0x [class.copy]p13:
// A copy/move constructor for class X is trivial if [...]
// [...]
// -- for each non-static data member of X that is of class type (or
// an array thereof), the constructor selected to copy/move that
// member is trivial;
if (!FieldRec->hasTrivialCopyConstructor())
data().HasTrivialSpecialMembers &= ~SMF_CopyConstructor;
if (!FieldRec->hasTrivialCopyConstructorForCall())
data().HasTrivialSpecialMembersForCall &= ~SMF_CopyConstructor;
// If the field doesn't have a simple move constructor, we'll eagerly
// declare the move constructor for this class and we'll decide whether
// it's trivial then.
if (!FieldRec->hasTrivialMoveConstructor())
data().HasTrivialSpecialMembers &= ~SMF_MoveConstructor;
if (!FieldRec->hasTrivialMoveConstructorForCall())
data().HasTrivialSpecialMembersForCall &= ~SMF_MoveConstructor;
// C++0x [class.copy]p27:
// A copy/move assignment operator for class X is trivial if [...]
// [...]
// -- for each non-static data member of X that is of class type (or
// an array thereof), the assignment operator selected to
// copy/move that member is trivial;
if (!FieldRec->hasTrivialCopyAssignment())
data().HasTrivialSpecialMembers &= ~SMF_CopyAssignment;
// If the field doesn't have a simple move assignment, we'll eagerly
// declare the move assignment for this class and we'll decide whether
// it's trivial then.
if (!FieldRec->hasTrivialMoveAssignment())
data().HasTrivialSpecialMembers &= ~SMF_MoveAssignment;
if (!FieldRec->hasTrivialDestructor())
data().HasTrivialSpecialMembers &= ~SMF_Destructor;
if (!FieldRec->hasTrivialDestructorForCall())
data().HasTrivialSpecialMembersForCall &= ~SMF_Destructor;
if (!FieldRec->hasIrrelevantDestructor())
data().HasIrrelevantDestructor = false;
if (FieldRec->isAnyDestructorNoReturn())
data().IsAnyDestructorNoReturn = true;
if (FieldRec->hasObjectMember())
setHasObjectMember(true);
if (FieldRec->hasVolatileMember())
setHasVolatileMember(true);
if (FieldRec->getArgPassingRestrictions() ==
RecordArgPassingKind::CanNeverPassInRegs)
setArgPassingRestrictions(RecordArgPassingKind::CanNeverPassInRegs);
// C++0x [class]p7:
// A standard-layout class is a class that:
// -- has no non-static data members of type non-standard-layout
// class (or array of such types) [...]
if (!FieldRec->isStandardLayout())
data().IsStandardLayout = false;
if (!FieldRec->isCXX11StandardLayout())
data().IsCXX11StandardLayout = false;
// C++2a [class]p7:
// A standard-layout class is a class that:
// [...]
// -- has no element of the set M(S) of types as a base class.
if (data().IsStandardLayout &&
(isUnion() || IsFirstField || IsZeroSize) &&
hasSubobjectAtOffsetZeroOfEmptyBaseType(Context, FieldRec))
data().IsStandardLayout = false;
// C++11 [class]p7:
// A standard-layout class is a class that:
// -- has no base classes of the same type as the first non-static
// data member
if (data().IsCXX11StandardLayout && IsFirstField) {
// FIXME: We should check all base classes here, not just direct
// base classes.
for (const auto &BI : bases()) {
if (Context.hasSameUnqualifiedType(BI.getType(), T)) {
data().IsCXX11StandardLayout = false;
break;
}
}
}
// Keep track of the presence of mutable fields.
if (FieldRec->hasMutableFields())
data().HasMutableFields = true;
if (Field->isMutable()) {
// Our copy constructor/assignment might call something other than
// the subobject's copy constructor/assignment if it's mutable and of
// class type.
data().NeedOverloadResolutionForCopyConstructor = true;
data().NeedOverloadResolutionForCopyAssignment = true;
}
// C++11 [class.copy]p13:
// If the implicitly-defined constructor would satisfy the
// requirements of a constexpr constructor, the implicitly-defined
// constructor is constexpr.
// C++11 [dcl.constexpr]p4:
// -- every constructor involved in initializing non-static data
// members [...] shall be a constexpr constructor
if (!Field->hasInClassInitializer() &&
!FieldRec->hasConstexprDefaultConstructor() && !isUnion())
// The standard requires any in-class initializer to be a constant
// expression. We consider this to be a defect.
data().DefaultedDefaultConstructorIsConstexpr =
Context.getLangOpts().CPlusPlus23;
// C++11 [class.copy]p8:
// The implicitly-declared copy constructor for a class X will have
// the form 'X::X(const X&)' if each potentially constructed subobject
// of a class type M (or array thereof) has a copy constructor whose
// first parameter is of type 'const M&' or 'const volatile M&'.
if (!FieldRec->hasCopyConstructorWithConstParam())
data().ImplicitCopyConstructorCanHaveConstParamForNonVBase = false;
// C++11 [class.copy]p18:
// The implicitly-declared copy assignment oeprator for a class X will
// have the form 'X& X::operator=(const X&)' if [...] for all the
// non-static data members of X that are of a class type M (or array
// thereof), each such class type has a copy assignment operator whose
// parameter is of type 'const M&', 'const volatile M&' or 'M'.
if (!FieldRec->hasCopyAssignmentWithConstParam())
data().ImplicitCopyAssignmentHasConstParam = false;
if (FieldRec->hasUninitializedExplicitInitFields() &&
FieldRec->isAggregate())
setHasUninitializedExplicitInitFields(true);
if (FieldRec->hasUninitializedReferenceMember() &&
!Field->hasInClassInitializer())
data().HasUninitializedReferenceMember = true;
// C++11 [class.union]p8, DR1460:
// a non-static data member of an anonymous union that is a member of
// X is also a variant member of X.
if (FieldRec->hasVariantMembers() &&
Field->isAnonymousStructOrUnion())
data().HasVariantMembers = true;
}
} else {
// Base element type of field is a non-class type.
if (!T->isLiteralType(Context) ||
(!Field->hasInClassInitializer() && !isUnion() &&
!Context.getLangOpts().CPlusPlus20))
data().DefaultedDefaultConstructorIsConstexpr = false;
// C++11 [class.copy]p23:
// A defaulted copy/move assignment operator for a class X is defined
// as deleted if X has:
// -- a non-static data member of const non-class type (or array
// thereof)
if (T.isConstQualified()) {
data().DefaultedCopyAssignmentIsDeleted = true;
data().DefaultedMoveAssignmentIsDeleted = true;
}
// C++20 [temp.param]p7:
// A structural type is [...] a literal class type [for which] the
// types of all non-static data members are structural types or
// (possibly multidimensional) array thereof
// We deal with class types elsewhere.
if (!T->isStructuralType())
data().StructuralIfLiteral = false;
}
// C++14 [meta.unary.prop]p4:
// T is a class type [...] with [...] no non-static data members other
// than subobjects of zero size
if (data().Empty && !IsZeroSize)
data().Empty = false;
if (getLangOpts().HLSL) {
const Type *Ty = Field->getType()->getUnqualifiedDesugaredType();
while (isa<ConstantArrayType>(Ty))
Ty = Ty->getArrayElementTypeNoTypeQual();
Ty = Ty->getUnqualifiedDesugaredType();
if (const RecordType *RT = dyn_cast<RecordType>(Ty))
data().IsHLSLIntangible |= RT->getAsCXXRecordDecl()->isHLSLIntangible();
else
data().IsHLSLIntangible |= (Ty->isHLSLAttributedResourceType() ||
Ty->isHLSLBuiltinIntangibleType());
}
}
// Handle using declarations of conversion functions.
if (auto *Shadow = dyn_cast<UsingShadowDecl>(D)) {
if (Shadow->getDeclName().getNameKind()
== DeclarationName::CXXConversionFunctionName) {
ASTContext &Ctx = getASTContext();
data().Conversions.get(Ctx).addDecl(Ctx, Shadow, Shadow->getAccess());
}
}
if (const auto *Using = dyn_cast<UsingDecl>(D)) {
if (Using->getDeclName().getNameKind() ==
DeclarationName::CXXConstructorName) {
data().HasInheritedConstructor = true;
// C++1z [dcl.init.aggr]p1:
// An aggregate is [...] a class [...] with no inherited constructors
data().Aggregate = false;
}
if (Using->getDeclName().getCXXOverloadedOperator() == OO_Equal)
data().HasInheritedAssignment = true;
}
// HLSL: All user-defined data types are aggregates and use aggregate
// initialization, meanwhile most, but not all built-in types behave like
// aggregates. Resource types, and some other HLSL types that wrap handles
// don't behave like aggregates. We can identify these as different because we
// implicitly define "special" member functions, which aren't spellable in
// HLSL. This all _needs_ to change in the future. There are two
// relevant HLSL feature proposals that will depend on this changing:
// * 0005-strict-initializer-lists.md
// * https://github.com/microsoft/hlsl-specs/pull/325
if (getLangOpts().HLSL)
data().Aggregate = data().UserDeclaredSpecialMembers == 0;
}
bool CXXRecordDecl::isLiteral() const {
const LangOptions &LangOpts = getLangOpts();
if (!(LangOpts.CPlusPlus20 ? hasConstexprDestructor()
: hasTrivialDestructor()))
return false;
if (hasNonLiteralTypeFieldsOrBases()) {
// CWG2598
// is an aggregate union type that has either no variant
// members or at least one variant member of non-volatile literal type,
if (!isUnion())
return false;
bool HasAtLeastOneLiteralMember =
fields().empty() || any_of(fields(), [this](const FieldDecl *D) {
return !D->getType().isVolatileQualified() &&
D->getType()->isLiteralType(getASTContext());
});
if (!HasAtLeastOneLiteralMember)
return false;
}
return isAggregate() || (isLambda() && LangOpts.CPlusPlus17) ||
hasConstexprNonCopyMoveConstructor() || hasTrivialDefaultConstructor();
}
void CXXRecordDecl::addedSelectedDestructor(CXXDestructorDecl *DD) {
DD->setIneligibleOrNotSelected(false);
addedEligibleSpecialMemberFunction(DD, SMF_Destructor);
}
void CXXRecordDecl::addedEligibleSpecialMemberFunction(const CXXMethodDecl *MD,
unsigned SMKind) {
// FIXME: We shouldn't change DeclaredNonTrivialSpecialMembers if `MD` is
// a function template, but this needs CWG attention before we break ABI.
// See https://github.com/llvm/llvm-project/issues/59206
if (const auto *DD = dyn_cast<CXXDestructorDecl>(MD)) {
if (DD->isUserProvided())
data().HasIrrelevantDestructor = false;
// If the destructor is explicitly defaulted and not trivial or not public
// or if the destructor is deleted, we clear HasIrrelevantDestructor in
// finishedDefaultedOrDeletedMember.
// C++11 [class.dtor]p5:
// A destructor is trivial if [...] the destructor is not virtual.
if (DD->isVirtual()) {
data().HasTrivialSpecialMembers &= ~SMF_Destructor;
data().HasTrivialSpecialMembersForCall &= ~SMF_Destructor;
}
if (DD->isNoReturn())
data().IsAnyDestructorNoReturn = true;
}
if (!MD->isImplicit() && !MD->isUserProvided()) {
// This method is user-declared but not user-provided. We can't work
// out whether it's trivial yet (not until we get to the end of the
// class). We'll handle this method in
// finishedDefaultedOrDeletedMember.
} else if (MD->isTrivial()) {
data().HasTrivialSpecialMembers |= SMKind;
data().HasTrivialSpecialMembersForCall |= SMKind;
} else if (MD->isTrivialForCall()) {
data().HasTrivialSpecialMembersForCall |= SMKind;
data().DeclaredNonTrivialSpecialMembers |= SMKind;
} else {
data().DeclaredNonTrivialSpecialMembers |= SMKind;
// If this is a user-provided function, do not set
// DeclaredNonTrivialSpecialMembersForCall here since we don't know
// yet whether the method would be considered non-trivial for the
// purpose of calls (attribute "trivial_abi" can be dropped from the
// class later, which can change the special method's triviality).
if (!MD->isUserProvided())
data().DeclaredNonTrivialSpecialMembersForCall |= SMKind;
}
}
void CXXRecordDecl::finishedDefaultedOrDeletedMember(CXXMethodDecl *D) {
assert(!D->isImplicit() && !D->isUserProvided());
// The kind of special member this declaration is, if any.
unsigned SMKind = 0;
if (const auto *Constructor = dyn_cast<CXXConstructorDecl>(D)) {
if (Constructor->isDefaultConstructor()) {
SMKind |= SMF_DefaultConstructor;
if (Constructor->isConstexpr())
data().HasConstexprDefaultConstructor = true;
}
if (Constructor->isCopyConstructor())
SMKind |= SMF_CopyConstructor;
else if (Constructor->isMoveConstructor())
SMKind |= SMF_MoveConstructor;
else if (Constructor->isConstexpr())
// We may now know that the constructor is constexpr.
data().HasConstexprNonCopyMoveConstructor = true;
} else if (isa<CXXDestructorDecl>(D)) {
SMKind |= SMF_Destructor;
if (!D->isTrivial() || D->getAccess() != AS_public || D->isDeleted())
data().HasIrrelevantDestructor = false;
} else if (D->isCopyAssignmentOperator())
SMKind |= SMF_CopyAssignment;
else if (D->isMoveAssignmentOperator())
SMKind |= SMF_MoveAssignment;
// Update which trivial / non-trivial special members we have.
// addedMember will have skipped this step for this member.
if (!D->isIneligibleOrNotSelected()) {
if (D->isTrivial())
data().HasTrivialSpecialMembers |= SMKind;
else
data().DeclaredNonTrivialSpecialMembers |= SMKind;
}
}
void CXXRecordDecl::LambdaDefinitionData::AddCaptureList(ASTContext &Ctx,
Capture *CaptureList) {
Captures.push_back(CaptureList);
if (Captures.size() == 2) {
// The TinyPtrVector member now needs destruction.
Ctx.addDestruction(&Captures);
}
}
void CXXRecordDecl::setCaptures(ASTContext &Context,
ArrayRef<LambdaCapture> Captures) {
CXXRecordDecl::LambdaDefinitionData &Data = getLambdaData();
// Copy captures.
Data.NumCaptures = Captures.size();
Data.NumExplicitCaptures = 0;
auto *ToCapture = (LambdaCapture *)Context.Allocate(sizeof(LambdaCapture) *
Captures.size());
Data.AddCaptureList(Context, ToCapture);
for (const LambdaCapture &C : Captures) {
if (C.isExplicit())
++Data.NumExplicitCaptures;
new (ToCapture) LambdaCapture(C);
ToCapture++;
}
if (!lambdaIsDefaultConstructibleAndAssignable())
Data.DefaultedCopyAssignmentIsDeleted = true;
}
void CXXRecordDecl::setTrivialForCallFlags(CXXMethodDecl *D) {
unsigned SMKind = 0;
if (const auto *Constructor = dyn_cast<CXXConstructorDecl>(D)) {
if (Constructor->isCopyConstructor())
SMKind = SMF_CopyConstructor;
else if (Constructor->isMoveConstructor())
SMKind = SMF_MoveConstructor;
} else if (isa<CXXDestructorDecl>(D))
SMKind = SMF_Destructor;
if (D->isTrivialForCall())
data().HasTrivialSpecialMembersForCall |= SMKind;
else
data().DeclaredNonTrivialSpecialMembersForCall |= SMKind;
}
bool CXXRecordDecl::isCLike() const {
if (getTagKind() == TagTypeKind::Class ||
getTagKind() == TagTypeKind::Interface ||
!TemplateOrInstantiation.isNull())
return false;
if (!hasDefinition())
return true;
return isPOD() && data().HasOnlyCMembers;
}
bool CXXRecordDecl::isGenericLambda() const {
if (!isLambda()) return false;
return getLambdaData().IsGenericLambda;
}
#ifndef NDEBUG
static bool allLookupResultsAreTheSame(const DeclContext::lookup_result &R) {
return llvm::all_of(R, [&](NamedDecl *D) {
return D->isInvalidDecl() || declaresSameEntity(D, R.front());
});
}
#endif
static NamedDecl* getLambdaCallOperatorHelper(const CXXRecordDecl &RD) {
if (!RD.isLambda()) return nullptr;
DeclarationName Name =
RD.getASTContext().DeclarationNames.getCXXOperatorName(OO_Call);
DeclContext::lookup_result Calls = RD.lookup(Name);
assert(!Calls.empty() && "Missing lambda call operator!");
assert(allLookupResultsAreTheSame(Calls) &&
"More than one lambda call operator!");
// FIXME: If we have multiple call operators, we might be in a situation
// where we merged this lambda with one from another module; in that
// case, return our method (instead of that of the other lambda).
//
// This avoids situations where, given two modules A and B, if we
// try to instantiate A's call operator in a function in B, anything
// in the call operator that relies on local decls in the surrounding
// function will crash because it tries to find A's decls, but we only
// instantiated B's:
//
// template <typename>
// void f() {
// using T = int; // We only instantiate B's version of this.
// auto L = [](T) { }; // But A's call operator would want A's here.
// }
//
// Walk the call operators redecl chain to find the one that belongs
// to this module.
//
// TODO: We need to fix this properly (see
// https://github.com/llvm/llvm-project/issues/90154).
Module *M = RD.getOwningModule();
for (Decl *D : Calls.front()->redecls()) {
auto *MD = cast<NamedDecl>(D);
if (MD->getOwningModule() == M)
return MD;
}
llvm_unreachable("Couldn't find our call operator!");
}
FunctionTemplateDecl* CXXRecordDecl::getDependentLambdaCallOperator() const {
NamedDecl *CallOp = getLambdaCallOperatorHelper(*this);
return dyn_cast_or_null<FunctionTemplateDecl>(CallOp);
}
CXXMethodDecl *CXXRecordDecl::getLambdaCallOperator() const {
NamedDecl *CallOp = getLambdaCallOperatorHelper(*this);
if (CallOp == nullptr)
return nullptr;
if (const auto *CallOpTmpl = dyn_cast<FunctionTemplateDecl>(CallOp))
return cast<CXXMethodDecl>(CallOpTmpl->getTemplatedDecl());
return cast<CXXMethodDecl>(CallOp);
}
CXXMethodDecl* CXXRecordDecl::getLambdaStaticInvoker() const {
CXXMethodDecl *CallOp = getLambdaCallOperator();
CallingConv CC = CallOp->getType()->castAs<FunctionType>()->getCallConv();
return getLambdaStaticInvoker(CC);
}
static DeclContext::lookup_result
getLambdaStaticInvokers(const CXXRecordDecl &RD) {
assert(RD.isLambda() && "Must be a lambda");
DeclarationName Name =
&RD.getASTContext().Idents.get(getLambdaStaticInvokerName());
return RD.lookup(Name);
}
static CXXMethodDecl *getInvokerAsMethod(NamedDecl *ND) {
if (const auto *InvokerTemplate = dyn_cast<FunctionTemplateDecl>(ND))
return cast<CXXMethodDecl>(InvokerTemplate->getTemplatedDecl());
return cast<CXXMethodDecl>(ND);
}
CXXMethodDecl *CXXRecordDecl::getLambdaStaticInvoker(CallingConv CC) const {
if (!isLambda())
return nullptr;
DeclContext::lookup_result Invoker = getLambdaStaticInvokers(*this);
for (NamedDecl *ND : Invoker) {
const auto *FTy =
cast<ValueDecl>(ND->getAsFunction())->getType()->castAs<FunctionType>();
if (FTy->getCallConv() == CC)
return getInvokerAsMethod(ND);
}
return nullptr;
}
void CXXRecordDecl::getCaptureFields(
llvm::DenseMap<const ValueDecl *, FieldDecl *> &Captures,
FieldDecl *&ThisCapture) const {
Captures.clear();
ThisCapture = nullptr;
LambdaDefinitionData &Lambda = getLambdaData();
for (const LambdaCapture *List : Lambda.Captures) {
RecordDecl::field_iterator Field = field_begin();
for (const LambdaCapture *C = List, *CEnd = C + Lambda.NumCaptures;
C != CEnd; ++C, ++Field) {
if (C->capturesThis())
ThisCapture = *Field;
else if (C->capturesVariable())
Captures[C->getCapturedVar()] = *Field;
}
assert(Field == field_end());
}
}
TemplateParameterList *
CXXRecordDecl::getGenericLambdaTemplateParameterList() const {
if (!isGenericLambda()) return nullptr;
CXXMethodDecl *CallOp = getLambdaCallOperator();
if (FunctionTemplateDecl *Tmpl = CallOp->getDescribedFunctionTemplate())
return Tmpl->getTemplateParameters();
return nullptr;
}
ArrayRef<NamedDecl *>
CXXRecordDecl::getLambdaExplicitTemplateParameters() const {
TemplateParameterList *List = getGenericLambdaTemplateParameterList();
if (!List)
return {};
assert(std::is_partitioned(List->begin(), List->end(),
[](const NamedDecl *D) { return !D->isImplicit(); })
&& "Explicit template params should be ordered before implicit ones");
const auto ExplicitEnd = llvm::partition_point(
*List, [](const NamedDecl *D) { return !D->isImplicit(); });
return llvm::ArrayRef(List->begin(), ExplicitEnd);
}
Decl *CXXRecordDecl::getLambdaContextDecl() const {
assert(isLambda() && "Not a lambda closure type!");
ExternalASTSource *Source = getParentASTContext().getExternalSource();
return getLambdaData().ContextDecl.get(Source);
}
void CXXRecordDecl::setLambdaNumbering(LambdaNumbering Numbering) {
assert(isLambda() && "Not a lambda closure type!");
getLambdaData().ManglingNumber = Numbering.ManglingNumber;
if (Numbering.DeviceManglingNumber)
getASTContext().DeviceLambdaManglingNumbers[this] =
Numbering.DeviceManglingNumber;
getLambdaData().IndexInContext = Numbering.IndexInContext;
getLambdaData().ContextDecl = Numbering.ContextDecl;
getLambdaData().HasKnownInternalLinkage = Numbering.HasKnownInternalLinkage;
}
unsigned CXXRecordDecl::getDeviceLambdaManglingNumber() const {
assert(isLambda() && "Not a lambda closure type!");
return getASTContext().DeviceLambdaManglingNumbers.lookup(this);
}
static CanQualType GetConversionType(ASTContext &Context, NamedDecl *Conv) {
QualType T =
cast<CXXConversionDecl>(Conv->getUnderlyingDecl()->getAsFunction())
->getConversionType();
return Context.getCanonicalType(T);
}
/// Collect the visible conversions of a base class.
///
/// \param Record a base class of the class we're considering
/// \param InVirtual whether this base class is a virtual base (or a base
/// of a virtual base)
/// \param Access the access along the inheritance path to this base
/// \param ParentHiddenTypes the conversions provided by the inheritors
/// of this base
/// \param Output the set to which to add conversions from non-virtual bases
/// \param VOutput the set to which to add conversions from virtual bases
/// \param HiddenVBaseCs the set of conversions which were hidden in a
/// virtual base along some inheritance path
static void CollectVisibleConversions(
ASTContext &Context, const CXXRecordDecl *Record, bool InVirtual,
AccessSpecifier Access,
const llvm::SmallPtrSet<CanQualType, 8> &ParentHiddenTypes,
ASTUnresolvedSet &Output, UnresolvedSetImpl &VOutput,
llvm::SmallPtrSet<NamedDecl *, 8> &HiddenVBaseCs) {
// The set of types which have conversions in this class or its
// subclasses. As an optimization, we don't copy the derived set
// unless it might change.
const llvm::SmallPtrSet<CanQualType, 8> *HiddenTypes = &ParentHiddenTypes;
llvm::SmallPtrSet<CanQualType, 8> HiddenTypesBuffer;
// Collect the direct conversions and figure out which conversions
// will be hidden in the subclasses.
CXXRecordDecl::conversion_iterator ConvI = Record->conversion_begin();
CXXRecordDecl::conversion_iterator ConvE = Record->conversion_end();
if (ConvI != ConvE) {
HiddenTypesBuffer = ParentHiddenTypes;
HiddenTypes = &HiddenTypesBuffer;
for (CXXRecordDecl::conversion_iterator I = ConvI; I != ConvE; ++I) {
CanQualType ConvType(GetConversionType(Context, I.getDecl()));
bool Hidden = ParentHiddenTypes.count(ConvType);
if (!Hidden)
HiddenTypesBuffer.insert(ConvType);
// If this conversion is hidden and we're in a virtual base,
// remember that it's hidden along some inheritance path.
if (Hidden && InVirtual)
HiddenVBaseCs.insert(cast<NamedDecl>(I.getDecl()->getCanonicalDecl()));
// If this conversion isn't hidden, add it to the appropriate output.
else if (!Hidden) {
AccessSpecifier IAccess
= CXXRecordDecl::MergeAccess(Access, I.getAccess());
if (InVirtual)
VOutput.addDecl(I.getDecl(), IAccess);
else
Output.addDecl(Context, I.getDecl(), IAccess);
}
}
}
// Collect information recursively from any base classes.
for (const auto &I : Record->bases()) {
const auto *RT = I.getType()->getAs<RecordType>();
if (!RT) continue;
AccessSpecifier BaseAccess
= CXXRecordDecl::MergeAccess(Access, I.getAccessSpecifier());
bool BaseInVirtual = InVirtual || I.isVirtual();
auto *Base = cast<CXXRecordDecl>(RT->getDecl());
CollectVisibleConversions(Context, Base, BaseInVirtual, BaseAccess,
*HiddenTypes, Output, VOutput, HiddenVBaseCs);
}
}
/// Collect the visible conversions of a class.
///
/// This would be extremely straightforward if it weren't for virtual
/// bases. It might be worth special-casing that, really.
static void CollectVisibleConversions(ASTContext &Context,
const CXXRecordDecl *Record,
ASTUnresolvedSet &Output) {
// The collection of all conversions in virtual bases that we've
// found. These will be added to the output as long as they don't
// appear in the hidden-conversions set.
UnresolvedSet<8> VBaseCs;
// The set of conversions in virtual bases that we've determined to
// be hidden.
llvm::SmallPtrSet<NamedDecl*, 8> HiddenVBaseCs;
// The set of types hidden by classes derived from this one.
llvm::SmallPtrSet<CanQualType, 8> HiddenTypes;
// Go ahead and collect the direct conversions and add them to the
// hidden-types set.
CXXRecordDecl::conversion_iterator ConvI = Record->conversion_begin();
CXXRecordDecl::conversion_iterator ConvE = Record->conversion_end();
Output.append(Context, ConvI, ConvE);
for (; ConvI != ConvE; ++ConvI)
HiddenTypes.insert(GetConversionType(Context, ConvI.getDecl()));
// Recursively collect conversions from base classes.
for (const auto &I : Record->bases()) {
const auto *RT = I.getType()->getAs<RecordType>();
if (!RT) continue;
CollectVisibleConversions(Context, cast<CXXRecordDecl>(RT->getDecl()),
I.isVirtual(), I.getAccessSpecifier(),
HiddenTypes, Output, VBaseCs, HiddenVBaseCs);
}
// Add any unhidden conversions provided by virtual bases.
for (UnresolvedSetIterator I = VBaseCs.begin(), E = VBaseCs.end();
I != E; ++I) {
if (!HiddenVBaseCs.count(cast<NamedDecl>(I.getDecl()->getCanonicalDecl())))
Output.addDecl(Context, I.getDecl(), I.getAccess());
}
}
/// getVisibleConversionFunctions - get all conversion functions visible
/// in current class; including conversion function templates.
llvm::iterator_range<CXXRecordDecl::conversion_iterator>
CXXRecordDecl::getVisibleConversionFunctions() const {
ASTContext &Ctx = getASTContext();
ASTUnresolvedSet *Set;
if (bases_begin() == bases_end()) {
// If root class, all conversions are visible.
Set = &data().Conversions.get(Ctx);
} else {
Set = &data().VisibleConversions.get(Ctx);
// If visible conversion list is not evaluated, evaluate it.
if (!data().ComputedVisibleConversions) {
CollectVisibleConversions(Ctx, this, *Set);
data().ComputedVisibleConversions = true;
}
}
return llvm::make_range(Set->begin(), Set->end());
}
void CXXRecordDecl::removeConversion(const NamedDecl *ConvDecl) {
// This operation is O(N) but extremely rare. Sema only uses it to
// remove UsingShadowDecls in a class that were followed by a direct
// declaration, e.g.:
// class A : B {
// using B::operator int;
// operator int();
// };
// This is uncommon by itself and even more uncommon in conjunction
// with sufficiently large numbers of directly-declared conversions
// that asymptotic behavior matters.
ASTUnresolvedSet &Convs = data().Conversions.get(getASTContext());
for (unsigned I = 0, E = Convs.size(); I != E; ++I) {
if (Convs[I].getDecl() == ConvDecl) {
Convs.erase(I);
assert(!llvm::is_contained(Convs, ConvDecl) &&
"conversion was found multiple times in unresolved set");
return;
}
}
llvm_unreachable("conversion not found in set!");
}
CXXRecordDecl *CXXRecordDecl::getInstantiatedFromMemberClass() const {
if (MemberSpecializationInfo *MSInfo = getMemberSpecializationInfo())
return cast<CXXRecordDecl>(MSInfo->getInstantiatedFrom());
return nullptr;
}
MemberSpecializationInfo *CXXRecordDecl::getMemberSpecializationInfo() const {
return dyn_cast_if_present<MemberSpecializationInfo *>(
TemplateOrInstantiation);
}
void
CXXRecordDecl::setInstantiationOfMemberClass(CXXRecordDecl *RD,
TemplateSpecializationKind TSK) {
assert(TemplateOrInstantiation.isNull() &&
"Previous template or instantiation?");
assert(!isa<ClassTemplatePartialSpecializationDecl>(this));
TemplateOrInstantiation
= new (getASTContext()) MemberSpecializationInfo(RD, TSK);
}
ClassTemplateDecl *CXXRecordDecl::getDescribedClassTemplate() const {
return dyn_cast_if_present<ClassTemplateDecl *>(TemplateOrInstantiation);
}
void CXXRecordDecl::setDescribedClassTemplate(ClassTemplateDecl *Template) {
TemplateOrInstantiation = Template;
}
TemplateSpecializationKind CXXRecordDecl::getTemplateSpecializationKind() const{
if (const auto *Spec = dyn_cast<ClassTemplateSpecializationDecl>(this))
return Spec->getSpecializationKind();
if (MemberSpecializationInfo *MSInfo = getMemberSpecializationInfo())
return MSInfo->getTemplateSpecializationKind();
return TSK_Undeclared;
}
void
CXXRecordDecl::setTemplateSpecializationKind(TemplateSpecializationKind TSK) {
if (auto *Spec = dyn_cast<ClassTemplateSpecializationDecl>(this)) {
Spec->setSpecializationKind(TSK);
return;
}
if (MemberSpecializationInfo *MSInfo = getMemberSpecializationInfo()) {
MSInfo->setTemplateSpecializationKind(TSK);
return;
}
llvm_unreachable("Not a class template or member class specialization");
}
const CXXRecordDecl *CXXRecordDecl::getTemplateInstantiationPattern() const {
auto GetDefinitionOrSelf =
[](const CXXRecordDecl *D) -> const CXXRecordDecl * {
if (auto *Def = D->getDefinition())
return Def;
return D;
};
// If it's a class template specialization, find the template or partial
// specialization from which it was instantiated.
if (auto *TD = dyn_cast<ClassTemplateSpecializationDecl>(this)) {
auto From = TD->getInstantiatedFrom();
if (auto *CTD = dyn_cast_if_present<ClassTemplateDecl *>(From)) {
while (auto *NewCTD = CTD->getInstantiatedFromMemberTemplate()) {
if (NewCTD->isMemberSpecialization())
break;
CTD = NewCTD;
}
return GetDefinitionOrSelf(CTD->getTemplatedDecl());
}
if (auto *CTPSD =
dyn_cast_if_present<ClassTemplatePartialSpecializationDecl *>(
From)) {
while (auto *NewCTPSD = CTPSD->getInstantiatedFromMember()) {
if (NewCTPSD->isMemberSpecialization())
break;
CTPSD = NewCTPSD;
}
return GetDefinitionOrSelf(CTPSD);
}
}
if (MemberSpecializationInfo *MSInfo = getMemberSpecializationInfo()) {
if (isTemplateInstantiation(MSInfo->getTemplateSpecializationKind())) {
const CXXRecordDecl *RD = this;
while (auto *NewRD = RD->getInstantiatedFromMemberClass())
RD = NewRD;
return GetDefinitionOrSelf(RD);
}
}
assert(!isTemplateInstantiation(this->getTemplateSpecializationKind()) &&
"couldn't find pattern for class template instantiation");
return nullptr;
}
CXXDestructorDecl *CXXRecordDecl::getDestructor() const {
ASTContext &Context = getASTContext();
QualType ClassType = Context.getTypeDeclType(this);
DeclarationName Name
= Context.DeclarationNames.getCXXDestructorName(
Context.getCanonicalType(ClassType));
DeclContext::lookup_result R = lookup(Name);
// If a destructor was marked as not selected, we skip it. We don't always
// have a selected destructor: dependent types, unnamed structs.
for (auto *Decl : R) {
auto* DD = dyn_cast<CXXDestructorDecl>(Decl);
if (DD && !DD->isIneligibleOrNotSelected())
return DD;
}
return nullptr;
}
static bool isDeclContextInNamespace(const DeclContext *DC) {
while (!DC->isTranslationUnit()) {
if (DC->isNamespace())
return true;
DC = DC->getParent();
}
return false;
}
bool CXXRecordDecl::isInterfaceLike() const {
assert(hasDefinition() && "checking for interface-like without a definition");
// All __interfaces are inheritently interface-like.
if (isInterface())
return true;
// Interface-like types cannot have a user declared constructor, destructor,
// friends, VBases, conversion functions, or fields. Additionally, lambdas
// cannot be interface types.
if (isLambda() || hasUserDeclaredConstructor() ||
hasUserDeclaredDestructor() || !field_empty() || hasFriends() ||
getNumVBases() > 0 || conversion_end() - conversion_begin() > 0)
return false;
// No interface-like type can have a method with a definition.
for (const auto *const Method : methods())
if (Method->isDefined() && !Method->isImplicit())
return false;
// Check "Special" types.
const auto *Uuid = getAttr<UuidAttr>();
// MS SDK declares IUnknown/IDispatch both in the root of a TU, or in an
// extern C++ block directly in the TU. These are only valid if in one
// of these two situations.
if (Uuid && isStruct() && !getDeclContext()->isExternCContext() &&
!isDeclContextInNamespace(getDeclContext()) &&
((getName() == "IUnknown" &&
Uuid->getGuid() == "00000000-0000-0000-C000-000000000046") ||
(getName() == "IDispatch" &&
Uuid->getGuid() == "00020400-0000-0000-C000-000000000046"))) {
if (getNumBases() > 0)
return false;
return true;
}
// FIXME: Any access specifiers is supposed to make this no longer interface
// like.
// If this isn't a 'special' type, it must have a single interface-like base.
if (getNumBases() != 1)
return false;
const auto BaseSpec = *bases_begin();
if (BaseSpec.isVirtual() || BaseSpec.getAccessSpecifier() != AS_public)
return false;
const auto *Base = BaseSpec.getType()->getAsCXXRecordDecl();
if (Base->isInterface() || !Base->isInterfaceLike())
return false;
return true;
}
void CXXRecordDecl::completeDefinition() {
completeDefinition(nullptr);
}
static bool hasPureVirtualFinalOverrider(
const CXXRecordDecl &RD, const CXXFinalOverriderMap *FinalOverriders) {
if (!FinalOverriders) {
CXXFinalOverriderMap MyFinalOverriders;
RD.getFinalOverriders(MyFinalOverriders);
return hasPureVirtualFinalOverrider(RD, &MyFinalOverriders);
}
for (const CXXFinalOverriderMap::value_type &
OverridingMethodsEntry : *FinalOverriders) {
for (const auto &[_, SubobjOverrides] : OverridingMethodsEntry.second) {
assert(SubobjOverrides.size() > 0 &&
"All virtual functions have overriding virtual functions");
if (SubobjOverrides.front().Method->isPureVirtual())
return true;
}
}
return false;
}
void CXXRecordDecl::completeDefinition(CXXFinalOverriderMap *FinalOverriders) {
RecordDecl::completeDefinition();
// If the class may be abstract (but hasn't been marked as such), check for
// any pure final overriders.
//
// C++ [class.abstract]p4:
// A class is abstract if it contains or inherits at least one
// pure virtual function for which the final overrider is pure
// virtual.
if (mayBeAbstract() && hasPureVirtualFinalOverrider(*this, FinalOverriders))
markAbstract();
// Set access bits correctly on the directly-declared conversions.
for (conversion_iterator I = conversion_begin(), E = conversion_end();
I != E; ++I)
I.setAccess((*I)->getAccess());
ASTContext &Context = getASTContext();
if (isAggregate() && hasUserDeclaredConstructor() &&
!Context.getLangOpts().CPlusPlus20) {
// Diagnose any aggregate behavior changes in C++20
for (const FieldDecl *FD : fields()) {
if (const auto *AT = FD->getAttr<ExplicitInitAttr>())
Context.getDiagnostics().Report(
AT->getLocation(),
diag::warn_cxx20_compat_requires_explicit_init_non_aggregate)
<< AT << FD << Context.getRecordType(this);
}
}
if (!isAggregate() && hasUninitializedExplicitInitFields()) {
// Diagnose any fields that required explicit initialization in a
// non-aggregate type. (Note that the fields may not be directly in this
// type, but in a subobject. In such cases we don't emit diagnoses here.)
for (const FieldDecl *FD : fields()) {
if (const auto *AT = FD->getAttr<ExplicitInitAttr>())
Context.getDiagnostics().Report(AT->getLocation(),
diag::warn_attribute_needs_aggregate)
<< AT << Context.getRecordType(this);
}
setHasUninitializedExplicitInitFields(false);
}
}
bool CXXRecordDecl::mayBeAbstract() const {
if (data().Abstract || isInvalidDecl() || !data().Polymorphic ||
isDependentContext())
return false;
for (const auto &B : bases()) {
const auto *BaseDecl =
cast<CXXRecordDecl>(B.getType()->castAs<RecordType>()->getDecl());
if (BaseDecl->isAbstract())
return true;
}
return false;
}
bool CXXRecordDecl::isEffectivelyFinal() const {
auto *Def = getDefinition();
if (!Def)
return false;
if (Def->hasAttr<FinalAttr>())
return true;
if (const auto *Dtor = Def->getDestructor())
if (Dtor->hasAttr<FinalAttr>())
return true;
return false;
}
void CXXDeductionGuideDecl::anchor() {}
bool ExplicitSpecifier::isEquivalent(const ExplicitSpecifier Other) const {
if ((getKind() != Other.getKind() ||
getKind() == ExplicitSpecKind::Unresolved)) {
if (getKind() == ExplicitSpecKind::Unresolved &&
Other.getKind() == ExplicitSpecKind::Unresolved) {
ODRHash SelfHash, OtherHash;
SelfHash.AddStmt(getExpr());
OtherHash.AddStmt(Other.getExpr());
return SelfHash.CalculateHash() == OtherHash.CalculateHash();
} else
return false;
}
return true;
}
ExplicitSpecifier ExplicitSpecifier::getFromDecl(FunctionDecl *Function) {
switch (Function->getDeclKind()) {
case Decl::Kind::CXXConstructor:
return cast<CXXConstructorDecl>(Function)->getExplicitSpecifier();
case Decl::Kind::CXXConversion:
return cast<CXXConversionDecl>(Function)->getExplicitSpecifier();
case Decl::Kind::CXXDeductionGuide:
return cast<CXXDeductionGuideDecl>(Function)->getExplicitSpecifier();
default:
return {};
}
}
CXXDeductionGuideDecl *CXXDeductionGuideDecl::Create(
ASTContext &C, DeclContext *DC, SourceLocation StartLoc,
ExplicitSpecifier ES, const DeclarationNameInfo &NameInfo, QualType T,
TypeSourceInfo *TInfo, SourceLocation EndLocation, CXXConstructorDecl *Ctor,
DeductionCandidate Kind, const AssociatedConstraint &TrailingRequiresClause,
const CXXDeductionGuideDecl *GeneratedFrom,
SourceDeductionGuideKind SourceKind) {
return new (C, DC) CXXDeductionGuideDecl(
C, DC, StartLoc, ES, NameInfo, T, TInfo, EndLocation, Ctor, Kind,
TrailingRequiresClause, GeneratedFrom, SourceKind);
}
CXXDeductionGuideDecl *
CXXDeductionGuideDecl::CreateDeserialized(ASTContext &C, GlobalDeclID ID) {
return new (C, ID) CXXDeductionGuideDecl(
C, /*DC=*/nullptr, SourceLocation(), ExplicitSpecifier(),
DeclarationNameInfo(), QualType(), /*TInfo=*/nullptr, SourceLocation(),
/*Ctor=*/nullptr, DeductionCandidate::Normal,
/*TrailingRequiresClause=*/{},
/*GeneratedFrom=*/nullptr, SourceDeductionGuideKind::None);
}
RequiresExprBodyDecl *RequiresExprBodyDecl::Create(
ASTContext &C, DeclContext *DC, SourceLocation StartLoc) {
return new (C, DC) RequiresExprBodyDecl(C, DC, StartLoc);
}
RequiresExprBodyDecl *
RequiresExprBodyDecl::CreateDeserialized(ASTContext &C, GlobalDeclID ID) {
return new (C, ID) RequiresExprBodyDecl(C, nullptr, SourceLocation());
}
void CXXMethodDecl::anchor() {}
bool CXXMethodDecl::isStatic() const {
const CXXMethodDecl *MD = getCanonicalDecl();
if (MD->getStorageClass() == SC_Static)
return true;
OverloadedOperatorKind OOK = getDeclName().getCXXOverloadedOperator();
return isStaticOverloadedOperator(OOK);
}
static bool recursivelyOverrides(const CXXMethodDecl *DerivedMD,
const CXXMethodDecl *BaseMD) {
for (const CXXMethodDecl *MD : DerivedMD->overridden_methods()) {
if (MD->getCanonicalDecl() == BaseMD->getCanonicalDecl())
return true;
if (recursivelyOverrides(MD, BaseMD))
return true;
}
return false;
}
CXXMethodDecl *
CXXMethodDecl::getCorrespondingMethodDeclaredInClass(const CXXRecordDecl *RD,
bool MayBeBase) {
if (this->getParent()->getCanonicalDecl() == RD->getCanonicalDecl())
return this;
// Lookup doesn't work for destructors, so handle them separately.
if (isa<CXXDestructorDecl>(this)) {
CXXMethodDecl *MD = RD->getDestructor();
if (MD) {
if (recursivelyOverrides(MD, this))
return MD;
if (MayBeBase && recursivelyOverrides(this, MD))
return MD;
}
return nullptr;
}
for (auto *ND : RD->lookup(getDeclName())) {
auto *MD = dyn_cast<CXXMethodDecl>(ND);
if (!MD)
continue;
if (recursivelyOverrides(MD, this))
return MD;
if (MayBeBase && recursivelyOverrides(this, MD))
return MD;
}
return nullptr;
}
CXXMethodDecl *
CXXMethodDecl::getCorrespondingMethodInClass(const CXXRecordDecl *RD,
bool MayBeBase) {
if (auto *MD = getCorrespondingMethodDeclaredInClass(RD, MayBeBase))
return MD;
llvm::SmallVector<CXXMethodDecl*, 4> FinalOverriders;
auto AddFinalOverrider = [&](CXXMethodDecl *D) {
// If this function is overridden by a candidate final overrider, it is not
// a final overrider.
for (CXXMethodDecl *OtherD : FinalOverriders) {
if (declaresSameEntity(D, OtherD) || recursivelyOverrides(OtherD, D))
return;
}
// Other candidate final overriders might be overridden by this function.
llvm::erase_if(FinalOverriders, [&](CXXMethodDecl *OtherD) {
return recursivelyOverrides(D, OtherD);
});
FinalOverriders.push_back(D);
};
for (const auto &I : RD->bases()) {
const RecordType *RT = I.getType()->getAs<RecordType>();
if (!RT)
continue;
const auto *Base = cast<CXXRecordDecl>(RT->getDecl());
if (CXXMethodDecl *D = this->getCorrespondingMethodInClass(Base))
AddFinalOverrider(D);
}
return FinalOverriders.size() == 1 ? FinalOverriders.front() : nullptr;
}
CXXMethodDecl *
CXXMethodDecl::Create(ASTContext &C, CXXRecordDecl *RD, SourceLocation StartLoc,
const DeclarationNameInfo &NameInfo, QualType T,
TypeSourceInfo *TInfo, StorageClass SC, bool UsesFPIntrin,
bool isInline, ConstexprSpecKind ConstexprKind,
SourceLocation EndLocation,
const AssociatedConstraint &TrailingRequiresClause) {
return new (C, RD) CXXMethodDecl(
CXXMethod, C, RD, StartLoc, NameInfo, T, TInfo, SC, UsesFPIntrin,
isInline, ConstexprKind, EndLocation, TrailingRequiresClause);
}
CXXMethodDecl *CXXMethodDecl::CreateDeserialized(ASTContext &C,
GlobalDeclID ID) {
return new (C, ID)
CXXMethodDecl(CXXMethod, C, nullptr, SourceLocation(),
DeclarationNameInfo(), QualType(), nullptr, SC_None, false,
false, ConstexprSpecKind::Unspecified, SourceLocation(),
/*TrailingRequiresClause=*/{});
}
CXXMethodDecl *CXXMethodDecl::getDevirtualizedMethod(const Expr *Base,
bool IsAppleKext) {
assert(isVirtual() && "this method is expected to be virtual");
// When building with -fapple-kext, all calls must go through the vtable since
// the kernel linker can do runtime patching of vtables.
if (IsAppleKext)
return nullptr;
// If the member function is marked 'final', we know that it can't be
// overridden and can therefore devirtualize it unless it's pure virtual.
if (hasAttr<FinalAttr>())
return isPureVirtual() ? nullptr : this;
// If Base is unknown, we cannot devirtualize.
if (!Base)
return nullptr;
// If the base expression (after skipping derived-to-base conversions) is a
// class prvalue, then we can devirtualize.
Base = Base->getBestDynamicClassTypeExpr();
if (Base->isPRValue() && Base->getType()->isRecordType())
return this;
// If we don't even know what we would call, we can't devirtualize.
const CXXRecordDecl *BestDynamicDecl = Base->getBestDynamicClassType();
if (!BestDynamicDecl)
return nullptr;
// There may be a method corresponding to MD in a derived class.
CXXMethodDecl *DevirtualizedMethod =
getCorrespondingMethodInClass(BestDynamicDecl);
// If there final overrider in the dynamic type is ambiguous, we can't
// devirtualize this call.
if (!DevirtualizedMethod)
return nullptr;
// If that method is pure virtual, we can't devirtualize. If this code is
// reached, the result would be UB, not a direct call to the derived class
// function, and we can't assume the derived class function is defined.
if (DevirtualizedMethod->isPureVirtual())
return nullptr;
// If that method is marked final, we can devirtualize it.
if (DevirtualizedMethod->hasAttr<FinalAttr>())
return DevirtualizedMethod;
// Similarly, if the class itself or its destructor is marked 'final',
// the class can't be derived from and we can therefore devirtualize the
// member function call.
if (BestDynamicDecl->isEffectivelyFinal())
return DevirtualizedMethod;
if (const auto *DRE = dyn_cast<DeclRefExpr>(Base)) {
if (const auto *VD = dyn_cast<VarDecl>(DRE->getDecl()))
if (VD->getType()->isRecordType())
// This is a record decl. We know the type and can devirtualize it.
return DevirtualizedMethod;
return nullptr;
}
// We can devirtualize calls on an object accessed by a class member access
// expression, since by C++11 [basic.life]p6 we know that it can't refer to
// a derived class object constructed in the same location.
if (const auto *ME = dyn_cast<MemberExpr>(Base)) {
const ValueDecl *VD = ME->getMemberDecl();
return VD->getType()->isRecordType() ? DevirtualizedMethod : nullptr;
}
// Likewise for calls on an object accessed by a (non-reference) pointer to
// member access.
if (auto *BO = dyn_cast<BinaryOperator>(Base)) {
if (BO->isPtrMemOp()) {
auto *MPT = BO->getRHS()->getType()->castAs<MemberPointerType>();
if (MPT->getPointeeType()->isRecordType())
return DevirtualizedMethod;
}
}
// We can't devirtualize the call.
return nullptr;
}
bool CXXMethodDecl::isUsualDeallocationFunction(
SmallVectorImpl<const FunctionDecl *> &PreventedBy) const {
assert(PreventedBy.empty() && "PreventedBy is expected to be empty");
if (!getDeclName().isAnyOperatorDelete())
return false;
if (isTypeAwareOperatorNewOrDelete()) {
// A variadic type aware allocation function is not a usual deallocation
// function
if (isVariadic())
return false;
// Type aware deallocation functions are only usual if they only accept the
// mandatory arguments
if (getNumParams() != FunctionDecl::RequiredTypeAwareDeleteParameterCount)
return false;
FunctionTemplateDecl *PrimaryTemplate = getPrimaryTemplate();
if (!PrimaryTemplate)
return true;
// A template instance is is only a usual deallocation function if it has a
// type-identity parameter, the type-identity parameter is a dependent type
// (i.e. the type-identity parameter is of type std::type_identity<U> where
// U shall be a dependent type), and the type-identity parameter is the only
// dependent parameter, and there are no template packs in the parameter
// list.
FunctionDecl *SpecializedDecl = PrimaryTemplate->getTemplatedDecl();
if (!SpecializedDecl->getParamDecl(0)->getType()->isDependentType())
return false;
for (unsigned Idx = 1; Idx < getNumParams(); ++Idx) {
if (SpecializedDecl->getParamDecl(Idx)->getType()->isDependentType())
return false;
}
return true;
}
// C++ [basic.stc.dynamic.deallocation]p2:
// A template instance is never a usual deallocation function,
// regardless of its signature.
// Post-P2719 adoption:
// A template instance is is only a usual deallocation function if it has a
// type-identity parameter
if (getPrimaryTemplate())
return false;
// C++ [basic.stc.dynamic.deallocation]p2:
// If a class T has a member deallocation function named operator delete
// with exactly one parameter, then that function is a usual (non-placement)
// deallocation function. [...]
if (getNumParams() == 1)
return true;
unsigned UsualParams = 1;
// C++ P0722:
// A destroying operator delete is a usual deallocation function if
// removing the std::destroying_delete_t parameter and changing the
// first parameter type from T* to void* results in the signature of
// a usual deallocation function.
if (isDestroyingOperatorDelete())
++UsualParams;
// C++ <=14 [basic.stc.dynamic.deallocation]p2:
// [...] If class T does not declare such an operator delete but does
// declare a member deallocation function named operator delete with
// exactly two parameters, the second of which has type std::size_t (18.1),
// then this function is a usual deallocation function.
//
// C++17 says a usual deallocation function is one with the signature
// (void* [, size_t] [, std::align_val_t] [, ...])
// and all such functions are usual deallocation functions. It's not clear
// that allowing varargs functions was intentional.
ASTContext &Context = getASTContext();
if (UsualParams < getNumParams() &&
Context.hasSameUnqualifiedType(getParamDecl(UsualParams)->getType(),
Context.getSizeType()))
++UsualParams;
if (UsualParams < getNumParams() &&
getParamDecl(UsualParams)->getType()->isAlignValT())
++UsualParams;
if (UsualParams != getNumParams())
return false;
// In C++17 onwards, all potential usual deallocation functions are actual
// usual deallocation functions. Honor this behavior when post-C++14
// deallocation functions are offered as extensions too.
// FIXME(EricWF): Destroying Delete should be a language option. How do we
// handle when destroying delete is used prior to C++17?
if (Context.getLangOpts().CPlusPlus17 ||
Context.getLangOpts().AlignedAllocation ||
isDestroyingOperatorDelete())
return true;
// This function is a usual deallocation function if there are no
// single-parameter deallocation functions of the same kind.
DeclContext::lookup_result R = getDeclContext()->lookup(getDeclName());
bool Result = true;
for (const auto *D : R) {
if (const auto *FD = dyn_cast<FunctionDecl>(D)) {
if (FD->getNumParams() == 1) {
PreventedBy.push_back(FD);
Result = false;
}
}
}
return Result;
}
bool CXXMethodDecl::isExplicitObjectMemberFunction() const {
// C++2b [dcl.fct]p6:
// An explicit object member function is a non-static member
// function with an explicit object parameter
return !isStatic() && hasCXXExplicitFunctionObjectParameter();
}
bool CXXMethodDecl::isImplicitObjectMemberFunction() const {
return !isStatic() && !hasCXXExplicitFunctionObjectParameter();
}
bool CXXMethodDecl::isCopyAssignmentOperator() const {
// C++0x [class.copy]p17:
// A user-declared copy assignment operator X::operator= is a non-static
// non-template member function of class X with exactly one parameter of
// type X, X&, const X&, volatile X& or const volatile X&.
if (/*operator=*/getOverloadedOperator() != OO_Equal ||
/*non-static*/ isStatic() ||
/*non-template*/ getPrimaryTemplate() || getDescribedFunctionTemplate() ||
getNumExplicitParams() != 1)
return false;
QualType ParamType = getNonObjectParameter(0)->getType();
if (const auto *Ref = ParamType->getAs<LValueReferenceType>())
ParamType = Ref->getPointeeType();
ASTContext &Context = getASTContext();
QualType ClassType
= Context.getCanonicalType(Context.getTypeDeclType(getParent()));
return Context.hasSameUnqualifiedType(ClassType, ParamType);
}
bool CXXMethodDecl::isMoveAssignmentOperator() const {
// C++0x [class.copy]p19:
// A user-declared move assignment operator X::operator= is a non-static
// non-template member function of class X with exactly one parameter of type
// X&&, const X&&, volatile X&&, or const volatile X&&.
if (getOverloadedOperator() != OO_Equal || isStatic() ||
getPrimaryTemplate() || getDescribedFunctionTemplate() ||
getNumExplicitParams() != 1)
return false;
QualType ParamType = getNonObjectParameter(0)->getType();
if (!ParamType->isRValueReferenceType())
return false;
ParamType = ParamType->getPointeeType();
ASTContext &Context = getASTContext();
QualType ClassType
= Context.getCanonicalType(Context.getTypeDeclType(getParent()));
return Context.hasSameUnqualifiedType(ClassType, ParamType);
}
void CXXMethodDecl::addOverriddenMethod(const CXXMethodDecl *MD) {
assert(MD->isCanonicalDecl() && "Method is not canonical!");
assert(MD->isVirtual() && "Method is not virtual!");
getASTContext().addOverriddenMethod(this, MD);
}
CXXMethodDecl::method_iterator CXXMethodDecl::begin_overridden_methods() const {
if (isa<CXXConstructorDecl>(this)) return nullptr;
return getASTContext().overridden_methods_begin(this);
}
CXXMethodDecl::method_iterator CXXMethodDecl::end_overridden_methods() const {
if (isa<CXXConstructorDecl>(this)) return nullptr;
return getASTContext().overridden_methods_end(this);
}
unsigned CXXMethodDecl::size_overridden_methods() const {
if (isa<CXXConstructorDecl>(this)) return 0;
return getASTContext().overridden_methods_size(this);
}
CXXMethodDecl::overridden_method_range
CXXMethodDecl::overridden_methods() const {
if (isa<CXXConstructorDecl>(this))
return overridden_method_range(nullptr, nullptr);
return getASTContext().overridden_methods(this);
}
static QualType getThisObjectType(ASTContext &C, const FunctionProtoType *FPT,
const CXXRecordDecl *Decl) {
QualType ClassTy = C.getTypeDeclType(Decl);
return C.getQualifiedType(ClassTy, FPT->getMethodQuals());
}
QualType CXXMethodDecl::getThisType(const FunctionProtoType *FPT,
const CXXRecordDecl *Decl) {
ASTContext &C = Decl->getASTContext();
QualType ObjectTy = ::getThisObjectType(C, FPT, Decl);
// Unlike 'const' and 'volatile', a '__restrict' qualifier must be
// attached to the pointer type, not the pointee.
bool Restrict = FPT->getMethodQuals().hasRestrict();
if (Restrict)
ObjectTy.removeLocalRestrict();
ObjectTy = C.getLangOpts().HLSL ? C.getLValueReferenceType(ObjectTy)
: C.getPointerType(ObjectTy);
if (Restrict)
ObjectTy.addRestrict();
return ObjectTy;
}
QualType CXXMethodDecl::getThisType() const {
// C++ 9.3.2p1: The type of this in a member function of a class X is X*.
// If the member function is declared const, the type of this is const X*,
// if the member function is declared volatile, the type of this is
// volatile X*, and if the member function is declared const volatile,
// the type of this is const volatile X*.
assert(isInstance() && "No 'this' for static methods!");
return CXXMethodDecl::getThisType(getType()->castAs<FunctionProtoType>(),
getParent());
}
QualType CXXMethodDecl::getFunctionObjectParameterReferenceType() const {
if (isExplicitObjectMemberFunction())
return parameters()[0]->getType();
ASTContext &C = getParentASTContext();
const FunctionProtoType *FPT = getType()->castAs<FunctionProtoType>();
QualType Type = ::getThisObjectType(C, FPT, getParent());
RefQualifierKind RK = FPT->getRefQualifier();
if (RK == RefQualifierKind::RQ_RValue)
return C.getRValueReferenceType(Type);
return C.getLValueReferenceType(Type);
}
bool CXXMethodDecl::hasInlineBody() const {
// If this function is a template instantiation, look at the template from
// which it was instantiated.
const FunctionDecl *CheckFn = getTemplateInstantiationPattern();
if (!CheckFn)
CheckFn = this;
const FunctionDecl *fn;
return CheckFn->isDefined(fn) && !fn->isOutOfLine() &&
(fn->doesThisDeclarationHaveABody() || fn->willHaveBody());
}
bool CXXMethodDecl::isLambdaStaticInvoker() const {
const CXXRecordDecl *P = getParent();
return P->isLambda() && getDeclName().isIdentifier() &&
getName() == getLambdaStaticInvokerName();
}
CXXCtorInitializer::CXXCtorInitializer(ASTContext &Context,
TypeSourceInfo *TInfo, bool IsVirtual,
SourceLocation L, Expr *Init,
SourceLocation R,
SourceLocation EllipsisLoc)
: Initializee(TInfo), Init(Init), MemberOrEllipsisLocation(EllipsisLoc),
LParenLoc(L), RParenLoc(R), IsDelegating(false), IsVirtual(IsVirtual),
IsWritten(false), SourceOrder(0) {}
CXXCtorInitializer::CXXCtorInitializer(ASTContext &Context, FieldDecl *Member,
SourceLocation MemberLoc,
SourceLocation L, Expr *Init,
SourceLocation R)
: Initializee(Member), Init(Init), MemberOrEllipsisLocation(MemberLoc),
LParenLoc(L), RParenLoc(R), IsDelegating(false), IsVirtual(false),
IsWritten(false), SourceOrder(0) {}
CXXCtorInitializer::CXXCtorInitializer(ASTContext &Context,
IndirectFieldDecl *Member,
SourceLocation MemberLoc,
SourceLocation L, Expr *Init,
SourceLocation R)
: Initializee(Member), Init(Init), MemberOrEllipsisLocation(MemberLoc),
LParenLoc(L), RParenLoc(R), IsDelegating(false), IsVirtual(false),
IsWritten(false), SourceOrder(0) {}
CXXCtorInitializer::CXXCtorInitializer(ASTContext &Context,
TypeSourceInfo *TInfo,
SourceLocation L, Expr *Init,
SourceLocation R)
: Initializee(TInfo), Init(Init), LParenLoc(L), RParenLoc(R),
IsDelegating(true), IsVirtual(false), IsWritten(false), SourceOrder(0) {}
int64_t CXXCtorInitializer::getID(const ASTContext &Context) const {
return Context.getAllocator()
.identifyKnownAlignedObject<CXXCtorInitializer>(this);
}
TypeLoc CXXCtorInitializer::getBaseClassLoc() const {
if (isBaseInitializer())
return cast<TypeSourceInfo *>(Initializee)->getTypeLoc();
else
return {};
}
const Type *CXXCtorInitializer::getBaseClass() const {
if (isBaseInitializer())
return cast<TypeSourceInfo *>(Initializee)->getType().getTypePtr();
else
return nullptr;
}
SourceLocation CXXCtorInitializer::getSourceLocation() const {
if (isInClassMemberInitializer())
return getAnyMember()->getLocation();
if (isAnyMemberInitializer())
return getMemberLocation();
if (const auto *TSInfo = cast<TypeSourceInfo *>(Initializee))
return TSInfo->getTypeLoc().getBeginLoc();
return {};
}
SourceRange CXXCtorInitializer::getSourceRange() const {
if (isInClassMemberInitializer()) {
FieldDecl *D = getAnyMember();
if (Expr *I = D->getInClassInitializer())
return I->getSourceRange();
return {};
}
return SourceRange(getSourceLocation(), getRParenLoc());
}
CXXConstructorDecl::CXXConstructorDecl(
ASTContext &C, CXXRecordDecl *RD, SourceLocation StartLoc,
const DeclarationNameInfo &NameInfo, QualType T, TypeSourceInfo *TInfo,
ExplicitSpecifier ES, bool UsesFPIntrin, bool isInline,
bool isImplicitlyDeclared, ConstexprSpecKind ConstexprKind,
InheritedConstructor Inherited,
const AssociatedConstraint &TrailingRequiresClause)
: CXXMethodDecl(CXXConstructor, C, RD, StartLoc, NameInfo, T, TInfo,
SC_None, UsesFPIntrin, isInline, ConstexprKind,
SourceLocation(), TrailingRequiresClause) {
setNumCtorInitializers(0);
setInheritingConstructor(static_cast<bool>(Inherited));
setImplicit(isImplicitlyDeclared);
CXXConstructorDeclBits.HasTrailingExplicitSpecifier = ES.getExpr() ? 1 : 0;
if (Inherited)
*getTrailingObjects<InheritedConstructor>() = Inherited;
setExplicitSpecifier(ES);
}
void CXXConstructorDecl::anchor() {}
CXXConstructorDecl *CXXConstructorDecl::CreateDeserialized(ASTContext &C,
GlobalDeclID ID,
uint64_t AllocKind) {
bool hasTrailingExplicit = static_cast<bool>(AllocKind & TAKHasTailExplicit);
bool isInheritingConstructor =
static_cast<bool>(AllocKind & TAKInheritsConstructor);
unsigned Extra =
additionalSizeToAlloc<InheritedConstructor, ExplicitSpecifier>(
isInheritingConstructor, hasTrailingExplicit);
auto *Result = new (C, ID, Extra) CXXConstructorDecl(
C, nullptr, SourceLocation(), DeclarationNameInfo(), QualType(), nullptr,
ExplicitSpecifier(), false, false, false, ConstexprSpecKind::Unspecified,
InheritedConstructor(), /*TrailingRequiresClause=*/{});
Result->setInheritingConstructor(isInheritingConstructor);
Result->CXXConstructorDeclBits.HasTrailingExplicitSpecifier =
hasTrailingExplicit;
Result->setExplicitSpecifier(ExplicitSpecifier());
return Result;
}
CXXConstructorDecl *CXXConstructorDecl::Create(
ASTContext &C, CXXRecordDecl *RD, SourceLocation StartLoc,
const DeclarationNameInfo &NameInfo, QualType T, TypeSourceInfo *TInfo,
ExplicitSpecifier ES, bool UsesFPIntrin, bool isInline,
bool isImplicitlyDeclared, ConstexprSpecKind ConstexprKind,
InheritedConstructor Inherited,
const AssociatedConstraint &TrailingRequiresClause) {
assert(NameInfo.getName().getNameKind()
== DeclarationName::CXXConstructorName &&
"Name must refer to a constructor");
unsigned Extra =
additionalSizeToAlloc<InheritedConstructor, ExplicitSpecifier>(
Inherited ? 1 : 0, ES.getExpr() ? 1 : 0);
return new (C, RD, Extra) CXXConstructorDecl(
C, RD, StartLoc, NameInfo, T, TInfo, ES, UsesFPIntrin, isInline,
isImplicitlyDeclared, ConstexprKind, Inherited, TrailingRequiresClause);
}
CXXConstructorDecl::init_const_iterator CXXConstructorDecl::init_begin() const {
return CtorInitializers.get(getASTContext().getExternalSource());
}
CXXConstructorDecl *CXXConstructorDecl::getTargetConstructor() const {
assert(isDelegatingConstructor() && "Not a delegating constructor!");
Expr *E = (*init_begin())->getInit()->IgnoreImplicit();
if (const auto *Construct = dyn_cast<CXXConstructExpr>(E))
return Construct->getConstructor();
return nullptr;
}
bool CXXConstructorDecl::isDefaultConstructor() const {
// C++ [class.default.ctor]p1:
// A default constructor for a class X is a constructor of class X for
// which each parameter that is not a function parameter pack has a default
// argument (including the case of a constructor with no parameters)
return getMinRequiredArguments() == 0;
}
bool
CXXConstructorDecl::isCopyConstructor(unsigned &TypeQuals) const {
return isCopyOrMoveConstructor(TypeQuals) &&
getParamDecl(0)->getType()->isLValueReferenceType();
}
bool CXXConstructorDecl::isMoveConstructor(unsigned &TypeQuals) const {
return isCopyOrMoveConstructor(TypeQuals) &&
getParamDecl(0)->getType()->isRValueReferenceType();
}
/// Determine whether this is a copy or move constructor.
bool CXXConstructorDecl::isCopyOrMoveConstructor(unsigned &TypeQuals) const {
// C++ [class.copy]p2:
// A non-template constructor for class X is a copy constructor
// if its first parameter is of type X&, const X&, volatile X& or
// const volatile X&, and either there are no other parameters
// or else all other parameters have default arguments (8.3.6).
// C++0x [class.copy]p3:
// A non-template constructor for class X is a move constructor if its
// first parameter is of type X&&, const X&&, volatile X&&, or
// const volatile X&&, and either there are no other parameters or else
// all other parameters have default arguments.
if (!hasOneParamOrDefaultArgs() || getPrimaryTemplate() != nullptr ||
getDescribedFunctionTemplate() != nullptr)
return false;
const ParmVarDecl *Param = getParamDecl(0);
// Do we have a reference type?
const auto *ParamRefType = Param->getType()->getAs<ReferenceType>();
if (!ParamRefType)
return false;
// Is it a reference to our class type?
ASTContext &Context = getASTContext();
CanQualType PointeeType
= Context.getCanonicalType(ParamRefType->getPointeeType());
CanQualType ClassTy
= Context.getCanonicalType(Context.getTagDeclType(getParent()));
if (PointeeType.getUnqualifiedType() != ClassTy)
return false;
// FIXME: other qualifiers?
// We have a copy or move constructor.
TypeQuals = PointeeType.getCVRQualifiers();
return true;
}
bool CXXConstructorDecl::isConvertingConstructor(bool AllowExplicit) const {
// C++ [class.conv.ctor]p1:
// A constructor declared without the function-specifier explicit
// that can be called with a single parameter specifies a
// conversion from the type of its first parameter to the type of
// its class. Such a constructor is called a converting
// constructor.
if (isExplicit() && !AllowExplicit)
return false;
// FIXME: This has nothing to do with the definition of converting
// constructor, but is convenient for how we use this function in overload
// resolution.
return getNumParams() == 0
? getType()->castAs<FunctionProtoType>()->isVariadic()
: getMinRequiredArguments() <= 1;
}
bool CXXConstructorDecl::isSpecializationCopyingObject() const {
if (!hasOneParamOrDefaultArgs() || getDescribedFunctionTemplate() != nullptr)
return false;
const ParmVarDecl *Param = getParamDecl(0);
ASTContext &Context = getASTContext();
CanQualType ParamType = Context.getCanonicalType(Param->getType());
// Is it the same as our class type?
CanQualType ClassTy
= Context.getCanonicalType(Context.getTagDeclType(getParent()));
if (ParamType.getUnqualifiedType() != ClassTy)
return false;
return true;
}
void CXXDestructorDecl::anchor() {}
CXXDestructorDecl *CXXDestructorDecl::CreateDeserialized(ASTContext &C,
GlobalDeclID ID) {
return new (C, ID) CXXDestructorDecl(
C, nullptr, SourceLocation(), DeclarationNameInfo(), QualType(), nullptr,
false, false, false, ConstexprSpecKind::Unspecified,
/*TrailingRequiresClause=*/{});
}
CXXDestructorDecl *CXXDestructorDecl::Create(
ASTContext &C, CXXRecordDecl *RD, SourceLocation StartLoc,
const DeclarationNameInfo &NameInfo, QualType T, TypeSourceInfo *TInfo,
bool UsesFPIntrin, bool isInline, bool isImplicitlyDeclared,
ConstexprSpecKind ConstexprKind,
const AssociatedConstraint &TrailingRequiresClause) {
assert(NameInfo.getName().getNameKind()
== DeclarationName::CXXDestructorName &&
"Name must refer to a destructor");
return new (C, RD) CXXDestructorDecl(
C, RD, StartLoc, NameInfo, T, TInfo, UsesFPIntrin, isInline,
isImplicitlyDeclared, ConstexprKind, TrailingRequiresClause);
}
void CXXDestructorDecl::setOperatorDelete(FunctionDecl *OD, Expr *ThisArg) {
auto *First = cast<CXXDestructorDecl>(getFirstDecl());
if (OD && !First->OperatorDelete) {
First->OperatorDelete = OD;
First->OperatorDeleteThisArg = ThisArg;
if (auto *L = getASTMutationListener())
L->ResolvedOperatorDelete(First, OD, ThisArg);
}
}
bool CXXDestructorDecl::isCalledByDelete(const FunctionDecl *OpDel) const {
// C++20 [expr.delete]p6: If the value of the operand of the delete-
// expression is not a null pointer value and the selected deallocation
// function (see below) is not a destroying operator delete, the delete-
// expression will invoke the destructor (if any) for the object or the
// elements of the array being deleted.
//
// This means we should not look at the destructor for a destroying
// delete operator, as that destructor is never called, unless the
// destructor is virtual (see [expr.delete]p8.1) because then the
// selected operator depends on the dynamic type of the pointer.
const FunctionDecl *SelectedOperatorDelete = OpDel ? OpDel : OperatorDelete;
if (!SelectedOperatorDelete)
return true;
if (!SelectedOperatorDelete->isDestroyingOperatorDelete())
return true;
// We have a destroying operator delete, so it depends on the dtor.
return isVirtual();
}
void CXXConversionDecl::anchor() {}
CXXConversionDecl *CXXConversionDecl::CreateDeserialized(ASTContext &C,
GlobalDeclID ID) {
return new (C, ID) CXXConversionDecl(
C, nullptr, SourceLocation(), DeclarationNameInfo(), QualType(), nullptr,
false, false, ExplicitSpecifier(), ConstexprSpecKind::Unspecified,
SourceLocation(), /*TrailingRequiresClause=*/{});
}
CXXConversionDecl *CXXConversionDecl::Create(
ASTContext &C, CXXRecordDecl *RD, SourceLocation StartLoc,
const DeclarationNameInfo &NameInfo, QualType T, TypeSourceInfo *TInfo,
bool UsesFPIntrin, bool isInline, ExplicitSpecifier ES,
ConstexprSpecKind ConstexprKind, SourceLocation EndLocation,
const AssociatedConstraint &TrailingRequiresClause) {
assert(NameInfo.getName().getNameKind()
== DeclarationName::CXXConversionFunctionName &&
"Name must refer to a conversion function");
return new (C, RD) CXXConversionDecl(
C, RD, StartLoc, NameInfo, T, TInfo, UsesFPIntrin, isInline, ES,
ConstexprKind, EndLocation, TrailingRequiresClause);
}
bool CXXConversionDecl::isLambdaToBlockPointerConversion() const {
return isImplicit() && getParent()->isLambda() &&
getConversionType()->isBlockPointerType();
}
LinkageSpecDecl::LinkageSpecDecl(DeclContext *DC, SourceLocation ExternLoc,
SourceLocation LangLoc,
LinkageSpecLanguageIDs lang, bool HasBraces)
: Decl(LinkageSpec, DC, LangLoc), DeclContext(LinkageSpec),
ExternLoc(ExternLoc), RBraceLoc(SourceLocation()) {
setLanguage(lang);
LinkageSpecDeclBits.HasBraces = HasBraces;
}
void LinkageSpecDecl::anchor() {}
LinkageSpecDecl *LinkageSpecDecl::Create(ASTContext &C, DeclContext *DC,
SourceLocation ExternLoc,
SourceLocation LangLoc,
LinkageSpecLanguageIDs Lang,
bool HasBraces) {
return new (C, DC) LinkageSpecDecl(DC, ExternLoc, LangLoc, Lang, HasBraces);
}
LinkageSpecDecl *LinkageSpecDecl::CreateDeserialized(ASTContext &C,
GlobalDeclID ID) {
return new (C, ID)
LinkageSpecDecl(nullptr, SourceLocation(), SourceLocation(),
LinkageSpecLanguageIDs::C, false);
}
void UsingDirectiveDecl::anchor() {}
UsingDirectiveDecl *UsingDirectiveDecl::Create(ASTContext &C, DeclContext *DC,
SourceLocation L,
SourceLocation NamespaceLoc,
NestedNameSpecifierLoc QualifierLoc,
SourceLocation IdentLoc,
NamedDecl *Used,
DeclContext *CommonAncestor) {
if (auto *NS = dyn_cast_or_null<NamespaceDecl>(Used))
Used = NS->getFirstDecl();
return new (C, DC) UsingDirectiveDecl(DC, L, NamespaceLoc, QualifierLoc,
IdentLoc, Used, CommonAncestor);
}
UsingDirectiveDecl *UsingDirectiveDecl::CreateDeserialized(ASTContext &C,
GlobalDeclID ID) {
return new (C, ID) UsingDirectiveDecl(nullptr, SourceLocation(),
SourceLocation(),
NestedNameSpecifierLoc(),
SourceLocation(), nullptr, nullptr);
}
NamespaceDecl *UsingDirectiveDecl::getNominatedNamespace() {
if (auto *NA = dyn_cast_or_null<NamespaceAliasDecl>(NominatedNamespace))
return NA->getNamespace();
return cast_or_null<NamespaceDecl>(NominatedNamespace);
}
NamespaceDecl::NamespaceDecl(ASTContext &C, DeclContext *DC, bool Inline,
SourceLocation StartLoc, SourceLocation IdLoc,
IdentifierInfo *Id, NamespaceDecl *PrevDecl,
bool Nested)
: NamedDecl(Namespace, DC, IdLoc, Id), DeclContext(Namespace),
redeclarable_base(C), LocStart(StartLoc) {
setInline(Inline);
setNested(Nested);
setPreviousDecl(PrevDecl);
}
NamespaceDecl *NamespaceDecl::Create(ASTContext &C, DeclContext *DC,
bool Inline, SourceLocation StartLoc,
SourceLocation IdLoc, IdentifierInfo *Id,
NamespaceDecl *PrevDecl, bool Nested) {
return new (C, DC)
NamespaceDecl(C, DC, Inline, StartLoc, IdLoc, Id, PrevDecl, Nested);
}
NamespaceDecl *NamespaceDecl::CreateDeserialized(ASTContext &C,
GlobalDeclID ID) {
return new (C, ID) NamespaceDecl(C, nullptr, false, SourceLocation(),
SourceLocation(), nullptr, nullptr, false);
}
NamespaceDecl *NamespaceDecl::getNextRedeclarationImpl() {
return getNextRedeclaration();
}
NamespaceDecl *NamespaceDecl::getPreviousDeclImpl() {
return getPreviousDecl();
}
NamespaceDecl *NamespaceDecl::getMostRecentDeclImpl() {
return getMostRecentDecl();
}
void NamespaceAliasDecl::anchor() {}
NamespaceAliasDecl *NamespaceAliasDecl::getNextRedeclarationImpl() {
return getNextRedeclaration();
}
NamespaceAliasDecl *NamespaceAliasDecl::getPreviousDeclImpl() {
return getPreviousDecl();
}
NamespaceAliasDecl *NamespaceAliasDecl::getMostRecentDeclImpl() {
return getMostRecentDecl();
}
NamespaceAliasDecl *NamespaceAliasDecl::Create(ASTContext &C, DeclContext *DC,
SourceLocation UsingLoc,
SourceLocation AliasLoc,
IdentifierInfo *Alias,
NestedNameSpecifierLoc QualifierLoc,
SourceLocation IdentLoc,
NamedDecl *Namespace) {
// FIXME: Preserve the aliased namespace as written.
if (auto *NS = dyn_cast_or_null<NamespaceDecl>(Namespace))
Namespace = NS->getFirstDecl();
return new (C, DC) NamespaceAliasDecl(C, DC, UsingLoc, AliasLoc, Alias,
QualifierLoc, IdentLoc, Namespace);
}
NamespaceAliasDecl *NamespaceAliasDecl::CreateDeserialized(ASTContext &C,
GlobalDeclID ID) {
return new (C, ID) NamespaceAliasDecl(C, nullptr, SourceLocation(),
SourceLocation(), nullptr,
NestedNameSpecifierLoc(),
SourceLocation(), nullptr);
}
void LifetimeExtendedTemporaryDecl::anchor() {}
/// Retrieve the storage duration for the materialized temporary.
StorageDuration LifetimeExtendedTemporaryDecl::getStorageDuration() const {
const ValueDecl *ExtendingDecl = getExtendingDecl();
if (!ExtendingDecl)
return SD_FullExpression;
// FIXME: This is not necessarily correct for a temporary materialized
// within a default initializer.
if (isa<FieldDecl>(ExtendingDecl))
return SD_Automatic;
// FIXME: This only works because storage class specifiers are not allowed
// on decomposition declarations.
if (isa<BindingDecl>(ExtendingDecl))
return ExtendingDecl->getDeclContext()->isFunctionOrMethod() ? SD_Automatic
: SD_Static;
return cast<VarDecl>(ExtendingDecl)->getStorageDuration();
}
APValue *LifetimeExtendedTemporaryDecl::getOrCreateValue(bool MayCreate) const {
assert(getStorageDuration() == SD_Static &&
"don't need to cache the computed value for this temporary");
if (MayCreate && !Value) {
Value = (new (getASTContext()) APValue);
getASTContext().addDestruction(Value);
}
assert(Value && "may not be null");
return Value;
}
void UsingShadowDecl::anchor() {}
UsingShadowDecl::UsingShadowDecl(Kind K, ASTContext &C, DeclContext *DC,
SourceLocation Loc, DeclarationName Name,
BaseUsingDecl *Introducer, NamedDecl *Target)
: NamedDecl(K, DC, Loc, Name), redeclarable_base(C),
UsingOrNextShadow(Introducer) {
if (Target) {
assert(!isa<UsingShadowDecl>(Target));
setTargetDecl(Target);
}
setImplicit();
}
UsingShadowDecl::UsingShadowDecl(Kind K, ASTContext &C, EmptyShell Empty)
: NamedDecl(K, nullptr, SourceLocation(), DeclarationName()),
redeclarable_base(C) {}
UsingShadowDecl *UsingShadowDecl::CreateDeserialized(ASTContext &C,
GlobalDeclID ID) {
return new (C, ID) UsingShadowDecl(UsingShadow, C, EmptyShell());
}
BaseUsingDecl *UsingShadowDecl::getIntroducer() const {
const UsingShadowDecl *Shadow = this;
while (const auto *NextShadow =
dyn_cast<UsingShadowDecl>(Shadow->UsingOrNextShadow))
Shadow = NextShadow;
return cast<BaseUsingDecl>(Shadow->UsingOrNextShadow);
}
void ConstructorUsingShadowDecl::anchor() {}
ConstructorUsingShadowDecl *
ConstructorUsingShadowDecl::Create(ASTContext &C, DeclContext *DC,
SourceLocation Loc, UsingDecl *Using,
NamedDecl *Target, bool IsVirtual) {
return new (C, DC) ConstructorUsingShadowDecl(C, DC, Loc, Using, Target,
IsVirtual);
}
ConstructorUsingShadowDecl *
ConstructorUsingShadowDecl::CreateDeserialized(ASTContext &C, GlobalDeclID ID) {
return new (C, ID) ConstructorUsingShadowDecl(C, EmptyShell());
}
CXXRecordDecl *ConstructorUsingShadowDecl::getNominatedBaseClass() const {
return getIntroducer()->getQualifier()->getAsRecordDecl();
}
void BaseUsingDecl::anchor() {}
void BaseUsingDecl::addShadowDecl(UsingShadowDecl *S) {
assert(!llvm::is_contained(shadows(), S) && "declaration already in set");
assert(S->getIntroducer() == this);
if (FirstUsingShadow.getPointer())
S->UsingOrNextShadow = FirstUsingShadow.getPointer();
FirstUsingShadow.setPointer(S);
}
void BaseUsingDecl::removeShadowDecl(UsingShadowDecl *S) {
assert(llvm::is_contained(shadows(), S) && "declaration not in set");
assert(S->getIntroducer() == this);
// Remove S from the shadow decl chain. This is O(n) but hopefully rare.
if (FirstUsingShadow.getPointer() == S) {
FirstUsingShadow.setPointer(
dyn_cast<UsingShadowDecl>(S->UsingOrNextShadow));
S->UsingOrNextShadow = this;
return;
}
UsingShadowDecl *Prev = FirstUsingShadow.getPointer();
while (Prev->UsingOrNextShadow != S)
Prev = cast<UsingShadowDecl>(Prev->UsingOrNextShadow);
Prev->UsingOrNextShadow = S->UsingOrNextShadow;
S->UsingOrNextShadow = this;
}
void UsingDecl::anchor() {}
UsingDecl *UsingDecl::Create(ASTContext &C, DeclContext *DC, SourceLocation UL,
NestedNameSpecifierLoc QualifierLoc,
const DeclarationNameInfo &NameInfo,
bool HasTypename) {
return new (C, DC) UsingDecl(DC, UL, QualifierLoc, NameInfo, HasTypename);
}
UsingDecl *UsingDecl::CreateDeserialized(ASTContext &C, GlobalDeclID ID) {
return new (C, ID) UsingDecl(nullptr, SourceLocation(),
NestedNameSpecifierLoc(), DeclarationNameInfo(),
false);
}
SourceRange UsingDecl::getSourceRange() const {
SourceLocation Begin = isAccessDeclaration()
? getQualifierLoc().getBeginLoc() : UsingLocation;
return SourceRange(Begin, getNameInfo().getEndLoc());
}
void UsingEnumDecl::anchor() {}
UsingEnumDecl *UsingEnumDecl::Create(ASTContext &C, DeclContext *DC,
SourceLocation UL,
SourceLocation EL,
SourceLocation NL,
TypeSourceInfo *EnumType) {
assert(isa<EnumDecl>(EnumType->getType()->getAsTagDecl()));
return new (C, DC)
UsingEnumDecl(DC, EnumType->getType()->getAsTagDecl()->getDeclName(), UL, EL, NL, EnumType);
}
UsingEnumDecl *UsingEnumDecl::CreateDeserialized(ASTContext &C,
GlobalDeclID ID) {
return new (C, ID)
UsingEnumDecl(nullptr, DeclarationName(), SourceLocation(),
SourceLocation(), SourceLocation(), nullptr);
}
SourceRange UsingEnumDecl::getSourceRange() const {
return SourceRange(UsingLocation, EnumType->getTypeLoc().getEndLoc());
}
void UsingPackDecl::anchor() {}
UsingPackDecl *UsingPackDecl::Create(ASTContext &C, DeclContext *DC,
NamedDecl *InstantiatedFrom,
ArrayRef<NamedDecl *> UsingDecls) {
size_t Extra = additionalSizeToAlloc<NamedDecl *>(UsingDecls.size());
return new (C, DC, Extra) UsingPackDecl(DC, InstantiatedFrom, UsingDecls);
}
UsingPackDecl *UsingPackDecl::CreateDeserialized(ASTContext &C, GlobalDeclID ID,
unsigned NumExpansions) {
size_t Extra = additionalSizeToAlloc<NamedDecl *>(NumExpansions);
auto *Result = new (C, ID, Extra) UsingPackDecl(nullptr, nullptr, {});
Result->NumExpansions = NumExpansions;
auto *Trail = Result->getTrailingObjects<NamedDecl *>();
for (unsigned I = 0; I != NumExpansions; ++I)
new (Trail + I) NamedDecl*(nullptr);
return Result;
}
void UnresolvedUsingValueDecl::anchor() {}
UnresolvedUsingValueDecl *
UnresolvedUsingValueDecl::Create(ASTContext &C, DeclContext *DC,
SourceLocation UsingLoc,
NestedNameSpecifierLoc QualifierLoc,
const DeclarationNameInfo &NameInfo,
SourceLocation EllipsisLoc) {
return new (C, DC) UnresolvedUsingValueDecl(DC, C.DependentTy, UsingLoc,
QualifierLoc, NameInfo,
EllipsisLoc);
}
UnresolvedUsingValueDecl *
UnresolvedUsingValueDecl::CreateDeserialized(ASTContext &C, GlobalDeclID ID) {
return new (C, ID) UnresolvedUsingValueDecl(nullptr, QualType(),
SourceLocation(),
NestedNameSpecifierLoc(),
DeclarationNameInfo(),
SourceLocation());
}
SourceRange UnresolvedUsingValueDecl::getSourceRange() const {
SourceLocation Begin = isAccessDeclaration()
? getQualifierLoc().getBeginLoc() : UsingLocation;
return SourceRange(Begin, getNameInfo().getEndLoc());
}
void UnresolvedUsingTypenameDecl::anchor() {}
UnresolvedUsingTypenameDecl *
UnresolvedUsingTypenameDecl::Create(ASTContext &C, DeclContext *DC,
SourceLocation UsingLoc,
SourceLocation TypenameLoc,
NestedNameSpecifierLoc QualifierLoc,
SourceLocation TargetNameLoc,
DeclarationName TargetName,
SourceLocation EllipsisLoc) {
return new (C, DC) UnresolvedUsingTypenameDecl(
DC, UsingLoc, TypenameLoc, QualifierLoc, TargetNameLoc,
TargetName.getAsIdentifierInfo(), EllipsisLoc);
}
UnresolvedUsingTypenameDecl *
UnresolvedUsingTypenameDecl::CreateDeserialized(ASTContext &C,
GlobalDeclID ID) {
return new (C, ID) UnresolvedUsingTypenameDecl(
nullptr, SourceLocation(), SourceLocation(), NestedNameSpecifierLoc(),
SourceLocation(), nullptr, SourceLocation());
}
UnresolvedUsingIfExistsDecl *
UnresolvedUsingIfExistsDecl::Create(ASTContext &Ctx, DeclContext *DC,
SourceLocation Loc, DeclarationName Name) {
return new (Ctx, DC) UnresolvedUsingIfExistsDecl(DC, Loc, Name);
}
UnresolvedUsingIfExistsDecl *
UnresolvedUsingIfExistsDecl::CreateDeserialized(ASTContext &Ctx,
GlobalDeclID ID) {
return new (Ctx, ID)
UnresolvedUsingIfExistsDecl(nullptr, SourceLocation(), DeclarationName());
}
UnresolvedUsingIfExistsDecl::UnresolvedUsingIfExistsDecl(DeclContext *DC,
SourceLocation Loc,
DeclarationName Name)
: NamedDecl(Decl::UnresolvedUsingIfExists, DC, Loc, Name) {}
void UnresolvedUsingIfExistsDecl::anchor() {}
void StaticAssertDecl::anchor() {}
StaticAssertDecl *StaticAssertDecl::Create(ASTContext &C, DeclContext *DC,
SourceLocation StaticAssertLoc,
Expr *AssertExpr, Expr *Message,
SourceLocation RParenLoc,
bool Failed) {
return new (C, DC) StaticAssertDecl(DC, StaticAssertLoc, AssertExpr, Message,
RParenLoc, Failed);
}
StaticAssertDecl *StaticAssertDecl::CreateDeserialized(ASTContext &C,
GlobalDeclID ID) {
return new (C, ID) StaticAssertDecl(nullptr, SourceLocation(), nullptr,
nullptr, SourceLocation(), false);
}
VarDecl *ValueDecl::getPotentiallyDecomposedVarDecl() {
assert((isa<VarDecl, BindingDecl>(this)) &&
"expected a VarDecl or a BindingDecl");
if (auto *Var = llvm::dyn_cast<VarDecl>(this))
return Var;
if (auto *BD = llvm::dyn_cast<BindingDecl>(this))
return llvm::dyn_cast_if_present<VarDecl>(BD->getDecomposedDecl());
return nullptr;
}
void BindingDecl::anchor() {}
BindingDecl *BindingDecl::Create(ASTContext &C, DeclContext *DC,
SourceLocation IdLoc, IdentifierInfo *Id,
QualType T) {
return new (C, DC) BindingDecl(DC, IdLoc, Id, T);
}
BindingDecl *BindingDecl::CreateDeserialized(ASTContext &C, GlobalDeclID ID) {
return new (C, ID)
BindingDecl(nullptr, SourceLocation(), nullptr, QualType());
}
VarDecl *BindingDecl::getHoldingVar() const {
Expr *B = getBinding();
if (!B)
return nullptr;
auto *DRE = dyn_cast<DeclRefExpr>(B->IgnoreImplicit());
if (!DRE)
return nullptr;
auto *VD = cast<VarDecl>(DRE->getDecl());
assert(VD->isImplicit() && "holding var for binding decl not implicit");
return VD;
}
llvm::ArrayRef<BindingDecl *> BindingDecl::getBindingPackDecls() const {
assert(Binding && "expecting a pack expr");
auto *FP = cast<FunctionParmPackExpr>(Binding);
ValueDecl *const *First = FP->getNumExpansions() > 0 ? FP->begin() : nullptr;
assert((!First || isa<BindingDecl>(*First)) && "expecting a BindingDecl");
return llvm::ArrayRef<BindingDecl *>(
reinterpret_cast<BindingDecl *const *>(First), FP->getNumExpansions());
}
void DecompositionDecl::anchor() {}
DecompositionDecl *DecompositionDecl::Create(ASTContext &C, DeclContext *DC,
SourceLocation StartLoc,
SourceLocation LSquareLoc,
QualType T, TypeSourceInfo *TInfo,
StorageClass SC,
ArrayRef<BindingDecl *> Bindings) {
size_t Extra = additionalSizeToAlloc<BindingDecl *>(Bindings.size());
return new (C, DC, Extra)
DecompositionDecl(C, DC, StartLoc, LSquareLoc, T, TInfo, SC, Bindings);
}
DecompositionDecl *DecompositionDecl::CreateDeserialized(ASTContext &C,
GlobalDeclID ID,
unsigned NumBindings) {
size_t Extra = additionalSizeToAlloc<BindingDecl *>(NumBindings);
auto *Result = new (C, ID, Extra)
DecompositionDecl(C, nullptr, SourceLocation(), SourceLocation(),
QualType(), nullptr, StorageClass(), {});
// Set up and clean out the bindings array.
Result->NumBindings = NumBindings;
auto *Trail = Result->getTrailingObjects<BindingDecl *>();
for (unsigned I = 0; I != NumBindings; ++I)
new (Trail + I) BindingDecl*(nullptr);
return Result;
}
void DecompositionDecl::printName(llvm::raw_ostream &OS,
const PrintingPolicy &Policy) const {
OS << '[';
bool Comma = false;
for (const auto *B : bindings()) {
if (Comma)
OS << ", ";
B->printName(OS, Policy);
Comma = true;
}
OS << ']';
}
void MSPropertyDecl::anchor() {}
MSPropertyDecl *MSPropertyDecl::Create(ASTContext &C, DeclContext *DC,
SourceLocation L, DeclarationName N,
QualType T, TypeSourceInfo *TInfo,
SourceLocation StartL,
IdentifierInfo *Getter,
IdentifierInfo *Setter) {
return new (C, DC) MSPropertyDecl(DC, L, N, T, TInfo, StartL, Getter, Setter);
}
MSPropertyDecl *MSPropertyDecl::CreateDeserialized(ASTContext &C,
GlobalDeclID ID) {
return new (C, ID) MSPropertyDecl(nullptr, SourceLocation(),
DeclarationName(), QualType(), nullptr,
SourceLocation(), nullptr, nullptr);
}
void MSGuidDecl::anchor() {}
MSGuidDecl::MSGuidDecl(DeclContext *DC, QualType T, Parts P)
: ValueDecl(Decl::MSGuid, DC, SourceLocation(), DeclarationName(), T),
PartVal(P) {}
MSGuidDecl *MSGuidDecl::Create(const ASTContext &C, QualType T, Parts P) {
DeclContext *DC = C.getTranslationUnitDecl();
return new (C, DC) MSGuidDecl(DC, T, P);
}
MSGuidDecl *MSGuidDecl::CreateDeserialized(ASTContext &C, GlobalDeclID ID) {
return new (C, ID) MSGuidDecl(nullptr, QualType(), Parts());
}
void MSGuidDecl::printName(llvm::raw_ostream &OS,
const PrintingPolicy &) const {
OS << llvm::format("GUID{%08" PRIx32 "-%04" PRIx16 "-%04" PRIx16 "-",
PartVal.Part1, PartVal.Part2, PartVal.Part3);
unsigned I = 0;
for (uint8_t Byte : PartVal.Part4And5) {
OS << llvm::format("%02" PRIx8, Byte);
if (++I == 2)
OS << '-';
}
OS << '}';
}
/// Determine if T is a valid 'struct _GUID' of the shape that we expect.
static bool isValidStructGUID(ASTContext &Ctx, QualType T) {
// FIXME: We only need to check this once, not once each time we compute a
// GUID APValue.
using MatcherRef = llvm::function_ref<bool(QualType)>;
auto IsInt = [&Ctx](unsigned N) {
return [&Ctx, N](QualType T) {
return T->isUnsignedIntegerOrEnumerationType() &&
Ctx.getIntWidth(T) == N;
};
};
auto IsArray = [&Ctx](MatcherRef Elem, unsigned N) {
return [&Ctx, Elem, N](QualType T) {
const ConstantArrayType *CAT = Ctx.getAsConstantArrayType(T);
return CAT && CAT->getSize() == N && Elem(CAT->getElementType());
};
};
auto IsStruct = [](std::initializer_list<MatcherRef> Fields) {
return [Fields](QualType T) {
const RecordDecl *RD = T->getAsRecordDecl();
if (!RD || RD->isUnion())
return false;
RD = RD->getDefinition();
if (!RD)
return false;
if (auto *CXXRD = dyn_cast<CXXRecordDecl>(RD))
if (CXXRD->getNumBases())
return false;
auto MatcherIt = Fields.begin();
for (const FieldDecl *FD : RD->fields()) {
if (FD->isUnnamedBitField())
continue;
if (FD->isBitField() || MatcherIt == Fields.end() ||
!(*MatcherIt)(FD->getType()))
return false;
++MatcherIt;
}
return MatcherIt == Fields.end();
};
};
// We expect an {i32, i16, i16, [8 x i8]}.
return IsStruct({IsInt(32), IsInt(16), IsInt(16), IsArray(IsInt(8), 8)})(T);
}
APValue &MSGuidDecl::getAsAPValue() const {
if (APVal.isAbsent() && isValidStructGUID(getASTContext(), getType())) {
using llvm::APInt;
using llvm::APSInt;
APVal = APValue(APValue::UninitStruct(), 0, 4);
APVal.getStructField(0) = APValue(APSInt(APInt(32, PartVal.Part1), true));
APVal.getStructField(1) = APValue(APSInt(APInt(16, PartVal.Part2), true));
APVal.getStructField(2) = APValue(APSInt(APInt(16, PartVal.Part3), true));
APValue &Arr = APVal.getStructField(3) =
APValue(APValue::UninitArray(), 8, 8);
for (unsigned I = 0; I != 8; ++I) {
Arr.getArrayInitializedElt(I) =
APValue(APSInt(APInt(8, PartVal.Part4And5[I]), true));
}
// Register this APValue to be destroyed if necessary. (Note that the
// MSGuidDecl destructor is never run.)
getASTContext().addDestruction(&APVal);
}
return APVal;
}
void UnnamedGlobalConstantDecl::anchor() {}
UnnamedGlobalConstantDecl::UnnamedGlobalConstantDecl(const ASTContext &C,
DeclContext *DC,
QualType Ty,
const APValue &Val)
: ValueDecl(Decl::UnnamedGlobalConstant, DC, SourceLocation(),
DeclarationName(), Ty),
Value(Val) {
// Cleanup the embedded APValue if required (note that our destructor is never
// run)
if (Value.needsCleanup())
C.addDestruction(&Value);
}
UnnamedGlobalConstantDecl *
UnnamedGlobalConstantDecl::Create(const ASTContext &C, QualType T,
const APValue &Value) {
DeclContext *DC = C.getTranslationUnitDecl();
return new (C, DC) UnnamedGlobalConstantDecl(C, DC, T, Value);
}
UnnamedGlobalConstantDecl *
UnnamedGlobalConstantDecl::CreateDeserialized(ASTContext &C, GlobalDeclID ID) {
return new (C, ID)
UnnamedGlobalConstantDecl(C, nullptr, QualType(), APValue());
}
void UnnamedGlobalConstantDecl::printName(llvm::raw_ostream &OS,
const PrintingPolicy &) const {
OS << "unnamed-global-constant";
}
static const char *getAccessName(AccessSpecifier AS) {
switch (AS) {
case AS_none:
llvm_unreachable("Invalid access specifier!");
case AS_public:
return "public";
case AS_private:
return "private";
case AS_protected:
return "protected";
}
llvm_unreachable("Invalid access specifier!");
}
const StreamingDiagnostic &clang::operator<<(const StreamingDiagnostic &DB,
AccessSpecifier AS) {
return DB << getAccessName(AS);
}