Skip to content

[clang] Improve nested name specifier AST representation #147835

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Aug 9, 2025

Conversation

mizvekov
Copy link
Contributor

@mizvekov mizvekov commented Jul 9, 2025

This is a major change on how we represent nested name qualifications in the AST.

  • The nested name specifier itself and how it's stored is changed. The prefixes for types are handled within the type hierarchy, which makes canonicalization for them super cheap, no memory allocation required. Also translating a type into nested name specifier form becomes a no-op. An identifier is stored as a DependentNameType. The nested name specifier gains a lightweight handle class, to be used instead of passing around pointers, which is similar to what is implemented for TemplateName. There is still one free bit available, and this handle can be used within a PointerUnion and PointerIntPair, which should keep bit-packing aficionados happy.
  • The ElaboratedType node is removed, all type nodes in which it could previously apply to can now store the elaborated keyword and name qualifier, tail allocating when present.
  • TagTypes can now point to the exact declaration found when producing these, as opposed to the previous situation of there only existing one TagType per entity. This increases the amount of type sugar retained, and can have several applications, for example in tracking module ownership, and other tools which care about source file origins, such as IWYU. These TagTypes are lazily allocated, in order to limit the increase in AST size.

This patch offers a great performance benefit.

It greatly improves compilation time for stdexec. For one datapoint, for test_on2.cpp in that project, which is the slowest compiling test, this patch improves -c compilation time by about 7.2%, with the -fsyntax-only improvement being at ~12%.

This has great results on compile-time-tracker as well:
image

This patch also further enables other optimziations in the future, and will reduce the performance impact of template specialization resugaring when that lands.

It has some other miscelaneous drive-by fixes.

About the review: Yes the patch is huge, sorry about that. Part of the reason is that I started by the nested name specifier part, before the ElaboratedType part, but that had a huge performance downside, as ElaboratedType is a big performance hog. I didn't have the steam to go back and change the patch after the fact.

There is also a lot of internal API changes, and it made sense to remove ElaboratedType in one go, versus removing it from one type at a time, as that would present much more churn to the users. Also, the nested name specifier having a different API avoids missing changes related to how prefixes work now, which could make existing code compile but not work.

How to review: The important changes are all in clang/include/clang/AST and clang/lib/AST, with also important changes in clang/lib/Sema/TreeTransform.h.

The rest and bulk of the changes are mostly consequences of the changes in API.

PS: TagType::getDecl is renamed to getOriginalDecl in this patch, just for easier to rebasing. I plan to rename it back after this lands.

Fixes #136624
Fixes #43179
Fixes #68670
Fixes #92757

facebook-github-bot pushed a commit to facebook/folly that referenced this pull request Aug 13, 2025
Summary:
Clang recently made major changes to the AST representation of nested name specifiers in [llvm-project#147835](llvm/llvm-project#147835). While waiting for Bindgen to be updated to understand the new representation, this diff changes our Bindgen invocation on `folly::IOBuf` to disregard the nested name `folly::IOBuf::Iterator`, which is unused anyway in the Rust `IOBuf` binding.

This unblocks staging builds for the LLVM Server Compiler team.

This diff causes the following difference in the Bindgen-generated Rust binding observed through `buck2 build fbcode//mode/opt fbcode//folly/rust/iobuf:iobuf-sys-bindgen`:

```lang=diff
@@ -571,8 +571,6 @@
         pub const IOBuf_CombinedOption_SEPARATE: root::folly::IOBuf_CombinedOption = 2;
         pub type IOBuf_CombinedOption = ::std::os::raw::c_int;
         pub type IOBuf_value_type = root::folly::ByteRange;
-        pub type IOBuf_iterator = root::folly::IOBuf_Iterator;
-        pub type IOBuf_const_iterator = root::folly::IOBuf_Iterator;
         pub type IOBuf_FreeFunction = ::std::option::Option<
             unsafe extern "C" fn(
                 buf: *mut ::std::os::raw::c_void,
@@ -765,31 +763,6 @@
                 "Offset of field: IOBuf::sharedInfo_",
             ][::std::mem::offset_of!(IOBuf, sharedInfo_) - 48usize];
         };
-        #[repr(C)]
-        #[derive(Debug, Copy, Clone)]
-        pub struct IOBuf_Iterator {
-            pub pos_: *const root::folly::IOBuf,
-            pub end_: *const root::folly::IOBuf,
-            pub val_: root::folly::ByteRange,
-        }
-        #[allow(clippy::unnecessary_operation, clippy::identity_op)]
-        const _: () = {
-            [
-                "Size of IOBuf_Iterator",
-            ][::std::mem::size_of::<IOBuf_Iterator>() - 32usize];
-            [
-                "Alignment of IOBuf_Iterator",
-            ][::std::mem::align_of::<IOBuf_Iterator>() - 8usize];
-            [
-                "Offset of field: IOBuf_Iterator::pos_",
-            ][::std::mem::offset_of!(IOBuf_Iterator, pos_) - 0usize];
-            [
-                "Offset of field: IOBuf_Iterator::end_",
-            ][::std::mem::offset_of!(IOBuf_Iterator, end_) - 8usize];
-            [
-                "Offset of field: IOBuf_Iterator::val_",
-            ][::std::mem::offset_of!(IOBuf_Iterator, val_) - 16usize];
-        };
     }
     pub mod facebook {
         #[allow(unused_imports)]
```

Bindgen issue: [bindgen#3264](rust-lang/rust-bindgen#3264)

Reviewed By: HighW4y2H3ll

Differential Revision: D80186965

fbshipit-source-id: 9f0546a2964b5d4e5c67e6bdd91b542e3e5f7b2c
searlmc1 pushed a commit to ROCm/llvm-project that referenced this pull request Aug 14, 2025
This is a major change on how we represent nested name qualifications in
the AST.

* The nested name specifier itself and how it's stored is changed. The
prefixes for types are handled within the type hierarchy, which makes
canonicalization for them super cheap, no memory allocation required.
Also translating a type into nested name specifier form becomes a no-op.
An identifier is stored as a DependentNameType. The nested name
specifier gains a lightweight handle class, to be used instead of
passing around pointers, which is similar to what is implemented for
TemplateName. There is still one free bit available, and this handle can
be used within a PointerUnion and PointerIntPair, which should keep
bit-packing aficionados happy.
* The ElaboratedType node is removed, all type nodes in which it could
previously apply to can now store the elaborated keyword and name
qualifier, tail allocating when present.
* TagTypes can now point to the exact declaration found when producing
these, as opposed to the previous situation of there only existing one
TagType per entity. This increases the amount of type sugar retained,
and can have several applications, for example in tracking module
ownership, and other tools which care about source file origins, such as
IWYU. These TagTypes are lazily allocated, in order to limit the
increase in AST size.

This patch offers a great performance benefit.

It greatly improves compilation time for
[stdexec](https://github.com/NVIDIA/stdexec). For one datapoint, for
`test_on2.cpp` in that project, which is the slowest compiling test,
this patch improves `-c` compilation time by about 7.2%, with the
`-fsyntax-only` improvement being at ~12%.

This has great results on compile-time-tracker as well:

![image](https://github.com/user-attachments/assets/700dce98-2cab-4aa8-97d1-b038c0bee831)

This patch also further enables other optimziations in the future, and
will reduce the performance impact of template specialization resugaring
when that lands.

It has some other miscelaneous drive-by fixes.

About the review: Yes the patch is huge, sorry about that. Part of the
reason is that I started by the nested name specifier part, before the
ElaboratedType part, but that had a huge performance downside, as
ElaboratedType is a big performance hog. I didn't have the steam to go
back and change the patch after the fact.

There is also a lot of internal API changes, and it made sense to remove
ElaboratedType in one go, versus removing it from one type at a time, as
that would present much more churn to the users. Also, the nested name
specifier having a different API avoids missing changes related to how
prefixes work now, which could make existing code compile but not work.

How to review: The important changes are all in
`clang/include/clang/AST` and `clang/lib/AST`, with also important
changes in `clang/lib/Sema/TreeTransform.h`.

The rest and bulk of the changes are mostly consequences of the changes
in API.

PS: TagType::getDecl is renamed to `getOriginalDecl` in this patch, just
for easier to rebasing. I plan to rename it back after this lands.

Fixes llvm#136624
Fixes llvm#43179
Fixes llvm#68670
Fixes llvm#92757
searlmc1 pushed a commit to ROCm/llvm-project that referenced this pull request Aug 14, 2025
…lvm#153344)

This fixes a regression reported here
llvm#147835 (comment),
where getTrivialTemplateArgumentLoc can't see through template name
sugar when producing a trivial TemplateArgumentLoc for template template
arguments.

Since this regression was never released, there are no release notes.
mizvekov added a commit that referenced this pull request Aug 14, 2025
…nType

This was a regression introduced in #147835

Since this regression was never released, there are no release notes.

Fixes #153540
mizvekov added a commit that referenced this pull request Aug 14, 2025
…nType (#153646)

This was a regression introduced in
#147835

Since this regression was never released, there are no release notes.

Fixes #153540
llvm-sync bot pushed a commit to arm/arm-toolchain that referenced this pull request Aug 14, 2025
…ecializationType (#153646)

This was a regression introduced in
llvm/llvm-project#147835

Since this regression was never released, there are no release notes.

Fixes llvm/llvm-project#153540
@Xazax-hun
Copy link
Collaborator

We started to see a CI failure around the time this PR landed in https://green.lab.llvm.org/job/llvm.org/job/clang-stage2-Rthinlto/1079/

The build hangs in this step (using a stage 2 compiler):

12:39:17  [384/5387] : && DYLD_LIBRARY_PATH=/Users/ec2-user/jenkins/workspace/llvm.org/clang-stage2-Rthinlto/host-compiler/lib/ "/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/libtool" -static -no_warning_for_no_symbols -o lib/libbenchmark_main.a  third-party/benchmark/src/CMakeFiles/benchmark_main.dir/benchmark_main.cc.o && :

This is where the compiler seems to hang:

Analysis of sampling clang++ (pid 12682) every 1 millisecond
Process:         clang-22 [12682]
Path:            /Users/*/clang-22
Load Address:    0x1054d1000
Identifier:      clang-22
Version:         22.0.0git (0)
Code Type:       X86-64
Platform:        macOS
Parent Process:  ninja [12327]

Date/Time:       2025-08-12 17:39:29.604 +0000
Launch Time:     2025-08-12 04:30:26.246 +0000
OS Version:      macOS 15.1.1 (24B91)
Report Version:  7
Analysis Tool:   /usr/bin/sample

Physical footprint:         83.4M
Physical footprint (peak):  83.4M
Idle exit:                  untracked
----

Call graph:
    120 Thread_240531   DispatchQueue_1: com.apple.main-thread  (serial)
      120 start  (in dyld) + 1805  [0x7ff804e212cd]
        120 main  (in clang-22) + 128  [0x1054e1670]
          120 clang_main(int, char**, llvm::ToolContext const&)  (in clang-22) + 5620  [0x1054d4254]
            120 clang::driver::Driver::ExecuteCompilation(clang::driver::Compilation&, llvm::SmallVectorImpl<std::pair<int, clang::driver::Command const*>>&)  (in clang-22) + 352  [0x108d26fa0]
              120 clang::driver::Compilation::ExecuteJobs(clang::driver::JobList const&, llvm::SmallVectorImpl<std::pair<int, clang::driver::Command const*>>&, bool) const  (in clang-22) + 143  [0x108d0cc5f]
                120 clang::driver::Compilation::ExecuteCommand(clang::driver::Command const&, clang::driver::Command const*&, bool) const  (in clang-22) + 524  [0x108d0ca5c]
                  120 clang::driver::CC1Command::Execute(llvm::ArrayRef<std::optional<llvm::StringRef>>, std::basic_string<char>*, bool*) const  (in clang-22) + 297  [0x108d433a9]
                    120 llvm::CrashRecoveryContext::RunSafely(llvm::function_ref<void ()>)  (in clang-22) + 130  [0x10b389442]
                      120 llvm::function_ref<void ()>::callback_fn<clang::driver::CC1Command::Execute(llvm::ArrayRef<std::optional<llvm::StringRef>>, std::basic_string<char>*, bool*) const::$_0>(long)  (in clang-22) + 30  [0x108d43ace]
                        120 ExecuteCC1Tool(llvm::SmallVectorImpl<char const*>&, llvm::ToolContext const&)  (in clang-22) + 395  [0x1054d49db]
                          120 cc1_main(llvm::ArrayRef<char const*>, char const*, void*)  (in clang-22) + 4289  [0x1054d77e1]
                            120 clang::ExecuteCompilerInvocation(clang::CompilerInstance*)  (in clang-22) + 632  [0x106d7c3e8]
                              120 clang::CompilerInstance::ExecuteAction(clang::FrontendAction&)  (in clang-22) + 941  [0x108bfc91d]
                                120 clang::FrontendAction::Execute()  (in clang-22) + 42  [0x108c7a14a]
                                  120 clang::ParseAST(clang::Sema&, bool, bool)  (in clang-22) + 782  [0x108ec538e]
                                    120 clang::Parser::ParseTopLevelDecl(clang::OpaquePtr<clang::DeclGroupRef>&, clang::Sema::ModuleImportState&)  (in clang-22) + 1528  [0x108f9c968]
                                      120 clang::Parser::ParseExternalDeclaration(clang::ParsedAttributes&, clang::ParsedAttributes&, clang::ParsingDeclSpec*)  (in clang-22) + 1391  [0x108f9e33f]
                                        120 clang::Parser::ParseDeclarationOrFunctionDefinition(clang::ParsedAttributes&, clang::ParsedAttributes&, clang::ParsingDeclSpec*, clang::AccessSpecifier)  (in clang-22) + 625  [0x108f9f8e1]
                                          120 clang::Parser::ParseDeclOrFunctionDefInternal(clang::ParsedAttributes&, clang::ParsedAttributes&, clang::ParsingDeclSpec&, clang::AccessSpecifier)  (in clang-22) + 1314  [0x108fa00a2]
                                            120 clang::Parser::ParseDeclGroup(clang::ParsingDeclSpec&, clang::DeclaratorContext, clang::ParsedAttributes&, clang::Parser::ParsedTemplateInfo&, clang::SourceLocation*, clang::Parser::ForRangeInit*)  (in clang-22) + 4893  [0x108ed9a1d]
                                              120 clang::Parser::ParseFunctionDefinition(clang::ParsingDeclarator&, clang::Parser::ParsedTemplateInfo const&, clang::Parser::LateParsedAttrList*)  (in clang-22) + 2342  [0x108fa0b86]
                                                120 clang::Sema::ActOnDefaultCtorInitializers(clang::Decl*)  (in clang-22) + 101  [0x1093acc95]
                                                  120 clang::Sema::SetCtorInitializers(clang::CXXConstructorDecl*, bool, llvm::ArrayRef<clang::CXXCtorInitializer*>)  (in clang-22) + 3444  [0x1093a8f84]
                                                    120 CollectFieldInitializer(clang::Sema&, (anonymous namespace)::BaseAndFieldInfo&, clang::FieldDecl*, clang::IndirectFieldDecl*)  (in clang-22) + 3017  [0x1093aa979]
                                                      120 clang::InitializationSequence::InitializeFrom(clang::Sema&, clang::InitializedEntity const&, clang::InitializationKind const&, llvm::MutableArrayRef<clang::Expr*>, bool, bool)  (in clang-22) + 2750  [0x10956527e]
                                                        120 TryConstructorInitialization(clang::Sema&, clang::InitializedEntity const&, clang::InitializationKind const&, llvm::MutableArrayRef<clang::Expr*>, clang::QualType, clang::QualType, clang::InitializationSequence&, bool, bool)  (in clang-22) + 565  [0x109583df5]
                                                          120 clang::Sema::LookupConstructors(clang::CXXRecordDecl*)  (in clang-22) + 152  [0x1095a60a8]
                                                            120 clang::DeclContext::lookupImpl(clang::DeclarationName, clang::DeclContext const*) const  (in clang-22) + 733  [0x109d4f6bd]
                                                              120 clang::ASTReader::FindExternalVisibleDeclsByName(clang::DeclContext const*, clang::DeclarationName, clang::DeclContext const*)  (in clang-22) + 453  [0x108ff3795]
                                                                120 _ZZN5clang9ASTReader30FindExternalVisibleDeclsByNameEPKNS_11DeclContextENS_15DeclarationNameES3_ENK3$_0clIRNS_13serialization20MultiOnDiskHashTableINS7_6reader29ASTDeclContextNameLookupTraitEEERS4_EEDaOT_OT0_  (in clang-22) + 105  [0x108ff39c9]
                                                                  120 clang::ASTReader::GetDecl(clang::GlobalDeclID)  (in clang-22) + 149  [0x108fe7615]
                                                                    120 clang::ASTReader::ReadDeclRecord(clang::GlobalDeclID)  (in clang-22) + 3749  [0x10905f335]
                                                                      120 clang::StackExhaustionHandler::runWithSufficientStackSpace(clang::SourceLocation, llvm::function_ref<void ()>)  (in clang-22) + 47  [0x10a3621af]
                                                                        120 clang::ASTDeclReader::Visit(clang::Decl*)  (in clang-22) + 21  [0x1090338f5]
                                                                          120 clang::ASTDeclReader::VisitCXXMethodDecl(clang::CXXMethodDecl*)  (in clang-22) + 22  [0x109040d36]
                                                                            120 clang::ASTDeclReader::VisitFunctionDecl(clang::FunctionDecl*)  (in clang-22) + 5529  [0x109038089]
                                                                              120 clang::ASTDeclReader::findExisting(clang::NamedDecl*)  (in clang-22) + 995  [0x10905a8b3]
                                                                                120 clang::ASTContext::isSameEntity(clang::NamedDecl const*, clang::NamedDecl const*) const  (in clang-22) + 1921  [0x109b58981]
                                                                                  120 clang::NamedDecl::getLinkageInternal() const  (in clang-22) + 168  [0x109d2cd78]
                                                                                    120 clang::LinkageComputer::getLVForDecl(clang::NamedDecl const*, clang::LVComputationKind)  (in clang-22) + 151  [0x109d29eb7]
                                                                                      120 clang::LinkageComputer::getLVForClassMember(clang::NamedDecl const*, clang::LVComputationKind, bool)  (in clang-22) + 648  [0x109d2b9b8]
                                                                                        120 clang::Type::getLinkage() const  (in clang-22) + 14  [0x10a20ac3e]
                                                                                          120 clang::TypePropertyCache<(anonymous namespace)::Private>::ensure(clang::Type const*)  (in clang-22) + 151  [0x10a20ad07]
                                                                                            120 clang::TypePropertyCache<(anonymous namespace)::Private>::ensure(clang::Type const*)  (in clang-22) + 664  [0x10a20af08]
                                                                                              120 clang::TypePropertyCache<(anonymous namespace)::Private>::ensure(clang::Type const*)  (in clang-22) + 380  [0x10a20adec]
                                                                                                120 clang::NamedDecl::getLinkageInternal() const  (in clang-22) + 168  [0x109d2cd78]
                                                                                                  91 clang::LinkageComputer::getLVForDecl(clang::NamedDecl const*, clang::LVComputationKind)  (in clang-22) + 351  [0x109d29f7f]
                                                                                                  + 74 clang::Redeclarable<clang::TagDecl>::DeclLink::getPrevious(clang::TagDecl const*) const  (in clang-22) + 6,1,...  [0x1070c6706,0x1070c6701,...]
                                                                                                  + 17 clang::TagDecl::getNextRedeclarationImpl()  (in clang-22) + 0,7,...  [0x109d3da70,0x109d3da77,...]
                                                                                                  29 clang::LinkageComputer::getLVForDecl(clang::NamedDecl const*, clang::LVComputationKind)  (in clang-22) + 337,315,...  [0x109d29f71,0x109d29f5b,...]

Total number in stack (recursive counted multiple, when >=5):

Sort by top of stack, same collapsed (when >= 5):
        clang::Redeclarable<clang::TagDecl>::DeclLink::getPrevious(clang::TagDecl const*) const  (in clang-22)        74
        clang::LinkageComputer::getLVForDecl(clang::NamedDecl const*, clang::LVComputationKind)  (in clang-22)        29
        clang::TagDecl::getNextRedeclarationImpl()  (in clang-22)        1

Any ideas what might be going on here?

@bolshakov-a
Copy link
Contributor

bolshakov-a commented Aug 15, 2025

Out of curiosity. The old getDecl() methods returned the tag type definition (if present) because they called getInterestingTagDecl function internally, whereas the new getOriginalDecl() returns just some of redeclarations (and not necessarily the first one). What is the reason of such change?

@DKLoehr
Copy link
Contributor

DKLoehr commented Aug 15, 2025

I don't know if this is the same as the other crash report, but we're also seeing assert failures when building chromium. I've minimized the repro to the following program:

template <typename T> struct S {
  using typename T::Ty;
  static Ty Val;
};
template <typename T>
S<T>::Ty S<T>::Val;

This fails when running clang++ -c repro.cc when clang is built with asserts (-std=c++20 silences a warning but isn't necessary). The crash looks like this:

llvm-project/clang/include/clang/AST/TypeLoc.h:79: T clang::TypeLoc::castAs() const [T = clang::DependentNameTypeLoc]: Assertion `T::isKind(*this)' failed.
PLEASE submit a bug report to https://github.com/llvm/llvm-project/issues/ and include the crash backtrace, preprocessed source, and associated run script.
Stack dump:
0.      Program arguments: clang++ -c repro.cc
1.      p.cc:6:19: current parser token ';'
<snip long backtrace>
clang++: error: clang frontend command failed with exit code 134 (use -v to see invocation)
clang version 22.0.0git (https://github.com/llvm/llvm-project.git 91cdd35008e9ab32dffb7e401cdd7313b3461892)
Target: x86_64-unknown-linux-gnu
Thread model: posix
Build config: +assertions

@mizvekov
Copy link
Contributor Author

mizvekov commented Aug 15, 2025

Out of curiosity. The old getDecl() methods returned the tag type definition (if present) because they called getInterestingTagDecl function internally, whereas the new getOriginalDecl() returns just some of redeclarations (and not necessarily the first one). What is the reason of such change?

getOriginalDecl() returns exactly the declaration that was found by type lookup when producing the type. If this was a forward declaration, because that was the only thing that was in scope at that point the type was written in source code, that is the declaration that will be returned.

This helps establishing the source file and module ownership origins of the dependencies of some piece of code.

@mizvekov
Copy link
Contributor Author

mizvekov commented Aug 15, 2025

We started to see a CI failure around the time this PR landed in https://green.lab.llvm.org/job/llvm.org/job/clang-stage2-Rthinlto/1079/
Any ideas what might be going on here?

Seems like it is stuck somewhere calculating the linkage and visibility for some type.
A repro would be helpful, any way you could get us one? Doesn't need to be reduced.

@bolshakov-a
Copy link
Contributor

If this was a forward declaration, because that was the only thing that was in scope ...

Thanks! However, for the following piece of code

struct A;
struct A {};
struct A;
A* p;

getOriginalDecl() called on the pointed-to type gives the forward-declaration from the 3rd line, and the forward-declaration from the 1st line for its canonical type.

mizvekov added a commit that referenced this pull request Aug 15, 2025
This fixes a regression reported here #147835 (comment)

Since this regression was never released, there are no release notes.
@mizvekov
Copy link
Contributor Author

Thanks! However, for the following piece of code
getOriginalDecl() called on the pointed-to type gives the forward-declaration from the 3rd line, and the forward-declaration from the 1st line for its canonical type.

Yes that is correct.

The 3rd line is what declaration was found by type lookup at that point, and the canonical declaration has long been established in clang as the first declaration.

@mizvekov
Copy link
Contributor Author

I don't know if this is the same as the other crash report, but we're also seeing assert failures when building chromium. I've minimized the repro to the following program:

Thanks, this is going to be fixed by #153862

@vbvictor
Copy link
Contributor

Hi!
We are seeing regression in clang-tidy in modernize-type-traits check #153649. I confirmed locally that on 91cdd35 it is bugged and commit before it is not fc44a4f

mdenson pushed a commit to mdenson/llvm-project that referenced this pull request Aug 16, 2025
…nType (llvm#153646)

This was a regression introduced in
llvm#147835

Since this regression was never released, there are no release notes.

Fixes llvm#153540
mizvekov added a commit that referenced this pull request Aug 16, 2025
…red bindings (#153923)

These are implicit vardecls which its type was never written in source
code. Don't create a TypeLoc and give it a fake source location.

The fake as-written type also didn't match the actual type, which after
fixing this gives some unrelated test churn on a CFG dump, since
statement printing prefers type source info if thats available.

Fixes #153649

This is a regression introduced in
#147835

This regression was never released, so no release notes are added.
@mizvekov
Copy link
Contributor Author

Hi! We are seeing regression in clang-tidy in modernize-type-traits check #153649. I confirmed locally that on 91cdd35 it is bugged and commit before it is not fc44a4f

Thanks for reporting. This has been fixed by #153923

llvm-sync bot pushed a commit to arm/arm-toolchain that referenced this pull request Aug 16, 2025
…for structured bindings (#153923)

These are implicit vardecls which its type was never written in source
code. Don't create a TypeLoc and give it a fake source location.

The fake as-written type also didn't match the actual type, which after
fixing this gives some unrelated test churn on a CFG dump, since
statement printing prefers type source info if thats available.

Fixes llvm/llvm-project#153649

This is a regression introduced in
llvm/llvm-project#147835

This regression was never released, so no release notes are added.
cast<InjectedClassNameType>(Ty)->getDecl());
mangleSourceNameWithAbiTags(cast<InjectedClassNameType>(Ty)
->getOriginalDecl()
->getDefinitionOrSelf());
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this getDefinitionOrSelf() call necessary here? Shouldn't InjectedClassNameType always refer to the definition where the class name is injected to?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not always.

I mean you could certainly attempt to define an InjectedClassNameType whose declaration necessarily is the definition.

This would cause many annoyances, most of them surmountable, as you wouldn't be able to form a type to an arbitrary declaration (which we do offhandedly in error recovery and printing diagnostics), and the type would play by different rules than other tag types.

Though I think at the end, you would come to the problem of demoted definitions, which come up for example when merging definitions from the GMF of different modules. You would have to throw away the ability to represent an InjectedClassNameType pointing to a demoted definition.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've caught an example:

template <typename>
struct Tpl;

template <typename>
struct Tpl {
  Tpl(const Tpl&) {}
};

The injected name from the copy ctor prototype refers to the forward-declaration from the first two lines. Looks a little strange but Ok.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are you sure it's not just its canonical type which refers to the first two lines?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Heh, interesting. getAs function actually returns the canonical type.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

getAs only desugars. This could happen with a TemplateSpecializationType which its canonical type is an InjectedClassNameType, since in that case desugar just returns the canonical type, but that is not the case for that constructor, since its using an InjectedClassNameType as written in source code.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've understood: there are specializations of getAs for the leaf non-sugar types:

template <> inline const Class##Type *Type::getAs() const { \
return dyn_cast<Class##Type>(CanonicalType); \
} \

If you call getAs<InjectedClassNameType>() on a Type which is already InjectedClassNameType, you get its canonical type.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah ok, interesting.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That looks like it needs to change, we probably don't want to do that for sugared leaf types.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Btw, it is a drawback of templates that you can easily overlook a specialization.

@mizvekov
Copy link
Contributor Author

We started to see a CI failure around the time this PR landed in https://green.lab.llvm.org/job/llvm.org/job/clang-stage2-Rthinlto/1079/
Any ideas what might be going on here?

@Xazax-hun this could be the same as #153933

Could you confirm if the hang is limited to assertions enabled llvm builds, and if #153996 is sufficient to fix the problem?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
backend:AArch64 backend:AMDGPU backend:ARC backend:ARM backend:CSKY backend:Hexagon backend:Lanai backend:loongarch backend:MIPS backend:PowerPC backend:RISC-V backend:Sparc backend:SystemZ backend:WebAssembly backend:X86 clang:analysis clang:as-a-library libclang and C++ API clang:bytecode Issues for the clang bytecode constexpr interpreter clang:codegen IR generation bugs: mangling, exceptions, etc. clang:dataflow Clang Dataflow Analysis framework - https://clang.llvm.org/docs/DataFlowAnalysisIntro.html clang:frontend Language frontend issues, e.g. anything involving "Sema" clang:modules C++20 modules and Clang Header Modules clang:openmp OpenMP related changes to Clang clang:static analyzer clang Clang issues not falling into any other category clang-tidy clang-tools-extra clangd coroutines C++20 coroutines debuginfo HLSL HLSL Language Support libc++ libc++ C++ Standard Library. Not GNU libstdc++. Not libc++abi. lldb
Projects
None yet