https://github.com/Michael137 created https://github.com/llvm/llvm-project/pull/168533
Alternative to https://github.com/llvm/llvm-project/pull/159592 >From f2469abd1634b498cb07710e70a83b9d8a72c385 Mon Sep 17 00:00:00 2001 From: Michael Buch <[email protected]> Date: Fri, 14 Nov 2025 11:17:33 +0000 Subject: [PATCH 1/4] [clang][TypePrinter] Remove AppendScope in favour of printNestedNameSpecifier --- clang/lib/AST/Decl.cpp | 3 + clang/lib/AST/TypePrinter.cpp | 67 ++----------------- clang/test/ASTMerge/struct/test.c | 4 +- clang/test/CXX/drs/cwg6xx.cpp | 2 +- clang/test/Index/print-type.c | 2 +- .../Layout/ms-x86-alias-avoidance-padding.cpp | 4 +- clang/test/Modules/compare-record.c | 3 +- clang/test/SemaObjCXX/arc-0x.mm | 8 +-- 8 files changed, 21 insertions(+), 72 deletions(-) diff --git a/clang/lib/AST/Decl.cpp b/clang/lib/AST/Decl.cpp index 8579e51e45697..b771faa4581ec 100644 --- a/clang/lib/AST/Decl.cpp +++ b/clang/lib/AST/Decl.cpp @@ -1742,6 +1742,9 @@ void NamedDecl::printNestedNameSpecifier(raw_ostream &OS, // Collect named contexts. DeclarationName NameInScope = getDeclName(); for (; Ctx; Ctx = Ctx->getParent()) { + if (P.Callbacks && P.Callbacks->isScopeVisible(Ctx)) + continue; + // Suppress anonymous namespace if requested. if (P.SuppressUnwrittenScope && isa<NamespaceDecl>(Ctx) && cast<NamespaceDecl>(Ctx)->isAnonymousNamespace()) diff --git a/clang/lib/AST/TypePrinter.cpp b/clang/lib/AST/TypePrinter.cpp index c18b2eafc722c..d2881d5ac518a 100644 --- a/clang/lib/AST/TypePrinter.cpp +++ b/clang/lib/AST/TypePrinter.cpp @@ -131,8 +131,6 @@ class TypePrinter { void printBefore(QualType T, raw_ostream &OS); void printAfter(QualType T, raw_ostream &OS); - void AppendScope(DeclContext *DC, raw_ostream &OS, - DeclarationName NameInScope); void printTagType(const TagType *T, raw_ostream &OS); void printFunctionAfter(const FunctionType::ExtInfo &Info, raw_ostream &OS); #define ABSTRACT_TYPE(CLASS, PARENT) @@ -1226,7 +1224,7 @@ void TypePrinter::printTypeSpec(NamedDecl *D, raw_ostream &OS) { // In C, this will always be empty except when the type // being printed is anonymous within other Record. if (!Policy.SuppressScope) - AppendScope(D->getDeclContext(), OS, D->getDeclName()); + D->printNestedNameSpecifier(OS, Policy); IdentifierInfo *II = D->getIdentifier(); OS << II->getName(); @@ -1240,7 +1238,7 @@ void TypePrinter::printUnresolvedUsingBefore(const UnresolvedUsingType *T, OS << ' '; auto *D = T->getDecl(); if (Policy.FullyQualifiedName || T->isCanonicalUnqualified()) { - AppendScope(D->getDeclContext(), OS, D->getDeclName()); + D->printNestedNameSpecifier(OS, Policy); } else { T->getQualifier().print(OS, Policy); } @@ -1257,7 +1255,7 @@ void TypePrinter::printUsingBefore(const UsingType *T, raw_ostream &OS) { OS << ' '; auto *D = T->getDecl(); if (Policy.FullyQualifiedName) { - AppendScope(D->getDeclContext(), OS, D->getDeclName()); + D->printNestedNameSpecifier(OS, Policy); } else { T->getQualifier().print(OS, Policy); } @@ -1273,7 +1271,7 @@ void TypePrinter::printTypedefBefore(const TypedefType *T, raw_ostream &OS) { OS << ' '; auto *D = T->getDecl(); if (Policy.FullyQualifiedName) { - AppendScope(D->getDeclContext(), OS, D->getDeclName()); + D->printNestedNameSpecifier(OS, Policy); } else { T->getQualifier().print(OS, Policy); } @@ -1511,59 +1509,6 @@ void TypePrinter::printPredefinedSugarBefore(const PredefinedSugarType *T, void TypePrinter::printPredefinedSugarAfter(const PredefinedSugarType *T, raw_ostream &OS) {} -/// Appends the given scope to the end of a string. -void TypePrinter::AppendScope(DeclContext *DC, raw_ostream &OS, - DeclarationName NameInScope) { - if (DC->isTranslationUnit()) - return; - - // FIXME: Consider replacing this with NamedDecl::printNestedNameSpecifier, - // which can also print names for function and method scopes. - if (DC->isFunctionOrMethod()) - return; - - if (Policy.Callbacks && Policy.Callbacks->isScopeVisible(DC)) - return; - - if (const auto *NS = dyn_cast<NamespaceDecl>(DC)) { - if (Policy.SuppressUnwrittenScope && NS->isAnonymousNamespace()) - return AppendScope(DC->getParent(), OS, NameInScope); - - // Only suppress an inline namespace if the name has the same lookup - // results in the enclosing namespace. - if (Policy.SuppressInlineNamespace != - PrintingPolicy::SuppressInlineNamespaceMode::None && - NS->isInline() && NameInScope && - NS->isRedundantInlineQualifierFor(NameInScope)) - return AppendScope(DC->getParent(), OS, NameInScope); - - AppendScope(DC->getParent(), OS, NS->getDeclName()); - if (NS->getIdentifier()) - OS << NS->getName() << "::"; - else - OS << "(anonymous namespace)::"; - } else if (const auto *Spec = dyn_cast<ClassTemplateSpecializationDecl>(DC)) { - AppendScope(DC->getParent(), OS, Spec->getDeclName()); - IncludeStrongLifetimeRAII Strong(Policy); - OS << Spec->getIdentifier()->getName(); - const TemplateArgumentList &TemplateArgs = Spec->getTemplateArgs(); - printTemplateArgumentList( - OS, TemplateArgs.asArray(), Policy, - Spec->getSpecializedTemplate()->getTemplateParameters()); - OS << "::"; - } else if (const auto *Tag = dyn_cast<TagDecl>(DC)) { - AppendScope(DC->getParent(), OS, Tag->getDeclName()); - if (TypedefNameDecl *Typedef = Tag->getTypedefNameForAnonDecl()) - OS << Typedef->getIdentifier()->getName() << "::"; - else if (Tag->getIdentifier()) - OS << Tag->getIdentifier()->getName() << "::"; - else - return; - } else { - AppendScope(DC->getParent(), OS, NameInScope); - } -} - void TypePrinter::printTagType(const TagType *T, raw_ostream &OS) { TagDecl *D = T->getDecl(); @@ -1593,7 +1538,7 @@ void TypePrinter::printTagType(const TagType *T, raw_ostream &OS) { // Compute the full nested-name-specifier for this type. // In C, this will always be empty except when the type // being printed is anonymous within other Record. - AppendScope(D->getDeclContext(), OS, D->getDeclName()); + D->printNestedNameSpecifier(OS, Policy); } if (const IdentifierInfo *II = D->getIdentifier()) @@ -1809,7 +1754,7 @@ void TypePrinter::printTemplateId(const TemplateSpecializationType *T, // FIXME: Null TD never exercised in test suite. if (FullyQualify && TD) { if (!Policy.SuppressScope) - AppendScope(TD->getDeclContext(), OS, TD->getDeclName()); + TD->printNestedNameSpecifier(OS, Policy); OS << TD->getName(); } else { diff --git a/clang/test/ASTMerge/struct/test.c b/clang/test/ASTMerge/struct/test.c index 10ea753b142bd..d11234472ef29 100644 --- a/clang/test/ASTMerge/struct/test.c +++ b/clang/test/ASTMerge/struct/test.c @@ -44,10 +44,10 @@ // CHECK: struct2.c:72:7: note: field 'i' has type 'int' here // CHECK: struct2.c:76:5: warning: external variable 'x13' declared with incompatible types in different translation units ('S13' vs. 'S13') // CHECK: struct1.c:79:5: note: declared here with type 'S13' -// CHECK: struct1.c:130:7: warning: type 'struct DeepUnnamedError::(unnamed at [[PATH_TO_INPUTS:.+]]struct1.c:130:7)' has incompatible definitions in different translation units +// CHECK: struct1.c:130:7: warning: type 'struct DeepUnnamedError::(anonymous union)::(anonymous union)::(unnamed at [[PATH_TO_INPUTS:.+]]struct1.c:130:7)' has incompatible definitions in different translation units // CHECK: struct1.c:131:14: note: field 'i' has type 'long' here // CHECK: struct2.c:128:15: note: field 'i' has type 'float' here -// CHECK: struct1.c:129:5: warning: type 'union DeepUnnamedError::(unnamed at [[PATH_TO_INPUTS]]struct1.c:129:5)' has incompatible definitions in different translation units +// CHECK: struct1.c:129:5: warning: type 'union DeepUnnamedError::(anonymous union)::(unnamed at [[PATH_TO_INPUTS]]struct1.c:129:5)' has incompatible definitions in different translation units // CHECK: struct1.c:132:9: note: field 'S' has type 'struct (unnamed struct at [[PATH_TO_INPUTS]]struct1.c:130:7)' here // CHECK: struct2.c:129:9: note: field 'S' has type 'struct (unnamed struct at [[PATH_TO_INPUTS]]struct2.c:127:7)' here // CHECK: struct2.c:138:3: warning: external variable 'x16' declared with incompatible types in different translation units ('struct DeepUnnamedError' vs. 'struct DeepUnnamedError') diff --git a/clang/test/CXX/drs/cwg6xx.cpp b/clang/test/CXX/drs/cwg6xx.cpp index 11eb0bf3a27b2..8eac049211193 100644 --- a/clang/test/CXX/drs/cwg6xx.cpp +++ b/clang/test/CXX/drs/cwg6xx.cpp @@ -1241,7 +1241,7 @@ namespace cwg686 { // cwg686: 3.0 #endif struct N { operator struct O{}(){}; - // expected-error@-1 {{'N::O' cannot be defined in a type specifier}} + // expected-error@-1 {{'cwg686::f()::N::O' cannot be defined in a type specifier}} }; try {} catch (struct P *) {} diff --git a/clang/test/Index/print-type.c b/clang/test/Index/print-type.c index d30f4bed246c5..9fb85f8da4e66 100644 --- a/clang/test/Index/print-type.c +++ b/clang/test/Index/print-type.c @@ -71,7 +71,7 @@ _Atomic(unsigned long) aul; // CHECK: TypeRef=struct Struct:16:8 [type=struct Struct] [typekind=Record] [isPOD=1] // CHECK: StructDecl=struct (unnamed at {{.*}}):18:1 (Definition) [type=struct (unnamed at {{.*}}print-type.c:18:1)] [typekind=Record] [isPOD=1] [nbFields=2] [isAnon=1] [isAnonRecDecl=0] // CHECK: StructDecl=struct (unnamed at {{.*}}):23:1 (Definition) [type=struct (unnamed at {{.*}}print-type.c:23:1)] [typekind=Record] [isPOD=1] [nbFields=1] [isAnon=1] [isAnonRecDecl=0] -// CHECK: StructDecl=struct (anonymous at {{.*}}):24:3 (Definition) [type=struct (anonymous at {{.*}}print-type.c:24:3)] [typekind=Record] [isPOD=1] [nbFields=2] [isAnon=1] [isAnonRecDecl=1] +// CHECK: StructDecl=struct (anonymous at {{.*}}):24:3 (Definition) [type=struct (anonymous struct)::(anonymous at {{.*}}print-type.c:24:3)] [typekind=Record] [isPOD=1] [nbFields=2] [isAnon=1] [isAnonRecDecl=1] // CHECK: FieldDecl=x:25:17 (Definition) [type=_Atomic(int)] [typekind=Atomic] [valuetype=int] [valuetypekind=Int] [isPOD=0] [isAnonRecDecl=0] // CHECK: FieldDecl=y:26:9 (Definition) [type=int] [typekind=Int] [isPOD=1] [isAnonRecDecl=0] // CHECK: StructDecl=struct (unnamed at {{.*}}):30:10 (Definition) [type=struct (unnamed at {{.*}}print-type.c:30:10)] [typekind=Record] [isPOD=1] [nbFields=2] [isAnon=1] [isAnonRecDecl=0] diff --git a/clang/test/Layout/ms-x86-alias-avoidance-padding.cpp b/clang/test/Layout/ms-x86-alias-avoidance-padding.cpp index 678537bb514f5..bc6a56ef37538 100644 --- a/clang/test/Layout/ms-x86-alias-avoidance-padding.cpp +++ b/clang/test/Layout/ms-x86-alias-avoidance-padding.cpp @@ -49,7 +49,7 @@ struct AT3 : AT2, AT1 { // CHECK-NEXT: 0 | struct AT2 (base) // CHECK-NEXT: 0 | struct AT0 t // CHECK-NEXT: 0 | union AT0::(unnamed at {{.*}} x -// CHECK-NEXT: 0 | struct AT0::(unnamed at {{.*}} y +// CHECK-NEXT: 0 | struct AT0::(anonymous union)::(unnamed at {{.*}} y // CHECK-NEXT: 0 | int a // CHECK-NEXT: 4 | struct AT t (empty) // CHECK: 0 | int b @@ -66,7 +66,7 @@ struct AT3 : AT2, AT1 { // CHECK-X64-NEXT: 0 | struct AT2 (base) // CHECK-X64-NEXT: 0 | struct AT0 t // CHECK-X64-NEXT: 0 | union AT0::(unnamed at {{.*}} x -// CHECK-X64-NEXT: 0 | struct AT0::(unnamed at {{.*}} y +// CHECK-X64-NEXT: 0 | struct AT0::(anonymous union)::(unnamed at {{.*}} y // CHECK-X64-NEXT: 0 | int a // CHECK-X64-NEXT: 4 | struct AT t (empty) // CHECK-X64: 0 | int b diff --git a/clang/test/Modules/compare-record.c b/clang/test/Modules/compare-record.c index ef4a3a5b0e90d..97caabd55fe33 100644 --- a/clang/test/Modules/compare-record.c +++ b/clang/test/Modules/compare-record.c @@ -496,6 +496,7 @@ struct CompareAnonymousNestedStruct compareAnonymousNestedStruct; // [email protected]:* {{declaration of 'anonymousNestedStructField' does not match}} #elif defined(CASE3) struct CompareDeeplyNestedAnonymousUnionsAndStructs compareDeeplyNested; -// [email protected]:* {{'CompareDeeplyNestedAnonymousUnionsAndStructs::(anonymous union)::(anonymous union)::(anonymous struct)::z' from module 'Second' is not present in definition of 'struct CompareDeeplyNestedAnonymousUnionsAndStructs::(anonymous at {{.*}})' in module 'First.Hidden'}} +// [email protected]:* {{'CompareDeeplyNestedAnonymousUnionsAndStructs::(anonymous union)::(anonymous union)::(anonymous struct)::z' from module 'Second' is not present in definition of 'struct CompareDeeplyNestedAnonymousUnionsAndStructs::(anonymous union)::(anonymous union)::(anonymous at {{.*}})' in module 'First.Hidden'}} + // [email protected]:* {{declaration of 'z' does not match}} #endif diff --git a/clang/test/SemaObjCXX/arc-0x.mm b/clang/test/SemaObjCXX/arc-0x.mm index bcaa5da6b9283..a7418eceb244b 100644 --- a/clang/test/SemaObjCXX/arc-0x.mm +++ b/clang/test/SemaObjCXX/arc-0x.mm @@ -162,7 +162,7 @@ void test() { struct S1 { union { - union { // expected-note-re {{copy constructor of 'S1' is implicitly deleted because field 'test_union::S1::(anonymous union at {{.*}})' has a deleted copy constructor}} expected-note-re {{copy assignment operator of 'S1' is implicitly deleted because field 'test_union::S1::(anonymous union at {{.*}})' has a deleted copy assignment operator}} expected-note-re 4 {{'S1' is implicitly deleted because field 'test_union::S1::(anonymous union at {{.*}})' has a deleted}} + union { // expected-note-re {{copy constructor of 'S1' is implicitly deleted because field 'test_union::S1::(anonymous union)::(anonymous union at {{.*}})' has a deleted copy constructor}} expected-note-re {{copy assignment operator of 'S1' is implicitly deleted because field 'test_union::S1::(anonymous union)::(anonymous union at {{.*}})' has a deleted copy assignment operator}} expected-note-re 4 {{'S1' is implicitly deleted because field 'test_union::S1::(anonymous union)::(anonymous union at {{.*}})' has a deleted}} id f0; // expected-note-re 2 {{{{.*}} of '(anonymous union at {{.*}}' is implicitly deleted because variant field 'f0' is an ObjC pointer}} char f1; }; @@ -173,7 +173,7 @@ void test() { struct S2 { union { // FIXME: the note should say 'f0' is causing the special functions to be deleted. - struct { // expected-note-re 6 {{'S2' is implicitly deleted because variant field 'test_union::S2::(anonymous struct at {{.*}})' has a non-trivial}} + struct { // expected-note-re 6 {{'S2' is implicitly deleted because variant field 'test_union::S2::(anonymous union)::(anonymous struct at {{.*}})' has a non-trivial}} id f0; int f1; }; @@ -195,8 +195,8 @@ void test() { }; static union { // expected-error {{call to implicitly-deleted default constructor of}} - union { // expected-note-re {{default constructor of '(unnamed union at {{.*}}' is implicitly deleted because field 'test_union::(anonymous union at {{.*}})' has a deleted default constructor}} - union { // expected-note-re {{default constructor of '(anonymous union at {{.*}}' is implicitly deleted because field 'test_union::(anonymous union at {{.*}})' has a deleted default constructor}} + union { // expected-note-re {{default constructor of '(unnamed union at {{.*}}' is implicitly deleted because field 'test_union::(anonymous union)::(anonymous union at {{.*}})' has a deleted default constructor}} + union { // expected-note-re {{default constructor of '(anonymous union at {{.*}}' is implicitly deleted because field 'test_union::(anonymous union)::(anonymous union)::(anonymous union at {{.*}})' has a deleted default constructor}} __weak id g1; // expected-note-re {{default constructor of '(anonymous union at {{.*}}' is implicitly deleted because variant field 'g1' is an ObjC pointer}} int g2; }; >From 902cee5f4475f50533fecc2cfa813db061a3fb06 Mon Sep 17 00:00:00 2001 From: Michael Buch <[email protected]> Date: Fri, 14 Nov 2025 16:28:19 +0000 Subject: [PATCH 2/4] [clang][TypePrinter] Add CanonicalAnonymousLambdaName --- clang/include/clang/AST/Decl.h | 5 +++-- clang/include/clang/AST/PrettyPrinter.h | 8 +++++++- clang/lib/AST/Decl.cpp | 8 +++++--- clang/lib/AST/TypePrinter.cpp | 23 +++++++++++++++++------ 4 files changed, 32 insertions(+), 12 deletions(-) diff --git a/clang/include/clang/AST/Decl.h b/clang/include/clang/AST/Decl.h index 406d79ebd6641..995a2a0720870 100644 --- a/clang/include/clang/AST/Decl.h +++ b/clang/include/clang/AST/Decl.h @@ -357,9 +357,10 @@ class NamedDecl : public Decl { /// including the '::' at the end. E.g. /// when `printQualifiedName(D)` prints "A::B::i", /// this function prints "A::B::". - void printNestedNameSpecifier(raw_ostream &OS) const; void printNestedNameSpecifier(raw_ostream &OS, - const PrintingPolicy &Policy) const; + bool AllowFunctionContext = false) const; + void printNestedNameSpecifier(raw_ostream &OS, const PrintingPolicy &Policy, + bool AllowFunctionContext = false) const; // FIXME: Remove string version. std::string getQualifiedNameAsString() const; diff --git a/clang/include/clang/AST/PrettyPrinter.h b/clang/include/clang/AST/PrettyPrinter.h index fd995a653d167..9bb6a7f203cf5 100644 --- a/clang/include/clang/AST/PrettyPrinter.h +++ b/clang/include/clang/AST/PrettyPrinter.h @@ -79,7 +79,8 @@ struct PrintingPolicy { PrintAsCanonical(false), PrintInjectedClassNameWithArguments(true), UsePreferredNames(true), AlwaysIncludeTypeForTemplateArgument(false), CleanUglifiedParameters(false), EntireContentsOfLargeArray(true), - UseEnumerators(true), UseHLSLTypes(LO.HLSL) {} + UseEnumerators(true), UseHLSLTypes(LO.HLSL), + CanonicalAnonymousLambdaName(false) {} /// Adjust this printing policy for cases where it's known that we're /// printing C++ code (for instance, if AST dumping reaches a C++-only @@ -346,6 +347,11 @@ struct PrintingPolicy { LLVM_PREFERRED_TYPE(bool) unsigned UseHLSLTypes : 1; + // TODO: this shouldn't be specific to unnamed lambdas, but also unnamed + // structures/unions/enums (?) + LLVM_PREFERRED_TYPE(bool) + unsigned CanonicalAnonymousLambdaName : 1; + /// Callbacks to use to allow the behavior of printing to be customized. const PrintingCallbacks *Callbacks = nullptr; }; diff --git a/clang/lib/AST/Decl.cpp b/clang/lib/AST/Decl.cpp index b771faa4581ec..0effc7d90fb11 100644 --- a/clang/lib/AST/Decl.cpp +++ b/clang/lib/AST/Decl.cpp @@ -1711,12 +1711,14 @@ void NamedDecl::printQualifiedName(raw_ostream &OS, } } -void NamedDecl::printNestedNameSpecifier(raw_ostream &OS) const { +void NamedDecl::printNestedNameSpecifier(raw_ostream &OS, + bool AllowFunctionContext) const { printNestedNameSpecifier(OS, getASTContext().getPrintingPolicy()); } void NamedDecl::printNestedNameSpecifier(raw_ostream &OS, - const PrintingPolicy &P) const { + const PrintingPolicy &P, + bool AllowFunctionContext) const { const DeclContext *Ctx = getDeclContext(); // For ObjC methods and properties, look through categories and use the @@ -1733,7 +1735,7 @@ void NamedDecl::printNestedNameSpecifier(raw_ostream &OS, Ctx = CI; } - if (Ctx->isFunctionOrMethod()) + if (Ctx->isFunctionOrMethod() && !AllowFunctionContext) return; using ContextsTy = SmallVector<const DeclContext *, 8>; diff --git a/clang/lib/AST/TypePrinter.cpp b/clang/lib/AST/TypePrinter.cpp index d2881d5ac518a..ed50fe71e0537 100644 --- a/clang/lib/AST/TypePrinter.cpp +++ b/clang/lib/AST/TypePrinter.cpp @@ -1532,16 +1532,23 @@ void TypePrinter::printTagType(const TagType *T, raw_ostream &OS) { OS << ' '; } + const IdentifierInfo *II = D->getIdentifier(); + const bool IsLambda = + isa<CXXRecordDecl>(D) && cast<CXXRecordDecl>(D)->isLambda(); + const bool PrintingCanonicalLambdaName = + !II && IsLambda && Policy.CanonicalAnonymousLambdaName; + if (!Policy.FullyQualifiedName && !T->isCanonicalUnqualified()) { T->getQualifier().print(OS, Policy); } else if (!Policy.SuppressScope) { // Compute the full nested-name-specifier for this type. // In C, this will always be empty except when the type // being printed is anonymous within other Record. - D->printNestedNameSpecifier(OS, Policy); + D->printNestedNameSpecifier( + OS, Policy, /*AllowFunctionContext=*/PrintingCanonicalLambdaName); } - if (const IdentifierInfo *II = D->getIdentifier()) + if (II) OS << II->getName(); else if (TypedefNameDecl *Typedef = D->getTypedefNameForAnonDecl()) { assert(Typedef->getIdentifier() && "Typedef without identifier?"); @@ -1549,12 +1556,15 @@ void TypePrinter::printTagType(const TagType *T, raw_ostream &OS) { } else { // Make an unambiguous representation for anonymous types, e.g. // (anonymous enum at /usr/include/string.h:120:9) - OS << (Policy.MSVCFormatting ? '`' : '('); + const bool AddParen = !PrintingCanonicalLambdaName || Policy.AnonymousTagLocations; + if (AddParen) + OS << (Policy.MSVCFormatting ? '`' : '('); - if (isa<CXXRecordDecl>(D) && cast<CXXRecordDecl>(D)->isLambda()) { + if (IsLambda) { OS << "lambda"; HasKindDecoration = true; - } else if ((isa<RecordDecl>(D) && cast<RecordDecl>(D)->isAnonymousStructOrUnion())) { + } else if ((isa<RecordDecl>(D) && + cast<RecordDecl>(D)->isAnonymousStructOrUnion())) { OS << "anonymous"; } else { OS << "unnamed"; @@ -1589,7 +1599,8 @@ void TypePrinter::printTagType(const TagType *T, raw_ostream &OS) { } } - OS << (Policy.MSVCFormatting ? '\'' : ')'); + if (AddParen) + OS << (Policy.MSVCFormatting ? '\'' : ')'); } // If this is a class template specialization, print the template >From 83db21dbb644d4d6bc5028e4b462e1e540676b91 Mon Sep 17 00:00:00 2001 From: Michael Buch <[email protected]> Date: Fri, 14 Nov 2025 16:28:39 +0000 Subject: [PATCH 3/4] [clang][DebugInfo] Canonical lambda names in debug-info --- clang/lib/AST/TypePrinter.cpp | 7 +++++-- clang/lib/CodeGen/CGDebugInfo.cpp | 1 + clang/test/DebugInfo/CXX/prefix-map-lambda.cpp | 10 ---------- clang/test/DebugInfo/CXX/simple-template-names.cpp | 4 ++-- .../DebugInfo/Generic/Inputs/debug-info-slash.cpp | 2 -- .../test/DebugInfo/Generic/Inputs/debug-info-slash.h | 6 ------ clang/test/DebugInfo/Generic/debug-prefix-map.cpp | 11 ----------- clang/test/DebugInfo/Generic/slash.test | 10 ---------- 8 files changed, 8 insertions(+), 43 deletions(-) delete mode 100644 clang/test/DebugInfo/CXX/prefix-map-lambda.cpp delete mode 100644 clang/test/DebugInfo/Generic/Inputs/debug-info-slash.cpp delete mode 100644 clang/test/DebugInfo/Generic/Inputs/debug-info-slash.h delete mode 100644 clang/test/DebugInfo/Generic/debug-prefix-map.cpp delete mode 100644 clang/test/DebugInfo/Generic/slash.test diff --git a/clang/lib/AST/TypePrinter.cpp b/clang/lib/AST/TypePrinter.cpp index ed50fe71e0537..ec65a7d6f438d 100644 --- a/clang/lib/AST/TypePrinter.cpp +++ b/clang/lib/AST/TypePrinter.cpp @@ -1556,12 +1556,15 @@ void TypePrinter::printTagType(const TagType *T, raw_ostream &OS) { } else { // Make an unambiguous representation for anonymous types, e.g. // (anonymous enum at /usr/include/string.h:120:9) - const bool AddParen = !PrintingCanonicalLambdaName || Policy.AnonymousTagLocations; + const bool AddParen = !PrintingCanonicalLambdaName; if (AddParen) OS << (Policy.MSVCFormatting ? '`' : '('); if (IsLambda) { OS << "lambda"; + if (PrintingCanonicalLambdaName) + OS << D->getASTContext().getManglingNumber(D); + HasKindDecoration = true; } else if ((isa<RecordDecl>(D) && cast<RecordDecl>(D)->isAnonymousStructOrUnion())) { @@ -1570,7 +1573,7 @@ void TypePrinter::printTagType(const TagType *T, raw_ostream &OS) { OS << "unnamed"; } - if (Policy.AnonymousTagLocations) { + if (Policy.AnonymousTagLocations && !PrintingCanonicalLambdaName) { // Suppress the redundant tag keyword if we just printed one. // We don't have to worry about ElaboratedTypes here because you can't // refer to an anonymous type with one. diff --git a/clang/lib/CodeGen/CGDebugInfo.cpp b/clang/lib/CodeGen/CGDebugInfo.cpp index bda7b7487f59b..054b274b5a44a 100644 --- a/clang/lib/CodeGen/CGDebugInfo.cpp +++ b/clang/lib/CodeGen/CGDebugInfo.cpp @@ -422,6 +422,7 @@ PrintingPolicy CGDebugInfo::getPrintingPolicy() const { PP.UsePreferredNames = false; PP.AlwaysIncludeTypeForTemplateArgument = true; PP.UseEnumerators = false; + PP.CanonicalAnonymousLambdaName = true; // Apply -fdebug-prefix-map. PP.Callbacks = &PrintCB; diff --git a/clang/test/DebugInfo/CXX/prefix-map-lambda.cpp b/clang/test/DebugInfo/CXX/prefix-map-lambda.cpp deleted file mode 100644 index f0fb1a312c8be..0000000000000 --- a/clang/test/DebugInfo/CXX/prefix-map-lambda.cpp +++ /dev/null @@ -1,10 +0,0 @@ -// RUN: %clang_cc1 -debug-info-kind=limited -triple %itanium_abi_triple \ -// RUN: -fdebug-prefix-map=%S=/SOURCE_ROOT %s -emit-llvm -o - | FileCheck %s - -template <typename T> void b(T) {} -void c() { - // CHECK: !DISubprogram(name: "b<(lambda at - // CHECK-SAME: SOURCE_ROOT - // CHECK-SAME: [[@LINE+1]]:{{[0-9]+}})>" - b([]{}); -} diff --git a/clang/test/DebugInfo/CXX/simple-template-names.cpp b/clang/test/DebugInfo/CXX/simple-template-names.cpp index 5a5d706e81972..8b02303e76355 100644 --- a/clang/test/DebugInfo/CXX/simple-template-names.cpp +++ b/clang/test/DebugInfo/CXX/simple-template-names.cpp @@ -70,9 +70,9 @@ void f() { // anything other than another unnamed class/struct. auto Lambda = [] {}; f1<decltype(Lambda)>(); - // CHECK: !DISubprogram(name: "f1<(lambda at {{.*}}simple-template-names.cpp:[[# @LINE - 2]]:17)>", + // CHECK: !DISubprogram(name: "f1<f()::lambda1>", f1<t1<t1<decltype(Lambda)>>>(); - // CHECK: !DISubprogram(name: "f1<t1<t1<(lambda at {{.*}}> > >", + // CHECK: !DISubprogram(name: "f1<t1<t1<f()::lambda1> > >", struct { } unnamed_struct; f1<decltype(unnamed_struct)>(); diff --git a/clang/test/DebugInfo/Generic/Inputs/debug-info-slash.cpp b/clang/test/DebugInfo/Generic/Inputs/debug-info-slash.cpp deleted file mode 100644 index 563077ed342a1..0000000000000 --- a/clang/test/DebugInfo/Generic/Inputs/debug-info-slash.cpp +++ /dev/null @@ -1,2 +0,0 @@ -#include "Inputs/debug-info-slash.h" -int main() { a(); return 0; } diff --git a/clang/test/DebugInfo/Generic/Inputs/debug-info-slash.h b/clang/test/DebugInfo/Generic/Inputs/debug-info-slash.h deleted file mode 100644 index 9092f4a5e8170..0000000000000 --- a/clang/test/DebugInfo/Generic/Inputs/debug-info-slash.h +++ /dev/null @@ -1,6 +0,0 @@ -template <typename... T> -void f1() {} -void a() { - auto Lambda = [] {}; - f1<decltype(Lambda)>(); -} diff --git a/clang/test/DebugInfo/Generic/debug-prefix-map.cpp b/clang/test/DebugInfo/Generic/debug-prefix-map.cpp deleted file mode 100644 index 174bef5a07699..0000000000000 --- a/clang/test/DebugInfo/Generic/debug-prefix-map.cpp +++ /dev/null @@ -1,11 +0,0 @@ -// RUN: %clang_cc1 -debug-info-kind=standalone -fdebug-prefix-map=%p=./UNLIKELY_PATH/empty %s -emit-llvm -o - | FileCheck %s - -struct alignas(64) an { - struct { - unsigned char x{0}; - } arr[64]; -}; - -struct an *pan = new an; - -// CHECK: !DISubprogram(name: "(unnamed struct at ./UNLIKELY_PATH/empty{{/|\\\\}}{{.*}}", diff --git a/clang/test/DebugInfo/Generic/slash.test b/clang/test/DebugInfo/Generic/slash.test deleted file mode 100644 index 0e42912c18d21..0000000000000 --- a/clang/test/DebugInfo/Generic/slash.test +++ /dev/null @@ -1,10 +0,0 @@ -RUN: rm -rf %t-dir -RUN: mkdir -p %t-dir/header/Inputs -RUN: cp %S/Inputs/debug-info-slash.cpp %t-dir/ -RUN: cp %S/Inputs/debug-info-slash.h %t-dir/header/Inputs -RUN: cd %t-dir -RUN: %clang -target x86_64-pc-win32 -emit-llvm -S -g %t-dir/debug-info-slash.cpp -Iheader -o - | FileCheck --check-prefix=WIN %s -RUN: %clang -target x86_64-linux-gnu -emit-llvm -S -g %t-dir/debug-info-slash.cpp -Iheader -o - | FileCheck --check-prefix=LINUX %s - -WIN: lambda at header\\Inputs\\debug-info-slash.h -LINUX: lambda at header/Inputs/debug-info-slash.h >From 4c075905f5d67d49419c442cf193a41d266373d4 Mon Sep 17 00:00:00 2001 From: Michael Buch <[email protected]> Date: Fri, 14 Nov 2025 17:28:48 +0000 Subject: [PATCH 4/4] [clang][DebugInfo] Treat unnamed records the same as lambdas --- clang/include/clang/AST/PrettyPrinter.h | 6 ++-- clang/lib/AST/TypePrinter.cpp | 35 ++++++++----------- clang/lib/CodeGen/CGDebugInfo.cpp | 2 +- .../DebugInfo/CXX/simple-template-names.cpp | 12 +++---- 4 files changed, 24 insertions(+), 31 deletions(-) diff --git a/clang/include/clang/AST/PrettyPrinter.h b/clang/include/clang/AST/PrettyPrinter.h index 9bb6a7f203cf5..4c5a34b90fc72 100644 --- a/clang/include/clang/AST/PrettyPrinter.h +++ b/clang/include/clang/AST/PrettyPrinter.h @@ -80,7 +80,7 @@ struct PrintingPolicy { UsePreferredNames(true), AlwaysIncludeTypeForTemplateArgument(false), CleanUglifiedParameters(false), EntireContentsOfLargeArray(true), UseEnumerators(true), UseHLSLTypes(LO.HLSL), - CanonicalAnonymousLambdaName(false) {} + CanonicalAnonymousEntities(false) {} /// Adjust this printing policy for cases where it's known that we're /// printing C++ code (for instance, if AST dumping reaches a C++-only @@ -347,10 +347,8 @@ struct PrintingPolicy { LLVM_PREFERRED_TYPE(bool) unsigned UseHLSLTypes : 1; - // TODO: this shouldn't be specific to unnamed lambdas, but also unnamed - // structures/unions/enums (?) LLVM_PREFERRED_TYPE(bool) - unsigned CanonicalAnonymousLambdaName : 1; + unsigned CanonicalAnonymousEntities : 1; /// Callbacks to use to allow the behavior of printing to be customized. const PrintingCallbacks *Callbacks = nullptr; diff --git a/clang/lib/AST/TypePrinter.cpp b/clang/lib/AST/TypePrinter.cpp index ec65a7d6f438d..eeb565b921fad 100644 --- a/clang/lib/AST/TypePrinter.cpp +++ b/clang/lib/AST/TypePrinter.cpp @@ -1533,10 +1533,8 @@ void TypePrinter::printTagType(const TagType *T, raw_ostream &OS) { } const IdentifierInfo *II = D->getIdentifier(); - const bool IsLambda = - isa<CXXRecordDecl>(D) && cast<CXXRecordDecl>(D)->isLambda(); - const bool PrintingCanonicalLambdaName = - !II && IsLambda && Policy.CanonicalAnonymousLambdaName; + const bool PrintingCanonicalAnonName = + !II && Policy.CanonicalAnonymousEntities; if (!Policy.FullyQualifiedName && !T->isCanonicalUnqualified()) { T->getQualifier().print(OS, Policy); @@ -1545,7 +1543,7 @@ void TypePrinter::printTagType(const TagType *T, raw_ostream &OS) { // In C, this will always be empty except when the type // being printed is anonymous within other Record. D->printNestedNameSpecifier( - OS, Policy, /*AllowFunctionContext=*/PrintingCanonicalLambdaName); + OS, Policy, /*AllowFunctionContext=*/PrintingCanonicalAnonName); } if (II) @@ -1556,15 +1554,10 @@ void TypePrinter::printTagType(const TagType *T, raw_ostream &OS) { } else { // Make an unambiguous representation for anonymous types, e.g. // (anonymous enum at /usr/include/string.h:120:9) - const bool AddParen = !PrintingCanonicalLambdaName; - if (AddParen) - OS << (Policy.MSVCFormatting ? '`' : '('); + OS << (Policy.MSVCFormatting ? '`' : '('); - if (IsLambda) { + if (isa<CXXRecordDecl>(D) && cast<CXXRecordDecl>(D)->isLambda()) { OS << "lambda"; - if (PrintingCanonicalLambdaName) - OS << D->getASTContext().getManglingNumber(D); - HasKindDecoration = true; } else if ((isa<RecordDecl>(D) && cast<RecordDecl>(D)->isAnonymousStructOrUnion())) { @@ -1573,13 +1566,16 @@ void TypePrinter::printTagType(const TagType *T, raw_ostream &OS) { OS << "unnamed"; } - if (Policy.AnonymousTagLocations && !PrintingCanonicalLambdaName) { - // Suppress the redundant tag keyword if we just printed one. - // We don't have to worry about ElaboratedTypes here because you can't - // refer to an anonymous type with one. - if (!HasKindDecoration) - OS << " " << D->getKindName(); + if (PrintingCanonicalAnonName) + OS << D->getASTContext().getManglingNumber(D); + + // Suppress the redundant tag keyword if we just printed one. + // We don't have to worry about ElaboratedTypes here because you can't + // refer to an anonymous type with one. + if (!HasKindDecoration) + OS << " " << D->getKindName(); + if (Policy.AnonymousTagLocations && !PrintingCanonicalAnonName) { PresumedLoc PLoc = D->getASTContext().getSourceManager().getPresumedLoc( D->getLocation()); if (PLoc.isValid()) { @@ -1602,8 +1598,7 @@ void TypePrinter::printTagType(const TagType *T, raw_ostream &OS) { } } - if (AddParen) - OS << (Policy.MSVCFormatting ? '\'' : ')'); + OS << (Policy.MSVCFormatting ? '\'' : ')'); } // If this is a class template specialization, print the template diff --git a/clang/lib/CodeGen/CGDebugInfo.cpp b/clang/lib/CodeGen/CGDebugInfo.cpp index 054b274b5a44a..ca63215f4b73a 100644 --- a/clang/lib/CodeGen/CGDebugInfo.cpp +++ b/clang/lib/CodeGen/CGDebugInfo.cpp @@ -422,7 +422,7 @@ PrintingPolicy CGDebugInfo::getPrintingPolicy() const { PP.UsePreferredNames = false; PP.AlwaysIncludeTypeForTemplateArgument = true; PP.UseEnumerators = false; - PP.CanonicalAnonymousLambdaName = true; + PP.CanonicalAnonymousEntities = true; // Apply -fdebug-prefix-map. PP.Callbacks = &PrintCB; diff --git a/clang/test/DebugInfo/CXX/simple-template-names.cpp b/clang/test/DebugInfo/CXX/simple-template-names.cpp index 8b02303e76355..2b6e9fd74130e 100644 --- a/clang/test/DebugInfo/CXX/simple-template-names.cpp +++ b/clang/test/DebugInfo/CXX/simple-template-names.cpp @@ -70,18 +70,18 @@ void f() { // anything other than another unnamed class/struct. auto Lambda = [] {}; f1<decltype(Lambda)>(); - // CHECK: !DISubprogram(name: "f1<f()::lambda1>", + // CHECK: !DISubprogram(name: "f1<f()::(lambda1)>", f1<t1<t1<decltype(Lambda)>>>(); - // CHECK: !DISubprogram(name: "f1<t1<t1<f()::lambda1> > >", + // CHECK: !DISubprogram(name: "f1<t1<t1<f()::(lambda1)> > >", struct { } unnamed_struct; f1<decltype(unnamed_struct)>(); - // CHECK: !DISubprogram(name: "f1<(unnamed struct at {{.*}}simple-template-names.cpp:[[# @LINE - 3]]:3)>", + // CHECK: !DISubprogram(name: "f1<f()::(unnamed1 struct)>", f1<void (decltype(unnamed_struct))>(); - // CHECK: !DISubprogram(name: "f1<void ((unnamed struct at {{.*}}simple-template-names.cpp:[[# @LINE - 5]]:3))>", + // CHECK: !DISubprogram(name: "f1<void (f()::(unnamed1 struct))>", enum {} unnamed_enum; f1<decltype(unnamed_enum)>(); - // CHECK: !DISubprogram(name: "f1<(unnamed enum at {{.*}}simple-template-names.cpp:[[# @LINE - 2]]:3)>", + // CHECK: !DISubprogram(name: "f1<f()::(unnamed1 enum)>", // Declarations can't readily be reversed as the value in the DWARF only // contains the address of the value - we'd have to do symbol lookup to find @@ -128,5 +128,5 @@ void f() { // CHECK: !DISubprogram(name: "f1<int () __attribute__((noreturn))>", f4<UnnamedEnum1>(); - // CHECK: !DISubprogram(name: "f4<((unnamed enum at {{.*}}))0>" + // CHECK: !DISubprogram(name: "f4<((unnamed1 enum))0>" } _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
