1//===--- ItaniumCXXABIUtils.cpp - Shared Itanium C++ ABI queries ----------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8
9#include "clang/CodeGenUtils/ItaniumCXXABIUtils.h"
10#include "clang/AST/CXXInheritance.h"
11#include "clang/AST/RecordLayout.h"
12
13namespace clang::CodeGenUtils {
14
15CharUnits computeOffsetHint(ASTContext &Ctx, const CXXRecordDecl *Src,
16 const CXXRecordDecl *Dst) {
17 CXXBasePaths Paths(/*FindAmbiguities=*/true, /*RecordPaths=*/true,
18 /*DetectVirtual=*/false);
19
20 // If Dst is not derived from Src we can skip the whole computation below and
21 // return that Src is not a public base of Dst. Record all inheritance paths.
22 if (!Dst->isDerivedFrom(Base: Src, Paths))
23 return CharUnits::fromQuantity(Quantity: -2ULL);
24
25 unsigned NumPublicPaths = 0;
26 CharUnits Offset;
27
28 // Now walk all possible inheritance paths.
29 for (const CXXBasePath &Path : Paths) {
30 if (Path.Access != AS_public) // Ignore non-public inheritance.
31 continue;
32
33 ++NumPublicPaths;
34
35 for (const CXXBasePathElement &PathElement : Path) {
36 // If the path contains a virtual base class we can't give any hint.
37 // -1: no hint.
38 if (PathElement.Base->isVirtual())
39 return CharUnits::fromQuantity(Quantity: -1ULL);
40
41 if (NumPublicPaths > 1) // Won't use offsets, skip computation.
42 continue;
43
44 // Accumulate the base class offsets.
45 const ASTRecordLayout &L = Ctx.getASTRecordLayout(D: PathElement.Class);
46 Offset += L.getBaseClassOffset(
47 Base: PathElement.Base->getType()->getAsCXXRecordDecl());
48 }
49 }
50
51 // -2: Src is not a public base of Dst.
52 if (NumPublicPaths == 0)
53 return CharUnits::fromQuantity(Quantity: -2ULL);
54
55 // -3: Src is a multiple public base type but never a virtual base type.
56 if (NumPublicPaths > 1)
57 return CharUnits::fromQuantity(Quantity: -3ULL);
58
59 // Otherwise, the Src type is a unique public nonvirtual base type of Dst.
60 // Return the offset of Src from the origin of Dst.
61 return Offset;
62}
63
64bool canUseSingleInheritance(const CXXRecordDecl *RD) {
65 // Check the number of bases.
66 if (RD->getNumBases() != 1)
67 return false;
68
69 // Get the base.
70 CXXRecordDecl::base_class_const_iterator Base = RD->bases_begin();
71
72 // Check that the base is not virtual.
73 if (Base->isVirtual())
74 return false;
75
76 // Check that the base is public.
77 if (Base->getAccessSpecifier() != AS_public)
78 return false;
79
80 // Check that the class is dynamic iff the base is.
81 auto *BaseDecl = Base->getType()->castAsCXXRecordDecl();
82 return BaseDecl->isEmpty() ||
83 BaseDecl->isDynamicClass() == RD->isDynamicClass();
84}
85
86namespace {
87/// Contains virtual and non-virtual bases seen when traversing a class
88/// hierarchy.
89struct SeenBases {
90 llvm::SmallPtrSet<const CXXRecordDecl *, 16> NonVirtualBases;
91 llvm::SmallPtrSet<const CXXRecordDecl *, 16> VirtualBases;
92};
93} // namespace
94
95static unsigned computeVMIClassTypeInfoFlags(const CXXBaseSpecifier *Base,
96 SeenBases &Bases) {
97 unsigned Flags = 0;
98
99 auto *BaseDecl = Base->getType()->castAsCXXRecordDecl();
100 if (Base->isVirtual()) {
101 // Mark the virtual base as seen.
102 if (!Bases.VirtualBases.insert(Ptr: BaseDecl).second) {
103 // If this virtual base has been seen before, then the class is diamond
104 // shaped.
105 Flags |= VMI_DiamondShaped;
106 } else {
107 if (Bases.NonVirtualBases.count(Ptr: BaseDecl))
108 Flags |= VMI_NonDiamondRepeat;
109 }
110 } else {
111 // Mark the non-virtual base as seen.
112 if (!Bases.NonVirtualBases.insert(Ptr: BaseDecl).second) {
113 // If this non-virtual base has been seen before, then the class has non-
114 // diamond shaped repeated inheritance.
115 Flags |= VMI_NonDiamondRepeat;
116 } else {
117 if (Bases.VirtualBases.count(Ptr: BaseDecl))
118 Flags |= VMI_NonDiamondRepeat;
119 }
120 }
121
122 // Walk all bases.
123 for (const auto &I : BaseDecl->bases())
124 Flags |= computeVMIClassTypeInfoFlags(Base: &I, Bases);
125
126 return Flags;
127}
128
129unsigned computeVMIClassTypeInfoFlags(const CXXRecordDecl *RD) {
130 unsigned Flags = 0;
131 SeenBases Bases;
132
133 // Walk all bases.
134 for (const auto &I : RD->bases())
135 Flags |= computeVMIClassTypeInfoFlags(Base: &I, Bases);
136
137 return Flags;
138}
139
140/// Returns whether the given record type is incomplete.
141static bool isIncompleteClassType(const RecordType *RecordTy) {
142 return !RecordTy->getDecl()->getDefinitionOrSelf()->isCompleteDefinition();
143}
144
145bool containsIncompleteClassType(QualType Ty) {
146 if (const auto *RecordTy = dyn_cast<RecordType>(Val&: Ty)) {
147 if (isIncompleteClassType(RecordTy))
148 return true;
149 }
150
151 if (const auto *PointerTy = dyn_cast<PointerType>(Val&: Ty))
152 return containsIncompleteClassType(Ty: PointerTy->getPointeeType());
153
154 if (const auto *MemberPointerTy = dyn_cast<MemberPointerType>(Val&: Ty)) {
155 // Check if the class type is incomplete.
156 if (!MemberPointerTy->getMostRecentCXXRecordDecl()->hasDefinition())
157 return true;
158
159 return containsIncompleteClassType(Ty: MemberPointerTy->getPointeeType());
160 }
161
162 return false;
163}
164
165unsigned extractPBaseFlags(const ASTContext &Ctx, QualType &Type) {
166 unsigned Flags = 0;
167
168 if (Type.isConstQualified())
169 Flags |= PTI_Const;
170 if (Type.isVolatileQualified())
171 Flags |= PTI_Volatile;
172 if (Type.isRestrictQualified())
173 Flags |= PTI_Restrict;
174 Type = Type.getUnqualifiedType();
175
176 // Itanium C++ ABI 2.9.5p7:
177 // When the abi::__pbase_type_info is for a direct or indirect pointer to an
178 // incomplete class type, the incomplete target type flag is set.
179 if (containsIncompleteClassType(Ty: Type))
180 Flags |= PTI_Incomplete;
181
182 if (auto *Proto = Type->getAs<FunctionProtoType>()) {
183 if (Proto->isNothrow()) {
184 Flags |= PTI_Noexcept;
185 Type = Ctx.getFunctionTypeWithExceptionSpec(Orig: Type, ESI: EST_None);
186 }
187 }
188
189 return Flags;
190}
191
192} // namespace clang::CodeGenUtils
193