2018-12-05 21:12:39 +00:00
|
|
|
//===--- TextNodeDumper.cpp - Printing of AST nodes -----------------------===//
|
|
|
|
//
|
2019-01-19 08:50:56 +00:00
|
|
|
// 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
|
2018-12-05 21:12:39 +00:00
|
|
|
//
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
//
|
|
|
|
// This file implements AST dumping of components of individual AST nodes.
|
|
|
|
//
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
|
|
|
#include "clang/AST/TextNodeDumper.h"
|
[clang] Rework how and when APValues are dumped
Currently APValues are dumped as a single string. This becomes quickly
completely unreadable since APValue is a tree-like structure. Even a simple
example is not pretty:
struct S { int arr[4]; float f; };
constexpr S s = { .arr = {1,2}, .f = 3.1415f };
// Struct fields: Array: Int: 1, Int: 2, 2 x Int: 0, Float: 3.141500e+00
With this patch this becomes:
-Struct
|-field: Array size=4
| |-elements: Int 1, Int 2
| `-filler: 2 x Int 0
`-field: Float 3.141500e+00
Additionally APValues are currently only dumped as part of visiting a
ConstantExpr. This patch also dump the value of the initializer of constexpr
variable declarations:
constexpr int foo(int a, int b) { return a + b - 42; }
constexpr int a = 1, b = 2;
constexpr int c = foo(a, b) > 0 ? foo(a, b) : foo(b, a);
// VarDecl 0x62100008aec8 <col:3, col:57> col:17 c 'const int' constexpr cinit
// |-value: Int -39
// `-ConditionalOperator 0x62100008b4d0 <col:21, col:57> 'int'
// <snip>
Do the above by moving the dump functions to TextNodeDumper which already has
the machinery to display trees. The cases APValue::LValue, APValue::MemberPointer
and APValue::AddrLabelDiff are left as they were before (unimplemented).
We try to display multiple elements on the same line if they are considered to
be "simple". This is to avoid wasting large amounts of vertical space in an
example like:
constexpr int arr[8] = {0,1,2,3,4,5,6,7};
// VarDecl 0x62100008bb78 <col:3, col:42> col:17 arr 'int const[8]' constexpr cinit
// |-value: Array size=8
// | |-elements: Int 0, Int 1, Int 2, Int 3
// | `-elements: Int 4, Int 5, Int 6, Int 7
Differential Revision: https://reviews.llvm.org/D83183
Reviewed By: aaron.ballman
2020-07-06 21:50:23 +01:00
|
|
|
#include "clang/AST/APValue.h"
|
2019-01-15 09:35:52 +00:00
|
|
|
#include "clang/AST/DeclFriend.h"
|
|
|
|
#include "clang/AST/DeclOpenMP.h"
|
2019-01-15 09:30:00 +00:00
|
|
|
#include "clang/AST/DeclTemplate.h"
|
2019-01-14 20:11:02 +00:00
|
|
|
#include "clang/AST/LocInfoType.h"
|
2023-09-05 18:15:06 +02:00
|
|
|
#include "clang/AST/NestedNameSpecifier.h"
|
[clang] Rework how and when APValues are dumped
Currently APValues are dumped as a single string. This becomes quickly
completely unreadable since APValue is a tree-like structure. Even a simple
example is not pretty:
struct S { int arr[4]; float f; };
constexpr S s = { .arr = {1,2}, .f = 3.1415f };
// Struct fields: Array: Int: 1, Int: 2, 2 x Int: 0, Float: 3.141500e+00
With this patch this becomes:
-Struct
|-field: Array size=4
| |-elements: Int 1, Int 2
| `-filler: 2 x Int 0
`-field: Float 3.141500e+00
Additionally APValues are currently only dumped as part of visiting a
ConstantExpr. This patch also dump the value of the initializer of constexpr
variable declarations:
constexpr int foo(int a, int b) { return a + b - 42; }
constexpr int a = 1, b = 2;
constexpr int c = foo(a, b) > 0 ? foo(a, b) : foo(b, a);
// VarDecl 0x62100008aec8 <col:3, col:57> col:17 c 'const int' constexpr cinit
// |-value: Int -39
// `-ConditionalOperator 0x62100008b4d0 <col:21, col:57> 'int'
// <snip>
Do the above by moving the dump functions to TextNodeDumper which already has
the machinery to display trees. The cases APValue::LValue, APValue::MemberPointer
and APValue::AddrLabelDiff are left as they were before (unimplemented).
We try to display multiple elements on the same line if they are considered to
be "simple". This is to avoid wasting large amounts of vertical space in an
example like:
constexpr int arr[8] = {0,1,2,3,4,5,6,7};
// VarDecl 0x62100008bb78 <col:3, col:42> col:17 arr 'int const[8]' constexpr cinit
// |-value: Array size=8
// | |-elements: Int 0, Int 1, Int 2, Int 3
// | `-elements: Int 4, Int 5, Int 6, Int 7
Differential Revision: https://reviews.llvm.org/D83183
Reviewed By: aaron.ballman
2020-07-06 21:50:23 +01:00
|
|
|
#include "clang/AST/Type.h"
|
[AST] Add dump() method to TypeLoc (#65484)
The ability to dump AST nodes is important to ad-hoc debugging, and
the fact this doesn't work with TypeLoc nodes is an obvious missing
feature in e.g. clang-query (`set output dump` simply does nothing).
Having TypeLoc::dump(), and enabling DynTypedNode::dump() for such nodes
seems like a clear win.
It looks like this:
```
int main(int argc, char **argv);
FunctionProtoTypeLoc <test.cc:3:1, col:31> 'int (int, char **)' cdecl
|-ParmVarDecl 0x30071a8 <col:10, col:14> col:14 argc 'int'
| `-BuiltinTypeLoc <col:10> 'int'
|-ParmVarDecl 0x3007250 <col:20, col:27> col:27 argv 'char **'
| `-PointerTypeLoc <col:20, col:26> 'char **'
| `-PointerTypeLoc <col:20, col:25> 'char *'
| `-BuiltinTypeLoc <col:20> 'char'
`-BuiltinTypeLoc <col:1> 'int'
```
It dumps the lexically nested tree of type locs.
This often looks similar to how types are dumped, but unlike types
we don't look at desugaring e.g. typedefs, as their underlying types
are not lexically spelled here.
---
Less clear is exactly when to include these nodes in existing text AST
dumps rooted at (TranslationUnit)Decls.
These already omit supported nodes sometimes, e.g. NestedNameSpecifiers
are often mentioned but not recursively dumped.
TypeLocs are a more extreme case: they're ~always more verbose
than the current AST dump.
So this patch punts on that, TypeLocs are only ever printed recursively
as part of a TypeLoc::dump() call.
It would also be nice to be able to invoke `clang` to dump a typeloc
somehow, like `clang -cc1 -ast-dump`. But I don't know exactly what the
best verison of that is, so this patch doesn't do it.
---
There are similar (less critical!) nodes: TemplateArgumentLoc etc,
these also don't have dump() functions today and are obvious extensions.
I suspect that we should add these, and Loc nodes should dump each other
(e.g. the ElaboratedTypeLoc `vector<int>::iterator` should dump
the NestedNameSpecifierLoc `vector<int>::`, which dumps the
TemplateSpecializationTypeLoc `vector<int>::` etc).
Maybe this generalizes further to a "full syntactic dump" mode, where
even Decls and Stmts would print the TypeLocs they lexically contain.
But this may be more complex than useful.
---
While here, ConceptReference JSON dumping must be implemented. It's not
totally clear to me why this implementation wasn't required before but
is now...
2024-01-31 16:40:29 +01:00
|
|
|
#include "clang/AST/TypeLocVisitor.h"
|
2020-02-27 18:13:54 -08:00
|
|
|
#include "clang/Basic/Module.h"
|
2020-02-27 11:01:58 -08:00
|
|
|
#include "clang/Basic/SourceManager.h"
|
2020-05-27 18:17:07 +02:00
|
|
|
#include "clang/Basic/Specifiers.h"
|
2020-06-11 14:08:27 +01:00
|
|
|
#include "clang/Basic/TypeTraits.h"
|
2021-06-11 13:19:00 +01:00
|
|
|
#include "llvm/ADT/StringExtras.h"
|
2018-12-05 21:12:39 +00:00
|
|
|
|
[clang] Rework how and when APValues are dumped
Currently APValues are dumped as a single string. This becomes quickly
completely unreadable since APValue is a tree-like structure. Even a simple
example is not pretty:
struct S { int arr[4]; float f; };
constexpr S s = { .arr = {1,2}, .f = 3.1415f };
// Struct fields: Array: Int: 1, Int: 2, 2 x Int: 0, Float: 3.141500e+00
With this patch this becomes:
-Struct
|-field: Array size=4
| |-elements: Int 1, Int 2
| `-filler: 2 x Int 0
`-field: Float 3.141500e+00
Additionally APValues are currently only dumped as part of visiting a
ConstantExpr. This patch also dump the value of the initializer of constexpr
variable declarations:
constexpr int foo(int a, int b) { return a + b - 42; }
constexpr int a = 1, b = 2;
constexpr int c = foo(a, b) > 0 ? foo(a, b) : foo(b, a);
// VarDecl 0x62100008aec8 <col:3, col:57> col:17 c 'const int' constexpr cinit
// |-value: Int -39
// `-ConditionalOperator 0x62100008b4d0 <col:21, col:57> 'int'
// <snip>
Do the above by moving the dump functions to TextNodeDumper which already has
the machinery to display trees. The cases APValue::LValue, APValue::MemberPointer
and APValue::AddrLabelDiff are left as they were before (unimplemented).
We try to display multiple elements on the same line if they are considered to
be "simple". This is to avoid wasting large amounts of vertical space in an
example like:
constexpr int arr[8] = {0,1,2,3,4,5,6,7};
// VarDecl 0x62100008bb78 <col:3, col:42> col:17 arr 'int const[8]' constexpr cinit
// |-value: Array size=8
// | |-elements: Int 0, Int 1, Int 2, Int 3
// | `-elements: Int 4, Int 5, Int 6, Int 7
Differential Revision: https://reviews.llvm.org/D83183
Reviewed By: aaron.ballman
2020-07-06 21:50:23 +01:00
|
|
|
#include <algorithm>
|
|
|
|
#include <utility>
|
|
|
|
|
2018-12-05 21:12:39 +00:00
|
|
|
using namespace clang;
|
|
|
|
|
2019-01-15 09:35:52 +00:00
|
|
|
static void dumpPreviousDeclImpl(raw_ostream &OS, ...) {}
|
|
|
|
|
|
|
|
template <typename T>
|
|
|
|
static void dumpPreviousDeclImpl(raw_ostream &OS, const Mergeable<T> *D) {
|
|
|
|
const T *First = D->getFirstDecl();
|
|
|
|
if (First != D)
|
|
|
|
OS << " first " << First;
|
|
|
|
}
|
|
|
|
|
|
|
|
template <typename T>
|
|
|
|
static void dumpPreviousDeclImpl(raw_ostream &OS, const Redeclarable<T> *D) {
|
|
|
|
const T *Prev = D->getPreviousDecl();
|
|
|
|
if (Prev)
|
|
|
|
OS << " prev " << Prev;
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Dump the previous declaration in the redeclaration chain for a declaration,
|
|
|
|
/// if any.
|
|
|
|
static void dumpPreviousDecl(raw_ostream &OS, const Decl *D) {
|
|
|
|
switch (D->getKind()) {
|
|
|
|
#define DECL(DERIVED, BASE) \
|
|
|
|
case Decl::DERIVED: \
|
|
|
|
return dumpPreviousDeclImpl(OS, cast<DERIVED##Decl>(D));
|
|
|
|
#define ABSTRACT_DECL(DECL)
|
|
|
|
#include "clang/AST/DeclNodes.inc"
|
|
|
|
}
|
|
|
|
llvm_unreachable("Decl that isn't part of DeclNodes.inc!");
|
|
|
|
}
|
|
|
|
|
2020-07-03 13:54:10 +01:00
|
|
|
TextNodeDumper::TextNodeDumper(raw_ostream &OS, const ASTContext &Context,
|
|
|
|
bool ShowColors)
|
|
|
|
: TextTreeStructure(OS, ShowColors), OS(OS), ShowColors(ShowColors),
|
|
|
|
Context(&Context), SM(&Context.getSourceManager()),
|
|
|
|
PrintPolicy(Context.getPrintingPolicy()),
|
|
|
|
Traits(&Context.getCommentCommandTraits()) {}
|
|
|
|
|
|
|
|
TextNodeDumper::TextNodeDumper(raw_ostream &OS, bool ShowColors)
|
|
|
|
: TextTreeStructure(OS, ShowColors), OS(OS), ShowColors(ShowColors) {}
|
2018-12-09 13:30:17 +00:00
|
|
|
|
|
|
|
void TextNodeDumper::Visit(const comments::Comment *C,
|
|
|
|
const comments::FullComment *FC) {
|
|
|
|
if (!C) {
|
|
|
|
ColorScope Color(OS, ShowColors, NullColor);
|
|
|
|
OS << "<<<NULL>>>";
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
{
|
|
|
|
ColorScope Color(OS, ShowColors, CommentColor);
|
|
|
|
OS << C->getCommentKindName();
|
|
|
|
}
|
|
|
|
dumpPointer(C);
|
|
|
|
dumpSourceRange(C->getSourceRange());
|
|
|
|
|
|
|
|
ConstCommentVisitor<TextNodeDumper, void,
|
|
|
|
const comments::FullComment *>::visit(C, FC);
|
|
|
|
}
|
2018-12-05 21:12:39 +00:00
|
|
|
|
2019-01-11 19:16:01 +00:00
|
|
|
void TextNodeDumper::Visit(const Attr *A) {
|
|
|
|
{
|
|
|
|
ColorScope Color(OS, ShowColors, AttrColor);
|
|
|
|
|
|
|
|
switch (A->getKind()) {
|
|
|
|
#define ATTR(X) \
|
|
|
|
case attr::X: \
|
|
|
|
OS << #X; \
|
|
|
|
break;
|
|
|
|
#include "clang/Basic/AttrList.inc"
|
|
|
|
}
|
|
|
|
OS << "Attr";
|
|
|
|
}
|
|
|
|
dumpPointer(A);
|
|
|
|
dumpSourceRange(A->getRange());
|
|
|
|
if (A->isInherited())
|
|
|
|
OS << " Inherited";
|
|
|
|
if (A->isImplicit())
|
|
|
|
OS << " Implicit";
|
|
|
|
|
|
|
|
ConstAttrVisitor<TextNodeDumper>::Visit(A);
|
|
|
|
}
|
|
|
|
|
2019-01-12 16:35:37 +00:00
|
|
|
void TextNodeDumper::Visit(const TemplateArgument &TA, SourceRange R,
|
|
|
|
const Decl *From, StringRef Label) {
|
|
|
|
OS << "TemplateArgument";
|
|
|
|
if (R.isValid())
|
|
|
|
dumpSourceRange(R);
|
|
|
|
|
2019-01-14 19:50:34 +00:00
|
|
|
if (From)
|
2019-01-12 16:35:37 +00:00
|
|
|
dumpDeclRef(From, Label);
|
|
|
|
|
|
|
|
ConstTemplateArgumentVisitor<TextNodeDumper>::Visit(TA);
|
|
|
|
}
|
|
|
|
|
2019-01-12 16:53:27 +00:00
|
|
|
void TextNodeDumper::Visit(const Stmt *Node) {
|
|
|
|
if (!Node) {
|
|
|
|
ColorScope Color(OS, ShowColors, NullColor);
|
|
|
|
OS << "<<<NULL>>>";
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
{
|
|
|
|
ColorScope Color(OS, ShowColors, StmtColor);
|
|
|
|
OS << Node->getStmtClassName();
|
|
|
|
}
|
|
|
|
dumpPointer(Node);
|
|
|
|
dumpSourceRange(Node->getSourceRange());
|
|
|
|
|
|
|
|
if (const auto *E = dyn_cast<Expr>(Node)) {
|
|
|
|
dumpType(E->getType());
|
|
|
|
|
2020-03-17 10:36:19 +01:00
|
|
|
if (E->containsErrors()) {
|
|
|
|
ColorScope Color(OS, ShowColors, ErrorsColor);
|
|
|
|
OS << " contains-errors";
|
|
|
|
}
|
|
|
|
|
2019-01-12 16:53:27 +00:00
|
|
|
{
|
|
|
|
ColorScope Color(OS, ShowColors, ValueKindColor);
|
|
|
|
switch (E->getValueKind()) {
|
2021-06-04 23:15:23 +02:00
|
|
|
case VK_PRValue:
|
2019-01-12 16:53:27 +00:00
|
|
|
break;
|
|
|
|
case VK_LValue:
|
|
|
|
OS << " lvalue";
|
|
|
|
break;
|
|
|
|
case VK_XValue:
|
|
|
|
OS << " xvalue";
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
{
|
|
|
|
ColorScope Color(OS, ShowColors, ObjectKindColor);
|
|
|
|
switch (E->getObjectKind()) {
|
|
|
|
case OK_Ordinary:
|
|
|
|
break;
|
|
|
|
case OK_BitField:
|
|
|
|
OS << " bitfield";
|
|
|
|
break;
|
|
|
|
case OK_ObjCProperty:
|
|
|
|
OS << " objcproperty";
|
|
|
|
break;
|
|
|
|
case OK_ObjCSubscript:
|
|
|
|
OS << " objcsubscript";
|
|
|
|
break;
|
|
|
|
case OK_VectorComponent:
|
|
|
|
OS << " vectorcomponent";
|
|
|
|
break;
|
[Matrix] Implement matrix index expressions ([][]).
This patch implements matrix index expressions
(matrix[RowIdx][ColumnIdx]).
It does so by introducing a new MatrixSubscriptExpr(Base, RowIdx, ColumnIdx).
MatrixSubscriptExprs are built in 2 steps in ActOnMatrixSubscriptExpr. First,
if the base of a subscript is of matrix type, we create a incomplete
MatrixSubscriptExpr(base, idx, nullptr). Second, if the base is an incomplete
MatrixSubscriptExpr, we create a complete
MatrixSubscriptExpr(base->getBase(), base->getRowIdx(), idx)
Similar to vector elements, it is not possible to take the address of
a MatrixSubscriptExpr.
For CodeGen, a new MatrixElt type is added to LValue, which is very
similar to VectorElt. The only difference is that we may need to cast
the type of the base from an array to a vector type when accessing it.
Reviewers: rjmccall, anemet, Bigcheese, rsmith, martong
Reviewed By: rjmccall
Differential Revision: https://reviews.llvm.org/D76791
2020-06-01 19:42:03 +01:00
|
|
|
case OK_MatrixComponent:
|
|
|
|
OS << " matrixcomponent";
|
|
|
|
break;
|
2019-01-12 16:53:27 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
ConstStmtVisitor<TextNodeDumper>::Visit(Node);
|
|
|
|
}
|
|
|
|
|
2019-01-14 20:11:02 +00:00
|
|
|
void TextNodeDumper::Visit(const Type *T) {
|
|
|
|
if (!T) {
|
|
|
|
ColorScope Color(OS, ShowColors, NullColor);
|
|
|
|
OS << "<<<NULL>>>";
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (isa<LocInfoType>(T)) {
|
|
|
|
{
|
|
|
|
ColorScope Color(OS, ShowColors, TypeColor);
|
|
|
|
OS << "LocInfo Type";
|
|
|
|
}
|
|
|
|
dumpPointer(T);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
{
|
|
|
|
ColorScope Color(OS, ShowColors, TypeColor);
|
|
|
|
OS << T->getTypeClassName() << "Type";
|
|
|
|
}
|
|
|
|
dumpPointer(T);
|
|
|
|
OS << " ";
|
|
|
|
dumpBareType(QualType(T, 0), false);
|
|
|
|
|
|
|
|
QualType SingleStepDesugar =
|
|
|
|
T->getLocallyUnqualifiedSingleStepDesugaredType();
|
|
|
|
if (SingleStepDesugar != QualType(T, 0))
|
|
|
|
OS << " sugar";
|
|
|
|
|
2020-06-16 09:08:02 +02:00
|
|
|
if (T->containsErrors()) {
|
|
|
|
ColorScope Color(OS, ShowColors, ErrorsColor);
|
|
|
|
OS << " contains-errors";
|
|
|
|
}
|
|
|
|
|
2019-01-14 20:11:02 +00:00
|
|
|
if (T->isDependentType())
|
|
|
|
OS << " dependent";
|
|
|
|
else if (T->isInstantiationDependentType())
|
|
|
|
OS << " instantiation_dependent";
|
|
|
|
|
|
|
|
if (T->isVariablyModifiedType())
|
|
|
|
OS << " variably_modified";
|
|
|
|
if (T->containsUnexpandedParameterPack())
|
|
|
|
OS << " contains_unexpanded_pack";
|
|
|
|
if (T->isFromAST())
|
|
|
|
OS << " imported";
|
2019-01-15 09:30:00 +00:00
|
|
|
|
|
|
|
TypeVisitor<TextNodeDumper>::Visit(T);
|
2019-01-14 20:11:02 +00:00
|
|
|
}
|
|
|
|
|
2019-01-14 20:15:29 +00:00
|
|
|
void TextNodeDumper::Visit(QualType T) {
|
|
|
|
OS << "QualType";
|
|
|
|
dumpPointer(T.getAsOpaquePtr());
|
|
|
|
OS << " ";
|
|
|
|
dumpBareType(T, false);
|
|
|
|
OS << " " << T.split().Quals.getAsString();
|
|
|
|
}
|
|
|
|
|
[AST] Add dump() method to TypeLoc (#65484)
The ability to dump AST nodes is important to ad-hoc debugging, and
the fact this doesn't work with TypeLoc nodes is an obvious missing
feature in e.g. clang-query (`set output dump` simply does nothing).
Having TypeLoc::dump(), and enabling DynTypedNode::dump() for such nodes
seems like a clear win.
It looks like this:
```
int main(int argc, char **argv);
FunctionProtoTypeLoc <test.cc:3:1, col:31> 'int (int, char **)' cdecl
|-ParmVarDecl 0x30071a8 <col:10, col:14> col:14 argc 'int'
| `-BuiltinTypeLoc <col:10> 'int'
|-ParmVarDecl 0x3007250 <col:20, col:27> col:27 argv 'char **'
| `-PointerTypeLoc <col:20, col:26> 'char **'
| `-PointerTypeLoc <col:20, col:25> 'char *'
| `-BuiltinTypeLoc <col:20> 'char'
`-BuiltinTypeLoc <col:1> 'int'
```
It dumps the lexically nested tree of type locs.
This often looks similar to how types are dumped, but unlike types
we don't look at desugaring e.g. typedefs, as their underlying types
are not lexically spelled here.
---
Less clear is exactly when to include these nodes in existing text AST
dumps rooted at (TranslationUnit)Decls.
These already omit supported nodes sometimes, e.g. NestedNameSpecifiers
are often mentioned but not recursively dumped.
TypeLocs are a more extreme case: they're ~always more verbose
than the current AST dump.
So this patch punts on that, TypeLocs are only ever printed recursively
as part of a TypeLoc::dump() call.
It would also be nice to be able to invoke `clang` to dump a typeloc
somehow, like `clang -cc1 -ast-dump`. But I don't know exactly what the
best verison of that is, so this patch doesn't do it.
---
There are similar (less critical!) nodes: TemplateArgumentLoc etc,
these also don't have dump() functions today and are obvious extensions.
I suspect that we should add these, and Loc nodes should dump each other
(e.g. the ElaboratedTypeLoc `vector<int>::iterator` should dump
the NestedNameSpecifierLoc `vector<int>::`, which dumps the
TemplateSpecializationTypeLoc `vector<int>::` etc).
Maybe this generalizes further to a "full syntactic dump" mode, where
even Decls and Stmts would print the TypeLocs they lexically contain.
But this may be more complex than useful.
---
While here, ConceptReference JSON dumping must be implemented. It's not
totally clear to me why this implementation wasn't required before but
is now...
2024-01-31 16:40:29 +01:00
|
|
|
void TextNodeDumper::Visit(TypeLoc TL) {
|
|
|
|
if (!TL) {
|
|
|
|
ColorScope Color(OS, ShowColors, NullColor);
|
|
|
|
OS << "<<<NULL>>>";
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
{
|
|
|
|
ColorScope Color(OS, ShowColors, TypeColor);
|
|
|
|
OS << (TL.getTypeLocClass() == TypeLoc::Qualified
|
|
|
|
? "Qualified"
|
|
|
|
: TL.getType()->getTypeClassName())
|
|
|
|
<< "TypeLoc";
|
|
|
|
}
|
|
|
|
dumpSourceRange(TL.getSourceRange());
|
|
|
|
OS << ' ';
|
|
|
|
dumpBareType(TL.getType(), /*Desugar=*/false);
|
|
|
|
|
|
|
|
TypeLocVisitor<TextNodeDumper>::Visit(TL);
|
|
|
|
}
|
|
|
|
|
2019-01-15 09:35:52 +00:00
|
|
|
void TextNodeDumper::Visit(const Decl *D) {
|
|
|
|
if (!D) {
|
|
|
|
ColorScope Color(OS, ShowColors, NullColor);
|
|
|
|
OS << "<<<NULL>>>";
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
{
|
|
|
|
ColorScope Color(OS, ShowColors, DeclKindNameColor);
|
|
|
|
OS << D->getDeclKindName() << "Decl";
|
|
|
|
}
|
|
|
|
dumpPointer(D);
|
|
|
|
if (D->getLexicalDeclContext() != D->getDeclContext())
|
|
|
|
OS << " parent " << cast<Decl>(D->getDeclContext());
|
|
|
|
dumpPreviousDecl(OS, D);
|
|
|
|
dumpSourceRange(D->getSourceRange());
|
|
|
|
OS << ' ';
|
|
|
|
dumpLocation(D->getLocation());
|
|
|
|
if (D->isFromASTFile())
|
|
|
|
OS << " imported";
|
|
|
|
if (Module *M = D->getOwningModule())
|
|
|
|
OS << " in " << M->getFullModuleName();
|
|
|
|
if (auto *ND = dyn_cast<NamedDecl>(D))
|
|
|
|
for (Module *M : D->getASTContext().getModulesWithMergedDefinition(
|
|
|
|
const_cast<NamedDecl *>(ND)))
|
|
|
|
AddChild([=] { OS << "also in " << M->getFullModuleName(); });
|
|
|
|
if (const NamedDecl *ND = dyn_cast<NamedDecl>(D))
|
2020-06-08 15:37:44 +02:00
|
|
|
if (!ND->isUnconditionallyVisible())
|
2019-01-15 09:35:52 +00:00
|
|
|
OS << " hidden";
|
|
|
|
if (D->isImplicit())
|
|
|
|
OS << " implicit";
|
|
|
|
|
|
|
|
if (D->isUsed())
|
|
|
|
OS << " used";
|
|
|
|
else if (D->isThisDeclarationReferenced())
|
|
|
|
OS << " referenced";
|
|
|
|
|
|
|
|
if (D->isInvalidDecl())
|
|
|
|
OS << " invalid";
|
2019-06-14 08:56:20 +00:00
|
|
|
if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) {
|
|
|
|
if (FD->isConstexprSpecified())
|
2019-01-15 09:35:52 +00:00
|
|
|
OS << " constexpr";
|
2019-06-14 08:56:20 +00:00
|
|
|
if (FD->isConsteval())
|
|
|
|
OS << " consteval";
|
2023-05-08 12:18:43 +02:00
|
|
|
else if (FD->isImmediateFunction())
|
|
|
|
OS << " immediate";
|
2022-03-02 14:10:33 -08:00
|
|
|
if (FD->isMultiVersion())
|
|
|
|
OS << " multiversion";
|
2019-06-14 08:56:20 +00:00
|
|
|
}
|
2019-01-19 09:05:55 +00:00
|
|
|
|
|
|
|
if (!isa<FunctionDecl>(*D)) {
|
|
|
|
const auto *MD = dyn_cast<ObjCMethodDecl>(D);
|
|
|
|
if (!MD || !MD->isThisDeclarationADefinition()) {
|
|
|
|
const auto *DC = dyn_cast<DeclContext>(D);
|
|
|
|
if (DC && DC->hasExternalLexicalStorage()) {
|
|
|
|
ColorScope Color(OS, ShowColors, UndeserializedColor);
|
|
|
|
OS << " <undeserialized declarations>";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2019-01-30 19:32:48 +00:00
|
|
|
|
2023-08-17 22:18:21 +00:00
|
|
|
switch (D->getFriendObjectKind()) {
|
|
|
|
case Decl::FOK_None:
|
|
|
|
break;
|
|
|
|
case Decl::FOK_Declared:
|
|
|
|
OS << " friend";
|
|
|
|
break;
|
|
|
|
case Decl::FOK_Undeclared:
|
|
|
|
OS << " friend_undeclared";
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2019-01-30 19:32:48 +00:00
|
|
|
ConstDeclVisitor<TextNodeDumper>::Visit(D);
|
2019-01-15 09:35:52 +00:00
|
|
|
}
|
|
|
|
|
2019-01-15 20:17:33 +00:00
|
|
|
void TextNodeDumper::Visit(const CXXCtorInitializer *Init) {
|
|
|
|
OS << "CXXCtorInitializer";
|
|
|
|
if (Init->isAnyMemberInitializer()) {
|
|
|
|
OS << ' ';
|
|
|
|
dumpBareDeclRef(Init->getAnyMember());
|
|
|
|
} else if (Init->isBaseInitializer()) {
|
|
|
|
dumpType(QualType(Init->getBaseClass(), 0));
|
|
|
|
} else if (Init->isDelegatingInitializer()) {
|
|
|
|
dumpType(Init->getTypeSourceInfo()->getType());
|
|
|
|
} else {
|
|
|
|
llvm_unreachable("Unknown initializer type");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-01-15 20:41:37 +00:00
|
|
|
void TextNodeDumper::Visit(const BlockDecl::Capture &C) {
|
|
|
|
OS << "capture";
|
|
|
|
if (C.isByRef())
|
|
|
|
OS << " byref";
|
|
|
|
if (C.isNested())
|
|
|
|
OS << " nested";
|
|
|
|
if (C.getVariable()) {
|
|
|
|
OS << ' ';
|
|
|
|
dumpBareDeclRef(C.getVariable());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-01-15 20:31:31 +00:00
|
|
|
void TextNodeDumper::Visit(const OMPClause *C) {
|
|
|
|
if (!C) {
|
|
|
|
ColorScope Color(OS, ShowColors, NullColor);
|
|
|
|
OS << "<<<NULL>>> OMPClause";
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
{
|
|
|
|
ColorScope Color(OS, ShowColors, AttrColor);
|
2020-03-30 19:58:40 -05:00
|
|
|
StringRef ClauseName(llvm::omp::getOpenMPClauseName(C->getClauseKind()));
|
2019-01-15 20:31:31 +00:00
|
|
|
OS << "OMP" << ClauseName.substr(/*Start=*/0, /*N=*/1).upper()
|
|
|
|
<< ClauseName.drop_front() << "Clause";
|
|
|
|
}
|
|
|
|
dumpPointer(C);
|
|
|
|
dumpSourceRange(SourceRange(C->getBeginLoc(), C->getEndLoc()));
|
|
|
|
if (C->isImplicit())
|
|
|
|
OS << " <implicit>";
|
|
|
|
}
|
|
|
|
|
2024-10-03 08:34:43 -07:00
|
|
|
void TextNodeDumper::VisitOpenACCAsteriskSizeExpr(
|
|
|
|
const OpenACCAsteriskSizeExpr *E) {
|
|
|
|
// Nothing to do here, only location exists, and that is printed elsewhere.
|
|
|
|
}
|
|
|
|
|
2024-04-05 10:06:44 -07:00
|
|
|
void TextNodeDumper::Visit(const OpenACCClause *C) {
|
|
|
|
if (!C) {
|
|
|
|
ColorScope Color(OS, ShowColors, NullColor);
|
|
|
|
OS << "<<<NULL>>> OpenACCClause";
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
{
|
|
|
|
ColorScope Color(OS, ShowColors, AttrColor);
|
|
|
|
OS << C->getClauseKind();
|
2024-04-10 07:10:24 -07:00
|
|
|
|
|
|
|
// Handle clauses with parens for types that have no children, likely
|
|
|
|
// because there is no sub expression.
|
|
|
|
switch (C->getClauseKind()) {
|
|
|
|
case OpenACCClauseKind::Default:
|
|
|
|
OS << '(' << cast<OpenACCDefaultClause>(C)->getDefaultClauseKind() << ')';
|
|
|
|
break;
|
2024-05-06 12:02:15 -07:00
|
|
|
case OpenACCClauseKind::Async:
|
2024-05-31 07:57:24 -07:00
|
|
|
case OpenACCClauseKind::Auto:
|
2024-05-03 08:54:06 -07:00
|
|
|
case OpenACCClauseKind::Attach:
|
2024-05-02 10:49:14 -07:00
|
|
|
case OpenACCClauseKind::Copy:
|
|
|
|
case OpenACCClauseKind::PCopy:
|
|
|
|
case OpenACCClauseKind::PresentOrCopy:
|
2025-01-09 07:15:05 -08:00
|
|
|
case OpenACCClauseKind::Host:
|
2024-04-12 14:13:31 -07:00
|
|
|
case OpenACCClauseKind::If:
|
2024-12-13 10:55:34 -08:00
|
|
|
case OpenACCClauseKind::IfPresent:
|
2024-05-31 07:57:24 -07:00
|
|
|
case OpenACCClauseKind::Independent:
|
2024-12-13 13:49:02 -08:00
|
|
|
case OpenACCClauseKind::Detach:
|
2024-12-16 06:27:59 -08:00
|
|
|
case OpenACCClauseKind::Delete:
|
2025-01-09 07:15:05 -08:00
|
|
|
case OpenACCClauseKind::Device:
|
2024-12-19 09:41:07 -08:00
|
|
|
case OpenACCClauseKind::DeviceNum:
|
2025-01-06 10:31:30 -08:00
|
|
|
case OpenACCClauseKind::DefaultAsync:
|
2025-01-21 11:27:23 -08:00
|
|
|
case OpenACCClauseKind::DeviceResident:
|
2024-05-03 08:54:06 -07:00
|
|
|
case OpenACCClauseKind::DevicePtr:
|
2024-12-13 09:34:25 -08:00
|
|
|
case OpenACCClauseKind::Finalize:
|
2024-05-02 07:13:24 -07:00
|
|
|
case OpenACCClauseKind::FirstPrivate:
|
2025-01-21 11:27:23 -08:00
|
|
|
case OpenACCClauseKind::Link:
|
2024-05-02 08:10:57 -07:00
|
|
|
case OpenACCClauseKind::NoCreate:
|
2025-03-06 12:15:27 -08:00
|
|
|
case OpenACCClauseKind::NoHost:
|
2024-04-22 08:57:25 -07:00
|
|
|
case OpenACCClauseKind::NumGangs:
|
2024-04-18 12:42:22 -07:00
|
|
|
case OpenACCClauseKind::NumWorkers:
|
2024-05-02 08:10:57 -07:00
|
|
|
case OpenACCClauseKind::Present:
|
2024-04-30 11:28:37 -07:00
|
|
|
case OpenACCClauseKind::Private:
|
2024-05-02 07:13:24 -07:00
|
|
|
case OpenACCClauseKind::Self:
|
2024-05-31 07:57:24 -07:00
|
|
|
case OpenACCClauseKind::Seq:
|
2024-10-03 08:34:43 -07:00
|
|
|
case OpenACCClauseKind::Tile:
|
2024-10-14 09:08:24 -07:00
|
|
|
case OpenACCClauseKind::Worker:
|
2024-12-16 09:07:10 -08:00
|
|
|
case OpenACCClauseKind::UseDevice:
|
2024-10-15 06:12:19 -07:00
|
|
|
case OpenACCClauseKind::Vector:
|
2024-04-18 12:42:22 -07:00
|
|
|
case OpenACCClauseKind::VectorLength:
|
2024-04-12 14:13:31 -07:00
|
|
|
// The condition expression will be printed as a part of the 'children',
|
|
|
|
// but print 'clause' here so it is clear what is happening from the dump.
|
|
|
|
OS << " clause";
|
|
|
|
break;
|
2024-10-11 09:05:19 -07:00
|
|
|
case OpenACCClauseKind::Gang: {
|
|
|
|
OS << " clause";
|
|
|
|
// print the list of all GangKinds, so that there is some sort of
|
|
|
|
// relationship to the expressions listed afterwards.
|
|
|
|
auto *GC = cast<OpenACCGangClause>(C);
|
|
|
|
|
|
|
|
for (unsigned I = 0; I < GC->getNumExprs(); ++I) {
|
|
|
|
OS << " " << GC->getExpr(I).first;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
2024-10-01 06:40:21 -07:00
|
|
|
case OpenACCClauseKind::Collapse:
|
|
|
|
OS << " clause";
|
|
|
|
if (cast<OpenACCCollapseClause>(C)->hasForce())
|
|
|
|
OS << ": force";
|
|
|
|
break;
|
|
|
|
|
2024-05-02 14:11:17 -07:00
|
|
|
case OpenACCClauseKind::CopyIn:
|
|
|
|
case OpenACCClauseKind::PCopyIn:
|
|
|
|
case OpenACCClauseKind::PresentOrCopyIn:
|
|
|
|
OS << " clause";
|
|
|
|
if (cast<OpenACCCopyInClause>(C)->isReadOnly())
|
|
|
|
OS << " : readonly";
|
|
|
|
break;
|
|
|
|
case OpenACCClauseKind::CopyOut:
|
|
|
|
case OpenACCClauseKind::PCopyOut:
|
|
|
|
case OpenACCClauseKind::PresentOrCopyOut:
|
|
|
|
OS << " clause";
|
|
|
|
if (cast<OpenACCCopyOutClause>(C)->isZero())
|
|
|
|
OS << " : zero";
|
|
|
|
break;
|
|
|
|
case OpenACCClauseKind::Create:
|
|
|
|
case OpenACCClauseKind::PCreate:
|
|
|
|
case OpenACCClauseKind::PresentOrCreate:
|
|
|
|
OS << " clause";
|
|
|
|
if (cast<OpenACCCreateClause>(C)->isZero())
|
|
|
|
OS << " : zero";
|
|
|
|
break;
|
2024-05-07 13:30:07 -07:00
|
|
|
case OpenACCClauseKind::Wait:
|
|
|
|
OS << " clause";
|
|
|
|
if (cast<OpenACCWaitClause>(C)->hasDevNumExpr())
|
|
|
|
OS << " has devnum";
|
|
|
|
if (cast<OpenACCWaitClause>(C)->hasQueuesTag())
|
|
|
|
OS << " has queues tag";
|
|
|
|
break;
|
2024-05-13 08:43:15 -07:00
|
|
|
case OpenACCClauseKind::DeviceType:
|
|
|
|
case OpenACCClauseKind::DType:
|
|
|
|
OS << "(";
|
|
|
|
llvm::interleaveComma(
|
|
|
|
cast<OpenACCDeviceTypeClause>(C)->getArchitectures(), OS,
|
|
|
|
[&](const DeviceTypeArgument &Arch) {
|
|
|
|
if (Arch.first == nullptr)
|
|
|
|
OS << "*";
|
|
|
|
else
|
|
|
|
OS << Arch.first->getName();
|
|
|
|
});
|
|
|
|
OS << ")";
|
|
|
|
break;
|
2024-05-21 06:51:25 -07:00
|
|
|
case OpenACCClauseKind::Reduction:
|
|
|
|
OS << " clause Operator: "
|
|
|
|
<< cast<OpenACCReductionClause>(C)->getReductionOp();
|
|
|
|
break;
|
2024-04-10 07:10:24 -07:00
|
|
|
default:
|
|
|
|
// Nothing to do here.
|
|
|
|
break;
|
|
|
|
}
|
2024-04-05 10:06:44 -07:00
|
|
|
}
|
|
|
|
dumpPointer(C);
|
|
|
|
dumpSourceRange(SourceRange(C->getBeginLoc(), C->getEndLoc()));
|
|
|
|
}
|
|
|
|
|
2019-01-29 22:22:55 +00:00
|
|
|
void TextNodeDumper::Visit(const GenericSelectionExpr::ConstAssociation &A) {
|
|
|
|
const TypeSourceInfo *TSI = A.getTypeSourceInfo();
|
|
|
|
if (TSI) {
|
|
|
|
OS << "case ";
|
|
|
|
dumpType(TSI->getType());
|
|
|
|
} else {
|
|
|
|
OS << "default";
|
|
|
|
}
|
|
|
|
|
|
|
|
if (A.isSelected())
|
|
|
|
OS << " selected";
|
|
|
|
}
|
|
|
|
|
2023-08-31 20:11:28 +02:00
|
|
|
void TextNodeDumper::Visit(const ConceptReference *R) {
|
|
|
|
if (!R) {
|
|
|
|
ColorScope Color(OS, ShowColors, NullColor);
|
|
|
|
OS << "<<<NULL>>> ConceptReference";
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
OS << "ConceptReference";
|
|
|
|
dumpPointer(R);
|
|
|
|
dumpSourceRange(R->getSourceRange());
|
|
|
|
OS << ' ';
|
|
|
|
dumpBareDeclRef(R->getNamedConcept());
|
|
|
|
}
|
|
|
|
|
2021-06-30 14:05:34 -07:00
|
|
|
void TextNodeDumper::Visit(const concepts::Requirement *R) {
|
|
|
|
if (!R) {
|
|
|
|
ColorScope Color(OS, ShowColors, NullColor);
|
|
|
|
OS << "<<<NULL>>> Requirement";
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
{
|
|
|
|
ColorScope Color(OS, ShowColors, StmtColor);
|
|
|
|
switch (R->getKind()) {
|
|
|
|
case concepts::Requirement::RK_Type:
|
|
|
|
OS << "TypeRequirement";
|
|
|
|
break;
|
|
|
|
case concepts::Requirement::RK_Simple:
|
|
|
|
OS << "SimpleRequirement";
|
|
|
|
break;
|
|
|
|
case concepts::Requirement::RK_Compound:
|
|
|
|
OS << "CompoundRequirement";
|
|
|
|
break;
|
|
|
|
case concepts::Requirement::RK_Nested:
|
|
|
|
OS << "NestedRequirement";
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
dumpPointer(R);
|
|
|
|
|
|
|
|
if (auto *ER = dyn_cast<concepts::ExprRequirement>(R)) {
|
|
|
|
if (ER->hasNoexceptRequirement())
|
|
|
|
OS << " noexcept";
|
|
|
|
}
|
|
|
|
|
|
|
|
if (R->isDependent())
|
|
|
|
OS << " dependent";
|
|
|
|
else
|
|
|
|
OS << (R->isSatisfied() ? " satisfied" : " unsatisfied");
|
|
|
|
if (R->containsUnexpandedParameterPack())
|
|
|
|
OS << " contains_unexpanded_pack";
|
|
|
|
}
|
|
|
|
|
[clang] Rework how and when APValues are dumped
Currently APValues are dumped as a single string. This becomes quickly
completely unreadable since APValue is a tree-like structure. Even a simple
example is not pretty:
struct S { int arr[4]; float f; };
constexpr S s = { .arr = {1,2}, .f = 3.1415f };
// Struct fields: Array: Int: 1, Int: 2, 2 x Int: 0, Float: 3.141500e+00
With this patch this becomes:
-Struct
|-field: Array size=4
| |-elements: Int 1, Int 2
| `-filler: 2 x Int 0
`-field: Float 3.141500e+00
Additionally APValues are currently only dumped as part of visiting a
ConstantExpr. This patch also dump the value of the initializer of constexpr
variable declarations:
constexpr int foo(int a, int b) { return a + b - 42; }
constexpr int a = 1, b = 2;
constexpr int c = foo(a, b) > 0 ? foo(a, b) : foo(b, a);
// VarDecl 0x62100008aec8 <col:3, col:57> col:17 c 'const int' constexpr cinit
// |-value: Int -39
// `-ConditionalOperator 0x62100008b4d0 <col:21, col:57> 'int'
// <snip>
Do the above by moving the dump functions to TextNodeDumper which already has
the machinery to display trees. The cases APValue::LValue, APValue::MemberPointer
and APValue::AddrLabelDiff are left as they were before (unimplemented).
We try to display multiple elements on the same line if they are considered to
be "simple". This is to avoid wasting large amounts of vertical space in an
example like:
constexpr int arr[8] = {0,1,2,3,4,5,6,7};
// VarDecl 0x62100008bb78 <col:3, col:42> col:17 arr 'int const[8]' constexpr cinit
// |-value: Array size=8
// | |-elements: Int 0, Int 1, Int 2, Int 3
// | `-elements: Int 4, Int 5, Int 6, Int 7
Differential Revision: https://reviews.llvm.org/D83183
Reviewed By: aaron.ballman
2020-07-06 21:50:23 +01:00
|
|
|
static double GetApproxValue(const llvm::APFloat &F) {
|
|
|
|
llvm::APFloat V = F;
|
|
|
|
bool ignored;
|
|
|
|
V.convert(llvm::APFloat::IEEEdouble(), llvm::APFloat::rmNearestTiesToEven,
|
|
|
|
&ignored);
|
|
|
|
return V.convertToDouble();
|
|
|
|
}
|
|
|
|
|
|
|
|
/// True if the \p APValue \p Value can be folded onto the current line.
|
|
|
|
static bool isSimpleAPValue(const APValue &Value) {
|
|
|
|
switch (Value.getKind()) {
|
|
|
|
case APValue::None:
|
|
|
|
case APValue::Indeterminate:
|
|
|
|
case APValue::Int:
|
|
|
|
case APValue::Float:
|
|
|
|
case APValue::FixedPoint:
|
|
|
|
case APValue::ComplexInt:
|
|
|
|
case APValue::ComplexFloat:
|
|
|
|
case APValue::LValue:
|
|
|
|
case APValue::MemberPointer:
|
|
|
|
case APValue::AddrLabelDiff:
|
|
|
|
return true;
|
|
|
|
case APValue::Vector:
|
|
|
|
case APValue::Array:
|
|
|
|
case APValue::Struct:
|
|
|
|
return false;
|
|
|
|
case APValue::Union:
|
|
|
|
return isSimpleAPValue(Value.getUnionValue());
|
|
|
|
}
|
|
|
|
llvm_unreachable("unexpected APValue kind!");
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Dump the children of the \p APValue \p Value.
|
|
|
|
///
|
|
|
|
/// \param[in] Value The \p APValue to visit
|
|
|
|
/// \param[in] Ty The \p QualType passed to \p Visit
|
|
|
|
///
|
|
|
|
/// \param[in] IdxToChildFun A function mapping an \p APValue and an index
|
|
|
|
/// to one of the child of the \p APValue
|
|
|
|
///
|
|
|
|
/// \param[in] NumChildren \p IdxToChildFun will be called on \p Value with
|
|
|
|
/// the indices in the range \p [0,NumChildren(
|
|
|
|
///
|
|
|
|
/// \param[in] LabelSingular The label to use on a line with a single child
|
|
|
|
/// \param[in] LabelPlurial The label to use on a line with multiple children
|
|
|
|
void TextNodeDumper::dumpAPValueChildren(
|
|
|
|
const APValue &Value, QualType Ty,
|
|
|
|
const APValue &(*IdxToChildFun)(const APValue &, unsigned),
|
|
|
|
unsigned NumChildren, StringRef LabelSingular, StringRef LabelPlurial) {
|
|
|
|
// To save some vertical space we print up to MaxChildrenPerLine APValues
|
|
|
|
// considered to be simple (by isSimpleAPValue) on a single line.
|
|
|
|
constexpr unsigned MaxChildrenPerLine = 4;
|
|
|
|
unsigned I = 0;
|
|
|
|
while (I < NumChildren) {
|
|
|
|
unsigned J = I;
|
|
|
|
while (J < NumChildren) {
|
|
|
|
if (isSimpleAPValue(IdxToChildFun(Value, J)) &&
|
|
|
|
(J - I < MaxChildrenPerLine)) {
|
|
|
|
++J;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
J = std::max(I + 1, J);
|
|
|
|
|
|
|
|
// Print [I,J) on a single line.
|
|
|
|
AddChild(J - I > 1 ? LabelPlurial : LabelSingular, [=]() {
|
|
|
|
for (unsigned X = I; X < J; ++X) {
|
|
|
|
Visit(IdxToChildFun(Value, X), Ty);
|
|
|
|
if (X + 1 != J)
|
|
|
|
OS << ", ";
|
|
|
|
}
|
|
|
|
});
|
|
|
|
I = J;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void TextNodeDumper::Visit(const APValue &Value, QualType Ty) {
|
|
|
|
ColorScope Color(OS, ShowColors, ValueKindColor);
|
|
|
|
switch (Value.getKind()) {
|
|
|
|
case APValue::None:
|
|
|
|
OS << "None";
|
|
|
|
return;
|
|
|
|
case APValue::Indeterminate:
|
|
|
|
OS << "Indeterminate";
|
|
|
|
return;
|
|
|
|
case APValue::Int:
|
|
|
|
OS << "Int ";
|
|
|
|
{
|
|
|
|
ColorScope Color(OS, ShowColors, ValueColor);
|
|
|
|
OS << Value.getInt();
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
case APValue::Float:
|
|
|
|
OS << "Float ";
|
|
|
|
{
|
|
|
|
ColorScope Color(OS, ShowColors, ValueColor);
|
|
|
|
OS << GetApproxValue(Value.getFloat());
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
case APValue::FixedPoint:
|
|
|
|
OS << "FixedPoint ";
|
|
|
|
{
|
|
|
|
ColorScope Color(OS, ShowColors, ValueColor);
|
|
|
|
OS << Value.getFixedPoint();
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
case APValue::Vector: {
|
|
|
|
unsigned VectorLength = Value.getVectorLength();
|
|
|
|
OS << "Vector length=" << VectorLength;
|
|
|
|
|
|
|
|
dumpAPValueChildren(
|
|
|
|
Value, Ty,
|
|
|
|
[](const APValue &Value, unsigned Index) -> const APValue & {
|
|
|
|
return Value.getVectorElt(Index);
|
|
|
|
},
|
|
|
|
VectorLength, "element", "elements");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
case APValue::ComplexInt:
|
|
|
|
OS << "ComplexInt ";
|
|
|
|
{
|
|
|
|
ColorScope Color(OS, ShowColors, ValueColor);
|
|
|
|
OS << Value.getComplexIntReal() << " + " << Value.getComplexIntImag()
|
|
|
|
<< 'i';
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
case APValue::ComplexFloat:
|
|
|
|
OS << "ComplexFloat ";
|
|
|
|
{
|
|
|
|
ColorScope Color(OS, ShowColors, ValueColor);
|
|
|
|
OS << GetApproxValue(Value.getComplexFloatReal()) << " + "
|
|
|
|
<< GetApproxValue(Value.getComplexFloatImag()) << 'i';
|
|
|
|
}
|
|
|
|
return;
|
2025-01-27 11:54:06 +01:00
|
|
|
case APValue::LValue: {
|
[clang] Rework how and when APValues are dumped
Currently APValues are dumped as a single string. This becomes quickly
completely unreadable since APValue is a tree-like structure. Even a simple
example is not pretty:
struct S { int arr[4]; float f; };
constexpr S s = { .arr = {1,2}, .f = 3.1415f };
// Struct fields: Array: Int: 1, Int: 2, 2 x Int: 0, Float: 3.141500e+00
With this patch this becomes:
-Struct
|-field: Array size=4
| |-elements: Int 1, Int 2
| `-filler: 2 x Int 0
`-field: Float 3.141500e+00
Additionally APValues are currently only dumped as part of visiting a
ConstantExpr. This patch also dump the value of the initializer of constexpr
variable declarations:
constexpr int foo(int a, int b) { return a + b - 42; }
constexpr int a = 1, b = 2;
constexpr int c = foo(a, b) > 0 ? foo(a, b) : foo(b, a);
// VarDecl 0x62100008aec8 <col:3, col:57> col:17 c 'const int' constexpr cinit
// |-value: Int -39
// `-ConditionalOperator 0x62100008b4d0 <col:21, col:57> 'int'
// <snip>
Do the above by moving the dump functions to TextNodeDumper which already has
the machinery to display trees. The cases APValue::LValue, APValue::MemberPointer
and APValue::AddrLabelDiff are left as they were before (unimplemented).
We try to display multiple elements on the same line if they are considered to
be "simple". This is to avoid wasting large amounts of vertical space in an
example like:
constexpr int arr[8] = {0,1,2,3,4,5,6,7};
// VarDecl 0x62100008bb78 <col:3, col:42> col:17 arr 'int const[8]' constexpr cinit
// |-value: Array size=8
// | |-elements: Int 0, Int 1, Int 2, Int 3
// | `-elements: Int 4, Int 5, Int 6, Int 7
Differential Revision: https://reviews.llvm.org/D83183
Reviewed By: aaron.ballman
2020-07-06 21:50:23 +01:00
|
|
|
(void)Context;
|
2025-01-27 11:54:06 +01:00
|
|
|
OS << "LValue Base=";
|
|
|
|
APValue::LValueBase B = Value.getLValueBase();
|
|
|
|
if (B.isNull())
|
|
|
|
OS << "null";
|
|
|
|
else if (const auto *BE = B.dyn_cast<const Expr *>()) {
|
|
|
|
OS << BE->getStmtClassName() << ' ';
|
|
|
|
dumpPointer(BE);
|
|
|
|
} else {
|
|
|
|
const auto *VDB = B.get<const ValueDecl *>();
|
|
|
|
OS << VDB->getDeclKindName() << "Decl";
|
|
|
|
dumpPointer(VDB);
|
|
|
|
}
|
|
|
|
OS << ", Null=" << Value.isNullPointer()
|
|
|
|
<< ", Offset=" << Value.getLValueOffset().getQuantity()
|
|
|
|
<< ", HasPath=" << Value.hasLValuePath();
|
|
|
|
if (Value.hasLValuePath()) {
|
|
|
|
OS << ", PathLength=" << Value.getLValuePath().size();
|
|
|
|
OS << ", Path=(";
|
|
|
|
llvm::ListSeparator Sep;
|
|
|
|
for (const auto &PathEntry : Value.getLValuePath()) {
|
|
|
|
// We're printing all entries as array indices because don't have the
|
|
|
|
// type information here to do anything else.
|
|
|
|
OS << Sep << PathEntry.getAsArrayIndex();
|
|
|
|
}
|
|
|
|
OS << ")";
|
|
|
|
}
|
[clang] Rework how and when APValues are dumped
Currently APValues are dumped as a single string. This becomes quickly
completely unreadable since APValue is a tree-like structure. Even a simple
example is not pretty:
struct S { int arr[4]; float f; };
constexpr S s = { .arr = {1,2}, .f = 3.1415f };
// Struct fields: Array: Int: 1, Int: 2, 2 x Int: 0, Float: 3.141500e+00
With this patch this becomes:
-Struct
|-field: Array size=4
| |-elements: Int 1, Int 2
| `-filler: 2 x Int 0
`-field: Float 3.141500e+00
Additionally APValues are currently only dumped as part of visiting a
ConstantExpr. This patch also dump the value of the initializer of constexpr
variable declarations:
constexpr int foo(int a, int b) { return a + b - 42; }
constexpr int a = 1, b = 2;
constexpr int c = foo(a, b) > 0 ? foo(a, b) : foo(b, a);
// VarDecl 0x62100008aec8 <col:3, col:57> col:17 c 'const int' constexpr cinit
// |-value: Int -39
// `-ConditionalOperator 0x62100008b4d0 <col:21, col:57> 'int'
// <snip>
Do the above by moving the dump functions to TextNodeDumper which already has
the machinery to display trees. The cases APValue::LValue, APValue::MemberPointer
and APValue::AddrLabelDiff are left as they were before (unimplemented).
We try to display multiple elements on the same line if they are considered to
be "simple". This is to avoid wasting large amounts of vertical space in an
example like:
constexpr int arr[8] = {0,1,2,3,4,5,6,7};
// VarDecl 0x62100008bb78 <col:3, col:42> col:17 arr 'int const[8]' constexpr cinit
// |-value: Array size=8
// | |-elements: Int 0, Int 1, Int 2, Int 3
// | `-elements: Int 4, Int 5, Int 6, Int 7
Differential Revision: https://reviews.llvm.org/D83183
Reviewed By: aaron.ballman
2020-07-06 21:50:23 +01:00
|
|
|
return;
|
2025-01-27 11:54:06 +01:00
|
|
|
}
|
[clang] Rework how and when APValues are dumped
Currently APValues are dumped as a single string. This becomes quickly
completely unreadable since APValue is a tree-like structure. Even a simple
example is not pretty:
struct S { int arr[4]; float f; };
constexpr S s = { .arr = {1,2}, .f = 3.1415f };
// Struct fields: Array: Int: 1, Int: 2, 2 x Int: 0, Float: 3.141500e+00
With this patch this becomes:
-Struct
|-field: Array size=4
| |-elements: Int 1, Int 2
| `-filler: 2 x Int 0
`-field: Float 3.141500e+00
Additionally APValues are currently only dumped as part of visiting a
ConstantExpr. This patch also dump the value of the initializer of constexpr
variable declarations:
constexpr int foo(int a, int b) { return a + b - 42; }
constexpr int a = 1, b = 2;
constexpr int c = foo(a, b) > 0 ? foo(a, b) : foo(b, a);
// VarDecl 0x62100008aec8 <col:3, col:57> col:17 c 'const int' constexpr cinit
// |-value: Int -39
// `-ConditionalOperator 0x62100008b4d0 <col:21, col:57> 'int'
// <snip>
Do the above by moving the dump functions to TextNodeDumper which already has
the machinery to display trees. The cases APValue::LValue, APValue::MemberPointer
and APValue::AddrLabelDiff are left as they were before (unimplemented).
We try to display multiple elements on the same line if they are considered to
be "simple". This is to avoid wasting large amounts of vertical space in an
example like:
constexpr int arr[8] = {0,1,2,3,4,5,6,7};
// VarDecl 0x62100008bb78 <col:3, col:42> col:17 arr 'int const[8]' constexpr cinit
// |-value: Array size=8
// | |-elements: Int 0, Int 1, Int 2, Int 3
// | `-elements: Int 4, Int 5, Int 6, Int 7
Differential Revision: https://reviews.llvm.org/D83183
Reviewed By: aaron.ballman
2020-07-06 21:50:23 +01:00
|
|
|
case APValue::Array: {
|
|
|
|
unsigned ArraySize = Value.getArraySize();
|
|
|
|
unsigned NumInitializedElements = Value.getArrayInitializedElts();
|
|
|
|
OS << "Array size=" << ArraySize;
|
|
|
|
|
|
|
|
dumpAPValueChildren(
|
|
|
|
Value, Ty,
|
|
|
|
[](const APValue &Value, unsigned Index) -> const APValue & {
|
|
|
|
return Value.getArrayInitializedElt(Index);
|
|
|
|
},
|
|
|
|
NumInitializedElements, "element", "elements");
|
|
|
|
|
|
|
|
if (Value.hasArrayFiller()) {
|
|
|
|
AddChild("filler", [=] {
|
|
|
|
{
|
|
|
|
ColorScope Color(OS, ShowColors, ValueColor);
|
|
|
|
OS << ArraySize - NumInitializedElements << " x ";
|
|
|
|
}
|
|
|
|
Visit(Value.getArrayFiller(), Ty);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
case APValue::Struct: {
|
|
|
|
OS << "Struct";
|
|
|
|
|
|
|
|
dumpAPValueChildren(
|
|
|
|
Value, Ty,
|
|
|
|
[](const APValue &Value, unsigned Index) -> const APValue & {
|
|
|
|
return Value.getStructBase(Index);
|
|
|
|
},
|
|
|
|
Value.getStructNumBases(), "base", "bases");
|
|
|
|
|
|
|
|
dumpAPValueChildren(
|
|
|
|
Value, Ty,
|
|
|
|
[](const APValue &Value, unsigned Index) -> const APValue & {
|
|
|
|
return Value.getStructField(Index);
|
|
|
|
},
|
|
|
|
Value.getStructNumFields(), "field", "fields");
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
case APValue::Union: {
|
|
|
|
OS << "Union";
|
|
|
|
{
|
|
|
|
ColorScope Color(OS, ShowColors, ValueColor);
|
|
|
|
if (const FieldDecl *FD = Value.getUnionField())
|
|
|
|
OS << " ." << *cast<NamedDecl>(FD);
|
|
|
|
}
|
|
|
|
// If the union value is considered to be simple, fold it into the
|
|
|
|
// current line to save some vertical space.
|
|
|
|
const APValue &UnionValue = Value.getUnionValue();
|
|
|
|
if (isSimpleAPValue(UnionValue)) {
|
|
|
|
OS << ' ';
|
|
|
|
Visit(UnionValue, Ty);
|
|
|
|
} else {
|
|
|
|
AddChild([=] { Visit(UnionValue, Ty); });
|
|
|
|
}
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
case APValue::MemberPointer:
|
|
|
|
OS << "MemberPointer <todo>";
|
|
|
|
return;
|
|
|
|
case APValue::AddrLabelDiff:
|
|
|
|
OS << "AddrLabelDiff <todo>";
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
llvm_unreachable("Unknown APValue kind!");
|
|
|
|
}
|
|
|
|
|
2018-12-05 21:12:39 +00:00
|
|
|
void TextNodeDumper::dumpPointer(const void *Ptr) {
|
|
|
|
ColorScope Color(OS, ShowColors, AddressColor);
|
|
|
|
OS << ' ' << Ptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
void TextNodeDumper::dumpLocation(SourceLocation Loc) {
|
|
|
|
if (!SM)
|
|
|
|
return;
|
|
|
|
|
|
|
|
ColorScope Color(OS, ShowColors, LocationColor);
|
|
|
|
SourceLocation SpellingLoc = SM->getSpellingLoc(Loc);
|
|
|
|
|
|
|
|
// The general format we print out is filename:line:col, but we drop pieces
|
|
|
|
// that haven't changed since the last loc printed.
|
|
|
|
PresumedLoc PLoc = SM->getPresumedLoc(SpellingLoc);
|
|
|
|
|
|
|
|
if (PLoc.isInvalid()) {
|
|
|
|
OS << "<invalid sloc>";
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (strcmp(PLoc.getFilename(), LastLocFilename) != 0) {
|
|
|
|
OS << PLoc.getFilename() << ':' << PLoc.getLine() << ':'
|
|
|
|
<< PLoc.getColumn();
|
|
|
|
LastLocFilename = PLoc.getFilename();
|
|
|
|
LastLocLine = PLoc.getLine();
|
|
|
|
} else if (PLoc.getLine() != LastLocLine) {
|
|
|
|
OS << "line" << ':' << PLoc.getLine() << ':' << PLoc.getColumn();
|
|
|
|
LastLocLine = PLoc.getLine();
|
|
|
|
} else {
|
|
|
|
OS << "col" << ':' << PLoc.getColumn();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void TextNodeDumper::dumpSourceRange(SourceRange R) {
|
|
|
|
// Can't translate locations if a SourceManager isn't available.
|
|
|
|
if (!SM)
|
|
|
|
return;
|
|
|
|
|
|
|
|
OS << " <";
|
|
|
|
dumpLocation(R.getBegin());
|
|
|
|
if (R.getBegin() != R.getEnd()) {
|
|
|
|
OS << ", ";
|
|
|
|
dumpLocation(R.getEnd());
|
|
|
|
}
|
|
|
|
OS << ">";
|
|
|
|
|
|
|
|
// <t2.c:123:421[blah], t2.c:412:321>
|
|
|
|
}
|
|
|
|
|
|
|
|
void TextNodeDumper::dumpBareType(QualType T, bool Desugar) {
|
|
|
|
ColorScope Color(OS, ShowColors, TypeColor);
|
|
|
|
|
|
|
|
SplitQualType T_split = T.split();
|
2023-10-26 19:28:28 +01:00
|
|
|
std::string T_str = QualType::getAsString(T_split, PrintPolicy);
|
|
|
|
OS << "'" << T_str << "'";
|
2018-12-05 21:12:39 +00:00
|
|
|
|
|
|
|
if (Desugar && !T.isNull()) {
|
2023-10-26 19:28:28 +01:00
|
|
|
// If the type is sugared, also dump a (shallow) desugared type when
|
|
|
|
// it is visibly different.
|
2018-12-05 21:12:39 +00:00
|
|
|
SplitQualType D_split = T.getSplitDesugaredType();
|
2023-10-26 19:28:28 +01:00
|
|
|
if (T_split != D_split) {
|
|
|
|
std::string D_str = QualType::getAsString(D_split, PrintPolicy);
|
|
|
|
if (T_str != D_str)
|
|
|
|
OS << ":'" << QualType::getAsString(D_split, PrintPolicy) << "'";
|
|
|
|
}
|
2018-12-05 21:12:39 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void TextNodeDumper::dumpType(QualType T) {
|
|
|
|
OS << ' ';
|
|
|
|
dumpBareType(T);
|
|
|
|
}
|
|
|
|
|
|
|
|
void TextNodeDumper::dumpBareDeclRef(const Decl *D) {
|
|
|
|
if (!D) {
|
|
|
|
ColorScope Color(OS, ShowColors, NullColor);
|
|
|
|
OS << "<<<NULL>>>";
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
{
|
|
|
|
ColorScope Color(OS, ShowColors, DeclKindNameColor);
|
|
|
|
OS << D->getDeclKindName();
|
|
|
|
}
|
|
|
|
dumpPointer(D);
|
|
|
|
|
|
|
|
if (const NamedDecl *ND = dyn_cast<NamedDecl>(D)) {
|
|
|
|
ColorScope Color(OS, ShowColors, DeclNameColor);
|
2025-01-29 00:31:53 -03:00
|
|
|
if (DeclarationName Name = ND->getDeclName())
|
|
|
|
OS << " '" << Name << '\'';
|
|
|
|
else
|
|
|
|
switch (ND->getKind()) {
|
|
|
|
case Decl::Decomposition: {
|
|
|
|
auto *DD = cast<DecompositionDecl>(ND);
|
|
|
|
OS << " first_binding '" << DD->bindings()[0]->getDeclName() << '\'';
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case Decl::Field: {
|
|
|
|
auto *FD = cast<FieldDecl>(ND);
|
|
|
|
OS << " field_index " << FD->getFieldIndex();
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case Decl::ParmVar: {
|
|
|
|
auto *PD = cast<ParmVarDecl>(ND);
|
|
|
|
OS << " depth " << PD->getFunctionScopeDepth() << " index "
|
|
|
|
<< PD->getFunctionScopeIndex();
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case Decl::TemplateTypeParm: {
|
|
|
|
auto *TD = cast<TemplateTypeParmDecl>(ND);
|
|
|
|
OS << " depth " << TD->getDepth() << " index " << TD->getIndex();
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case Decl::NonTypeTemplateParm: {
|
|
|
|
auto *TD = cast<NonTypeTemplateParmDecl>(ND);
|
|
|
|
OS << " depth " << TD->getDepth() << " index " << TD->getIndex();
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
// Var, Namespace, (CXX)Record: Nothing else besides source location.
|
|
|
|
dumpSourceRange(ND->getSourceRange());
|
|
|
|
break;
|
|
|
|
}
|
2018-12-05 21:12:39 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (const ValueDecl *VD = dyn_cast<ValueDecl>(D))
|
|
|
|
dumpType(VD->getType());
|
|
|
|
}
|
|
|
|
|
|
|
|
void TextNodeDumper::dumpName(const NamedDecl *ND) {
|
|
|
|
if (ND->getDeclName()) {
|
|
|
|
ColorScope Color(OS, ShowColors, DeclNameColor);
|
2020-08-05 11:48:09 +01:00
|
|
|
OS << ' ' << ND->getDeclName();
|
2018-12-05 21:12:39 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void TextNodeDumper::dumpAccessSpecifier(AccessSpecifier AS) {
|
2020-05-27 18:17:07 +02:00
|
|
|
const auto AccessSpelling = getAccessSpelling(AS);
|
|
|
|
if (AccessSpelling.empty())
|
|
|
|
return;
|
|
|
|
OS << AccessSpelling;
|
2018-12-05 21:12:39 +00:00
|
|
|
}
|
|
|
|
|
2020-03-10 14:06:25 -07:00
|
|
|
void TextNodeDumper::dumpCleanupObject(
|
|
|
|
const ExprWithCleanups::CleanupObject &C) {
|
2025-01-30 17:58:10 -08:00
|
|
|
if (auto *BD = dyn_cast<BlockDecl *>(C))
|
2020-03-10 14:06:25 -07:00
|
|
|
dumpDeclRef(BD, "cleanup");
|
2025-01-30 17:58:10 -08:00
|
|
|
else if (auto *CLE = dyn_cast<CompoundLiteralExpr *>(C))
|
2020-03-10 14:06:25 -07:00
|
|
|
AddChild([=] {
|
|
|
|
OS << "cleanup ";
|
|
|
|
{
|
|
|
|
ColorScope Color(OS, ShowColors, StmtColor);
|
|
|
|
OS << CLE->getStmtClassName();
|
|
|
|
}
|
|
|
|
dumpPointer(CLE);
|
|
|
|
});
|
|
|
|
else
|
|
|
|
llvm_unreachable("unexpected cleanup type");
|
|
|
|
}
|
|
|
|
|
2023-08-15 14:17:24 +00:00
|
|
|
void clang::TextNodeDumper::dumpTemplateSpecializationKind(
|
|
|
|
TemplateSpecializationKind TSK) {
|
|
|
|
switch (TSK) {
|
|
|
|
case TSK_Undeclared:
|
|
|
|
break;
|
|
|
|
case TSK_ImplicitInstantiation:
|
|
|
|
OS << " implicit_instantiation";
|
|
|
|
break;
|
|
|
|
case TSK_ExplicitSpecialization:
|
|
|
|
OS << " explicit_specialization";
|
|
|
|
break;
|
|
|
|
case TSK_ExplicitInstantiationDeclaration:
|
|
|
|
OS << " explicit_instantiation_declaration";
|
|
|
|
break;
|
|
|
|
case TSK_ExplicitInstantiationDefinition:
|
|
|
|
OS << " explicit_instantiation_definition";
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-08-15 16:26:47 +00:00
|
|
|
void clang::TextNodeDumper::dumpNestedNameSpecifier(const NestedNameSpecifier *NNS) {
|
|
|
|
if (!NNS)
|
|
|
|
return;
|
|
|
|
|
|
|
|
AddChild([=] {
|
|
|
|
OS << "NestedNameSpecifier";
|
|
|
|
|
|
|
|
switch (NNS->getKind()) {
|
|
|
|
case NestedNameSpecifier::Identifier:
|
|
|
|
OS << " Identifier";
|
|
|
|
OS << " '" << NNS->getAsIdentifier()->getName() << "'";
|
|
|
|
break;
|
|
|
|
case NestedNameSpecifier::Namespace:
|
2023-09-05 18:15:06 +02:00
|
|
|
OS << " "; // "Namespace" is printed as the decl kind.
|
2023-08-15 16:26:47 +00:00
|
|
|
dumpBareDeclRef(NNS->getAsNamespace());
|
|
|
|
break;
|
|
|
|
case NestedNameSpecifier::NamespaceAlias:
|
2023-09-05 18:15:06 +02:00
|
|
|
OS << " "; // "NamespaceAlias" is printed as the decl kind.
|
2023-08-15 16:26:47 +00:00
|
|
|
dumpBareDeclRef(NNS->getAsNamespaceAlias());
|
|
|
|
break;
|
|
|
|
case NestedNameSpecifier::TypeSpec:
|
|
|
|
OS << " TypeSpec";
|
|
|
|
dumpType(QualType(NNS->getAsType(), 0));
|
|
|
|
break;
|
|
|
|
case NestedNameSpecifier::TypeSpecWithTemplate:
|
|
|
|
OS << " TypeSpecWithTemplate";
|
|
|
|
dumpType(QualType(NNS->getAsType(), 0));
|
|
|
|
break;
|
|
|
|
case NestedNameSpecifier::Global:
|
|
|
|
OS << " Global";
|
|
|
|
break;
|
|
|
|
case NestedNameSpecifier::Super:
|
|
|
|
OS << " Super";
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
dumpNestedNameSpecifier(NNS->getPrefix());
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2019-01-12 15:45:05 +00:00
|
|
|
void TextNodeDumper::dumpDeclRef(const Decl *D, StringRef Label) {
|
2019-01-08 23:11:24 +00:00
|
|
|
if (!D)
|
|
|
|
return;
|
|
|
|
|
2019-01-10 20:58:21 +00:00
|
|
|
AddChild([=] {
|
2019-01-12 15:45:05 +00:00
|
|
|
if (!Label.empty())
|
2019-01-08 23:11:24 +00:00
|
|
|
OS << Label << ' ';
|
|
|
|
dumpBareDeclRef(D);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2024-05-29 15:23:44 -03:00
|
|
|
void TextNodeDumper::dumpTemplateArgument(const TemplateArgument &TA) {
|
|
|
|
llvm::SmallString<128> Str;
|
|
|
|
{
|
|
|
|
llvm::raw_svector_ostream SS(Str);
|
|
|
|
TA.print(PrintPolicy, SS, /*IncludeType=*/true);
|
|
|
|
}
|
|
|
|
OS << " '" << Str << "'";
|
|
|
|
|
2024-06-06 04:14:10 -03:00
|
|
|
if (!Context)
|
|
|
|
return;
|
|
|
|
|
2024-05-29 15:23:44 -03:00
|
|
|
if (TemplateArgument CanonTA = Context->getCanonicalTemplateArgument(TA);
|
|
|
|
!CanonTA.structurallyEquals(TA)) {
|
|
|
|
llvm::SmallString<128> CanonStr;
|
|
|
|
{
|
|
|
|
llvm::raw_svector_ostream SS(CanonStr);
|
|
|
|
CanonTA.print(PrintPolicy, SS, /*IncludeType=*/true);
|
|
|
|
}
|
|
|
|
if (CanonStr != Str)
|
|
|
|
OS << ":'" << CanonStr << "'";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-12-09 13:30:17 +00:00
|
|
|
const char *TextNodeDumper::getCommandName(unsigned CommandID) {
|
|
|
|
if (Traits)
|
|
|
|
return Traits->getCommandInfo(CommandID)->Name;
|
|
|
|
const comments::CommandInfo *Info =
|
|
|
|
comments::CommandTraits::getBuiltinCommandInfo(CommandID);
|
|
|
|
if (Info)
|
|
|
|
return Info->Name;
|
|
|
|
return "<not a builtin command>";
|
|
|
|
}
|
|
|
|
|
2020-07-24 12:04:19 +07:00
|
|
|
void TextNodeDumper::printFPOptions(FPOptionsOverride FPO) {
|
|
|
|
#define OPTION(NAME, TYPE, WIDTH, PREVIOUS) \
|
|
|
|
if (FPO.has##NAME##Override()) \
|
|
|
|
OS << " " #NAME "=" << FPO.get##NAME##Override();
|
|
|
|
#include "clang/Basic/FPOptions.def"
|
|
|
|
}
|
|
|
|
|
2018-12-09 13:30:17 +00:00
|
|
|
void TextNodeDumper::visitTextComment(const comments::TextComment *C,
|
|
|
|
const comments::FullComment *) {
|
|
|
|
OS << " Text=\"" << C->getText() << "\"";
|
|
|
|
}
|
|
|
|
|
|
|
|
void TextNodeDumper::visitInlineCommandComment(
|
|
|
|
const comments::InlineCommandComment *C, const comments::FullComment *) {
|
|
|
|
OS << " Name=\"" << getCommandName(C->getCommandID()) << "\"";
|
|
|
|
switch (C->getRenderKind()) {
|
2023-11-06 22:38:29 +03:00
|
|
|
case comments::InlineCommandRenderKind::Normal:
|
2018-12-09 13:30:17 +00:00
|
|
|
OS << " RenderNormal";
|
|
|
|
break;
|
2023-11-06 22:38:29 +03:00
|
|
|
case comments::InlineCommandRenderKind::Bold:
|
2018-12-09 13:30:17 +00:00
|
|
|
OS << " RenderBold";
|
|
|
|
break;
|
2023-11-06 22:38:29 +03:00
|
|
|
case comments::InlineCommandRenderKind::Monospaced:
|
2018-12-09 13:30:17 +00:00
|
|
|
OS << " RenderMonospaced";
|
|
|
|
break;
|
2023-11-06 22:38:29 +03:00
|
|
|
case comments::InlineCommandRenderKind::Emphasized:
|
2018-12-09 13:30:17 +00:00
|
|
|
OS << " RenderEmphasized";
|
|
|
|
break;
|
2023-11-06 22:38:29 +03:00
|
|
|
case comments::InlineCommandRenderKind::Anchor:
|
2019-12-21 14:47:52 +01:00
|
|
|
OS << " RenderAnchor";
|
|
|
|
break;
|
2018-12-09 13:30:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
for (unsigned i = 0, e = C->getNumArgs(); i != e; ++i)
|
|
|
|
OS << " Arg[" << i << "]=\"" << C->getArgText(i) << "\"";
|
|
|
|
}
|
|
|
|
|
|
|
|
void TextNodeDumper::visitHTMLStartTagComment(
|
|
|
|
const comments::HTMLStartTagComment *C, const comments::FullComment *) {
|
|
|
|
OS << " Name=\"" << C->getTagName() << "\"";
|
|
|
|
if (C->getNumAttrs() != 0) {
|
|
|
|
OS << " Attrs: ";
|
|
|
|
for (unsigned i = 0, e = C->getNumAttrs(); i != e; ++i) {
|
|
|
|
const comments::HTMLStartTagComment::Attribute &Attr = C->getAttr(i);
|
|
|
|
OS << " \"" << Attr.Name << "=\"" << Attr.Value << "\"";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (C->isSelfClosing())
|
|
|
|
OS << " SelfClosing";
|
|
|
|
}
|
|
|
|
|
|
|
|
void TextNodeDumper::visitHTMLEndTagComment(
|
|
|
|
const comments::HTMLEndTagComment *C, const comments::FullComment *) {
|
|
|
|
OS << " Name=\"" << C->getTagName() << "\"";
|
|
|
|
}
|
|
|
|
|
|
|
|
void TextNodeDumper::visitBlockCommandComment(
|
|
|
|
const comments::BlockCommandComment *C, const comments::FullComment *) {
|
|
|
|
OS << " Name=\"" << getCommandName(C->getCommandID()) << "\"";
|
|
|
|
for (unsigned i = 0, e = C->getNumArgs(); i != e; ++i)
|
|
|
|
OS << " Arg[" << i << "]=\"" << C->getArgText(i) << "\"";
|
|
|
|
}
|
|
|
|
|
|
|
|
void TextNodeDumper::visitParamCommandComment(
|
|
|
|
const comments::ParamCommandComment *C, const comments::FullComment *FC) {
|
|
|
|
OS << " "
|
|
|
|
<< comments::ParamCommandComment::getDirectionAsString(C->getDirection());
|
|
|
|
|
|
|
|
if (C->isDirectionExplicit())
|
|
|
|
OS << " explicitly";
|
|
|
|
else
|
|
|
|
OS << " implicitly";
|
|
|
|
|
|
|
|
if (C->hasParamName()) {
|
|
|
|
if (C->isParamIndexValid())
|
|
|
|
OS << " Param=\"" << C->getParamName(FC) << "\"";
|
|
|
|
else
|
|
|
|
OS << " Param=\"" << C->getParamNameAsWritten() << "\"";
|
|
|
|
}
|
|
|
|
|
|
|
|
if (C->isParamIndexValid() && !C->isVarArgParam())
|
|
|
|
OS << " ParamIndex=" << C->getParamIndex();
|
|
|
|
}
|
|
|
|
|
|
|
|
void TextNodeDumper::visitTParamCommandComment(
|
|
|
|
const comments::TParamCommandComment *C, const comments::FullComment *FC) {
|
|
|
|
if (C->hasParamName()) {
|
|
|
|
if (C->isPositionValid())
|
|
|
|
OS << " Param=\"" << C->getParamName(FC) << "\"";
|
|
|
|
else
|
|
|
|
OS << " Param=\"" << C->getParamNameAsWritten() << "\"";
|
|
|
|
}
|
|
|
|
|
|
|
|
if (C->isPositionValid()) {
|
|
|
|
OS << " Position=<";
|
|
|
|
for (unsigned i = 0, e = C->getDepth(); i != e; ++i) {
|
|
|
|
OS << C->getIndex(i);
|
|
|
|
if (i != e - 1)
|
|
|
|
OS << ", ";
|
|
|
|
}
|
|
|
|
OS << ">";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void TextNodeDumper::visitVerbatimBlockComment(
|
|
|
|
const comments::VerbatimBlockComment *C, const comments::FullComment *) {
|
|
|
|
OS << " Name=\"" << getCommandName(C->getCommandID())
|
|
|
|
<< "\""
|
|
|
|
" CloseName=\""
|
|
|
|
<< C->getCloseName() << "\"";
|
|
|
|
}
|
|
|
|
|
|
|
|
void TextNodeDumper::visitVerbatimBlockLineComment(
|
|
|
|
const comments::VerbatimBlockLineComment *C,
|
|
|
|
const comments::FullComment *) {
|
|
|
|
OS << " Text=\"" << C->getText() << "\"";
|
|
|
|
}
|
|
|
|
|
|
|
|
void TextNodeDumper::visitVerbatimLineComment(
|
|
|
|
const comments::VerbatimLineComment *C, const comments::FullComment *) {
|
|
|
|
OS << " Text=\"" << C->getText() << "\"";
|
|
|
|
}
|
2019-01-12 16:35:37 +00:00
|
|
|
|
|
|
|
void TextNodeDumper::VisitNullTemplateArgument(const TemplateArgument &) {
|
|
|
|
OS << " null";
|
|
|
|
}
|
|
|
|
|
|
|
|
void TextNodeDumper::VisitTypeTemplateArgument(const TemplateArgument &TA) {
|
|
|
|
OS << " type";
|
2024-05-29 15:23:44 -03:00
|
|
|
dumpTemplateArgument(TA);
|
2019-01-12 16:35:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void TextNodeDumper::VisitDeclarationTemplateArgument(
|
|
|
|
const TemplateArgument &TA) {
|
|
|
|
OS << " decl";
|
2024-05-29 15:23:44 -03:00
|
|
|
dumpTemplateArgument(TA);
|
2019-01-12 16:35:37 +00:00
|
|
|
dumpDeclRef(TA.getAsDecl());
|
|
|
|
}
|
|
|
|
|
2024-05-29 15:23:44 -03:00
|
|
|
void TextNodeDumper::VisitNullPtrTemplateArgument(const TemplateArgument &TA) {
|
2019-01-12 16:35:37 +00:00
|
|
|
OS << " nullptr";
|
2024-05-29 15:23:44 -03:00
|
|
|
dumpTemplateArgument(TA);
|
2019-01-12 16:35:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void TextNodeDumper::VisitIntegralTemplateArgument(const TemplateArgument &TA) {
|
2024-05-29 15:23:44 -03:00
|
|
|
OS << " integral";
|
|
|
|
dumpTemplateArgument(TA);
|
|
|
|
}
|
|
|
|
|
2025-02-13 14:06:45 +08:00
|
|
|
void TextNodeDumper::VisitStructuralValueTemplateArgument(
|
|
|
|
const TemplateArgument &TA) {
|
|
|
|
OS << " structural value";
|
|
|
|
dumpTemplateArgument(TA);
|
|
|
|
}
|
|
|
|
|
2024-05-30 04:48:45 -03:00
|
|
|
void TextNodeDumper::dumpTemplateName(TemplateName TN, StringRef Label) {
|
|
|
|
AddChild(Label, [=] {
|
|
|
|
{
|
|
|
|
llvm::SmallString<128> Str;
|
|
|
|
{
|
|
|
|
llvm::raw_svector_ostream SS(Str);
|
|
|
|
TN.print(SS, PrintPolicy);
|
|
|
|
}
|
2024-06-13 08:29:05 +02:00
|
|
|
OS << "'" << Str << "'";
|
2024-05-30 04:48:45 -03:00
|
|
|
|
2024-06-06 04:14:10 -03:00
|
|
|
if (Context) {
|
|
|
|
if (TemplateName CanonTN = Context->getCanonicalTemplateName(TN);
|
|
|
|
CanonTN != TN) {
|
|
|
|
llvm::SmallString<128> CanonStr;
|
|
|
|
{
|
|
|
|
llvm::raw_svector_ostream SS(CanonStr);
|
|
|
|
CanonTN.print(SS, PrintPolicy);
|
|
|
|
}
|
|
|
|
if (CanonStr != Str)
|
|
|
|
OS << ":'" << CanonStr << "'";
|
2024-05-30 04:48:45 -03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
dumpBareTemplateName(TN);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
void TextNodeDumper::dumpBareTemplateName(TemplateName TN) {
|
2024-05-29 15:23:44 -03:00
|
|
|
switch (TN.getKind()) {
|
|
|
|
case TemplateName::Template:
|
|
|
|
AddChild([=] { Visit(TN.getAsTemplateDecl()); });
|
|
|
|
return;
|
|
|
|
case TemplateName::UsingTemplate: {
|
|
|
|
const UsingShadowDecl *USD = TN.getAsUsingShadowDecl();
|
|
|
|
AddChild([=] { Visit(USD); });
|
|
|
|
AddChild("target", [=] { Visit(USD->getTargetDecl()); });
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
case TemplateName::QualifiedTemplate: {
|
|
|
|
OS << " qualified";
|
|
|
|
const QualifiedTemplateName *QTN = TN.getAsQualifiedTemplateName();
|
|
|
|
if (QTN->hasTemplateKeyword())
|
|
|
|
OS << " keyword";
|
|
|
|
dumpNestedNameSpecifier(QTN->getQualifier());
|
2024-05-30 04:48:45 -03:00
|
|
|
dumpBareTemplateName(QTN->getUnderlyingTemplate());
|
2024-05-29 15:23:44 -03:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
case TemplateName::DependentTemplate: {
|
|
|
|
OS << " dependent";
|
|
|
|
const DependentTemplateName *DTN = TN.getAsDependentTemplateName();
|
|
|
|
dumpNestedNameSpecifier(DTN->getQualifier());
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
case TemplateName::SubstTemplateTemplateParm: {
|
|
|
|
OS << " subst";
|
|
|
|
const SubstTemplateTemplateParmStorage *STS =
|
|
|
|
TN.getAsSubstTemplateTemplateParm();
|
|
|
|
OS << " index " << STS->getIndex();
|
|
|
|
if (std::optional<unsigned int> PackIndex = STS->getPackIndex())
|
|
|
|
OS << " pack_index " << *PackIndex;
|
|
|
|
if (const TemplateTemplateParmDecl *P = STS->getParameter())
|
|
|
|
AddChild("parameter", [=] { Visit(P); });
|
|
|
|
dumpDeclRef(STS->getAssociatedDecl(), "associated");
|
2024-05-30 04:48:45 -03:00
|
|
|
dumpTemplateName(STS->getReplacement(), "replacement");
|
2024-05-29 15:23:44 -03:00
|
|
|
return;
|
|
|
|
}
|
[clang] Implement CWG2398 provisional TTP matching to class templates (#94981)
This extends default argument deduction to cover class templates as
well, applying only to partial ordering, adding to the provisional
wording introduced in https://github.com/llvm/llvm-project/pull/89807.
This solves some ambuguity introduced in P0522 regarding how template
template parameters are partially ordered, and should reduce the
negative impact of enabling `-frelaxed-template-template-args` by
default.
Given the following example:
```C++
template <class T1, class T2 = float> struct A;
template <class T3> struct B;
template <template <class T4> class TT1, class T5> struct B<TT1<T5>>; // #1
template <class T6, class T7> struct B<A<T6, T7>>; // #2
template struct B<A<int>>;
```
Prior to P0522, `#2` was picked. Afterwards, this became ambiguous. This
patch restores the pre-P0522 behavior, `#2` is picked again.
2024-09-07 15:49:07 -03:00
|
|
|
case TemplateName::DeducedTemplate: {
|
|
|
|
OS << " deduced";
|
|
|
|
const DeducedTemplateStorage *DTS = TN.getAsDeducedTemplateName();
|
|
|
|
dumpTemplateName(DTS->getUnderlying(), "underlying");
|
|
|
|
AddChild("defaults", [=] {
|
|
|
|
auto [StartPos, Args] = DTS->getDefaultArguments();
|
|
|
|
OS << " start " << StartPos;
|
|
|
|
for (const TemplateArgument &Arg : Args)
|
|
|
|
AddChild([=] { Visit(Arg, SourceRange()); });
|
|
|
|
});
|
|
|
|
return;
|
|
|
|
}
|
2024-05-29 15:23:44 -03:00
|
|
|
// FIXME: Implement these.
|
|
|
|
case TemplateName::OverloadedTemplate:
|
|
|
|
OS << " overloaded";
|
|
|
|
return;
|
|
|
|
case TemplateName::AssumedTemplate:
|
|
|
|
OS << " assumed";
|
|
|
|
return;
|
|
|
|
case TemplateName::SubstTemplateTemplateParmPack:
|
|
|
|
OS << " subst_pack";
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
llvm_unreachable("Unexpected TemplateName Kind");
|
2019-01-12 16:35:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void TextNodeDumper::VisitTemplateTemplateArgument(const TemplateArgument &TA) {
|
2024-05-29 15:23:44 -03:00
|
|
|
OS << " template";
|
|
|
|
dumpTemplateArgument(TA);
|
2024-05-30 04:48:45 -03:00
|
|
|
dumpBareTemplateName(TA.getAsTemplate());
|
2019-01-12 16:35:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void TextNodeDumper::VisitTemplateExpansionTemplateArgument(
|
|
|
|
const TemplateArgument &TA) {
|
2024-05-29 15:23:44 -03:00
|
|
|
OS << " template expansion";
|
|
|
|
dumpTemplateArgument(TA);
|
2024-05-30 04:48:45 -03:00
|
|
|
dumpBareTemplateName(TA.getAsTemplateOrTemplatePattern());
|
2019-01-12 16:35:37 +00:00
|
|
|
}
|
|
|
|
|
2024-05-29 15:23:44 -03:00
|
|
|
void TextNodeDumper::VisitExpressionTemplateArgument(
|
|
|
|
const TemplateArgument &TA) {
|
2019-01-12 16:35:37 +00:00
|
|
|
OS << " expr";
|
2024-05-29 15:23:44 -03:00
|
|
|
dumpTemplateArgument(TA);
|
2019-01-12 16:35:37 +00:00
|
|
|
}
|
|
|
|
|
2024-05-29 15:23:44 -03:00
|
|
|
void TextNodeDumper::VisitPackTemplateArgument(const TemplateArgument &TA) {
|
2019-01-12 16:35:37 +00:00
|
|
|
OS << " pack";
|
2024-05-29 15:23:44 -03:00
|
|
|
dumpTemplateArgument(TA);
|
2019-01-12 16:35:37 +00:00
|
|
|
}
|
2019-01-12 16:53:27 +00:00
|
|
|
|
|
|
|
static void dumpBasePath(raw_ostream &OS, const CastExpr *Node) {
|
|
|
|
if (Node->path_empty())
|
|
|
|
return;
|
|
|
|
|
|
|
|
OS << " (";
|
|
|
|
bool First = true;
|
|
|
|
for (CastExpr::path_const_iterator I = Node->path_begin(),
|
|
|
|
E = Node->path_end();
|
|
|
|
I != E; ++I) {
|
|
|
|
const CXXBaseSpecifier *Base = *I;
|
|
|
|
if (!First)
|
|
|
|
OS << " -> ";
|
|
|
|
|
2019-10-03 11:22:48 +00:00
|
|
|
const auto *RD =
|
|
|
|
cast<CXXRecordDecl>(Base->getType()->castAs<RecordType>()->getDecl());
|
2019-01-12 16:53:27 +00:00
|
|
|
|
|
|
|
if (Base->isVirtual())
|
|
|
|
OS << "virtual ";
|
|
|
|
OS << RD->getName();
|
|
|
|
First = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
OS << ')';
|
|
|
|
}
|
|
|
|
|
|
|
|
void TextNodeDumper::VisitIfStmt(const IfStmt *Node) {
|
|
|
|
if (Node->hasInitStorage())
|
|
|
|
OS << " has_init";
|
|
|
|
if (Node->hasVarStorage())
|
|
|
|
OS << " has_var";
|
|
|
|
if (Node->hasElseStorage())
|
|
|
|
OS << " has_else";
|
2021-10-05 08:02:53 -04:00
|
|
|
if (Node->isConstexpr())
|
|
|
|
OS << " constexpr";
|
|
|
|
if (Node->isConsteval()) {
|
|
|
|
OS << " ";
|
|
|
|
if (Node->isNegatedConsteval())
|
|
|
|
OS << "!";
|
|
|
|
OS << "consteval";
|
|
|
|
}
|
2019-01-12 16:53:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void TextNodeDumper::VisitSwitchStmt(const SwitchStmt *Node) {
|
|
|
|
if (Node->hasInitStorage())
|
|
|
|
OS << " has_init";
|
|
|
|
if (Node->hasVarStorage())
|
|
|
|
OS << " has_var";
|
|
|
|
}
|
|
|
|
|
|
|
|
void TextNodeDumper::VisitWhileStmt(const WhileStmt *Node) {
|
|
|
|
if (Node->hasVarStorage())
|
|
|
|
OS << " has_var";
|
|
|
|
}
|
|
|
|
|
|
|
|
void TextNodeDumper::VisitLabelStmt(const LabelStmt *Node) {
|
|
|
|
OS << " '" << Node->getName() << "'";
|
[Windows SEH]: HARDWARE EXCEPTION HANDLING (MSVC -EHa) - Part 1
This patch is the Part-1 (FE Clang) implementation of HW Exception handling.
This new feature adds the support of Hardware Exception for Microsoft Windows
SEH (Structured Exception Handling).
This is the first step of this project; only X86_64 target is enabled in this patch.
Compiler options:
For clang-cl.exe, the option is -EHa, the same as MSVC.
For clang.exe, the extra option is -fasync-exceptions,
plus -triple x86_64-windows -fexceptions and -fcxx-exceptions as usual.
NOTE:: Without the -EHa or -fasync-exceptions, this patch is a NO-DIFF change.
The rules for C code:
For C-code, one way (MSVC approach) to achieve SEH -EHa semantic is to follow
three rules:
* First, no exception can move in or out of _try region., i.e., no "potential
faulty instruction can be moved across _try boundary.
* Second, the order of exceptions for instructions 'directly' under a _try
must be preserved (not applied to those in callees).
* Finally, global states (local/global/heap variables) that can be read
outside of _try region must be updated in memory (not just in register)
before the subsequent exception occurs.
The impact to C++ code:
Although SEH is a feature for C code, -EHa does have a profound effect on C++
side. When a C++ function (in the same compilation unit with option -EHa ) is
called by a SEH C function, a hardware exception occurs in C++ code can also
be handled properly by an upstream SEH _try-handler or a C++ catch(...).
As such, when that happens in the middle of an object's life scope, the dtor
must be invoked the same way as C++ Synchronous Exception during unwinding
process.
Design:
A natural way to achieve the rules above in LLVM today is to allow an EH edge
added on memory/computation instruction (previous iload/istore idea) so that
exception path is modeled in Flow graph preciously. However, tracking every
single memory instruction and potential faulty instruction can create many
Invokes, complicate flow graph and possibly result in negative performance
impact for downstream optimization and code generation. Making all
optimizations be aware of the new semantic is also substantial.
This design does not intend to model exception path at instruction level.
Instead, the proposed design tracks and reports EH state at BLOCK-level to
reduce the complexity of flow graph and minimize the performance-impact on CPP
code under -EHa option.
One key element of this design is the ability to compute State number at
block-level. Our algorithm is based on the following rationales:
A _try scope is always a SEME (Single Entry Multiple Exits) region as jumping
into a _try is not allowed. The single entry must start with a seh_try_begin()
invoke with a correct State number that is the initial state of the SEME.
Through control-flow, state number is propagated into all blocks. Side exits
marked by seh_try_end() will unwind to parent state based on existing
SEHUnwindMap[].
Note side exits can ONLY jump into parent scopes (lower state number).
Thus, when a block succeeds various states from its predecessors, the lowest
State triumphs others. If some exits flow to unreachable, propagation on those
paths terminate, not affecting remaining blocks.
For CPP code, object lifetime region is usually a SEME as SEH _try.
However there is one rare exception: jumping into a lifetime that has Dtor but
has no Ctor is warned, but allowed:
Warning: jump bypasses variable with a non-trivial destructor
In that case, the region is actually a MEME (multiple entry multiple exits).
Our solution is to inject a eha_scope_begin() invoke in the side entry block to
ensure a correct State.
Implementation:
Part-1: Clang implementation described below.
Two intrinsic are created to track CPP object scopes; eha_scope_begin() and eha_scope_end().
_scope_begin() is immediately added after ctor() is called and EHStack is pushed.
So it must be an invoke, not a call. With that it's also guaranteed an
EH-cleanup-pad is created regardless whether there exists a call in this scope.
_scope_end is added before dtor(). These two intrinsics make the computation of
Block-State possible in downstream code gen pass, even in the presence of
ctor/dtor inlining.
Two intrinsic, seh_try_begin() and seh_try_end(), are added for C-code to mark
_try boundary and to prevent from exceptions being moved across _try boundary.
All memory instructions inside a _try are considered as 'volatile' to assure
2nd and 3rd rules for C-code above. This is a little sub-optimized. But it's
acceptable as the amount of code directly under _try is very small.
Part-2 (will be in Part-2 patch): LLVM implementation described below.
For both C++ & C-code, the state of each block is computed at the same place in
BE (WinEHPreparing pass) where all other EH tables/maps are calculated.
In addition to _scope_begin & _scope_end, the computation of block state also
rely on the existing State tracking code (UnwindMap and InvokeStateMap).
For both C++ & C-code, the state of each block with potential trap instruction
is marked and reported in DAG Instruction Selection pass, the same place where
the state for -EHsc (synchronous exceptions) is done.
If the first instruction in a reported block scope can trap, a Nop is injected
before this instruction. This nop is needed to accommodate LLVM Windows EH
implementation, in which the address in IPToState table is offset by +1.
(note the purpose of that is to ensure the return address of a call is in the
same scope as the call address.
The handler for catch(...) for -EHa must handle HW exception. So it is
'adjective' flag is reset (it cannot be IsStdDotDot (0x40) that only catches
C++ exceptions).
Suppress push/popTerminate() scope (from noexcept/noTHrow) so that HW
exceptions can be passed through.
Original llvm-dev [RFC] discussions can be found in these two threads below:
https://lists.llvm.org/pipermail/llvm-dev/2020-March/140541.html
https://lists.llvm.org/pipermail/llvm-dev/2020-April/141338.html
Differential Revision: https://reviews.llvm.org/D80344/new/
2021-05-17 22:06:32 -07:00
|
|
|
if (Node->isSideEntry())
|
|
|
|
OS << " side_entry";
|
2019-01-12 16:53:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void TextNodeDumper::VisitGotoStmt(const GotoStmt *Node) {
|
|
|
|
OS << " '" << Node->getLabel()->getName() << "'";
|
|
|
|
dumpPointer(Node->getLabel());
|
|
|
|
}
|
|
|
|
|
|
|
|
void TextNodeDumper::VisitCaseStmt(const CaseStmt *Node) {
|
|
|
|
if (Node->caseStmtIsGNURange())
|
|
|
|
OS << " gnu_range";
|
|
|
|
}
|
|
|
|
|
2023-08-15 15:59:15 +00:00
|
|
|
void clang::TextNodeDumper::VisitReturnStmt(const ReturnStmt *Node) {
|
|
|
|
if (const VarDecl *Cand = Node->getNRVOCandidate()) {
|
|
|
|
OS << " nrvo_candidate(";
|
|
|
|
dumpBareDeclRef(Cand);
|
|
|
|
OS << ")";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-01-09 10:32:06 +01:00
|
|
|
void clang::TextNodeDumper::VisitCoawaitExpr(const CoawaitExpr *Node) {
|
|
|
|
if (Node->isImplicit())
|
|
|
|
OS << " implicit";
|
|
|
|
}
|
|
|
|
|
|
|
|
void clang::TextNodeDumper::VisitCoreturnStmt(const CoreturnStmt *Node) {
|
|
|
|
if (Node->isImplicit())
|
|
|
|
OS << " implicit";
|
|
|
|
}
|
|
|
|
|
[clang] Add storage for APValue in ConstantExpr
Summary:
When using ConstantExpr we often need the result of the expression to be kept in the AST. Currently this is done on a by the node that needs the result and has been done multiple times for enumerator, for constexpr variables... . This patch adds to ConstantExpr the ability to store the result of evaluating the expression. no functional changes expected.
Changes:
- Add trailling object to ConstantExpr that can hold an APValue or an uint64_t. the uint64_t is here because most ConstantExpr yield integral values so there is an optimized layout for integral values.
- Add basic* serialization support for the trailing result.
- Move conversion functions from an enum to a fltSemantics from clang::FloatingLiteral to llvm::APFloatBase. this change is to make it usable for serializing APValues.
- Add basic* Import support for the trailing result.
- ConstantExpr created in CheckConvertedConstantExpression now stores the result in the ConstantExpr Node.
- Adapt AST dump to print the result when present.
basic* : None, Indeterminate, Int, Float, FixedPoint, ComplexInt, ComplexFloat,
the result is not yet used anywhere but for -ast-dump.
Reviewers: rsmith, martong, shafik
Reviewed By: rsmith
Subscribers: rnkovacs, hiraditya, dexonsmith, cfe-commits, llvm-commits
Tags: #clang, #llvm
Differential Revision: https://reviews.llvm.org/D62399
llvm-svn: 363493
2019-06-15 10:24:47 +00:00
|
|
|
void TextNodeDumper::VisitConstantExpr(const ConstantExpr *Node) {
|
[clang] Rework how and when APValues are dumped
Currently APValues are dumped as a single string. This becomes quickly
completely unreadable since APValue is a tree-like structure. Even a simple
example is not pretty:
struct S { int arr[4]; float f; };
constexpr S s = { .arr = {1,2}, .f = 3.1415f };
// Struct fields: Array: Int: 1, Int: 2, 2 x Int: 0, Float: 3.141500e+00
With this patch this becomes:
-Struct
|-field: Array size=4
| |-elements: Int 1, Int 2
| `-filler: 2 x Int 0
`-field: Float 3.141500e+00
Additionally APValues are currently only dumped as part of visiting a
ConstantExpr. This patch also dump the value of the initializer of constexpr
variable declarations:
constexpr int foo(int a, int b) { return a + b - 42; }
constexpr int a = 1, b = 2;
constexpr int c = foo(a, b) > 0 ? foo(a, b) : foo(b, a);
// VarDecl 0x62100008aec8 <col:3, col:57> col:17 c 'const int' constexpr cinit
// |-value: Int -39
// `-ConditionalOperator 0x62100008b4d0 <col:21, col:57> 'int'
// <snip>
Do the above by moving the dump functions to TextNodeDumper which already has
the machinery to display trees. The cases APValue::LValue, APValue::MemberPointer
and APValue::AddrLabelDiff are left as they were before (unimplemented).
We try to display multiple elements on the same line if they are considered to
be "simple". This is to avoid wasting large amounts of vertical space in an
example like:
constexpr int arr[8] = {0,1,2,3,4,5,6,7};
// VarDecl 0x62100008bb78 <col:3, col:42> col:17 arr 'int const[8]' constexpr cinit
// |-value: Array size=8
// | |-elements: Int 0, Int 1, Int 2, Int 3
// | `-elements: Int 4, Int 5, Int 6, Int 7
Differential Revision: https://reviews.llvm.org/D83183
Reviewed By: aaron.ballman
2020-07-06 21:50:23 +01:00
|
|
|
if (Node->hasAPValueResult())
|
|
|
|
AddChild("value",
|
|
|
|
[=] { Visit(Node->getAPValueResult(), Node->getType()); });
|
[clang] Add storage for APValue in ConstantExpr
Summary:
When using ConstantExpr we often need the result of the expression to be kept in the AST. Currently this is done on a by the node that needs the result and has been done multiple times for enumerator, for constexpr variables... . This patch adds to ConstantExpr the ability to store the result of evaluating the expression. no functional changes expected.
Changes:
- Add trailling object to ConstantExpr that can hold an APValue or an uint64_t. the uint64_t is here because most ConstantExpr yield integral values so there is an optimized layout for integral values.
- Add basic* serialization support for the trailing result.
- Move conversion functions from an enum to a fltSemantics from clang::FloatingLiteral to llvm::APFloatBase. this change is to make it usable for serializing APValues.
- Add basic* Import support for the trailing result.
- ConstantExpr created in CheckConvertedConstantExpression now stores the result in the ConstantExpr Node.
- Adapt AST dump to print the result when present.
basic* : None, Indeterminate, Int, Float, FixedPoint, ComplexInt, ComplexFloat,
the result is not yet used anywhere but for -ast-dump.
Reviewers: rsmith, martong, shafik
Reviewed By: rsmith
Subscribers: rnkovacs, hiraditya, dexonsmith, cfe-commits, llvm-commits
Tags: #clang, #llvm
Differential Revision: https://reviews.llvm.org/D62399
llvm-svn: 363493
2019-06-15 10:24:47 +00:00
|
|
|
}
|
|
|
|
|
2019-01-12 16:53:27 +00:00
|
|
|
void TextNodeDumper::VisitCallExpr(const CallExpr *Node) {
|
|
|
|
if (Node->usesADL())
|
|
|
|
OS << " adl";
|
2020-07-24 12:04:19 +07:00
|
|
|
if (Node->hasStoredFPFeatures())
|
|
|
|
printFPOptions(Node->getFPFeatures());
|
2019-01-12 16:53:27 +00:00
|
|
|
}
|
|
|
|
|
2020-06-09 15:03:22 +01:00
|
|
|
void TextNodeDumper::VisitCXXOperatorCallExpr(const CXXOperatorCallExpr *Node) {
|
|
|
|
const char *OperatorSpelling = clang::getOperatorSpelling(Node->getOperator());
|
|
|
|
if (OperatorSpelling)
|
|
|
|
OS << " '" << OperatorSpelling << "'";
|
|
|
|
|
|
|
|
VisitCallExpr(Node);
|
|
|
|
}
|
|
|
|
|
2019-01-12 16:53:27 +00:00
|
|
|
void TextNodeDumper::VisitCastExpr(const CastExpr *Node) {
|
|
|
|
OS << " <";
|
|
|
|
{
|
|
|
|
ColorScope Color(OS, ShowColors, CastColor);
|
|
|
|
OS << Node->getCastKindName();
|
|
|
|
}
|
|
|
|
dumpBasePath(OS, Node);
|
|
|
|
OS << ">";
|
2020-09-12 21:54:14 +07:00
|
|
|
if (Node->hasStoredFPFeatures())
|
|
|
|
printFPOptions(Node->getFPFeatures());
|
2019-01-12 16:53:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void TextNodeDumper::VisitImplicitCastExpr(const ImplicitCastExpr *Node) {
|
|
|
|
VisitCastExpr(Node);
|
|
|
|
if (Node->isPartOfExplicitCast())
|
|
|
|
OS << " part_of_explicit_cast";
|
|
|
|
}
|
|
|
|
|
|
|
|
void TextNodeDumper::VisitDeclRefExpr(const DeclRefExpr *Node) {
|
|
|
|
OS << " ";
|
|
|
|
dumpBareDeclRef(Node->getDecl());
|
2023-08-15 16:26:47 +00:00
|
|
|
dumpNestedNameSpecifier(Node->getQualifier());
|
2019-01-12 16:53:27 +00:00
|
|
|
if (Node->getDecl() != Node->getFoundDecl()) {
|
|
|
|
OS << " (";
|
|
|
|
dumpBareDeclRef(Node->getFoundDecl());
|
|
|
|
OS << ")";
|
|
|
|
}
|
2019-06-11 17:50:32 +00:00
|
|
|
switch (Node->isNonOdrUse()) {
|
|
|
|
case NOUR_None: break;
|
|
|
|
case NOUR_Unevaluated: OS << " non_odr_use_unevaluated"; break;
|
|
|
|
case NOUR_Constant: OS << " non_odr_use_constant"; break;
|
|
|
|
case NOUR_Discarded: OS << " non_odr_use_discarded"; break;
|
|
|
|
}
|
[Clang] [Sema] Fix dependence of DREs in lambdas with an explicit object parameter (#84473)
This fixes some problems wrt dependence of captures in lambdas with
an explicit object parameter.
[temp.dep.expr] states that
> An id-expression is type-dependent if [...] its terminal name is
> - associated by name lookup with an entity captured by copy
> ([expr.prim.lambda.capture]) in a lambda-expression that has
> an explicit object parameter whose type is dependent [dcl.fct].
There were several issues with our implementation of this:
1. we were treating by-reference captures as dependent rather than
by-value captures;
2. tree transform wasn't checking whether referring to such a
by-value capture should make a DRE dependent;
3. when checking whether a DRE refers to such a by-value capture, we
were only looking at the immediately enclosing lambda, and not
at any parent lambdas;
4. we also forgot to check for implicit by-value captures;
5. lastly, we were attempting to determine whether a lambda has an
explicit object parameter by checking the `LambdaScopeInfo`'s
`ExplicitObjectParameter`, but it seems that that simply wasn't
set (yet) by the time we got to the check.
All of these should be fixed now.
This fixes #70604, #79754, #84163, #84425, #86054, #86398, and #86399.
2024-04-09 14:52:52 +02:00
|
|
|
if (Node->isCapturedByCopyInLambdaWithExplicitObjectParameter())
|
|
|
|
OS << " dependent_capture";
|
|
|
|
else if (Node->refersToEnclosingVariableOrCapture())
|
2023-08-18 13:54:55 +00:00
|
|
|
OS << " refers_to_enclosing_variable_or_capture";
|
[Clang] [Sema] Fix dependence of DREs in lambdas with an explicit object parameter (#84473)
This fixes some problems wrt dependence of captures in lambdas with
an explicit object parameter.
[temp.dep.expr] states that
> An id-expression is type-dependent if [...] its terminal name is
> - associated by name lookup with an entity captured by copy
> ([expr.prim.lambda.capture]) in a lambda-expression that has
> an explicit object parameter whose type is dependent [dcl.fct].
There were several issues with our implementation of this:
1. we were treating by-reference captures as dependent rather than
by-value captures;
2. tree transform wasn't checking whether referring to such a
by-value capture should make a DRE dependent;
3. when checking whether a DRE refers to such a by-value capture, we
were only looking at the immediately enclosing lambda, and not
at any parent lambdas;
4. we also forgot to check for implicit by-value captures;
5. lastly, we were attempting to determine whether a lambda has an
explicit object parameter by checking the `LambdaScopeInfo`'s
`ExplicitObjectParameter`, but it seems that that simply wasn't
set (yet) by the time we got to the check.
All of these should be fixed now.
This fixes #70604, #79754, #84163, #84425, #86054, #86398, and #86399.
2024-04-09 14:52:52 +02:00
|
|
|
|
2023-05-08 12:18:43 +02:00
|
|
|
if (Node->isImmediateEscalating())
|
|
|
|
OS << " immediate-escalating";
|
2019-01-12 16:53:27 +00:00
|
|
|
}
|
|
|
|
|
2023-08-15 16:26:47 +00:00
|
|
|
void clang::TextNodeDumper::VisitDependentScopeDeclRefExpr(
|
|
|
|
const DependentScopeDeclRefExpr *Node) {
|
|
|
|
|
|
|
|
dumpNestedNameSpecifier(Node->getQualifier());
|
|
|
|
}
|
|
|
|
|
2019-01-12 16:53:27 +00:00
|
|
|
void TextNodeDumper::VisitUnresolvedLookupExpr(
|
|
|
|
const UnresolvedLookupExpr *Node) {
|
|
|
|
OS << " (";
|
|
|
|
if (!Node->requiresADL())
|
|
|
|
OS << "no ";
|
|
|
|
OS << "ADL) = '" << Node->getName() << '\'';
|
|
|
|
|
|
|
|
UnresolvedLookupExpr::decls_iterator I = Node->decls_begin(),
|
|
|
|
E = Node->decls_end();
|
|
|
|
if (I == E)
|
|
|
|
OS << " empty";
|
|
|
|
for (; I != E; ++I)
|
|
|
|
dumpPointer(*I);
|
|
|
|
}
|
|
|
|
|
|
|
|
void TextNodeDumper::VisitObjCIvarRefExpr(const ObjCIvarRefExpr *Node) {
|
|
|
|
{
|
|
|
|
ColorScope Color(OS, ShowColors, DeclKindNameColor);
|
|
|
|
OS << " " << Node->getDecl()->getDeclKindName() << "Decl";
|
|
|
|
}
|
|
|
|
OS << "='" << *Node->getDecl() << "'";
|
|
|
|
dumpPointer(Node->getDecl());
|
|
|
|
if (Node->isFreeIvar())
|
|
|
|
OS << " isFreeIvar";
|
|
|
|
}
|
|
|
|
|
2021-04-23 08:22:35 -07:00
|
|
|
void TextNodeDumper::VisitSYCLUniqueStableNameExpr(
|
|
|
|
const SYCLUniqueStableNameExpr *Node) {
|
|
|
|
dumpType(Node->getTypeSourceInfo()->getType());
|
|
|
|
}
|
|
|
|
|
2019-01-12 16:53:27 +00:00
|
|
|
void TextNodeDumper::VisitPredefinedExpr(const PredefinedExpr *Node) {
|
|
|
|
OS << " " << PredefinedExpr::getIdentKindName(Node->getIdentKind());
|
|
|
|
}
|
|
|
|
|
|
|
|
void TextNodeDumper::VisitCharacterLiteral(const CharacterLiteral *Node) {
|
|
|
|
ColorScope Color(OS, ShowColors, ValueColor);
|
|
|
|
OS << " " << Node->getValue();
|
|
|
|
}
|
|
|
|
|
|
|
|
void TextNodeDumper::VisitIntegerLiteral(const IntegerLiteral *Node) {
|
|
|
|
bool isSigned = Node->getType()->isSignedIntegerType();
|
|
|
|
ColorScope Color(OS, ShowColors, ValueColor);
|
2021-06-11 13:19:00 +01:00
|
|
|
OS << " " << toString(Node->getValue(), 10, isSigned);
|
2019-01-12 16:53:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void TextNodeDumper::VisitFixedPointLiteral(const FixedPointLiteral *Node) {
|
|
|
|
ColorScope Color(OS, ShowColors, ValueColor);
|
|
|
|
OS << " " << Node->getValueAsString(/*Radix=*/10);
|
|
|
|
}
|
|
|
|
|
|
|
|
void TextNodeDumper::VisitFloatingLiteral(const FloatingLiteral *Node) {
|
|
|
|
ColorScope Color(OS, ShowColors, ValueColor);
|
|
|
|
OS << " " << Node->getValueAsApproximateDouble();
|
|
|
|
}
|
|
|
|
|
|
|
|
void TextNodeDumper::VisitStringLiteral(const StringLiteral *Str) {
|
|
|
|
ColorScope Color(OS, ShowColors, ValueColor);
|
|
|
|
OS << " ";
|
|
|
|
Str->outputString(OS);
|
|
|
|
}
|
|
|
|
|
|
|
|
void TextNodeDumper::VisitInitListExpr(const InitListExpr *ILE) {
|
|
|
|
if (auto *Field = ILE->getInitializedFieldInUnion()) {
|
|
|
|
OS << " field ";
|
|
|
|
dumpBareDeclRef(Field);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-01-29 22:58:28 +00:00
|
|
|
void TextNodeDumper::VisitGenericSelectionExpr(const GenericSelectionExpr *E) {
|
|
|
|
if (E->isResultDependent())
|
|
|
|
OS << " result_dependent";
|
|
|
|
}
|
|
|
|
|
2019-01-12 16:53:27 +00:00
|
|
|
void TextNodeDumper::VisitUnaryOperator(const UnaryOperator *Node) {
|
|
|
|
OS << " " << (Node->isPostfix() ? "postfix" : "prefix") << " '"
|
|
|
|
<< UnaryOperator::getOpcodeStr(Node->getOpcode()) << "'";
|
|
|
|
if (!Node->canOverflow())
|
|
|
|
OS << " cannot overflow";
|
2020-07-24 12:04:19 +07:00
|
|
|
if (Node->hasStoredFPFeatures())
|
|
|
|
printFPOptions(Node->getStoredFPFeatures());
|
2019-01-12 16:53:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void TextNodeDumper::VisitUnaryExprOrTypeTraitExpr(
|
|
|
|
const UnaryExprOrTypeTraitExpr *Node) {
|
2020-06-11 14:08:27 +01:00
|
|
|
OS << " " << getTraitSpelling(Node->getKind());
|
|
|
|
|
2019-01-12 16:53:27 +00:00
|
|
|
if (Node->isArgumentType())
|
|
|
|
dumpType(Node->getArgumentType());
|
|
|
|
}
|
|
|
|
|
|
|
|
void TextNodeDumper::VisitMemberExpr(const MemberExpr *Node) {
|
|
|
|
OS << " " << (Node->isArrow() ? "->" : ".") << *Node->getMemberDecl();
|
|
|
|
dumpPointer(Node->getMemberDecl());
|
2023-08-15 16:26:47 +00:00
|
|
|
dumpNestedNameSpecifier(Node->getQualifier());
|
2019-06-11 17:50:36 +00:00
|
|
|
switch (Node->isNonOdrUse()) {
|
|
|
|
case NOUR_None: break;
|
|
|
|
case NOUR_Unevaluated: OS << " non_odr_use_unevaluated"; break;
|
|
|
|
case NOUR_Constant: OS << " non_odr_use_constant"; break;
|
|
|
|
case NOUR_Discarded: OS << " non_odr_use_discarded"; break;
|
|
|
|
}
|
2019-01-12 16:53:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void TextNodeDumper::VisitExtVectorElementExpr(
|
|
|
|
const ExtVectorElementExpr *Node) {
|
|
|
|
OS << " " << Node->getAccessor().getNameStart();
|
|
|
|
}
|
|
|
|
|
|
|
|
void TextNodeDumper::VisitBinaryOperator(const BinaryOperator *Node) {
|
|
|
|
OS << " '" << BinaryOperator::getOpcodeStr(Node->getOpcode()) << "'";
|
2020-07-24 12:04:19 +07:00
|
|
|
if (Node->hasStoredFPFeatures())
|
|
|
|
printFPOptions(Node->getStoredFPFeatures());
|
2019-01-12 16:53:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void TextNodeDumper::VisitCompoundAssignOperator(
|
|
|
|
const CompoundAssignOperator *Node) {
|
|
|
|
OS << " '" << BinaryOperator::getOpcodeStr(Node->getOpcode())
|
|
|
|
<< "' ComputeLHSTy=";
|
|
|
|
dumpBareType(Node->getComputationLHSType());
|
|
|
|
OS << " ComputeResultTy=";
|
|
|
|
dumpBareType(Node->getComputationResultType());
|
2020-09-17 14:10:07 +07:00
|
|
|
if (Node->hasStoredFPFeatures())
|
|
|
|
printFPOptions(Node->getStoredFPFeatures());
|
2019-01-12 16:53:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void TextNodeDumper::VisitAddrLabelExpr(const AddrLabelExpr *Node) {
|
|
|
|
OS << " " << Node->getLabel()->getName();
|
|
|
|
dumpPointer(Node->getLabel());
|
|
|
|
}
|
|
|
|
|
|
|
|
void TextNodeDumper::VisitCXXNamedCastExpr(const CXXNamedCastExpr *Node) {
|
|
|
|
OS << " " << Node->getCastName() << "<"
|
|
|
|
<< Node->getTypeAsWritten().getAsString() << ">"
|
|
|
|
<< " <" << Node->getCastKindName();
|
|
|
|
dumpBasePath(OS, Node);
|
|
|
|
OS << ">";
|
|
|
|
}
|
|
|
|
|
|
|
|
void TextNodeDumper::VisitCXXBoolLiteralExpr(const CXXBoolLiteralExpr *Node) {
|
|
|
|
OS << " " << (Node->getValue() ? "true" : "false");
|
|
|
|
}
|
|
|
|
|
|
|
|
void TextNodeDumper::VisitCXXThisExpr(const CXXThisExpr *Node) {
|
2019-02-03 18:20:27 +00:00
|
|
|
if (Node->isImplicit())
|
|
|
|
OS << " implicit";
|
[Clang] [Sema] Fix dependence of DREs in lambdas with an explicit object parameter (#84473)
This fixes some problems wrt dependence of captures in lambdas with
an explicit object parameter.
[temp.dep.expr] states that
> An id-expression is type-dependent if [...] its terminal name is
> - associated by name lookup with an entity captured by copy
> ([expr.prim.lambda.capture]) in a lambda-expression that has
> an explicit object parameter whose type is dependent [dcl.fct].
There were several issues with our implementation of this:
1. we were treating by-reference captures as dependent rather than
by-value captures;
2. tree transform wasn't checking whether referring to such a
by-value capture should make a DRE dependent;
3. when checking whether a DRE refers to such a by-value capture, we
were only looking at the immediately enclosing lambda, and not
at any parent lambdas;
4. we also forgot to check for implicit by-value captures;
5. lastly, we were attempting to determine whether a lambda has an
explicit object parameter by checking the `LambdaScopeInfo`'s
`ExplicitObjectParameter`, but it seems that that simply wasn't
set (yet) by the time we got to the check.
All of these should be fixed now.
This fixes #70604, #79754, #84163, #84425, #86054, #86398, and #86399.
2024-04-09 14:52:52 +02:00
|
|
|
if (Node->isCapturedByCopyInLambdaWithExplicitObjectParameter())
|
|
|
|
OS << " dependent_capture";
|
2019-01-12 16:53:27 +00:00
|
|
|
OS << " this";
|
|
|
|
}
|
|
|
|
|
|
|
|
void TextNodeDumper::VisitCXXFunctionalCastExpr(
|
|
|
|
const CXXFunctionalCastExpr *Node) {
|
|
|
|
OS << " functional cast to " << Node->getTypeAsWritten().getAsString() << " <"
|
|
|
|
<< Node->getCastKindName() << ">";
|
2020-09-12 21:54:14 +07:00
|
|
|
if (Node->hasStoredFPFeatures())
|
|
|
|
printFPOptions(Node->getFPFeatures());
|
|
|
|
}
|
|
|
|
|
|
|
|
void TextNodeDumper::VisitCXXStaticCastExpr(const CXXStaticCastExpr *Node) {
|
|
|
|
VisitCXXNamedCastExpr(Node);
|
|
|
|
if (Node->hasStoredFPFeatures())
|
|
|
|
printFPOptions(Node->getFPFeatures());
|
2019-01-12 16:53:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void TextNodeDumper::VisitCXXUnresolvedConstructExpr(
|
|
|
|
const CXXUnresolvedConstructExpr *Node) {
|
|
|
|
dumpType(Node->getTypeAsWritten());
|
|
|
|
if (Node->isListInitialization())
|
|
|
|
OS << " list";
|
|
|
|
}
|
|
|
|
|
|
|
|
void TextNodeDumper::VisitCXXConstructExpr(const CXXConstructExpr *Node) {
|
|
|
|
CXXConstructorDecl *Ctor = Node->getConstructor();
|
|
|
|
dumpType(Ctor->getType());
|
|
|
|
if (Node->isElidable())
|
|
|
|
OS << " elidable";
|
|
|
|
if (Node->isListInitialization())
|
|
|
|
OS << " list";
|
|
|
|
if (Node->isStdInitListInitialization())
|
|
|
|
OS << " std::initializer_list";
|
|
|
|
if (Node->requiresZeroInitialization())
|
|
|
|
OS << " zeroing";
|
2023-05-08 12:18:43 +02:00
|
|
|
if (Node->isImmediateEscalating())
|
|
|
|
OS << " immediate-escalating";
|
2019-01-12 16:53:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void TextNodeDumper::VisitCXXBindTemporaryExpr(
|
|
|
|
const CXXBindTemporaryExpr *Node) {
|
2019-06-20 15:10:45 +00:00
|
|
|
OS << " (CXXTemporary";
|
|
|
|
dumpPointer(Node);
|
|
|
|
OS << ")";
|
2019-01-12 16:53:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void TextNodeDumper::VisitCXXNewExpr(const CXXNewExpr *Node) {
|
|
|
|
if (Node->isGlobalNew())
|
|
|
|
OS << " global";
|
|
|
|
if (Node->isArray())
|
|
|
|
OS << " array";
|
|
|
|
if (Node->getOperatorNew()) {
|
|
|
|
OS << ' ';
|
|
|
|
dumpBareDeclRef(Node->getOperatorNew());
|
|
|
|
}
|
|
|
|
// We could dump the deallocation function used in case of error, but it's
|
|
|
|
// usually not that interesting.
|
|
|
|
}
|
|
|
|
|
|
|
|
void TextNodeDumper::VisitCXXDeleteExpr(const CXXDeleteExpr *Node) {
|
|
|
|
if (Node->isGlobalDelete())
|
|
|
|
OS << " global";
|
|
|
|
if (Node->isArrayForm())
|
|
|
|
OS << " array";
|
|
|
|
if (Node->getOperatorDelete()) {
|
|
|
|
OS << ' ';
|
|
|
|
dumpBareDeclRef(Node->getOperatorDelete());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-06-11 17:36:28 +01:00
|
|
|
void TextNodeDumper::VisitTypeTraitExpr(const TypeTraitExpr *Node) {
|
|
|
|
OS << " " << getTraitSpelling(Node->getTrait());
|
|
|
|
}
|
|
|
|
|
|
|
|
void TextNodeDumper::VisitArrayTypeTraitExpr(const ArrayTypeTraitExpr *Node) {
|
|
|
|
OS << " " << getTraitSpelling(Node->getTrait());
|
|
|
|
}
|
|
|
|
|
|
|
|
void TextNodeDumper::VisitExpressionTraitExpr(const ExpressionTraitExpr *Node) {
|
|
|
|
OS << " " << getTraitSpelling(Node->getTrait());
|
|
|
|
}
|
|
|
|
|
2024-01-31 06:33:21 +08:00
|
|
|
void TextNodeDumper::VisitCXXDefaultArgExpr(const CXXDefaultArgExpr *Node) {
|
[Clang] Fix AST dump for {CXXDefaultArgExpr, CXXDefaultInitExpr} (#88269)
This PR fix a AST dump issue since
https://github.com/llvm/llvm-project/pull/80001
When Clang dumps `CXXDefaultArgExpr`/`CXXDefaultInitExpr`, there has no
recursively dump the complete `CXXDefaultArgExpr`/`CXXDefaultInitExpr`.
Since this PR, Clang will recursively dump a
`CXXDefaultArgExpr`/`CXXDefaultInitExpr` node, even if the node has no
rewritten init.
*Consider*:
```
struct A {
int arr[1];
};
struct B {
const A &a = A{{0}};
};
void test() {
B b{};
}
```
*Before*:
```
`-FunctionDecl <line:9:1, line:11:1> line:9:6 test 'void ()'
`-CompoundStmt <col:13, line:11:1>
`-DeclStmt <line:10:3, col:8>
`-VarDecl <col:3, col:7> col:5 b 'B' listinit
`-InitListExpr <col:6, col:7> 'B'
`-CXXDefaultInitExpr <col:7> 'const A' lvalue has rewritten init
`-ExprWithCleanups <line:6:16, col:21> 'const A' lvalue
```
*After*:
```
`-FunctionDecl 0x15a9455a8 <line:9:1, line:11:1> line:9:6 test 'void ()'
`-CompoundStmt 0x15a945850 <col:13, line:11:1>
`-DeclStmt 0x15a945838 <line:10:3, col:8>
`-VarDecl 0x15a945708 <col:3, col:7> col:5 b 'B' listinit
`-InitListExpr 0x15a9457b0 <col:6, col:7> 'B'
`-CXXDefaultInitExpr 0x15a9457f8 <col:7> 'const A' lvalue has rewritten init
`-ExprWithCleanups 0x15a945568 <line:6:16, col:21> 'const A' lvalue
`-MaterializeTemporaryExpr 0x15a945500 <col:16, col:21> 'const A' lvalue extended by Field 0x15a945160 'a' 'const A &'
`-ImplicitCastExpr 0x15a9454e8 <col:16, col:21> 'const A' <NoOp>
`-CXXFunctionalCastExpr 0x15a9454c0 <col:16, col:21> 'A' functional cast to A <NoOp>
`-InitListExpr 0x15a9452c0 <col:17, col:21> 'A'
`-InitListExpr 0x15a945308 <col:18, col:20> 'int[1]'
`-IntegerLiteral 0x15a945210 <col:19> 'int' 0
```
---------
Signed-off-by: yronglin <yronglin777@gmail.com>
2024-04-12 22:39:40 +08:00
|
|
|
if (Node->hasRewrittenInit())
|
2024-01-31 06:33:21 +08:00
|
|
|
OS << " has rewritten init";
|
|
|
|
}
|
|
|
|
|
|
|
|
void TextNodeDumper::VisitCXXDefaultInitExpr(const CXXDefaultInitExpr *Node) {
|
[Clang] Fix AST dump for {CXXDefaultArgExpr, CXXDefaultInitExpr} (#88269)
This PR fix a AST dump issue since
https://github.com/llvm/llvm-project/pull/80001
When Clang dumps `CXXDefaultArgExpr`/`CXXDefaultInitExpr`, there has no
recursively dump the complete `CXXDefaultArgExpr`/`CXXDefaultInitExpr`.
Since this PR, Clang will recursively dump a
`CXXDefaultArgExpr`/`CXXDefaultInitExpr` node, even if the node has no
rewritten init.
*Consider*:
```
struct A {
int arr[1];
};
struct B {
const A &a = A{{0}};
};
void test() {
B b{};
}
```
*Before*:
```
`-FunctionDecl <line:9:1, line:11:1> line:9:6 test 'void ()'
`-CompoundStmt <col:13, line:11:1>
`-DeclStmt <line:10:3, col:8>
`-VarDecl <col:3, col:7> col:5 b 'B' listinit
`-InitListExpr <col:6, col:7> 'B'
`-CXXDefaultInitExpr <col:7> 'const A' lvalue has rewritten init
`-ExprWithCleanups <line:6:16, col:21> 'const A' lvalue
```
*After*:
```
`-FunctionDecl 0x15a9455a8 <line:9:1, line:11:1> line:9:6 test 'void ()'
`-CompoundStmt 0x15a945850 <col:13, line:11:1>
`-DeclStmt 0x15a945838 <line:10:3, col:8>
`-VarDecl 0x15a945708 <col:3, col:7> col:5 b 'B' listinit
`-InitListExpr 0x15a9457b0 <col:6, col:7> 'B'
`-CXXDefaultInitExpr 0x15a9457f8 <col:7> 'const A' lvalue has rewritten init
`-ExprWithCleanups 0x15a945568 <line:6:16, col:21> 'const A' lvalue
`-MaterializeTemporaryExpr 0x15a945500 <col:16, col:21> 'const A' lvalue extended by Field 0x15a945160 'a' 'const A &'
`-ImplicitCastExpr 0x15a9454e8 <col:16, col:21> 'const A' <NoOp>
`-CXXFunctionalCastExpr 0x15a9454c0 <col:16, col:21> 'A' functional cast to A <NoOp>
`-InitListExpr 0x15a9452c0 <col:17, col:21> 'A'
`-InitListExpr 0x15a945308 <col:18, col:20> 'int[1]'
`-IntegerLiteral 0x15a945210 <col:19> 'int' 0
```
---------
Signed-off-by: yronglin <yronglin777@gmail.com>
2024-04-12 22:39:40 +08:00
|
|
|
if (Node->hasRewrittenInit())
|
2024-01-31 06:33:21 +08:00
|
|
|
OS << " has rewritten init";
|
|
|
|
}
|
|
|
|
|
2019-01-12 16:53:27 +00:00
|
|
|
void TextNodeDumper::VisitMaterializeTemporaryExpr(
|
|
|
|
const MaterializeTemporaryExpr *Node) {
|
|
|
|
if (const ValueDecl *VD = Node->getExtendingDecl()) {
|
|
|
|
OS << " extended by ";
|
|
|
|
dumpBareDeclRef(VD);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void TextNodeDumper::VisitExprWithCleanups(const ExprWithCleanups *Node) {
|
|
|
|
for (unsigned i = 0, e = Node->getNumObjects(); i != e; ++i)
|
2020-03-10 14:06:25 -07:00
|
|
|
dumpCleanupObject(Node->getObject(i));
|
2019-01-12 16:53:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void TextNodeDumper::VisitSizeOfPackExpr(const SizeOfPackExpr *Node) {
|
|
|
|
dumpPointer(Node->getPack());
|
|
|
|
dumpName(Node->getPack());
|
|
|
|
}
|
|
|
|
|
|
|
|
void TextNodeDumper::VisitCXXDependentScopeMemberExpr(
|
|
|
|
const CXXDependentScopeMemberExpr *Node) {
|
|
|
|
OS << " " << (Node->isArrow() ? "->" : ".") << Node->getMember();
|
|
|
|
}
|
|
|
|
|
|
|
|
void TextNodeDumper::VisitObjCMessageExpr(const ObjCMessageExpr *Node) {
|
|
|
|
OS << " selector=";
|
|
|
|
Node->getSelector().print(OS);
|
|
|
|
switch (Node->getReceiverKind()) {
|
|
|
|
case ObjCMessageExpr::Instance:
|
|
|
|
break;
|
|
|
|
|
|
|
|
case ObjCMessageExpr::Class:
|
|
|
|
OS << " class=";
|
|
|
|
dumpBareType(Node->getClassReceiver());
|
|
|
|
break;
|
|
|
|
|
|
|
|
case ObjCMessageExpr::SuperInstance:
|
|
|
|
OS << " super (instance)";
|
|
|
|
break;
|
|
|
|
|
|
|
|
case ObjCMessageExpr::SuperClass:
|
|
|
|
OS << " super (class)";
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void TextNodeDumper::VisitObjCBoxedExpr(const ObjCBoxedExpr *Node) {
|
|
|
|
if (auto *BoxingMethod = Node->getBoxingMethod()) {
|
|
|
|
OS << " selector=";
|
|
|
|
BoxingMethod->getSelector().print(OS);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void TextNodeDumper::VisitObjCAtCatchStmt(const ObjCAtCatchStmt *Node) {
|
|
|
|
if (!Node->getCatchParamDecl())
|
|
|
|
OS << " catch all";
|
|
|
|
}
|
|
|
|
|
|
|
|
void TextNodeDumper::VisitObjCEncodeExpr(const ObjCEncodeExpr *Node) {
|
|
|
|
dumpType(Node->getEncodedType());
|
|
|
|
}
|
|
|
|
|
|
|
|
void TextNodeDumper::VisitObjCSelectorExpr(const ObjCSelectorExpr *Node) {
|
|
|
|
OS << " ";
|
|
|
|
Node->getSelector().print(OS);
|
|
|
|
}
|
|
|
|
|
|
|
|
void TextNodeDumper::VisitObjCProtocolExpr(const ObjCProtocolExpr *Node) {
|
|
|
|
OS << ' ' << *Node->getProtocol();
|
|
|
|
}
|
|
|
|
|
|
|
|
void TextNodeDumper::VisitObjCPropertyRefExpr(const ObjCPropertyRefExpr *Node) {
|
|
|
|
if (Node->isImplicitProperty()) {
|
|
|
|
OS << " Kind=MethodRef Getter=\"";
|
|
|
|
if (Node->getImplicitPropertyGetter())
|
|
|
|
Node->getImplicitPropertyGetter()->getSelector().print(OS);
|
|
|
|
else
|
|
|
|
OS << "(null)";
|
|
|
|
|
|
|
|
OS << "\" Setter=\"";
|
|
|
|
if (ObjCMethodDecl *Setter = Node->getImplicitPropertySetter())
|
|
|
|
Setter->getSelector().print(OS);
|
|
|
|
else
|
|
|
|
OS << "(null)";
|
|
|
|
OS << "\"";
|
|
|
|
} else {
|
|
|
|
OS << " Kind=PropertyRef Property=\"" << *Node->getExplicitProperty()
|
|
|
|
<< '"';
|
|
|
|
}
|
|
|
|
|
|
|
|
if (Node->isSuperReceiver())
|
|
|
|
OS << " super";
|
|
|
|
|
|
|
|
OS << " Messaging=";
|
|
|
|
if (Node->isMessagingGetter() && Node->isMessagingSetter())
|
|
|
|
OS << "Getter&Setter";
|
|
|
|
else if (Node->isMessagingGetter())
|
|
|
|
OS << "Getter";
|
|
|
|
else if (Node->isMessagingSetter())
|
|
|
|
OS << "Setter";
|
|
|
|
}
|
|
|
|
|
|
|
|
void TextNodeDumper::VisitObjCSubscriptRefExpr(
|
|
|
|
const ObjCSubscriptRefExpr *Node) {
|
|
|
|
if (Node->isArraySubscriptRefExpr())
|
|
|
|
OS << " Kind=ArraySubscript GetterForArray=\"";
|
|
|
|
else
|
|
|
|
OS << " Kind=DictionarySubscript GetterForDictionary=\"";
|
|
|
|
if (Node->getAtIndexMethodDecl())
|
|
|
|
Node->getAtIndexMethodDecl()->getSelector().print(OS);
|
|
|
|
else
|
|
|
|
OS << "(null)";
|
|
|
|
|
|
|
|
if (Node->isArraySubscriptRefExpr())
|
|
|
|
OS << "\" SetterForArray=\"";
|
|
|
|
else
|
|
|
|
OS << "\" SetterForDictionary=\"";
|
|
|
|
if (Node->setAtIndexMethodDecl())
|
|
|
|
Node->setAtIndexMethodDecl()->getSelector().print(OS);
|
|
|
|
else
|
|
|
|
OS << "(null)";
|
|
|
|
}
|
|
|
|
|
|
|
|
void TextNodeDumper::VisitObjCBoolLiteralExpr(const ObjCBoolLiteralExpr *Node) {
|
|
|
|
OS << " " << (Node->getValue() ? "__objc_yes" : "__objc_no");
|
|
|
|
}
|
2019-01-15 09:30:00 +00:00
|
|
|
|
2020-04-01 15:06:38 -04:00
|
|
|
void TextNodeDumper::VisitOMPIteratorExpr(const OMPIteratorExpr *Node) {
|
|
|
|
OS << " ";
|
|
|
|
for (unsigned I = 0, E = Node->numOfIterators(); I < E; ++I) {
|
|
|
|
Visit(Node->getIteratorDecl(I));
|
|
|
|
OS << " = ";
|
|
|
|
const OMPIteratorExpr::IteratorRange Range = Node->getIteratorRange(I);
|
|
|
|
OS << " begin ";
|
|
|
|
Visit(Range.Begin);
|
|
|
|
OS << " end ";
|
|
|
|
Visit(Range.End);
|
|
|
|
if (Range.Step) {
|
|
|
|
OS << " step ";
|
|
|
|
Visit(Range.Step);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-08-04 15:58:12 +02:00
|
|
|
void TextNodeDumper::VisitConceptSpecializationExpr(
|
|
|
|
const ConceptSpecializationExpr *Node) {
|
|
|
|
OS << " ";
|
|
|
|
dumpBareDeclRef(Node->getFoundDecl());
|
|
|
|
}
|
|
|
|
|
2021-06-30 14:05:34 -07:00
|
|
|
void TextNodeDumper::VisitRequiresExpr(
|
|
|
|
const RequiresExpr *Node) {
|
|
|
|
if (!Node->isValueDependent())
|
|
|
|
OS << (Node->isSatisfied() ? " satisfied" : " unsatisfied");
|
|
|
|
}
|
|
|
|
|
2019-01-15 09:30:00 +00:00
|
|
|
void TextNodeDumper::VisitRValueReferenceType(const ReferenceType *T) {
|
|
|
|
if (T->isSpelledAsLValue())
|
|
|
|
OS << " written as lvalue reference";
|
|
|
|
}
|
|
|
|
|
|
|
|
void TextNodeDumper::VisitArrayType(const ArrayType *T) {
|
|
|
|
switch (T->getSizeModifier()) {
|
2023-10-31 17:43:10 +03:00
|
|
|
case ArraySizeModifier::Normal:
|
2019-01-15 09:30:00 +00:00
|
|
|
break;
|
2023-10-31 17:43:10 +03:00
|
|
|
case ArraySizeModifier::Static:
|
2019-01-15 09:30:00 +00:00
|
|
|
OS << " static";
|
|
|
|
break;
|
2023-10-31 17:43:10 +03:00
|
|
|
case ArraySizeModifier::Star:
|
2019-01-15 09:30:00 +00:00
|
|
|
OS << " *";
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
OS << " " << T->getIndexTypeQualifiers().getAsString();
|
|
|
|
}
|
|
|
|
|
|
|
|
void TextNodeDumper::VisitConstantArrayType(const ConstantArrayType *T) {
|
|
|
|
OS << " " << T->getSize();
|
|
|
|
VisitArrayType(T);
|
|
|
|
}
|
|
|
|
|
|
|
|
void TextNodeDumper::VisitVariableArrayType(const VariableArrayType *T) {
|
|
|
|
OS << " ";
|
|
|
|
dumpSourceRange(T->getBracketsRange());
|
|
|
|
VisitArrayType(T);
|
|
|
|
}
|
|
|
|
|
|
|
|
void TextNodeDumper::VisitDependentSizedArrayType(
|
|
|
|
const DependentSizedArrayType *T) {
|
|
|
|
VisitArrayType(T);
|
|
|
|
OS << " ";
|
|
|
|
dumpSourceRange(T->getBracketsRange());
|
|
|
|
}
|
|
|
|
|
|
|
|
void TextNodeDumper::VisitDependentSizedExtVectorType(
|
|
|
|
const DependentSizedExtVectorType *T) {
|
|
|
|
OS << " ";
|
|
|
|
dumpLocation(T->getAttributeLoc());
|
|
|
|
}
|
|
|
|
|
|
|
|
void TextNodeDumper::VisitVectorType(const VectorType *T) {
|
|
|
|
switch (T->getVectorKind()) {
|
2023-10-31 21:48:42 +03:00
|
|
|
case VectorKind::Generic:
|
2019-01-15 09:30:00 +00:00
|
|
|
break;
|
2023-10-31 21:48:42 +03:00
|
|
|
case VectorKind::AltiVecVector:
|
2019-01-15 09:30:00 +00:00
|
|
|
OS << " altivec";
|
|
|
|
break;
|
2023-10-31 21:48:42 +03:00
|
|
|
case VectorKind::AltiVecPixel:
|
2019-01-15 09:30:00 +00:00
|
|
|
OS << " altivec pixel";
|
|
|
|
break;
|
2023-10-31 21:48:42 +03:00
|
|
|
case VectorKind::AltiVecBool:
|
2019-01-15 09:30:00 +00:00
|
|
|
OS << " altivec bool";
|
|
|
|
break;
|
2023-10-31 21:48:42 +03:00
|
|
|
case VectorKind::Neon:
|
2019-01-15 09:30:00 +00:00
|
|
|
OS << " neon";
|
|
|
|
break;
|
2023-10-31 21:48:42 +03:00
|
|
|
case VectorKind::NeonPoly:
|
2019-01-15 09:30:00 +00:00
|
|
|
OS << " neon poly";
|
|
|
|
break;
|
2023-10-31 21:48:42 +03:00
|
|
|
case VectorKind::SveFixedLengthData:
|
2020-08-11 13:04:21 +00:00
|
|
|
OS << " fixed-length sve data vector";
|
|
|
|
break;
|
2023-10-31 21:48:42 +03:00
|
|
|
case VectorKind::SveFixedLengthPredicate:
|
2020-08-11 13:04:21 +00:00
|
|
|
OS << " fixed-length sve predicate vector";
|
|
|
|
break;
|
2023-10-31 21:48:42 +03:00
|
|
|
case VectorKind::RVVFixedLengthData:
|
2023-04-28 15:41:17 -07:00
|
|
|
OS << " fixed-length rvv data vector";
|
|
|
|
break;
|
2024-01-25 09:39:29 -08:00
|
|
|
case VectorKind::RVVFixedLengthMask:
|
2024-08-08 12:45:20 +03:00
|
|
|
case VectorKind::RVVFixedLengthMask_1:
|
|
|
|
case VectorKind::RVVFixedLengthMask_2:
|
|
|
|
case VectorKind::RVVFixedLengthMask_4:
|
2024-01-25 09:39:29 -08:00
|
|
|
OS << " fixed-length rvv mask vector";
|
|
|
|
break;
|
2019-01-15 09:30:00 +00:00
|
|
|
}
|
|
|
|
OS << " " << T->getNumElements();
|
|
|
|
}
|
|
|
|
|
|
|
|
void TextNodeDumper::VisitFunctionType(const FunctionType *T) {
|
|
|
|
auto EI = T->getExtInfo();
|
|
|
|
if (EI.getNoReturn())
|
|
|
|
OS << " noreturn";
|
|
|
|
if (EI.getProducesResult())
|
|
|
|
OS << " produces_result";
|
|
|
|
if (EI.getHasRegParm())
|
|
|
|
OS << " regparm " << EI.getRegParm();
|
|
|
|
OS << " " << FunctionType::getNameForCallConv(EI.getCC());
|
|
|
|
}
|
|
|
|
|
|
|
|
void TextNodeDumper::VisitFunctionProtoType(const FunctionProtoType *T) {
|
|
|
|
auto EPI = T->getExtProtoInfo();
|
|
|
|
if (EPI.HasTrailingReturn)
|
|
|
|
OS << " trailing_return";
|
|
|
|
if (T->isConst())
|
|
|
|
OS << " const";
|
|
|
|
if (T->isVolatile())
|
|
|
|
OS << " volatile";
|
|
|
|
if (T->isRestrict())
|
|
|
|
OS << " restrict";
|
2019-01-18 21:38:30 +00:00
|
|
|
if (T->getExtProtoInfo().Variadic)
|
|
|
|
OS << " variadic";
|
2019-01-15 09:30:00 +00:00
|
|
|
switch (EPI.RefQualifier) {
|
|
|
|
case RQ_None:
|
|
|
|
break;
|
|
|
|
case RQ_LValue:
|
|
|
|
OS << " &";
|
|
|
|
break;
|
|
|
|
case RQ_RValue:
|
|
|
|
OS << " &&";
|
|
|
|
break;
|
|
|
|
}
|
2023-08-10 18:45:56 +00:00
|
|
|
|
|
|
|
switch (EPI.ExceptionSpec.Type) {
|
|
|
|
case EST_None:
|
|
|
|
break;
|
|
|
|
case EST_DynamicNone:
|
|
|
|
OS << " exceptionspec_dynamic_none";
|
|
|
|
break;
|
|
|
|
case EST_Dynamic:
|
|
|
|
OS << " exceptionspec_dynamic";
|
|
|
|
break;
|
|
|
|
case EST_MSAny:
|
|
|
|
OS << " exceptionspec_ms_any";
|
|
|
|
break;
|
|
|
|
case EST_NoThrow:
|
|
|
|
OS << " exceptionspec_nothrow";
|
|
|
|
break;
|
|
|
|
case EST_BasicNoexcept:
|
|
|
|
OS << " exceptionspec_basic_noexcept";
|
|
|
|
break;
|
|
|
|
case EST_DependentNoexcept:
|
|
|
|
OS << " exceptionspec_dependent_noexcept";
|
|
|
|
break;
|
|
|
|
case EST_NoexceptFalse:
|
|
|
|
OS << " exceptionspec_noexcept_false";
|
|
|
|
break;
|
|
|
|
case EST_NoexceptTrue:
|
|
|
|
OS << " exceptionspec_noexcept_true";
|
|
|
|
break;
|
|
|
|
case EST_Unevaluated:
|
|
|
|
OS << " exceptionspec_unevaluated";
|
|
|
|
break;
|
|
|
|
case EST_Uninstantiated:
|
|
|
|
OS << " exceptionspec_uninstantiated";
|
|
|
|
break;
|
|
|
|
case EST_Unparsed:
|
|
|
|
OS << " exceptionspec_unparsed";
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (!EPI.ExceptionSpec.Exceptions.empty()) {
|
|
|
|
AddChild([=] {
|
|
|
|
OS << "Exceptions:";
|
|
|
|
for (unsigned I = 0, N = EPI.ExceptionSpec.Exceptions.size(); I != N;
|
|
|
|
++I) {
|
|
|
|
if (I)
|
|
|
|
OS << ",";
|
|
|
|
dumpType(EPI.ExceptionSpec.Exceptions[I]);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
if (EPI.ExceptionSpec.NoexceptExpr) {
|
|
|
|
AddChild([=] {
|
|
|
|
OS << "NoexceptExpr: ";
|
|
|
|
Visit(EPI.ExceptionSpec.NoexceptExpr);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
dumpDeclRef(EPI.ExceptionSpec.SourceDecl, "ExceptionSourceDecl");
|
|
|
|
dumpDeclRef(EPI.ExceptionSpec.SourceTemplate, "ExceptionSourceTemplate");
|
|
|
|
|
2019-01-15 09:30:00 +00:00
|
|
|
// FIXME: Consumed parameters.
|
|
|
|
VisitFunctionType(T);
|
|
|
|
}
|
|
|
|
|
|
|
|
void TextNodeDumper::VisitUnresolvedUsingType(const UnresolvedUsingType *T) {
|
|
|
|
dumpDeclRef(T->getDecl());
|
|
|
|
}
|
|
|
|
|
2021-12-20 18:01:33 +01:00
|
|
|
void TextNodeDumper::VisitUsingType(const UsingType *T) {
|
|
|
|
dumpDeclRef(T->getFoundDecl());
|
2022-09-04 04:45:40 +02:00
|
|
|
if (!T->typeMatchesDecl())
|
|
|
|
OS << " divergent";
|
2021-12-20 18:01:33 +01:00
|
|
|
}
|
|
|
|
|
2019-01-15 09:30:00 +00:00
|
|
|
void TextNodeDumper::VisitTypedefType(const TypedefType *T) {
|
|
|
|
dumpDeclRef(T->getDecl());
|
2022-09-04 04:45:40 +02:00
|
|
|
if (!T->typeMatchesDecl())
|
|
|
|
OS << " divergent";
|
2019-01-15 09:30:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void TextNodeDumper::VisitUnaryTransformType(const UnaryTransformType *T) {
|
|
|
|
switch (T->getUTTKind()) {
|
2022-08-22 00:27:10 +00:00
|
|
|
#define TRANSFORM_TYPE_TRAIT_DEF(Enum, Trait) \
|
|
|
|
case UnaryTransformType::Enum: \
|
|
|
|
OS << " " #Trait; \
|
2019-01-15 09:30:00 +00:00
|
|
|
break;
|
2022-08-22 00:27:10 +00:00
|
|
|
#include "clang/Basic/TransformTypeTraits.def"
|
2019-01-15 09:30:00 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void TextNodeDumper::VisitTagType(const TagType *T) {
|
|
|
|
dumpDeclRef(T->getDecl());
|
|
|
|
}
|
|
|
|
|
|
|
|
void TextNodeDumper::VisitTemplateTypeParmType(const TemplateTypeParmType *T) {
|
|
|
|
OS << " depth " << T->getDepth() << " index " << T->getIndex();
|
|
|
|
if (T->isParameterPack())
|
|
|
|
OS << " pack";
|
|
|
|
dumpDeclRef(T->getDecl());
|
|
|
|
}
|
|
|
|
|
2022-06-18 04:21:59 +02:00
|
|
|
void TextNodeDumper::VisitSubstTemplateTypeParmType(
|
|
|
|
const SubstTemplateTypeParmType *T) {
|
2022-08-14 13:48:18 +02:00
|
|
|
dumpDeclRef(T->getAssociatedDecl());
|
|
|
|
VisitTemplateTypeParmDecl(T->getReplacedParameter());
|
2022-06-18 04:21:59 +02:00
|
|
|
if (auto PackIndex = T->getPackIndex())
|
|
|
|
OS << " pack_index " << *PackIndex;
|
|
|
|
}
|
|
|
|
|
2022-08-14 13:48:18 +02:00
|
|
|
void TextNodeDumper::VisitSubstTemplateTypeParmPackType(
|
|
|
|
const SubstTemplateTypeParmPackType *T) {
|
|
|
|
dumpDeclRef(T->getAssociatedDecl());
|
|
|
|
VisitTemplateTypeParmDecl(T->getReplacedParameter());
|
|
|
|
}
|
|
|
|
|
2019-01-15 09:30:00 +00:00
|
|
|
void TextNodeDumper::VisitAutoType(const AutoType *T) {
|
|
|
|
if (T->isDecltypeAuto())
|
|
|
|
OS << " decltype(auto)";
|
|
|
|
if (!T->isDeduced())
|
|
|
|
OS << " undeduced";
|
[AST] Add dump() method to TypeLoc (#65484)
The ability to dump AST nodes is important to ad-hoc debugging, and
the fact this doesn't work with TypeLoc nodes is an obvious missing
feature in e.g. clang-query (`set output dump` simply does nothing).
Having TypeLoc::dump(), and enabling DynTypedNode::dump() for such nodes
seems like a clear win.
It looks like this:
```
int main(int argc, char **argv);
FunctionProtoTypeLoc <test.cc:3:1, col:31> 'int (int, char **)' cdecl
|-ParmVarDecl 0x30071a8 <col:10, col:14> col:14 argc 'int'
| `-BuiltinTypeLoc <col:10> 'int'
|-ParmVarDecl 0x3007250 <col:20, col:27> col:27 argv 'char **'
| `-PointerTypeLoc <col:20, col:26> 'char **'
| `-PointerTypeLoc <col:20, col:25> 'char *'
| `-BuiltinTypeLoc <col:20> 'char'
`-BuiltinTypeLoc <col:1> 'int'
```
It dumps the lexically nested tree of type locs.
This often looks similar to how types are dumped, but unlike types
we don't look at desugaring e.g. typedefs, as their underlying types
are not lexically spelled here.
---
Less clear is exactly when to include these nodes in existing text AST
dumps rooted at (TranslationUnit)Decls.
These already omit supported nodes sometimes, e.g. NestedNameSpecifiers
are often mentioned but not recursively dumped.
TypeLocs are a more extreme case: they're ~always more verbose
than the current AST dump.
So this patch punts on that, TypeLocs are only ever printed recursively
as part of a TypeLoc::dump() call.
It would also be nice to be able to invoke `clang` to dump a typeloc
somehow, like `clang -cc1 -ast-dump`. But I don't know exactly what the
best verison of that is, so this patch doesn't do it.
---
There are similar (less critical!) nodes: TemplateArgumentLoc etc,
these also don't have dump() functions today and are obvious extensions.
I suspect that we should add these, and Loc nodes should dump each other
(e.g. the ElaboratedTypeLoc `vector<int>::iterator` should dump
the NestedNameSpecifierLoc `vector<int>::`, which dumps the
TemplateSpecializationTypeLoc `vector<int>::` etc).
Maybe this generalizes further to a "full syntactic dump" mode, where
even Decls and Stmts would print the TypeLocs they lexically contain.
But this may be more complex than useful.
---
While here, ConceptReference JSON dumping must be implemented. It's not
totally clear to me why this implementation wasn't required before but
is now...
2024-01-31 16:40:29 +01:00
|
|
|
if (T->isConstrained())
|
2020-01-22 02:03:05 +02:00
|
|
|
dumpDeclRef(T->getTypeConstraintConcept());
|
2019-01-15 09:30:00 +00:00
|
|
|
}
|
|
|
|
|
2022-04-12 15:51:16 +02:00
|
|
|
void TextNodeDumper::VisitDeducedTemplateSpecializationType(
|
|
|
|
const DeducedTemplateSpecializationType *T) {
|
2024-05-30 04:48:45 -03:00
|
|
|
dumpTemplateName(T->getTemplateName(), "name");
|
2022-04-12 15:51:16 +02:00
|
|
|
}
|
|
|
|
|
2019-01-15 09:30:00 +00:00
|
|
|
void TextNodeDumper::VisitTemplateSpecializationType(
|
|
|
|
const TemplateSpecializationType *T) {
|
|
|
|
if (T->isTypeAlias())
|
|
|
|
OS << " alias";
|
2024-05-30 04:48:45 -03:00
|
|
|
dumpTemplateName(T->getTemplateName(), "name");
|
2019-01-15 09:30:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void TextNodeDumper::VisitInjectedClassNameType(
|
|
|
|
const InjectedClassNameType *T) {
|
|
|
|
dumpDeclRef(T->getDecl());
|
|
|
|
}
|
|
|
|
|
|
|
|
void TextNodeDumper::VisitObjCInterfaceType(const ObjCInterfaceType *T) {
|
|
|
|
dumpDeclRef(T->getDecl());
|
|
|
|
}
|
|
|
|
|
|
|
|
void TextNodeDumper::VisitPackExpansionType(const PackExpansionType *T) {
|
|
|
|
if (auto N = T->getNumExpansions())
|
|
|
|
OS << " expansions " << *N;
|
|
|
|
}
|
2019-01-30 19:32:48 +00:00
|
|
|
|
[AST] Add dump() method to TypeLoc (#65484)
The ability to dump AST nodes is important to ad-hoc debugging, and
the fact this doesn't work with TypeLoc nodes is an obvious missing
feature in e.g. clang-query (`set output dump` simply does nothing).
Having TypeLoc::dump(), and enabling DynTypedNode::dump() for such nodes
seems like a clear win.
It looks like this:
```
int main(int argc, char **argv);
FunctionProtoTypeLoc <test.cc:3:1, col:31> 'int (int, char **)' cdecl
|-ParmVarDecl 0x30071a8 <col:10, col:14> col:14 argc 'int'
| `-BuiltinTypeLoc <col:10> 'int'
|-ParmVarDecl 0x3007250 <col:20, col:27> col:27 argv 'char **'
| `-PointerTypeLoc <col:20, col:26> 'char **'
| `-PointerTypeLoc <col:20, col:25> 'char *'
| `-BuiltinTypeLoc <col:20> 'char'
`-BuiltinTypeLoc <col:1> 'int'
```
It dumps the lexically nested tree of type locs.
This often looks similar to how types are dumped, but unlike types
we don't look at desugaring e.g. typedefs, as their underlying types
are not lexically spelled here.
---
Less clear is exactly when to include these nodes in existing text AST
dumps rooted at (TranslationUnit)Decls.
These already omit supported nodes sometimes, e.g. NestedNameSpecifiers
are often mentioned but not recursively dumped.
TypeLocs are a more extreme case: they're ~always more verbose
than the current AST dump.
So this patch punts on that, TypeLocs are only ever printed recursively
as part of a TypeLoc::dump() call.
It would also be nice to be able to invoke `clang` to dump a typeloc
somehow, like `clang -cc1 -ast-dump`. But I don't know exactly what the
best verison of that is, so this patch doesn't do it.
---
There are similar (less critical!) nodes: TemplateArgumentLoc etc,
these also don't have dump() functions today and are obvious extensions.
I suspect that we should add these, and Loc nodes should dump each other
(e.g. the ElaboratedTypeLoc `vector<int>::iterator` should dump
the NestedNameSpecifierLoc `vector<int>::`, which dumps the
TemplateSpecializationTypeLoc `vector<int>::` etc).
Maybe this generalizes further to a "full syntactic dump" mode, where
even Decls and Stmts would print the TypeLocs they lexically contain.
But this may be more complex than useful.
---
While here, ConceptReference JSON dumping must be implemented. It's not
totally clear to me why this implementation wasn't required before but
is now...
2024-01-31 16:40:29 +01:00
|
|
|
void TextNodeDumper::VisitTypeLoc(TypeLoc TL) {
|
|
|
|
// By default, add extra Type details with no extra loc info.
|
|
|
|
TypeVisitor<TextNodeDumper>::Visit(TL.getTypePtr());
|
|
|
|
}
|
|
|
|
// FIXME: override behavior for TypeLocs that have interesting location
|
|
|
|
// information, such as the qualifier in ElaboratedTypeLoc.
|
|
|
|
|
2019-01-30 19:32:48 +00:00
|
|
|
void TextNodeDumper::VisitLabelDecl(const LabelDecl *D) { dumpName(D); }
|
|
|
|
|
|
|
|
void TextNodeDumper::VisitTypedefDecl(const TypedefDecl *D) {
|
|
|
|
dumpName(D);
|
|
|
|
dumpType(D->getUnderlyingType());
|
|
|
|
if (D->isModulePrivate())
|
|
|
|
OS << " __module_private__";
|
|
|
|
}
|
|
|
|
|
|
|
|
void TextNodeDumper::VisitEnumDecl(const EnumDecl *D) {
|
|
|
|
if (D->isScoped()) {
|
|
|
|
if (D->isScopedUsingClassTag())
|
|
|
|
OS << " class";
|
|
|
|
else
|
|
|
|
OS << " struct";
|
|
|
|
}
|
|
|
|
dumpName(D);
|
|
|
|
if (D->isModulePrivate())
|
|
|
|
OS << " __module_private__";
|
|
|
|
if (D->isFixed())
|
|
|
|
dumpType(D->getIntegerType());
|
2025-02-18 17:07:03 +01:00
|
|
|
|
|
|
|
if (const auto *Instance = D->getInstantiatedFromMemberEnum()) {
|
|
|
|
OS << " instantiated_from";
|
|
|
|
dumpPointer(Instance);
|
|
|
|
}
|
2019-01-30 19:32:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void TextNodeDumper::VisitRecordDecl(const RecordDecl *D) {
|
|
|
|
OS << ' ' << D->getKindName();
|
|
|
|
dumpName(D);
|
|
|
|
if (D->isModulePrivate())
|
|
|
|
OS << " __module_private__";
|
|
|
|
if (D->isCompleteDefinition())
|
|
|
|
OS << " definition";
|
|
|
|
}
|
|
|
|
|
|
|
|
void TextNodeDumper::VisitEnumConstantDecl(const EnumConstantDecl *D) {
|
|
|
|
dumpName(D);
|
|
|
|
dumpType(D->getType());
|
|
|
|
}
|
|
|
|
|
|
|
|
void TextNodeDumper::VisitIndirectFieldDecl(const IndirectFieldDecl *D) {
|
|
|
|
dumpName(D);
|
|
|
|
dumpType(D->getType());
|
|
|
|
|
|
|
|
for (const auto *Child : D->chain())
|
|
|
|
dumpDeclRef(Child);
|
|
|
|
}
|
|
|
|
|
|
|
|
void TextNodeDumper::VisitFunctionDecl(const FunctionDecl *D) {
|
|
|
|
dumpName(D);
|
|
|
|
dumpType(D->getType());
|
2023-08-15 14:17:24 +00:00
|
|
|
dumpTemplateSpecializationKind(D->getTemplateSpecializationKind());
|
2019-01-30 19:32:48 +00:00
|
|
|
|
|
|
|
StorageClass SC = D->getStorageClass();
|
2021-01-04 23:17:45 +01:00
|
|
|
if (SC != SC_None)
|
2019-01-30 19:32:48 +00:00
|
|
|
OS << ' ' << VarDecl::getStorageClassSpecifierString(SC);
|
|
|
|
if (D->isInlineSpecified())
|
|
|
|
OS << " inline";
|
|
|
|
if (D->isVirtualAsWritten())
|
|
|
|
OS << " virtual";
|
|
|
|
if (D->isModulePrivate())
|
|
|
|
OS << " __module_private__";
|
|
|
|
|
2024-01-18 15:30:58 +01:00
|
|
|
if (D->isPureVirtual())
|
2019-01-30 19:32:48 +00:00
|
|
|
OS << " pure";
|
|
|
|
if (D->isDefaulted()) {
|
|
|
|
OS << " default";
|
|
|
|
if (D->isDeleted())
|
|
|
|
OS << "_delete";
|
|
|
|
}
|
|
|
|
if (D->isDeletedAsWritten())
|
|
|
|
OS << " delete";
|
|
|
|
if (D->isTrivial())
|
|
|
|
OS << " trivial";
|
|
|
|
|
2024-04-14 12:30:01 +02:00
|
|
|
if (const StringLiteral *M = D->getDeletedMessage())
|
|
|
|
AddChild("delete message", [=] { Visit(M); });
|
|
|
|
|
2022-06-16 20:52:12 +03:00
|
|
|
if (D->isIneligibleOrNotSelected())
|
|
|
|
OS << (isa<CXXDestructorDecl>(D) ? " not_selected" : " ineligible");
|
|
|
|
|
2019-01-30 19:32:48 +00:00
|
|
|
if (const auto *FPT = D->getType()->getAs<FunctionProtoType>()) {
|
|
|
|
FunctionProtoType::ExtProtoInfo EPI = FPT->getExtProtoInfo();
|
|
|
|
switch (EPI.ExceptionSpec.Type) {
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
case EST_Unevaluated:
|
|
|
|
OS << " noexcept-unevaluated " << EPI.ExceptionSpec.SourceDecl;
|
|
|
|
break;
|
|
|
|
case EST_Uninstantiated:
|
|
|
|
OS << " noexcept-uninstantiated " << EPI.ExceptionSpec.SourceTemplate;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (const auto *MD = dyn_cast<CXXMethodDecl>(D)) {
|
|
|
|
if (MD->size_overridden_methods() != 0) {
|
|
|
|
auto dumpOverride = [=](const CXXMethodDecl *D) {
|
|
|
|
SplitQualType T_split = D->getType().split();
|
2020-08-05 11:48:09 +01:00
|
|
|
OS << D << " " << D->getParent()->getName() << "::" << D->getDeclName()
|
|
|
|
<< " '" << QualType::getAsString(T_split, PrintPolicy) << "'";
|
2019-01-30 19:32:48 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
AddChild([=] {
|
|
|
|
auto Overrides = MD->overridden_methods();
|
|
|
|
OS << "Overrides: [ ";
|
|
|
|
dumpOverride(*Overrides.begin());
|
2023-09-22 17:29:10 -07:00
|
|
|
for (const auto *Override : llvm::drop_begin(Overrides)) {
|
2019-01-30 19:32:48 +00:00
|
|
|
OS << ", ";
|
|
|
|
dumpOverride(Override);
|
|
|
|
}
|
|
|
|
OS << " ]";
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-07-03 14:27:10 +01:00
|
|
|
if (!D->isInlineSpecified() && D->isInlined()) {
|
|
|
|
OS << " implicit-inline";
|
|
|
|
}
|
2019-01-30 19:32:48 +00:00
|
|
|
// Since NumParams comes from the FunctionProtoType of the FunctionDecl and
|
|
|
|
// the Params are set later, it is possible for a dump during debugging to
|
|
|
|
// encounter a FunctionDecl that has been created but hasn't been assigned
|
|
|
|
// ParmVarDecls yet.
|
|
|
|
if (!D->param_empty() && !D->param_begin())
|
|
|
|
OS << " <<<NULL params x " << D->getNumParams() << ">>>";
|
2023-08-17 13:23:00 +00:00
|
|
|
|
|
|
|
if (const auto *Instance = D->getInstantiatedFromMemberFunction()) {
|
|
|
|
OS << " instantiated_from";
|
|
|
|
dumpPointer(Instance);
|
|
|
|
}
|
2019-01-30 19:32:48 +00:00
|
|
|
}
|
|
|
|
|
2024-03-28 13:07:58 +01:00
|
|
|
void TextNodeDumper::VisitCXXDeductionGuideDecl(
|
|
|
|
const CXXDeductionGuideDecl *D) {
|
|
|
|
VisitFunctionDecl(D);
|
|
|
|
switch (D->getDeductionCandidateKind()) {
|
|
|
|
case DeductionCandidate::Normal:
|
|
|
|
case DeductionCandidate::Copy:
|
|
|
|
return;
|
|
|
|
case DeductionCandidate::Aggregate:
|
|
|
|
OS << " aggregate ";
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-11-30 16:42:33 +01:00
|
|
|
void TextNodeDumper::VisitLifetimeExtendedTemporaryDecl(
|
|
|
|
const LifetimeExtendedTemporaryDecl *D) {
|
|
|
|
OS << " extended by ";
|
|
|
|
dumpBareDeclRef(D->getExtendingDecl());
|
|
|
|
OS << " mangling ";
|
|
|
|
{
|
|
|
|
ColorScope Color(OS, ShowColors, ValueColor);
|
|
|
|
OS << D->getManglingNumber();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-01-30 19:32:48 +00:00
|
|
|
void TextNodeDumper::VisitFieldDecl(const FieldDecl *D) {
|
|
|
|
dumpName(D);
|
|
|
|
dumpType(D->getType());
|
|
|
|
if (D->isMutable())
|
|
|
|
OS << " mutable";
|
|
|
|
if (D->isModulePrivate())
|
|
|
|
OS << " __module_private__";
|
|
|
|
}
|
|
|
|
|
|
|
|
void TextNodeDumper::VisitVarDecl(const VarDecl *D) {
|
2023-08-15 16:26:47 +00:00
|
|
|
dumpNestedNameSpecifier(D->getQualifier());
|
2019-01-30 19:32:48 +00:00
|
|
|
dumpName(D);
|
2022-01-27 13:55:08 +01:00
|
|
|
if (const auto *P = dyn_cast<ParmVarDecl>(D);
|
|
|
|
P && P->isExplicitObjectParameter())
|
|
|
|
OS << " this";
|
|
|
|
|
2019-01-30 19:32:48 +00:00
|
|
|
dumpType(D->getType());
|
2023-08-15 14:17:24 +00:00
|
|
|
dumpTemplateSpecializationKind(D->getTemplateSpecializationKind());
|
2019-01-30 19:32:48 +00:00
|
|
|
StorageClass SC = D->getStorageClass();
|
2021-01-04 23:17:45 +01:00
|
|
|
if (SC != SC_None)
|
2019-01-30 19:32:48 +00:00
|
|
|
OS << ' ' << VarDecl::getStorageClassSpecifierString(SC);
|
|
|
|
switch (D->getTLSKind()) {
|
|
|
|
case VarDecl::TLS_None:
|
|
|
|
break;
|
|
|
|
case VarDecl::TLS_Static:
|
|
|
|
OS << " tls";
|
|
|
|
break;
|
|
|
|
case VarDecl::TLS_Dynamic:
|
|
|
|
OS << " tls_dynamic";
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (D->isModulePrivate())
|
|
|
|
OS << " __module_private__";
|
|
|
|
if (D->isNRVOVariable())
|
|
|
|
OS << " nrvo";
|
|
|
|
if (D->isInline())
|
|
|
|
OS << " inline";
|
|
|
|
if (D->isConstexpr())
|
|
|
|
OS << " constexpr";
|
|
|
|
if (D->hasInit()) {
|
|
|
|
switch (D->getInitStyle()) {
|
|
|
|
case VarDecl::CInit:
|
|
|
|
OS << " cinit";
|
|
|
|
break;
|
|
|
|
case VarDecl::CallInit:
|
|
|
|
OS << " callinit";
|
|
|
|
break;
|
|
|
|
case VarDecl::ListInit:
|
|
|
|
OS << " listinit";
|
|
|
|
break;
|
2023-01-04 15:12:00 -08:00
|
|
|
case VarDecl::ParenListInit:
|
|
|
|
OS << " parenlistinit";
|
2019-01-30 19:32:48 +00:00
|
|
|
}
|
|
|
|
}
|
2019-09-29 05:08:46 +00:00
|
|
|
if (D->needsDestruction(D->getASTContext()))
|
|
|
|
OS << " destroyed";
|
2019-05-21 20:10:50 +00:00
|
|
|
if (D->isParameterPack())
|
|
|
|
OS << " pack";
|
[clang] Rework how and when APValues are dumped
Currently APValues are dumped as a single string. This becomes quickly
completely unreadable since APValue is a tree-like structure. Even a simple
example is not pretty:
struct S { int arr[4]; float f; };
constexpr S s = { .arr = {1,2}, .f = 3.1415f };
// Struct fields: Array: Int: 1, Int: 2, 2 x Int: 0, Float: 3.141500e+00
With this patch this becomes:
-Struct
|-field: Array size=4
| |-elements: Int 1, Int 2
| `-filler: 2 x Int 0
`-field: Float 3.141500e+00
Additionally APValues are currently only dumped as part of visiting a
ConstantExpr. This patch also dump the value of the initializer of constexpr
variable declarations:
constexpr int foo(int a, int b) { return a + b - 42; }
constexpr int a = 1, b = 2;
constexpr int c = foo(a, b) > 0 ? foo(a, b) : foo(b, a);
// VarDecl 0x62100008aec8 <col:3, col:57> col:17 c 'const int' constexpr cinit
// |-value: Int -39
// `-ConditionalOperator 0x62100008b4d0 <col:21, col:57> 'int'
// <snip>
Do the above by moving the dump functions to TextNodeDumper which already has
the machinery to display trees. The cases APValue::LValue, APValue::MemberPointer
and APValue::AddrLabelDiff are left as they were before (unimplemented).
We try to display multiple elements on the same line if they are considered to
be "simple". This is to avoid wasting large amounts of vertical space in an
example like:
constexpr int arr[8] = {0,1,2,3,4,5,6,7};
// VarDecl 0x62100008bb78 <col:3, col:42> col:17 arr 'int const[8]' constexpr cinit
// |-value: Array size=8
// | |-elements: Int 0, Int 1, Int 2, Int 3
// | `-elements: Int 4, Int 5, Int 6, Int 7
Differential Revision: https://reviews.llvm.org/D83183
Reviewed By: aaron.ballman
2020-07-06 21:50:23 +01:00
|
|
|
|
|
|
|
if (D->hasInit()) {
|
|
|
|
const Expr *E = D->getInit();
|
|
|
|
// Only dump the value of constexpr VarDecls for now.
|
2023-05-23 00:29:20 +09:00
|
|
|
if (E && !E->isValueDependent() && D->isConstexpr() &&
|
|
|
|
!D->getType()->isDependentType()) {
|
[clang] Rework how and when APValues are dumped
Currently APValues are dumped as a single string. This becomes quickly
completely unreadable since APValue is a tree-like structure. Even a simple
example is not pretty:
struct S { int arr[4]; float f; };
constexpr S s = { .arr = {1,2}, .f = 3.1415f };
// Struct fields: Array: Int: 1, Int: 2, 2 x Int: 0, Float: 3.141500e+00
With this patch this becomes:
-Struct
|-field: Array size=4
| |-elements: Int 1, Int 2
| `-filler: 2 x Int 0
`-field: Float 3.141500e+00
Additionally APValues are currently only dumped as part of visiting a
ConstantExpr. This patch also dump the value of the initializer of constexpr
variable declarations:
constexpr int foo(int a, int b) { return a + b - 42; }
constexpr int a = 1, b = 2;
constexpr int c = foo(a, b) > 0 ? foo(a, b) : foo(b, a);
// VarDecl 0x62100008aec8 <col:3, col:57> col:17 c 'const int' constexpr cinit
// |-value: Int -39
// `-ConditionalOperator 0x62100008b4d0 <col:21, col:57> 'int'
// <snip>
Do the above by moving the dump functions to TextNodeDumper which already has
the machinery to display trees. The cases APValue::LValue, APValue::MemberPointer
and APValue::AddrLabelDiff are left as they were before (unimplemented).
We try to display multiple elements on the same line if they are considered to
be "simple". This is to avoid wasting large amounts of vertical space in an
example like:
constexpr int arr[8] = {0,1,2,3,4,5,6,7};
// VarDecl 0x62100008bb78 <col:3, col:42> col:17 arr 'int const[8]' constexpr cinit
// |-value: Array size=8
// | |-elements: Int 0, Int 1, Int 2, Int 3
// | `-elements: Int 4, Int 5, Int 6, Int 7
Differential Revision: https://reviews.llvm.org/D83183
Reviewed By: aaron.ballman
2020-07-06 21:50:23 +01:00
|
|
|
const APValue *Value = D->evaluateValue();
|
|
|
|
if (Value)
|
|
|
|
AddChild("value", [=] { Visit(*Value, E->getType()); });
|
|
|
|
}
|
|
|
|
}
|
2019-01-30 19:32:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void TextNodeDumper::VisitBindingDecl(const BindingDecl *D) {
|
|
|
|
dumpName(D);
|
|
|
|
dumpType(D->getType());
|
|
|
|
}
|
|
|
|
|
|
|
|
void TextNodeDumper::VisitCapturedDecl(const CapturedDecl *D) {
|
|
|
|
if (D->isNothrow())
|
|
|
|
OS << " nothrow";
|
|
|
|
}
|
|
|
|
|
|
|
|
void TextNodeDumper::VisitImportDecl(const ImportDecl *D) {
|
|
|
|
OS << ' ' << D->getImportedModule()->getFullModuleName();
|
2019-02-05 23:37:13 +00:00
|
|
|
|
|
|
|
for (Decl *InitD :
|
|
|
|
D->getASTContext().getModuleInitializers(D->getImportedModule()))
|
|
|
|
dumpDeclRef(InitD, "initializer");
|
2019-01-30 19:32:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void TextNodeDumper::VisitPragmaCommentDecl(const PragmaCommentDecl *D) {
|
|
|
|
OS << ' ';
|
|
|
|
switch (D->getCommentKind()) {
|
|
|
|
case PCK_Unknown:
|
|
|
|
llvm_unreachable("unexpected pragma comment kind");
|
|
|
|
case PCK_Compiler:
|
|
|
|
OS << "compiler";
|
|
|
|
break;
|
|
|
|
case PCK_ExeStr:
|
|
|
|
OS << "exestr";
|
|
|
|
break;
|
|
|
|
case PCK_Lib:
|
|
|
|
OS << "lib";
|
|
|
|
break;
|
|
|
|
case PCK_Linker:
|
|
|
|
OS << "linker";
|
|
|
|
break;
|
|
|
|
case PCK_User:
|
|
|
|
OS << "user";
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
StringRef Arg = D->getArg();
|
|
|
|
if (!Arg.empty())
|
|
|
|
OS << " \"" << Arg << "\"";
|
|
|
|
}
|
|
|
|
|
|
|
|
void TextNodeDumper::VisitPragmaDetectMismatchDecl(
|
|
|
|
const PragmaDetectMismatchDecl *D) {
|
|
|
|
OS << " \"" << D->getName() << "\" \"" << D->getValue() << "\"";
|
|
|
|
}
|
|
|
|
|
[clang][OpeMP] Model OpenMP structured-block in AST (PR40563)
Summary:
https://www.openmp.org/wp-content/uploads/OpenMP-API-Specification-5.0.pdf, page 3:
```
structured block
For C/C++, an executable statement, possibly compound, with a single entry at the
top and a single exit at the bottom, or an OpenMP construct.
COMMENT: See Section 2.1 on page 38 for restrictions on structured
blocks.
```
```
2.1 Directive Format
Some executable directives include a structured block. A structured block:
• may contain infinite loops where the point of exit is never reached;
• may halt due to an IEEE exception;
• may contain calls to exit(), _Exit(), quick_exit(), abort() or functions with a
_Noreturn specifier (in C) or a noreturn attribute (in C/C++);
• may be an expression statement, iteration statement, selection statement, or try block, provided
that the corresponding compound statement obtained by enclosing it in { and } would be a
structured block; and
Restrictions
Restrictions to structured blocks are as follows:
• Entry to a structured block must not be the result of a branch.
• The point of exit cannot be a branch out of the structured block.
C / C++
• The point of entry to a structured block must not be a call to setjmp().
• longjmp() and throw() must not violate the entry/exit criteria.
```
Of particular note here is the fact that OpenMP structured blocks are as-if `noexcept`,
in the same sense as with the normal `noexcept` functions in C++.
I.e. if throw happens, and it attempts to travel out of the `noexcept` function
(here: out of the current structured-block), then the program terminates.
Now, one of course can say that since it is explicitly prohibited by the Specification,
then any and all programs that violate this Specification contain undefined behavior,
and are unspecified, and thus no one should care about them. Just don't write broken code /s
But i'm not sure this is a reasonable approach.
I have personally had oss-fuzz issues of this origin - exception thrown inside
of an OpenMP structured-block that is not caught, thus causing program termination.
This issue isn't all that hard to catch, it's not any particularly different from
diagnosing the same situation with the normal `noexcept` function.
Now, clang static analyzer does not presently model exceptions.
But clang-tidy has a simplisic [[ https://clang.llvm.org/extra/clang-tidy/checks/bugprone-exception-escape.html | bugprone-exception-escape ]] check,
and it is even refactored as a `ExceptionAnalyzer` class for reuse.
So it would be trivial to use that analyzer to check for
exceptions escaping out of OpenMP structured blocks. (D59466)
All that sounds too great to be true. Indeed, there is a caveat.
Presently, it's practically impossible to do. To check a OpenMP structured block
you need to somehow 'get' the OpenMP structured block, and you can't because
it's simply not modelled in AST. `CapturedStmt`/`CapturedDecl` is not it's representation.
Now, it is of course possible to write e.g. some AST matcher that would e.g.
match every OpenMP executable directive, and then return the whatever `Stmt` is
the structured block of said executable directive, if any.
But i said //practically//. This isn't practical for the following reasons:
1. This **will** bitrot. That matcher will need to be kept up-to-date,
and refreshed with every new OpenMP spec version.
2. Every single piece of code that would want that knowledge would need to
have such matcher. Well, okay, if it is an AST matcher, it could be shared.
But then you still have `RecursiveASTVisitor` and friends.
`2 > 1`, so now you have code duplication.
So it would be reasonable (and is fully within clang AST spirit) to not
force every single consumer to do that work, but instead store that knowledge
in the correct, and appropriate place - AST, class structure.
Now, there is another hoop we need to get through.
It isn't fully obvious //how// to model this.
The best solution would of course be to simply add a `OMPStructuredBlock` transparent
node. It would be optimal, it would give us two properties:
* Given this `OMPExecutableDirective`, what's it OpenMP structured block?
* It is trivial to check whether the `Stmt*` is a OpenMP structured block (`isa<OMPStructuredBlock>(ptr)`)
But OpenMP structured block isn't **necessarily** the first, direct child of `OMP*Directive`.
(even ignoring the clang's `CapturedStmt`/`CapturedDecl` that were inserted inbetween).
So i'm not sure whether or not we could re-create AST statements after they were already created?
There would be other costs to a new AST node: https://bugs.llvm.org/show_bug.cgi?id=40563#c12
```
1. You will need to break the representation of loops. The body should be replaced by the "structured block" entity.
2. You will need to support serialization/deserialization.
3. You will need to support template instantiation.
4. You will need to support codegen and take this new construct to account in each OpenMP directive.
```
Instead, there **is** an functionally-equivalent, alternative solution, consisting of two parts.
Part 1:
* Add a member function `isStandaloneDirective()` to the `OMPExecutableDirective` class,
that will tell whether this directive is stand-alone or not, as per the spec.
We need it because we can't just check for the existance of associated statements,
see code comment.
* Add a member function `getStructuredBlock()` to the OMPExecutableDirective` class itself,
that assert that this is not a stand-alone directive, and either return the correct loop body
if this is a loop-like directive, or the captured statement.
This way, given an `OMPExecutableDirective`, we can get it's structured block.
Also, since the knowledge is ingrained into the clang OpenMP implementation,
it will not cause any duplication, and //hopefully// won't bitrot.
Great we achieved 1 of 2 properties of `OMPStructuredBlock` approach.
Thus, there is a second part needed:
* How can we check whether a given `Stmt*` is `OMPStructuredBlock`?
Well, we can't really, in general. I can see this workaround:
```
class FunctionASTVisitor : public RecursiveASTVisitor<FunctionASTVisitor> {
using Base = RecursiveASTVisitor<FunctionASTVisitor>;
public:
bool VisitOMPExecDir(OMPExecDir *D) {
OmpStructuredStmts.emplace_back(D.getStructuredStmt());
}
bool VisitSOMETHINGELSE(???) {
if(InOmpStructuredStmt)
HI!
}
bool TraverseStmt(Stmt *Node) {
if (!Node)
return Base::TraverseStmt(Node);
if (OmpStructuredStmts.back() == Node)
++InOmpStructuredStmt;
Base::TraverseStmt(Node);
if (OmpStructuredStmts.back() == Node) {
OmpStructuredStmts.pop_back();
--InOmpStructuredStmt;
}
return true;
}
std::vector<Stmt*> OmpStructuredStmts;
int InOmpStructuredStmt = 0;
};
```
But i really don't see using it in practice.
It's just too intrusive; and again, requires knowledge duplication.
.. but no. The solution lies right on the ground.
Why don't we simply store this `i'm a openmp structured block` in the bitfield of the `Stmt` itself?
This does not appear to have any impact on the memory footprint of the clang AST,
since it's just a single extra bit in the bitfield. At least the static assertions don't fail.
Thus, indeed, we can achieve both of the properties without a new AST node.
We can cheaply set that bit right in sema, at the end of `Sema::ActOnOpenMPExecutableDirective()`,
by just calling the `getStructuredBlock()` that we just added.
Test coverage that demonstrates all this has been added.
This isn't as great with serialization though. Most of it does not use abbrevs,
so we do end up paying the full price (4 bytes?) instead of a single bit.
That price, of course, can be reclaimed by using abbrevs.
In fact, i suspect that //might// not just reclaim these bytes, but pack these PCH significantly.
I'm not seeing a third solution. If there is one, it would be interesting to hear about it.
("just don't write code that would require `isa<OMPStructuredBlock>(ptr)`" is not a solution.)
Fixes [[ https://bugs.llvm.org/show_bug.cgi?id=40563 | PR40563 ]].
Reviewers: ABataev, rjmccall, hfinkel, rsmith, riccibruno, gribozavr
Reviewed By: ABataev, gribozavr
Subscribers: mgorny, aaron.ballman, steveire, guansong, jfb, jdoerfert, cfe-commits
Tags: #clang, #openmp
Differential Revision: https://reviews.llvm.org/D59214
llvm-svn: 356570
2019-03-20 16:32:36 +00:00
|
|
|
void TextNodeDumper::VisitOMPExecutableDirective(
|
|
|
|
const OMPExecutableDirective *D) {
|
|
|
|
if (D->isStandaloneDirective())
|
|
|
|
OS << " openmp_standalone_directive";
|
|
|
|
}
|
|
|
|
|
2019-01-30 19:32:48 +00:00
|
|
|
void TextNodeDumper::VisitOMPDeclareReductionDecl(
|
|
|
|
const OMPDeclareReductionDecl *D) {
|
|
|
|
dumpName(D);
|
|
|
|
dumpType(D->getType());
|
|
|
|
OS << " combiner";
|
|
|
|
dumpPointer(D->getCombiner());
|
|
|
|
if (const auto *Initializer = D->getInitializer()) {
|
|
|
|
OS << " initializer";
|
|
|
|
dumpPointer(Initializer);
|
|
|
|
switch (D->getInitializerKind()) {
|
2023-11-01 12:39:33 +03:00
|
|
|
case OMPDeclareReductionInitKind::Direct:
|
2019-01-30 19:32:48 +00:00
|
|
|
OS << " omp_priv = ";
|
|
|
|
break;
|
2023-11-01 12:39:33 +03:00
|
|
|
case OMPDeclareReductionInitKind::Copy:
|
2019-01-30 19:32:48 +00:00
|
|
|
OS << " omp_priv ()";
|
|
|
|
break;
|
2023-11-01 12:39:33 +03:00
|
|
|
case OMPDeclareReductionInitKind::Call:
|
2019-01-30 19:32:48 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void TextNodeDumper::VisitOMPRequiresDecl(const OMPRequiresDecl *D) {
|
|
|
|
for (const auto *C : D->clauselists()) {
|
|
|
|
AddChild([=] {
|
|
|
|
if (!C) {
|
|
|
|
ColorScope Color(OS, ShowColors, NullColor);
|
|
|
|
OS << "<<<NULL>>> OMPClause";
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
{
|
|
|
|
ColorScope Color(OS, ShowColors, AttrColor);
|
2020-03-30 19:58:40 -05:00
|
|
|
StringRef ClauseName(
|
|
|
|
llvm::omp::getOpenMPClauseName(C->getClauseKind()));
|
2019-01-30 19:32:48 +00:00
|
|
|
OS << "OMP" << ClauseName.substr(/*Start=*/0, /*N=*/1).upper()
|
|
|
|
<< ClauseName.drop_front() << "Clause";
|
|
|
|
}
|
|
|
|
dumpPointer(C);
|
|
|
|
dumpSourceRange(SourceRange(C->getBeginLoc(), C->getEndLoc()));
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void TextNodeDumper::VisitOMPCapturedExprDecl(const OMPCapturedExprDecl *D) {
|
|
|
|
dumpName(D);
|
|
|
|
dumpType(D->getType());
|
|
|
|
}
|
|
|
|
|
|
|
|
void TextNodeDumper::VisitNamespaceDecl(const NamespaceDecl *D) {
|
|
|
|
dumpName(D);
|
|
|
|
if (D->isInline())
|
|
|
|
OS << " inline";
|
2022-11-24 12:44:33 +00:00
|
|
|
if (D->isNested())
|
|
|
|
OS << " nested";
|
2024-07-15 13:57:56 -04:00
|
|
|
if (!D->isFirstDecl())
|
|
|
|
dumpDeclRef(D->getFirstDecl(), "original");
|
2019-01-30 19:32:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void TextNodeDumper::VisitUsingDirectiveDecl(const UsingDirectiveDecl *D) {
|
|
|
|
OS << ' ';
|
|
|
|
dumpBareDeclRef(D->getNominatedNamespace());
|
|
|
|
}
|
|
|
|
|
|
|
|
void TextNodeDumper::VisitNamespaceAliasDecl(const NamespaceAliasDecl *D) {
|
|
|
|
dumpName(D);
|
|
|
|
dumpDeclRef(D->getAliasedNamespace());
|
|
|
|
}
|
|
|
|
|
|
|
|
void TextNodeDumper::VisitTypeAliasDecl(const TypeAliasDecl *D) {
|
|
|
|
dumpName(D);
|
|
|
|
dumpType(D->getUnderlyingType());
|
|
|
|
}
|
|
|
|
|
|
|
|
void TextNodeDumper::VisitTypeAliasTemplateDecl(
|
|
|
|
const TypeAliasTemplateDecl *D) {
|
|
|
|
dumpName(D);
|
|
|
|
}
|
|
|
|
|
|
|
|
void TextNodeDumper::VisitCXXRecordDecl(const CXXRecordDecl *D) {
|
|
|
|
VisitRecordDecl(D);
|
2023-08-17 13:23:00 +00:00
|
|
|
if (const auto *Instance = D->getInstantiatedFromMemberClass()) {
|
|
|
|
OS << " instantiated_from";
|
|
|
|
dumpPointer(Instance);
|
|
|
|
}
|
2025-02-05 12:02:24 -03:00
|
|
|
if (const auto *CTSD = dyn_cast<ClassTemplateSpecializationDecl>(D)) {
|
2023-08-15 14:17:24 +00:00
|
|
|
dumpTemplateSpecializationKind(CTSD->getSpecializationKind());
|
2025-02-05 13:16:33 -03:00
|
|
|
if (CTSD->hasStrictPackMatch())
|
2025-02-05 12:02:24 -03:00
|
|
|
OS << " strict-pack-match";
|
|
|
|
}
|
2023-08-15 14:17:24 +00:00
|
|
|
|
2023-08-15 16:26:47 +00:00
|
|
|
dumpNestedNameSpecifier(D->getQualifier());
|
|
|
|
|
2019-01-30 19:32:48 +00:00
|
|
|
if (!D->isCompleteDefinition())
|
|
|
|
return;
|
|
|
|
|
|
|
|
AddChild([=] {
|
|
|
|
{
|
|
|
|
ColorScope Color(OS, ShowColors, DeclKindNameColor);
|
|
|
|
OS << "DefinitionData";
|
|
|
|
}
|
|
|
|
#define FLAG(fn, name) \
|
|
|
|
if (D->fn()) \
|
|
|
|
OS << " " #name;
|
|
|
|
FLAG(isParsingBaseSpecifiers, parsing_base_specifiers);
|
|
|
|
|
|
|
|
FLAG(isGenericLambda, generic);
|
|
|
|
FLAG(isLambda, lambda);
|
|
|
|
|
2019-08-12 17:07:49 +00:00
|
|
|
FLAG(isAnonymousStructOrUnion, is_anonymous);
|
2019-01-30 19:32:48 +00:00
|
|
|
FLAG(canPassInRegisters, pass_in_registers);
|
|
|
|
FLAG(isEmpty, empty);
|
|
|
|
FLAG(isAggregate, aggregate);
|
|
|
|
FLAG(isStandardLayout, standard_layout);
|
|
|
|
FLAG(isTriviallyCopyable, trivially_copyable);
|
|
|
|
FLAG(isPOD, pod);
|
|
|
|
FLAG(isTrivial, trivial);
|
|
|
|
FLAG(isPolymorphic, polymorphic);
|
|
|
|
FLAG(isAbstract, abstract);
|
|
|
|
FLAG(isLiteral, literal);
|
|
|
|
|
|
|
|
FLAG(hasUserDeclaredConstructor, has_user_declared_ctor);
|
|
|
|
FLAG(hasConstexprNonCopyMoveConstructor, has_constexpr_non_copy_move_ctor);
|
|
|
|
FLAG(hasMutableFields, has_mutable_fields);
|
|
|
|
FLAG(hasVariantMembers, has_variant_members);
|
|
|
|
FLAG(allowConstDefaultInit, can_const_default_init);
|
|
|
|
|
|
|
|
AddChild([=] {
|
|
|
|
{
|
|
|
|
ColorScope Color(OS, ShowColors, DeclKindNameColor);
|
|
|
|
OS << "DefaultConstructor";
|
|
|
|
}
|
|
|
|
FLAG(hasDefaultConstructor, exists);
|
|
|
|
FLAG(hasTrivialDefaultConstructor, trivial);
|
|
|
|
FLAG(hasNonTrivialDefaultConstructor, non_trivial);
|
|
|
|
FLAG(hasUserProvidedDefaultConstructor, user_provided);
|
|
|
|
FLAG(hasConstexprDefaultConstructor, constexpr);
|
|
|
|
FLAG(needsImplicitDefaultConstructor, needs_implicit);
|
|
|
|
FLAG(defaultedDefaultConstructorIsConstexpr, defaulted_is_constexpr);
|
|
|
|
});
|
|
|
|
|
|
|
|
AddChild([=] {
|
|
|
|
{
|
|
|
|
ColorScope Color(OS, ShowColors, DeclKindNameColor);
|
|
|
|
OS << "CopyConstructor";
|
|
|
|
}
|
|
|
|
FLAG(hasSimpleCopyConstructor, simple);
|
|
|
|
FLAG(hasTrivialCopyConstructor, trivial);
|
|
|
|
FLAG(hasNonTrivialCopyConstructor, non_trivial);
|
|
|
|
FLAG(hasUserDeclaredCopyConstructor, user_declared);
|
|
|
|
FLAG(hasCopyConstructorWithConstParam, has_const_param);
|
|
|
|
FLAG(needsImplicitCopyConstructor, needs_implicit);
|
|
|
|
FLAG(needsOverloadResolutionForCopyConstructor,
|
|
|
|
needs_overload_resolution);
|
|
|
|
if (!D->needsOverloadResolutionForCopyConstructor())
|
|
|
|
FLAG(defaultedCopyConstructorIsDeleted, defaulted_is_deleted);
|
|
|
|
FLAG(implicitCopyConstructorHasConstParam, implicit_has_const_param);
|
|
|
|
});
|
|
|
|
|
|
|
|
AddChild([=] {
|
|
|
|
{
|
|
|
|
ColorScope Color(OS, ShowColors, DeclKindNameColor);
|
|
|
|
OS << "MoveConstructor";
|
|
|
|
}
|
|
|
|
FLAG(hasMoveConstructor, exists);
|
|
|
|
FLAG(hasSimpleMoveConstructor, simple);
|
|
|
|
FLAG(hasTrivialMoveConstructor, trivial);
|
|
|
|
FLAG(hasNonTrivialMoveConstructor, non_trivial);
|
|
|
|
FLAG(hasUserDeclaredMoveConstructor, user_declared);
|
|
|
|
FLAG(needsImplicitMoveConstructor, needs_implicit);
|
|
|
|
FLAG(needsOverloadResolutionForMoveConstructor,
|
|
|
|
needs_overload_resolution);
|
|
|
|
if (!D->needsOverloadResolutionForMoveConstructor())
|
|
|
|
FLAG(defaultedMoveConstructorIsDeleted, defaulted_is_deleted);
|
|
|
|
});
|
|
|
|
|
|
|
|
AddChild([=] {
|
|
|
|
{
|
|
|
|
ColorScope Color(OS, ShowColors, DeclKindNameColor);
|
|
|
|
OS << "CopyAssignment";
|
|
|
|
}
|
2020-06-04 19:16:05 -07:00
|
|
|
FLAG(hasSimpleCopyAssignment, simple);
|
2019-01-30 19:32:48 +00:00
|
|
|
FLAG(hasTrivialCopyAssignment, trivial);
|
|
|
|
FLAG(hasNonTrivialCopyAssignment, non_trivial);
|
|
|
|
FLAG(hasCopyAssignmentWithConstParam, has_const_param);
|
|
|
|
FLAG(hasUserDeclaredCopyAssignment, user_declared);
|
|
|
|
FLAG(needsImplicitCopyAssignment, needs_implicit);
|
|
|
|
FLAG(needsOverloadResolutionForCopyAssignment, needs_overload_resolution);
|
|
|
|
FLAG(implicitCopyAssignmentHasConstParam, implicit_has_const_param);
|
|
|
|
});
|
|
|
|
|
|
|
|
AddChild([=] {
|
|
|
|
{
|
|
|
|
ColorScope Color(OS, ShowColors, DeclKindNameColor);
|
|
|
|
OS << "MoveAssignment";
|
|
|
|
}
|
|
|
|
FLAG(hasMoveAssignment, exists);
|
|
|
|
FLAG(hasSimpleMoveAssignment, simple);
|
|
|
|
FLAG(hasTrivialMoveAssignment, trivial);
|
|
|
|
FLAG(hasNonTrivialMoveAssignment, non_trivial);
|
|
|
|
FLAG(hasUserDeclaredMoveAssignment, user_declared);
|
|
|
|
FLAG(needsImplicitMoveAssignment, needs_implicit);
|
|
|
|
FLAG(needsOverloadResolutionForMoveAssignment, needs_overload_resolution);
|
|
|
|
});
|
|
|
|
|
|
|
|
AddChild([=] {
|
|
|
|
{
|
|
|
|
ColorScope Color(OS, ShowColors, DeclKindNameColor);
|
|
|
|
OS << "Destructor";
|
|
|
|
}
|
|
|
|
FLAG(hasSimpleDestructor, simple);
|
|
|
|
FLAG(hasIrrelevantDestructor, irrelevant);
|
|
|
|
FLAG(hasTrivialDestructor, trivial);
|
|
|
|
FLAG(hasNonTrivialDestructor, non_trivial);
|
|
|
|
FLAG(hasUserDeclaredDestructor, user_declared);
|
2020-09-07 14:50:13 +02:00
|
|
|
FLAG(hasConstexprDestructor, constexpr);
|
2019-01-30 19:32:48 +00:00
|
|
|
FLAG(needsImplicitDestructor, needs_implicit);
|
|
|
|
FLAG(needsOverloadResolutionForDestructor, needs_overload_resolution);
|
|
|
|
if (!D->needsOverloadResolutionForDestructor())
|
|
|
|
FLAG(defaultedDestructorIsDeleted, defaulted_is_deleted);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
for (const auto &I : D->bases()) {
|
|
|
|
AddChild([=] {
|
|
|
|
if (I.isVirtual())
|
|
|
|
OS << "virtual ";
|
|
|
|
dumpAccessSpecifier(I.getAccessSpecifier());
|
|
|
|
dumpType(I.getType());
|
|
|
|
if (I.isPackExpansion())
|
|
|
|
OS << "...";
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void TextNodeDumper::VisitFunctionTemplateDecl(const FunctionTemplateDecl *D) {
|
|
|
|
dumpName(D);
|
|
|
|
}
|
|
|
|
|
|
|
|
void TextNodeDumper::VisitClassTemplateDecl(const ClassTemplateDecl *D) {
|
|
|
|
dumpName(D);
|
|
|
|
}
|
|
|
|
|
|
|
|
void TextNodeDumper::VisitVarTemplateDecl(const VarTemplateDecl *D) {
|
|
|
|
dumpName(D);
|
|
|
|
}
|
|
|
|
|
|
|
|
void TextNodeDumper::VisitBuiltinTemplateDecl(const BuiltinTemplateDecl *D) {
|
|
|
|
dumpName(D);
|
|
|
|
}
|
|
|
|
|
|
|
|
void TextNodeDumper::VisitTemplateTypeParmDecl(const TemplateTypeParmDecl *D) {
|
2020-01-15 02:48:42 +02:00
|
|
|
if (const auto *TC = D->getTypeConstraint()) {
|
|
|
|
OS << " ";
|
|
|
|
dumpBareDeclRef(TC->getNamedConcept());
|
|
|
|
if (TC->getNamedConcept() != TC->getFoundDecl()) {
|
|
|
|
OS << " (";
|
|
|
|
dumpBareDeclRef(TC->getFoundDecl());
|
|
|
|
OS << ")";
|
|
|
|
}
|
|
|
|
} else if (D->wasDeclaredWithTypename())
|
2019-01-30 19:32:48 +00:00
|
|
|
OS << " typename";
|
|
|
|
else
|
|
|
|
OS << " class";
|
|
|
|
OS << " depth " << D->getDepth() << " index " << D->getIndex();
|
|
|
|
if (D->isParameterPack())
|
|
|
|
OS << " ...";
|
|
|
|
dumpName(D);
|
|
|
|
}
|
|
|
|
|
|
|
|
void TextNodeDumper::VisitNonTypeTemplateParmDecl(
|
|
|
|
const NonTypeTemplateParmDecl *D) {
|
|
|
|
dumpType(D->getType());
|
|
|
|
OS << " depth " << D->getDepth() << " index " << D->getIndex();
|
|
|
|
if (D->isParameterPack())
|
|
|
|
OS << " ...";
|
|
|
|
dumpName(D);
|
|
|
|
}
|
|
|
|
|
|
|
|
void TextNodeDumper::VisitTemplateTemplateParmDecl(
|
|
|
|
const TemplateTemplateParmDecl *D) {
|
|
|
|
OS << " depth " << D->getDepth() << " index " << D->getIndex();
|
|
|
|
if (D->isParameterPack())
|
|
|
|
OS << " ...";
|
|
|
|
dumpName(D);
|
|
|
|
}
|
|
|
|
|
|
|
|
void TextNodeDumper::VisitUsingDecl(const UsingDecl *D) {
|
|
|
|
OS << ' ';
|
|
|
|
if (D->getQualifier())
|
|
|
|
D->getQualifier()->print(OS, D->getASTContext().getPrintingPolicy());
|
2020-08-05 11:48:09 +01:00
|
|
|
OS << D->getDeclName();
|
2023-08-15 16:26:47 +00:00
|
|
|
dumpNestedNameSpecifier(D->getQualifier());
|
2019-01-30 19:32:48 +00:00
|
|
|
}
|
|
|
|
|
2021-05-05 08:55:02 -07:00
|
|
|
void TextNodeDumper::VisitUsingEnumDecl(const UsingEnumDecl *D) {
|
|
|
|
OS << ' ';
|
|
|
|
dumpBareDeclRef(D->getEnumDecl());
|
|
|
|
}
|
|
|
|
|
2019-01-30 19:32:48 +00:00
|
|
|
void TextNodeDumper::VisitUnresolvedUsingTypenameDecl(
|
|
|
|
const UnresolvedUsingTypenameDecl *D) {
|
|
|
|
OS << ' ';
|
|
|
|
if (D->getQualifier())
|
|
|
|
D->getQualifier()->print(OS, D->getASTContext().getPrintingPolicy());
|
2020-08-05 11:48:09 +01:00
|
|
|
OS << D->getDeclName();
|
2019-01-30 19:32:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void TextNodeDumper::VisitUnresolvedUsingValueDecl(
|
|
|
|
const UnresolvedUsingValueDecl *D) {
|
|
|
|
OS << ' ';
|
|
|
|
if (D->getQualifier())
|
|
|
|
D->getQualifier()->print(OS, D->getASTContext().getPrintingPolicy());
|
2020-08-05 11:48:09 +01:00
|
|
|
OS << D->getDeclName();
|
2019-01-30 19:32:48 +00:00
|
|
|
dumpType(D->getType());
|
|
|
|
}
|
|
|
|
|
|
|
|
void TextNodeDumper::VisitUsingShadowDecl(const UsingShadowDecl *D) {
|
|
|
|
OS << ' ';
|
|
|
|
dumpBareDeclRef(D->getTargetDecl());
|
|
|
|
}
|
|
|
|
|
|
|
|
void TextNodeDumper::VisitConstructorUsingShadowDecl(
|
|
|
|
const ConstructorUsingShadowDecl *D) {
|
|
|
|
if (D->constructsVirtualBase())
|
|
|
|
OS << " virtual";
|
|
|
|
|
|
|
|
AddChild([=] {
|
|
|
|
OS << "target ";
|
|
|
|
dumpBareDeclRef(D->getTargetDecl());
|
|
|
|
});
|
|
|
|
|
|
|
|
AddChild([=] {
|
|
|
|
OS << "nominated ";
|
|
|
|
dumpBareDeclRef(D->getNominatedBaseClass());
|
|
|
|
OS << ' ';
|
|
|
|
dumpBareDeclRef(D->getNominatedBaseClassShadowDecl());
|
|
|
|
});
|
|
|
|
|
|
|
|
AddChild([=] {
|
|
|
|
OS << "constructed ";
|
|
|
|
dumpBareDeclRef(D->getConstructedBaseClass());
|
|
|
|
OS << ' ';
|
|
|
|
dumpBareDeclRef(D->getConstructedBaseClassShadowDecl());
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
void TextNodeDumper::VisitLinkageSpecDecl(const LinkageSpecDecl *D) {
|
|
|
|
switch (D->getLanguage()) {
|
2023-11-01 16:43:18 +03:00
|
|
|
case LinkageSpecLanguageIDs::C:
|
2019-01-30 19:32:48 +00:00
|
|
|
OS << " C";
|
|
|
|
break;
|
2023-11-01 16:43:18 +03:00
|
|
|
case LinkageSpecLanguageIDs::CXX:
|
2019-01-30 19:32:48 +00:00
|
|
|
OS << " C++";
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void TextNodeDumper::VisitAccessSpecDecl(const AccessSpecDecl *D) {
|
|
|
|
OS << ' ';
|
|
|
|
dumpAccessSpecifier(D->getAccess());
|
|
|
|
}
|
|
|
|
|
|
|
|
void TextNodeDumper::VisitFriendDecl(const FriendDecl *D) {
|
|
|
|
if (TypeSourceInfo *T = D->getFriendType())
|
|
|
|
dumpType(T->getType());
|
2024-08-15 21:16:30 +02:00
|
|
|
if (D->isPackExpansion())
|
|
|
|
OS << "...";
|
2019-01-30 19:32:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void TextNodeDumper::VisitObjCIvarDecl(const ObjCIvarDecl *D) {
|
|
|
|
dumpName(D);
|
|
|
|
dumpType(D->getType());
|
|
|
|
if (D->getSynthesize())
|
|
|
|
OS << " synthesize";
|
|
|
|
|
|
|
|
switch (D->getAccessControl()) {
|
|
|
|
case ObjCIvarDecl::None:
|
|
|
|
OS << " none";
|
|
|
|
break;
|
|
|
|
case ObjCIvarDecl::Private:
|
|
|
|
OS << " private";
|
|
|
|
break;
|
|
|
|
case ObjCIvarDecl::Protected:
|
|
|
|
OS << " protected";
|
|
|
|
break;
|
|
|
|
case ObjCIvarDecl::Public:
|
|
|
|
OS << " public";
|
|
|
|
break;
|
|
|
|
case ObjCIvarDecl::Package:
|
|
|
|
OS << " package";
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void TextNodeDumper::VisitObjCMethodDecl(const ObjCMethodDecl *D) {
|
|
|
|
if (D->isInstanceMethod())
|
|
|
|
OS << " -";
|
|
|
|
else
|
|
|
|
OS << " +";
|
|
|
|
dumpName(D);
|
|
|
|
dumpType(D->getReturnType());
|
|
|
|
|
|
|
|
if (D->isVariadic())
|
|
|
|
OS << " variadic";
|
|
|
|
}
|
|
|
|
|
|
|
|
void TextNodeDumper::VisitObjCTypeParamDecl(const ObjCTypeParamDecl *D) {
|
|
|
|
dumpName(D);
|
|
|
|
switch (D->getVariance()) {
|
|
|
|
case ObjCTypeParamVariance::Invariant:
|
|
|
|
break;
|
|
|
|
|
|
|
|
case ObjCTypeParamVariance::Covariant:
|
|
|
|
OS << " covariant";
|
|
|
|
break;
|
|
|
|
|
|
|
|
case ObjCTypeParamVariance::Contravariant:
|
|
|
|
OS << " contravariant";
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (D->hasExplicitBound())
|
|
|
|
OS << " bounded";
|
|
|
|
dumpType(D->getUnderlyingType());
|
|
|
|
}
|
|
|
|
|
|
|
|
void TextNodeDumper::VisitObjCCategoryDecl(const ObjCCategoryDecl *D) {
|
|
|
|
dumpName(D);
|
|
|
|
dumpDeclRef(D->getClassInterface());
|
|
|
|
dumpDeclRef(D->getImplementation());
|
|
|
|
for (const auto *P : D->protocols())
|
|
|
|
dumpDeclRef(P);
|
|
|
|
}
|
|
|
|
|
|
|
|
void TextNodeDumper::VisitObjCCategoryImplDecl(const ObjCCategoryImplDecl *D) {
|
|
|
|
dumpName(D);
|
|
|
|
dumpDeclRef(D->getClassInterface());
|
|
|
|
dumpDeclRef(D->getCategoryDecl());
|
|
|
|
}
|
|
|
|
|
|
|
|
void TextNodeDumper::VisitObjCProtocolDecl(const ObjCProtocolDecl *D) {
|
|
|
|
dumpName(D);
|
|
|
|
|
|
|
|
for (const auto *Child : D->protocols())
|
|
|
|
dumpDeclRef(Child);
|
|
|
|
}
|
|
|
|
|
|
|
|
void TextNodeDumper::VisitObjCInterfaceDecl(const ObjCInterfaceDecl *D) {
|
|
|
|
dumpName(D);
|
|
|
|
dumpDeclRef(D->getSuperClass(), "super");
|
|
|
|
|
|
|
|
dumpDeclRef(D->getImplementation());
|
|
|
|
for (const auto *Child : D->protocols())
|
|
|
|
dumpDeclRef(Child);
|
|
|
|
}
|
|
|
|
|
|
|
|
void TextNodeDumper::VisitObjCImplementationDecl(
|
|
|
|
const ObjCImplementationDecl *D) {
|
|
|
|
dumpName(D);
|
|
|
|
dumpDeclRef(D->getSuperClass(), "super");
|
|
|
|
dumpDeclRef(D->getClassInterface());
|
|
|
|
}
|
|
|
|
|
|
|
|
void TextNodeDumper::VisitObjCCompatibleAliasDecl(
|
|
|
|
const ObjCCompatibleAliasDecl *D) {
|
|
|
|
dumpName(D);
|
|
|
|
dumpDeclRef(D->getClassInterface());
|
|
|
|
}
|
|
|
|
|
|
|
|
void TextNodeDumper::VisitObjCPropertyDecl(const ObjCPropertyDecl *D) {
|
|
|
|
dumpName(D);
|
|
|
|
dumpType(D->getType());
|
|
|
|
|
|
|
|
if (D->getPropertyImplementation() == ObjCPropertyDecl::Required)
|
|
|
|
OS << " required";
|
|
|
|
else if (D->getPropertyImplementation() == ObjCPropertyDecl::Optional)
|
|
|
|
OS << " optional";
|
|
|
|
|
2020-04-23 02:20:56 -04:00
|
|
|
ObjCPropertyAttribute::Kind Attrs = D->getPropertyAttributes();
|
|
|
|
if (Attrs != ObjCPropertyAttribute::kind_noattr) {
|
|
|
|
if (Attrs & ObjCPropertyAttribute::kind_readonly)
|
2019-01-30 19:32:48 +00:00
|
|
|
OS << " readonly";
|
2020-04-23 02:20:56 -04:00
|
|
|
if (Attrs & ObjCPropertyAttribute::kind_assign)
|
2019-01-30 19:32:48 +00:00
|
|
|
OS << " assign";
|
2020-04-23 02:20:56 -04:00
|
|
|
if (Attrs & ObjCPropertyAttribute::kind_readwrite)
|
2019-01-30 19:32:48 +00:00
|
|
|
OS << " readwrite";
|
2020-04-23 02:20:56 -04:00
|
|
|
if (Attrs & ObjCPropertyAttribute::kind_retain)
|
2019-01-30 19:32:48 +00:00
|
|
|
OS << " retain";
|
2020-04-23 02:20:56 -04:00
|
|
|
if (Attrs & ObjCPropertyAttribute::kind_copy)
|
2019-01-30 19:32:48 +00:00
|
|
|
OS << " copy";
|
2020-04-23 02:20:56 -04:00
|
|
|
if (Attrs & ObjCPropertyAttribute::kind_nonatomic)
|
2019-01-30 19:32:48 +00:00
|
|
|
OS << " nonatomic";
|
2020-04-23 02:20:56 -04:00
|
|
|
if (Attrs & ObjCPropertyAttribute::kind_atomic)
|
2019-01-30 19:32:48 +00:00
|
|
|
OS << " atomic";
|
2020-04-23 02:20:56 -04:00
|
|
|
if (Attrs & ObjCPropertyAttribute::kind_weak)
|
2019-01-30 19:32:48 +00:00
|
|
|
OS << " weak";
|
2020-04-23 02:20:56 -04:00
|
|
|
if (Attrs & ObjCPropertyAttribute::kind_strong)
|
2019-01-30 19:32:48 +00:00
|
|
|
OS << " strong";
|
2020-04-23 02:20:56 -04:00
|
|
|
if (Attrs & ObjCPropertyAttribute::kind_unsafe_unretained)
|
2019-01-30 19:32:48 +00:00
|
|
|
OS << " unsafe_unretained";
|
2020-04-23 02:20:56 -04:00
|
|
|
if (Attrs & ObjCPropertyAttribute::kind_class)
|
2019-01-30 19:32:48 +00:00
|
|
|
OS << " class";
|
2020-04-23 02:20:56 -04:00
|
|
|
if (Attrs & ObjCPropertyAttribute::kind_direct)
|
Implement __attribute__((objc_direct)), __attribute__((objc_direct_members))
__attribute__((objc_direct)) is an attribute on methods declaration, and
__attribute__((objc_direct_members)) on implementation, categories or
extensions.
A `direct` property specifier is added (@property(direct) type name)
These attributes / specifiers cause the method to have no associated
Objective-C metadata (for the property or the method itself), and the
calling convention to be a direct C function call.
The symbol for the method has enforced hidden visibility and such direct
calls are hence unreachable cross image. An explicit C function must be
made if so desired to wrap them.
The implicit `self` and `_cmd` arguments are preserved, however to
maintain compatibility with the usual `objc_msgSend` semantics,
3 fundamental precautions are taken:
1) for instance methods, `self` is nil-checked. On arm64 backends this
typically adds a single instruction (cbz x0, <closest-ret>) to the
codegen, for the vast majority of the cases when the return type is a
scalar.
2) for class methods, because the class may not be realized/initialized
yet, a call to `[self self]` is emitted. When the proper deployment
target is used, this is optimized to `objc_opt_self(self)`.
However, long term we might want to emit something better that the
optimizer can reason about. When inlining kicks in, these calls
aren't optimized away as the optimizer has no idea that a single call
is really necessary.
3) the calling convention for the `_cmd` argument is changed: the caller
leaves the second argument to the call undefined, and the selector is
loaded inside the body when it's referenced only.
As far as error reporting goes, the compiler refuses:
- making any overloads direct,
- making an overload of a direct method,
- implementations marked as direct when the declaration in the
interface isn't (the other way around is allowed, as the direct
attribute is inherited from the declaration),
- marking methods required for protocol conformance as direct,
- messaging an unqualified `id` with a direct method,
- forming any @selector() expression with only direct selectors.
As warnings:
- any inconsistency of direct-related calling convention when
@selector() or messaging is used,
- forming any @selector() expression with a possibly direct selector.
Lastly an `objc_direct_members` attribute is added that can decorate
`@implementation` blocks and causes methods only declared there (and in
no `@interface`) to be automatically direct. When decorating an
`@interface` then all methods and properties declared in this block are
marked direct.
Radar-ID: rdar://problem/2684889
Differential Revision: https://reviews.llvm.org/D69991
Reviewed-By: John McCall
2019-11-07 23:14:58 -08:00
|
|
|
OS << " direct";
|
2020-04-23 02:20:56 -04:00
|
|
|
if (Attrs & ObjCPropertyAttribute::kind_getter)
|
2019-01-30 19:32:48 +00:00
|
|
|
dumpDeclRef(D->getGetterMethodDecl(), "getter");
|
2020-04-23 02:20:56 -04:00
|
|
|
if (Attrs & ObjCPropertyAttribute::kind_setter)
|
2019-01-30 19:32:48 +00:00
|
|
|
dumpDeclRef(D->getSetterMethodDecl(), "setter");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void TextNodeDumper::VisitObjCPropertyImplDecl(const ObjCPropertyImplDecl *D) {
|
|
|
|
dumpName(D->getPropertyDecl());
|
|
|
|
if (D->getPropertyImplementation() == ObjCPropertyImplDecl::Synthesize)
|
|
|
|
OS << " synthesize";
|
|
|
|
else
|
|
|
|
OS << " dynamic";
|
|
|
|
dumpDeclRef(D->getPropertyDecl());
|
|
|
|
dumpDeclRef(D->getPropertyIvarDecl());
|
|
|
|
}
|
|
|
|
|
|
|
|
void TextNodeDumper::VisitBlockDecl(const BlockDecl *D) {
|
|
|
|
if (D->isVariadic())
|
|
|
|
OS << " variadic";
|
|
|
|
|
|
|
|
if (D->capturesCXXThis())
|
|
|
|
OS << " captures_this";
|
|
|
|
}
|
2019-07-10 21:25:49 +00:00
|
|
|
|
|
|
|
void TextNodeDumper::VisitConceptDecl(const ConceptDecl *D) {
|
|
|
|
dumpName(D);
|
2019-07-11 15:26:45 +00:00
|
|
|
}
|
[FPEnv] Allow CompoundStmt to keep FP options
This is a recommit of b822efc7404bf09ccfdc1ab7657475026966c3b2,
reverted in dc34d8df4c48b3a8f474360970cae8a58e6c84f0. The commit caused
fails because the test ast-print-fp-pragmas.c did not specify particular
target, and it failed on targets which do not support constrained
intrinsics. The original commit message is below.
AST does not have special nodes for pragmas. Instead a pragma modifies
some state variables of Sema, which in turn results in modified
attributes of AST nodes. This technique applies to floating point
operations as well. Every AST node that can depend on FP options keeps
current set of them.
This technique works well for options like exception behavior or fast
math options. They represent instructions to the compiler how to modify
code generation for the affected nodes. However treatment of FP control
modes has problems with this technique. Modifying FP control mode
(like rounding direction) usually requires operations on hardware, like
writing to control registers. It must be done prior to the first
operation that depends on the control mode. In particular, such
operations are required for implementation of `pragma STDC FENV_ROUND`,
compiler should set up necessary rounding direction at the beginning of
compound statement where the pragma occurs. As there is no representation
for pragmas in AST, the code generation becomes a complicated task in
this case.
To solve this issue FP options are kept inside CompoundStmt. Unlike to FP
options in expressions, these does not affect any operation on FP values,
but only inform the codegen about the FP options that act in the body of
the statement. As all pragmas that modify FP environment may occurs only
at the start of compound statement or at global level, such solution
works for all relevant pragmas. The options are kept as a difference
from the options in the enclosing compound statement or default options,
it helps codegen to set only changed control modes.
Differential Revision: https://reviews.llvm.org/D123952
2022-07-01 18:32:26 +07:00
|
|
|
|
|
|
|
void TextNodeDumper::VisitCompoundStmt(const CompoundStmt *S) {
|
|
|
|
VisitStmt(S);
|
|
|
|
if (S->hasStoredFPFeatures())
|
|
|
|
printFPOptions(S->getStoredFPFeatures());
|
|
|
|
}
|
2022-07-15 10:45:57 -07:00
|
|
|
|
|
|
|
void TextNodeDumper::VisitHLSLBufferDecl(const HLSLBufferDecl *D) {
|
|
|
|
if (D->isCBuffer())
|
|
|
|
OS << " cbuffer";
|
|
|
|
else
|
|
|
|
OS << " tbuffer";
|
|
|
|
dumpName(D);
|
|
|
|
}
|
[OpenACC] Implement AST for OpenACC Compute Constructs (#81188)
'serial', 'parallel', and 'kernel' constructs are all considered
'Compute' constructs. This patch creates the AST type, plus the required
infrastructure for such a type, plus some base types that will be useful
in the future for breaking this up.
The only difference between the three is the 'kind'( plus some minor
clause legalization rules, but those can be differentiated easily
enough), so rather than representing them as separate AST nodes, it
seems
to make sense to make them the same.
Additionally, no clause AST functionality is being implemented yet, as
that fits better in a separate patch, and this is enough to get the
'naked' constructs implemented.
This is otherwise an 'NFC' patch, as it doesn't alter execution at all,
so there aren't any tests. I did this to break up the review workload
and to get feedback on the layout.
2024-02-13 06:02:13 -08:00
|
|
|
|
2024-08-31 10:59:08 -05:00
|
|
|
void TextNodeDumper::VisitHLSLOutArgExpr(const HLSLOutArgExpr *E) {
|
|
|
|
OS << (E->isInOut() ? " inout" : " out");
|
|
|
|
}
|
|
|
|
|
[OpenACC] Implement AST for OpenACC Compute Constructs (#81188)
'serial', 'parallel', and 'kernel' constructs are all considered
'Compute' constructs. This patch creates the AST type, plus the required
infrastructure for such a type, plus some base types that will be useful
in the future for breaking this up.
The only difference between the three is the 'kind'( plus some minor
clause legalization rules, but those can be differentiated easily
enough), so rather than representing them as separate AST nodes, it
seems
to make sense to make them the same.
Additionally, no clause AST functionality is being implemented yet, as
that fits better in a separate patch, and this is enough to get the
'naked' constructs implemented.
This is otherwise an 'NFC' patch, as it doesn't alter execution at all,
so there aren't any tests. I did this to break up the review workload
and to get feedback on the layout.
2024-02-13 06:02:13 -08:00
|
|
|
void TextNodeDumper::VisitOpenACCConstructStmt(const OpenACCConstructStmt *S) {
|
|
|
|
OS << " " << S->getDirectiveKind();
|
|
|
|
}
|
2024-06-05 06:21:48 -07:00
|
|
|
void TextNodeDumper::VisitOpenACCLoopConstruct(const OpenACCLoopConstruct *S) {
|
|
|
|
if (S->isOrphanedLoopConstruct())
|
|
|
|
OS << " <orphan>";
|
|
|
|
else
|
2024-11-08 09:57:19 -08:00
|
|
|
OS << " parent: " << S->getParentComputeConstructKind();
|
2024-06-05 06:21:48 -07:00
|
|
|
}
|
2024-06-20 14:38:46 +02:00
|
|
|
|
2024-11-07 14:17:55 -08:00
|
|
|
void TextNodeDumper::VisitOpenACCCombinedConstruct(
|
|
|
|
const OpenACCCombinedConstruct *S) {
|
2025-01-06 11:59:04 -08:00
|
|
|
VisitOpenACCConstructStmt(S);
|
2024-11-07 14:17:55 -08:00
|
|
|
}
|
|
|
|
|
2024-12-10 12:55:15 -08:00
|
|
|
void TextNodeDumper::VisitOpenACCDataConstruct(const OpenACCDataConstruct *S) {
|
2025-01-06 11:59:04 -08:00
|
|
|
VisitOpenACCConstructStmt(S);
|
2024-12-10 12:55:15 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
void TextNodeDumper::VisitOpenACCEnterDataConstruct(
|
|
|
|
const OpenACCEnterDataConstruct *S) {
|
2025-01-06 11:59:04 -08:00
|
|
|
VisitOpenACCConstructStmt(S);
|
2024-12-10 12:55:15 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
void TextNodeDumper::VisitOpenACCExitDataConstruct(
|
|
|
|
const OpenACCExitDataConstruct *S) {
|
2025-01-06 11:59:04 -08:00
|
|
|
VisitOpenACCConstructStmt(S);
|
2024-12-10 12:55:15 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
void TextNodeDumper::VisitOpenACCHostDataConstruct(
|
|
|
|
const OpenACCHostDataConstruct *S) {
|
2025-01-06 11:59:04 -08:00
|
|
|
VisitOpenACCConstructStmt(S);
|
2024-12-10 12:55:15 -08:00
|
|
|
}
|
|
|
|
|
2024-12-17 07:39:20 -08:00
|
|
|
void TextNodeDumper::VisitOpenACCWaitConstruct(const OpenACCWaitConstruct *S) {
|
2025-01-06 11:59:04 -08:00
|
|
|
VisitOpenACCConstructStmt(S);
|
2024-12-17 07:39:20 -08:00
|
|
|
}
|
2025-03-03 10:26:53 -08:00
|
|
|
void TextNodeDumper::VisitOpenACCCacheConstruct(
|
|
|
|
const OpenACCCacheConstruct *S) {
|
|
|
|
VisitOpenACCConstructStmt(S);
|
|
|
|
if (S->hasReadOnly())
|
|
|
|
OS <<" readonly";
|
|
|
|
}
|
2024-12-19 06:11:36 -08:00
|
|
|
void TextNodeDumper::VisitOpenACCInitConstruct(const OpenACCInitConstruct *S) {
|
2025-01-06 11:59:04 -08:00
|
|
|
VisitOpenACCConstructStmt(S);
|
2024-12-19 06:11:36 -08:00
|
|
|
}
|
|
|
|
void TextNodeDumper::VisitOpenACCShutdownConstruct(
|
|
|
|
const OpenACCShutdownConstruct *S) {
|
2025-01-06 11:59:04 -08:00
|
|
|
VisitOpenACCConstructStmt(S);
|
2024-12-19 06:11:36 -08:00
|
|
|
}
|
2025-01-03 10:00:12 -08:00
|
|
|
void TextNodeDumper::VisitOpenACCSetConstruct(const OpenACCSetConstruct *S) {
|
2025-01-06 11:59:04 -08:00
|
|
|
VisitOpenACCConstructStmt(S);
|
|
|
|
}
|
|
|
|
void TextNodeDumper::VisitOpenACCUpdateConstruct(
|
|
|
|
const OpenACCUpdateConstruct *S) {
|
|
|
|
VisitOpenACCConstructStmt(S);
|
2025-01-03 10:00:12 -08:00
|
|
|
}
|
2024-12-17 07:39:20 -08:00
|
|
|
|
2025-01-22 12:22:03 -08:00
|
|
|
void TextNodeDumper::VisitOpenACCAtomicConstruct(
|
|
|
|
const OpenACCAtomicConstruct *S) {
|
|
|
|
VisitOpenACCConstructStmt(S);
|
|
|
|
OS << ' ' << S->getAtomicKind();
|
|
|
|
}
|
|
|
|
|
2025-01-21 11:27:23 -08:00
|
|
|
void TextNodeDumper::VisitOpenACCDeclareDecl(const OpenACCDeclareDecl *D) {
|
|
|
|
OS << " " << D->getDirectiveKind();
|
|
|
|
|
|
|
|
for (const OpenACCClause *C : D->clauses())
|
|
|
|
AddChild([=] {
|
|
|
|
Visit(C);
|
|
|
|
for (const Stmt *S : C->children())
|
|
|
|
AddChild([=] { Visit(S); });
|
|
|
|
});
|
|
|
|
}
|
2025-01-22 06:47:14 -08:00
|
|
|
void TextNodeDumper::VisitOpenACCRoutineDecl(const OpenACCRoutineDecl *D) {
|
|
|
|
OS << " " << D->getDirectiveKind();
|
|
|
|
|
|
|
|
if (D->hasNameSpecified()) {
|
|
|
|
OS << " name_specified";
|
|
|
|
dumpSourceRange(SourceRange{D->getLParenLoc(), D->getRParenLoc()});
|
|
|
|
}
|
|
|
|
|
|
|
|
AddChild([=] { Visit(D->getFunctionReference()); });
|
|
|
|
|
|
|
|
for (const OpenACCClause *C : D->clauses())
|
|
|
|
AddChild([=] {
|
|
|
|
Visit(C);
|
|
|
|
for (const Stmt *S : C->children())
|
|
|
|
AddChild([=] { Visit(S); });
|
|
|
|
});
|
|
|
|
}
|
2025-01-21 11:27:23 -08:00
|
|
|
|
2024-06-20 14:38:46 +02:00
|
|
|
void TextNodeDumper::VisitEmbedExpr(const EmbedExpr *S) {
|
|
|
|
AddChild("begin", [=] { OS << S->getStartingElementPos(); });
|
|
|
|
AddChild("number of elements", [=] { OS << S->getDataElementCount(); });
|
|
|
|
}
|
2024-08-13 09:50:34 +08:00
|
|
|
|
|
|
|
void TextNodeDumper::VisitAtomicExpr(const AtomicExpr *AE) {
|
|
|
|
OS << ' ' << AE->getOpAsString();
|
|
|
|
}
|
2025-02-19 18:03:18 +01:00
|
|
|
|
|
|
|
void TextNodeDumper::VisitConvertVectorExpr(const ConvertVectorExpr *S) {
|
|
|
|
VisitStmt(S);
|
|
|
|
if (S->hasStoredFPFeatures())
|
|
|
|
printFPOptions(S->getStoredFPFeatures());
|
|
|
|
}
|