clang 24.0.0git
InferAlloc.cpp
Go to the documentation of this file.
1//===--- InferAlloc.cpp - Allocation type inference -----------------------===//
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// This file implements allocation-related type inference.
10//
11//===----------------------------------------------------------------------===//
12
15#include "clang/AST/Decl.h"
16#include "clang/AST/DeclCXX.h"
17#include "clang/AST/Expr.h"
18#include "clang/AST/Type.h"
20#include "llvm/ADT/SmallPtrSet.h"
21
22using namespace clang;
23using namespace infer_alloc;
24
25static bool
28 bool &IncompleteType) {
29 QualType CanonicalType = T.getCanonicalType();
30 if (CanonicalType->isAnyPointerType() || CanonicalType->isReferenceType() ||
31 CanonicalType->isMemberFunctionPointerType() ||
32 CanonicalType->isBlockPointerType())
33 return true; // base case
34
35 // Look through typedef chain to check for special types.
36 for (QualType CurrentT = T; const auto *TT = CurrentT->getAs<TypedefType>();
37 CurrentT = TT->getDecl()->getUnderlyingType()) {
38 const IdentifierInfo *II = TT->getDecl()->getIdentifier();
39 // Special Case: Syntactically uintptr_t is not a pointer; semantically,
40 // however, very likely used as such. Therefore, classify uintptr_t as a
41 // pointer, too.
42 if (II && II->isStr("uintptr_t"))
43 return true;
44 }
45
46 // The type is an array; check the element type.
47 if (const ArrayType *AT = dyn_cast<ArrayType>(CanonicalType))
48 return typeContainsPointer(AT->getElementType(), VisitedRD, IncompleteType);
49
50 // The type is an atomic type.
51 if (const AtomicType *AT = dyn_cast<AtomicType>(CanonicalType))
52 return typeContainsPointer(AT->getValueType(), VisitedRD, IncompleteType);
53
54 // The type is a struct, class, or union.
55 if (const RecordDecl *RD = CanonicalType->getAsRecordDecl()) {
56 if (!RD->isCompleteDefinition()) {
57 IncompleteType = true;
58 return false;
59 }
60 if (!VisitedRD.insert(RD).second)
61 return false; // already visited
62 // Check all fields.
63 for (const FieldDecl *Field : RD->fields()) {
64 if (typeContainsPointer(Field->getType(), VisitedRD, IncompleteType))
65 return true;
66 }
67 // For C++ classes, also check base classes.
68 if (const CXXRecordDecl *CXXRD = dyn_cast<CXXRecordDecl>(RD)) {
69 // Polymorphic types require a vptr.
70 if (CXXRD->isDynamicClass())
71 return true;
72 for (const CXXBaseSpecifier &Base : CXXRD->bases()) {
73 if (typeContainsPointer(Base.getType(), VisitedRD, IncompleteType))
74 return true;
75 }
76 }
77 }
78 return false;
79}
80
81/// Infer type from a simple sizeof expression.
83 const Expr *Arg = E->IgnoreParenImpCasts();
84 if (const auto *UET = dyn_cast<UnaryExprOrTypeTraitExpr>(Arg)) {
85 if (UET->getKind() == UETT_SizeOf) {
86 if (UET->isArgumentType())
87 return UET->getArgumentTypeInfo()->getType();
88 else
89 return UET->getArgumentExpr()->getType();
90 }
91 }
92 return QualType();
93}
94
95/// Infer type from an arithmetic expression involving a sizeof. For example:
96///
97/// malloc(sizeof(MyType) + padding); // infers 'MyType'
98/// malloc(sizeof(MyType) * 32); // infers 'MyType'
99/// malloc(32 * sizeof(MyType)); // infers 'MyType'
100/// malloc(sizeof(MyType) << 1); // infers 'MyType'
101/// ...
102///
103/// More complex arithmetic expressions are supported, but are a heuristic, e.g.
104/// when considering allocations for structs with flexible array members:
105///
106/// malloc(sizeof(HasFlexArray) + sizeof(int) * 32); // infers 'HasFlexArray'
107///
109 const Expr *Arg = E->IgnoreParenImpCasts();
110 // The argument is a lone sizeof expression.
111 if (QualType T = inferTypeFromSizeofExpr(Arg); !T.isNull())
112 return T;
113 if (const auto *BO = dyn_cast<BinaryOperator>(Arg)) {
114 // Argument is an arithmetic expression. Cover common arithmetic patterns
115 // involving sizeof.
116 switch (BO->getOpcode()) {
117 case BO_Add:
118 case BO_Div:
119 case BO_Mul:
120 case BO_Shl:
121 case BO_Shr:
122 case BO_Sub:
124 !T.isNull())
125 return T;
127 !T.isNull())
128 return T;
129 break;
130 default:
131 break;
132 }
133 }
134 return QualType();
135}
136
137/// If the expression E is a reference to a variable, infer the type from a
138/// variable's initializer if it contains a sizeof. Beware, this is a heuristic
139/// and ignores if a variable is later reassigned. For example:
140///
141/// size_t my_size = sizeof(MyType);
142/// void *x = malloc(my_size); // infers 'MyType'
143///
145 const Expr *Arg = E->IgnoreParenImpCasts();
146 if (const auto *DRE = dyn_cast<DeclRefExpr>(Arg)) {
147 if (const auto *VD = dyn_cast<VarDecl>(DRE->getDecl())) {
148 if (const Expr *Init = VD->getInit())
150 }
151 }
152 return QualType();
153}
154
155/// Deduces the allocated type by checking if the allocation call's result
156/// is immediately used in a cast expression. For example:
157///
158/// MyType *x = (MyType *)malloc(4096); // infers 'MyType'
159///
161 const CastExpr *CastE) {
162 if (!CastE)
163 return QualType();
164 QualType PtrType = CastE->getType();
165 if (PtrType->isPointerType())
166 return PtrType->getPointeeType();
167 return QualType();
168}
169
171 const ASTContext &Ctx,
172 const CastExpr *CastE) {
173 QualType AllocType;
174 // First check arguments.
175 for (const Expr *Arg : E->arguments()) {
177 if (AllocType.isNull())
179 if (!AllocType.isNull())
180 break;
181 }
182 // Then check later casts.
183 if (AllocType.isNull())
184 AllocType = inferPossibleTypeFromCastExpr(E, CastE);
185 return AllocType;
186}
187
188std::optional<llvm::AllocTokenMetadata>
190 llvm::AllocTokenMetadata ATMD;
191
192 // Get unique type name.
193 PrintingPolicy Policy(Ctx.getLangOpts());
194 Policy.SuppressTagKeyword = true;
195 Policy.FullyQualifiedName = true;
196 llvm::raw_svector_ostream TypeNameOS(ATMD.TypeName);
197 T.getCanonicalType().print(TypeNameOS, Policy);
198
199 // Check if QualType contains a pointer. Implements a simple DFS to
200 // recursively check if a type contains a pointer type.
202 bool IncompleteType = false;
203 ATMD.ContainsPointer = typeContainsPointer(T, VisitedRD, IncompleteType);
204 if (!ATMD.ContainsPointer && IncompleteType)
205 return std::nullopt;
206
207 return ATMD;
208}
Defines the clang::ASTContext interface.
Defines the C++ Decl subclasses, other than those for templates (found in DeclTemplate....
Defines the clang::IdentifierInfo, clang::IdentifierTable, and clang::Selector interfaces.
static QualType inferTypeFromSizeofExpr(const Expr *E)
Infer type from a simple sizeof expression.
static QualType inferPossibleTypeFromArithSizeofExpr(const Expr *E)
Infer type from an arithmetic expression involving a sizeof.
static bool typeContainsPointer(QualType T, llvm::SmallPtrSet< const RecordDecl *, 4 > &VisitedRD, bool &IncompleteType)
static QualType inferPossibleTypeFromVarInitSizeofExpr(const Expr *E)
If the expression E is a reference to a variable, infer the type from a variable's initializer if it ...
static QualType inferPossibleTypeFromCastExpr(const CallExpr *CallE, const CastExpr *CastE)
Deduces the allocated type by checking if the allocation call's result is immediately used in a cast ...
static QualType getUnderlyingType(const SubRegion *R)
C Language Family Type Representation.
Holds long-lived AST nodes (such as types and decls) that can be referred to throughout the semantic ...
Definition ASTContext.h:239
const LangOptions & getLangOpts() const
Represents an array type, per C99 6.7.5.2 - Array Declarators.
Definition TypeBase.h:3820
Represents a base class of a C++ class.
Definition DeclCXX.h:146
Represents a C++ struct/union/class.
Definition DeclCXX.h:258
CallExpr - Represents a function call (C99 6.5.2.2, C++ [expr.call]).
Definition Expr.h:2987
arg_range arguments()
Definition Expr.h:3239
CastExpr - Base class for type casts, including both implicit casts (ImplicitCastExpr) and explicit c...
Definition Expr.h:3720
This represents one expression.
Definition Expr.h:113
Expr * IgnoreParenImpCasts() LLVM_READONLY
Skip past any parentheses and implicit casts which might surround this expression until reaching a fi...
Definition Expr.cpp:3126
QualType getType() const
Definition Expr.h:145
Represents a member of a struct/union/class.
Definition Decl.h:3295
One of these records is kept for each identifier that is lexed.
bool isStr(const char(&Str)[StrLen]) const
Return true if this is the identifier for the specified string.
A (possibly-)qualified type.
Definition TypeBase.h:938
bool isNull() const
Return true if this QualType doesn't point to a type yet.
Definition TypeBase.h:1005
Represents a struct/union/class.
Definition Decl.h:4460
bool isBlockPointerType() const
Definition TypeBase.h:8703
RecordDecl * getAsRecordDecl() const
Retrieves the RecordDecl this type refers to.
Definition Type.h:41
bool isPointerType() const
Definition TypeBase.h:8683
bool isReferenceType() const
Definition TypeBase.h:8707
QualType getPointeeType() const
If this is a pointer, ObjC object pointer, or block pointer, this returns the respective pointee.
Definition Type.cpp:885
bool isMemberFunctionPointerType() const
Definition TypeBase.h:8768
bool isAnyPointerType() const
Definition TypeBase.h:8691
TypedefNameDecl * getDecl() const
Definition TypeBase.h:6260
std::optional< llvm::AllocTokenMetadata > getAllocTokenMetadata(QualType T, const ASTContext &Ctx)
Get the information required for construction of an allocation token ID.
QualType inferPossibleType(const CallExpr *E, const ASTContext &Ctx, const CastExpr *CastE)
Infer the possible allocated type from an allocation call expression.
Top level wrappers for InstallAPI frontend operations.
const FunctionProtoType * T
Describes how types, statements, expressions, and declarations should be printed.
unsigned SuppressTagKeyword
Whether type printing should skip printing the tag keyword.