From d67784cceaca8f9d47c8c7e11a5ba006729f970b Mon Sep 17 00:00:00 2001 From: Tony Allevato Date: Mon, 24 Nov 2025 12:51:08 -0500 Subject: [PATCH 1/3] [ASTDumper] Don't attempt to compute type USR for `ModuleType`s. `ModuleType`s show up in some rare places, like the left-hand-side of a `DotSyntaxBaseIgnoredExpr` for a module-qualified function call. ASTMangler does not support these because they're not real types. --- lib/AST/ASTDumper.cpp | 13 +++++++++++++ test/Frontend/ast-dump-json-no-crash.swift | 7 +++++++ 2 files changed, 20 insertions(+) diff --git a/lib/AST/ASTDumper.cpp b/lib/AST/ASTDumper.cpp index 23c9019700934..3cf421d99bcb1 100644 --- a/lib/AST/ASTDumper.cpp +++ b/lib/AST/ASTDumper.cpp @@ -260,6 +260,12 @@ std::string typeUSR(Type type) { if (!type) return ""; + if (type->is()) { + // ASTMangler does not support "module types". This can appear, for + // example, on the left-hand side of a `DotSyntaxBaseIgnoredExpr` for a + // module-qualified free function call: `Swift.print()`. + return ""; + } if (type->hasArchetype()) { type = type->mapTypeOutOfEnvironment(); } @@ -280,6 +286,13 @@ std::string declTypeUSR(const ValueDecl *D) { if (!D) return ""; + if (isa(D)) { + // ASTMangler does not support "module types". This can appear, for + // example, on the left-hand side of a `DotSyntaxBaseIgnoredExpr` for a + // module-qualified free function call: `Swift.print()`. + return ""; + } + std::string usr; llvm::raw_string_ostream os(usr); if (swift::ide::printDeclTypeUSR(D, os)) diff --git a/test/Frontend/ast-dump-json-no-crash.swift b/test/Frontend/ast-dump-json-no-crash.swift index d7bc2a052ef16..f5a57aa30053c 100644 --- a/test/Frontend/ast-dump-json-no-crash.swift +++ b/test/Frontend/ast-dump-json-no-crash.swift @@ -484,3 +484,10 @@ dynamic func toBeReplaced(arg: Int) {} @_dynamicReplacement(for: toBeReplaced(arg:)) func toReplaceWith(arg: Int) {} + +// Regression test: Swift 6.2 and earlier crashed trying to form the type USR +// of the "module type" of a `DotSyntaxBaseIgnoredExpr` when calling a +// module-qualified free function. +func moduleTypeUSRRegressionTest() { + Swift.print("") +} From e2a3fb4bc1f46a61ad491eb5fbd2786fda5f27f8 Mon Sep 17 00:00:00 2001 From: Tony Allevato Date: Mon, 24 Nov 2025 13:12:02 -0500 Subject: [PATCH 2/3] [ASTDumper] Print decl-refs, not full decls, for function captures. This avoids infinite recursion when a function captures itself; e.g., ```swift func foo() { func bar() { bar() } bar() } ``` It also just avoids printing unnecessarily verbose information, since those decls would have already been dumped elsewhere in the tree. --- lib/AST/ASTDumper.cpp | 6 +++++- test/Frontend/ast-dump-json-no-crash.swift | 12 ++++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/lib/AST/ASTDumper.cpp b/lib/AST/ASTDumper.cpp index 3cf421d99bcb1..ba07c0334e348 100644 --- a/lib/AST/ASTDumper.cpp +++ b/lib/AST/ASTDumper.cpp @@ -1484,7 +1484,11 @@ namespace { printFlag(value.isLocalCapture(), "is_local_capture"); printFlag(value.isDynamicSelfMetadata(), "is_dynamic_self_metadata"); if (auto *D = value.getDecl()) { - printRec(D, Label::always("decl")); + // We print the decl ref, not the full decl, to avoid infinite + // recursion when a function captures itself (and also because + // those decls are already printed elsewhere, so we don't need to + // print what could be a very large amount of information twice). + printDeclRefField(D, Label::always("decl")); } if (auto *E = value.getExpr()) { printRec(E, Label::always("expr")); diff --git a/test/Frontend/ast-dump-json-no-crash.swift b/test/Frontend/ast-dump-json-no-crash.swift index f5a57aa30053c..8fbbf46a4d106 100644 --- a/test/Frontend/ast-dump-json-no-crash.swift +++ b/test/Frontend/ast-dump-json-no-crash.swift @@ -491,3 +491,15 @@ func toReplaceWith(arg: Int) {} func moduleTypeUSRRegressionTest() { Swift.print("") } + +// Regression test: When a function captures another function, don't print the +// entire captured decl. This causes infinite recursion in the dumper when a +// local (nested) function captures itself. +func outerFn() { + func innerFun(shouldRecurse: Bool) { + if shouldRecurse { + innerFun(shouldRecurse: false) + } + } + innerFun(shouldRecurse: true) +} From 674b51eef0ef15cfe68c4817e455c8b2ecdc2299 Mon Sep 17 00:00:00 2001 From: Tony Allevato Date: Tue, 2 Dec 2025 15:59:54 -0500 Subject: [PATCH 3/3] [ASTDumper] Fix malformed JSON for `async let _ = ...`. By calling `printCommon` twice, this inserts a JSON object into another JSON object without a key. This asserts inside LLVM's JSON helper, but only if the compiler is built with assertions enabled (which Xcode's compiler doesn't). This also fixes the S-expression version, which is similarly busted: ``` (pattern_entry (async_let type="Int" (pattern_any type="Int") (original_init=call_expr type="Int" ... ``` --- lib/AST/ASTDumper.cpp | 4 +--- test/Frontend/ast-dump-json-no-crash.swift | 8 ++++++++ 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/lib/AST/ASTDumper.cpp b/lib/AST/ASTDumper.cpp index ba07c0334e348..699b754721f63 100644 --- a/lib/AST/ASTDumper.cpp +++ b/lib/AST/ASTDumper.cpp @@ -1913,10 +1913,8 @@ namespace { printFoot(); } void visitAnyPattern(AnyPattern *P, Label label) { - if (P->isAsyncLet()) { - printCommon(P, "async_let ", label); - } printCommon(P, "pattern_any", label); + printFlag(P->isAsyncLet(), "async_let", DeclModifierColor); printFoot(); } void visitTypedPattern(TypedPattern *P, Label label) { diff --git a/test/Frontend/ast-dump-json-no-crash.swift b/test/Frontend/ast-dump-json-no-crash.swift index 8fbbf46a4d106..e4db14fdc364e 100644 --- a/test/Frontend/ast-dump-json-no-crash.swift +++ b/test/Frontend/ast-dump-json-no-crash.swift @@ -503,3 +503,11 @@ func outerFn() { } innerFun(shouldRecurse: true) } + +// Regression test: Discarded async lets were calling `printCommon` twice, +// which resulted in invalid JSON (and not-so-great S-expression output) +// either. +func discardedAsyncLet() async { + func someTask() async {} + async let _ = someTask() +}