clang 22.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->isPointerType())
31 return true; // base case
32
33 // Look through typedef chain to check for special types.
34 for (QualType CurrentT = T; const auto *TT = CurrentT->getAs<TypedefType>();
35 CurrentT = TT->getDecl()->getUnderlyingType()) {
36 const IdentifierInfo *II = TT->getDecl()->getIdentifier();
37 // Special Case: Syntactically uintptr_t is not a pointer; semantically,
38 // however, very likely used as such. Therefore, classify uintptr_t as a
39 // pointer, too.
40 if (II && II->isStr("uintptr_t"))
41 return true;
42 }
43
44 // The type is an array; check the element type.
45 if (const ArrayType *AT = dyn_cast<ArrayType>(CanonicalType))
46 return typeContainsPointer(AT->getElementType(), VisitedRD, IncompleteType);
47 // The type is a struct, class, or union.
48 if (const RecordDecl *RD = CanonicalType->getAsRecordDecl()) {
49 if (!RD->isCompleteDefinition()) {
50 IncompleteType = true;
51 return false;
52 }
53 if (!VisitedRD.insert(RD).second)
54 return false; // already visited
55 // Check all fields.
56 for (const FieldDecl *Field : RD->fields()) {
57 if (typeContainsPointer(Field->getType(), VisitedRD, IncompleteType))
58 return true;
59 }
60 // For C++ classes, also check base classes.
61 if (const CXXRecordDecl *CXXRD = dyn_cast<CXXRecordDecl>(RD)) {
62 // Polymorphic types require a vptr.
63 if (CXXRD->isDynamicClass())
64 return true;
65 for (const CXXBaseSpecifier &Base : CXXRD->bases()) {
66 if (typeContainsPointer(Base.getType(), VisitedRD, IncompleteType))
67 return true;
68 }
69 }
70 }
71 return false;
72}
73
74/// Infer type from a simple sizeof expression.
76 const Expr *Arg = E->IgnoreParenImpCasts();
77 if (const auto *UET = dyn_cast<UnaryExprOrTypeTraitExpr>(Arg)) {
78 if (UET->getKind() == UETT_SizeOf) {
79 if (UET->isArgumentType())
80 return UET->getArgumentTypeInfo()->getType();
81 else
82 return UET->getArgumentExpr()->getType();
83 }
84 }
85 return QualType();
86}
87
88/// Infer type from an arithmetic expression involving a sizeof. For example:
89///
90/// malloc(sizeof(MyType) + padding); // infers 'MyType'
91/// malloc(sizeof(MyType) * 32); // infers 'MyType'
92/// malloc(32 * sizeof(MyType)); // infers 'MyType'
93/// malloc(sizeof(MyType) << 1); // infers 'MyType'
94/// ...
95///
96/// More complex arithmetic expressions are supported, but are a heuristic, e.g.
97/// when considering allocations for structs with flexible array members:
98///
99/// malloc(sizeof(HasFlexArray) + sizeof(int) * 32); // infers 'HasFlexArray'
100///
102 const Expr *Arg = E->IgnoreParenImpCasts();
103 // The argument is a lone sizeof expression.
104 if (QualType T = inferTypeFromSizeofExpr(Arg); !T.isNull())
105 return T;
106 if (const auto *BO = dyn_cast<BinaryOperator>(Arg)) {
107 // Argument is an arithmetic expression. Cover common arithmetic patterns
108 // involving sizeof.
109 switch (BO->getOpcode()) {
110 case BO_Add:
111 case BO_Div:
112 case BO_Mul:
113 case BO_Shl:
114 case BO_Shr:
115 case BO_Sub:
117 !T.isNull())
118 return T;
120 !T.isNull())
121 return T;
122 break;
123 default:
124 break;
125 }
126 }
127 return QualType();
128}
129
130/// If the expression E is a reference to a variable, infer the type from a
131/// variable's initializer if it contains a sizeof. Beware, this is a heuristic
132/// and ignores if a variable is later reassigned. For example:
133///
134/// size_t my_size = sizeof(MyType);
135/// void *x = malloc(my_size); // infers 'MyType'
136///
138 const Expr *Arg = E->IgnoreParenImpCasts();
139 if (const auto *DRE = dyn_cast<DeclRefExpr>(Arg)) {
140 if (const auto *VD = dyn_cast<VarDecl>(DRE->getDecl())) {
141 if (const Expr *Init = VD->getInit())
143 }
144 }
145 return QualType();
146}
147
148/// Deduces the allocated type by checking if the allocation call's result
149/// is immediately used in a cast expression. For example:
150///
151/// MyType *x = (MyType *)malloc(4096); // infers 'MyType'
152///
154 const CastExpr *CastE) {
155 if (!CastE)
156 return QualType();
157 QualType PtrType = CastE->getType();
158 if (PtrType->isPointerType())
159 return PtrType->getPointeeType();
160 return QualType();
161}
162
164 const ASTContext &Ctx,
165 const CastExpr *CastE) {
166 QualType AllocType;
167 // First check arguments.
168 for (const Expr *Arg : E->arguments()) {
170 if (AllocType.isNull())
172 if (!AllocType.isNull())
173 break;
174 }
175 // Then check later casts.
176 if (AllocType.isNull())
177 AllocType = inferPossibleTypeFromCastExpr(E, CastE);
178 return AllocType;
179}
180
181std::optional<llvm::AllocTokenMetadata>
183 llvm::AllocTokenMetadata ATMD;
184
185 // Get unique type name.
186 PrintingPolicy Policy(Ctx.getLangOpts());
187 Policy.SuppressTagKeyword = true;
188 Policy.FullyQualifiedName = true;
189 llvm::raw_svector_ostream TypeNameOS(ATMD.TypeName);
190 T.getCanonicalType().print(TypeNameOS, Policy);
191
192 // Check if QualType contains a pointer. Implements a simple DFS to
193 // recursively check if a type contains a pointer type.
195 bool IncompleteType = false;
196 ATMD.ContainsPointer = typeContainsPointer(T, VisitedRD, IncompleteType);
197 if (!ATMD.ContainsPointer && IncompleteType)
198 return std::nullopt;
199
200 return ATMD;
201}
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:220
const LangOptions & getLangOpts() const
Definition ASTContext.h:926
Represents an array type, per C99 6.7.5.2 - Array Declarators.
Definition TypeBase.h:3722
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:2877
arg_range arguments()
Definition Expr.h:3129
CastExpr - Base class for type casts, including both implicit casts (ImplicitCastExpr) and explicit c...
Definition Expr.h:3610
This represents one expression.
Definition Expr.h:112
Expr * IgnoreParenImpCasts() LLVM_READONLY
Skip past any parentheses and implicit casts which might surround this expression until reaching a fi...
Definition Expr.cpp:3085
QualType getType() const
Definition Expr.h:144
Represents a member of a struct/union/class.
Definition Decl.h:3160
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:937
bool isNull() const
Return true if this QualType doesn't point to a type yet.
Definition TypeBase.h:1004
Represents a struct/union/class.
Definition Decl.h:4312
RecordDecl * getAsRecordDecl() const
Retrieves the RecordDecl this type refers to.
Definition Type.h:41
bool isPointerType() const
Definition TypeBase.h:8515
QualType getPointeeType() const
If this is a pointer, ObjC object pointer, or block pointer, this returns the respective pointee.
Definition Type.cpp:752
TypedefNameDecl * getDecl() const
Definition TypeBase.h:6099
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.
The JSON file list parser is used to communicate input to InstallAPI.
const FunctionProtoType * T
Describes how types, statements, expressions, and declarations should be printed.
unsigned FullyQualifiedName
When true, print the fully qualified name of function declarations.
unsigned SuppressTagKeyword
Whether type printing should skip printing the tag keyword.