Skip to content

Commit 65a9bff

Browse files
committed
Rework mangling of macro expansions in local contexts to not trigger type checking.
The mangling of macro expansions relies on having a type-checked AST for its enclosing context. When that enclosing context is within a local context (say, a local type), mangling would trigger type checking of that local type, which could then involve assigning local discriminators. However, if this happens before type checking of the enclosing function body, we would end up failing to assign closure discriminators to (e.g.) autoclosures within the body. The fundamental problem here is the interaction between discriminator assignment (which can only happen after type checking) and mangling of macro expansion buffers (which can happen during that type checking). Break this cycle by providing a different approach to mangling macro expansions within local contexts as the innermost non-local context + a name-based discriminator within that local context. These manglings are not ABI and are not stable, so we can adjust them later if we come up with a scheme we like better. However, by breaking this cycle, we eliminate assertions and miscompiles that come from missing discriminators in this case. Fixes rdar://139734958.
1 parent 3ab2a3e commit 65a9bff

File tree

5 files changed

+199
-26
lines changed

5 files changed

+199
-26
lines changed

include/swift/AST/ASTMangler.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -401,7 +401,9 @@ class ASTMangler : public Mangler {
401401
std::string mangleAttachedMacroExpansion(
402402
const Decl *decl, CustomAttr *attr, MacroRole role);
403403

404-
void appendMacroExpansionContext(SourceLoc loc, DeclContext *origDC);
404+
void appendMacroExpansion(const FreestandingMacroExpansion *expansion);
405+
void appendMacroExpansionContext(SourceLoc loc, DeclContext *origDC,
406+
const FreestandingMacroExpansion *expansion);
405407
void appendMacroExpansionOperator(
406408
StringRef macroName, MacroRole role, unsigned discriminator);
407409

lib/AST/ASTMangler.cpp

Lines changed: 137 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -4071,12 +4071,7 @@ void ASTMangler::appendEntity(const ValueDecl *decl) {
40714071
}
40724072

40734073
if (auto expansion = dyn_cast<MacroExpansionDecl>(decl)) {
4074-
appendMacroExpansionContext(
4075-
expansion->getLoc(), expansion->getDeclContext());
4076-
appendMacroExpansionOperator(
4077-
expansion->getMacroName().getBaseName().userFacingName(),
4078-
MacroRole::Declaration,
4079-
expansion->getDiscriminator());
4074+
appendMacroExpansion(expansion);
40804075
return;
40814076
}
40824077

@@ -4516,14 +4511,51 @@ std::string ASTMangler::mangleDistributedThunk(const AbstractFunctionDecl *thunk
45164511
return finalize();
45174512
}
45184513

4514+
/// Retrieve the outermost local context, or return NULL if there is no such
4515+
/// local context.
4516+
static const DeclContext *getOutermostLocalContext(const DeclContext *dc) {
4517+
// If the parent has an outermost local context, it's ours as well.
4518+
if (auto parentDC = dc->getParent()) {
4519+
if (auto outermost = getOutermostLocalContext(parentDC))
4520+
return outermost;
4521+
}
4522+
4523+
return dc->isLocalContext() ? dc : nullptr;
4524+
}
4525+
4526+
/// Enable a precheck discriminator into the identifier name. These mangled
4527+
/// names are not ABI and are not stable.
4528+
static Identifier encodeLocalPrecheckedDiscriminator(
4529+
ASTContext &ctx, Identifier name, unsigned discriminator) {
4530+
llvm::SmallString<16> discriminatedName;
4531+
{
4532+
llvm::raw_svector_ostream out(discriminatedName);
4533+
out << name.str() << "_$l" << discriminator;
4534+
}
4535+
4536+
return ctx.getIdentifier(discriminatedName);
4537+
}
4538+
45194539
void ASTMangler::appendMacroExpansionContext(
4520-
SourceLoc loc, DeclContext *origDC
4540+
SourceLoc loc, DeclContext *origDC,
4541+
const FreestandingMacroExpansion *expansion
45214542
) {
45224543
origDC = MacroDiscriminatorContext::getInnermostMacroContext(origDC);
45234544
BaseEntitySignature nullBase(nullptr);
45244545

4525-
if (loc.isInvalid())
4526-
return appendContext(origDC, nullBase, StringRef());
4546+
if (loc.isInvalid()) {
4547+
if (auto outermostLocalDC = getOutermostLocalContext(origDC)) {
4548+
auto innermostNonlocalDC = outermostLocalDC->getParent();
4549+
appendContext(innermostNonlocalDC, nullBase, StringRef());
4550+
Identifier name = expansion->getMacroName().getBaseIdentifier();
4551+
ASTContext &ctx = origDC->getASTContext();
4552+
unsigned discriminator = expansion->getDiscriminator();
4553+
name = encodeLocalPrecheckedDiscriminator(ctx, name, discriminator);
4554+
appendIdentifier(name.str());
4555+
} else {
4556+
return appendContext(origDC, nullBase, StringRef());
4557+
}
4558+
}
45274559

45284560
SourceManager &sourceMgr = Context.SourceMgr;
45294561

@@ -4622,7 +4654,7 @@ void ASTMangler::appendMacroExpansionContext(
46224654
return appendMacroExpansionLoc();
46234655

46244656
// Append our own context and discriminator.
4625-
appendMacroExpansionContext(outerExpansionLoc, origDC);
4657+
appendMacroExpansionContext(outerExpansionLoc, origDC, expansion);
46264658
appendMacroExpansionOperator(
46274659
baseName.userFacingName(), role, discriminator);
46284660
}
@@ -4686,11 +4718,11 @@ static StringRef getPrivateDiscriminatorIfNecessary(
46864718
}
46874719
}
46884720

4689-
std::string
4690-
ASTMangler::mangleMacroExpansion(const FreestandingMacroExpansion *expansion) {
4691-
beginMangling();
4721+
void
4722+
ASTMangler::appendMacroExpansion(const FreestandingMacroExpansion *expansion) {
46924723
appendMacroExpansionContext(expansion->getPoundLoc(),
4693-
expansion->getDeclContext());
4724+
expansion->getDeclContext(),
4725+
expansion);
46944726
auto privateDiscriminator = getPrivateDiscriminatorIfNecessary(expansion);
46954727
if (!privateDiscriminator.empty()) {
46964728
appendIdentifier(privateDiscriminator);
@@ -4700,25 +4732,106 @@ ASTMangler::mangleMacroExpansion(const FreestandingMacroExpansion *expansion) {
47004732
expansion->getMacroName().getBaseName().userFacingName(),
47014733
MacroRole::Declaration,
47024734
expansion->getDiscriminator());
4735+
}
4736+
4737+
std::string
4738+
ASTMangler::mangleMacroExpansion(const FreestandingMacroExpansion *expansion) {
4739+
beginMangling();
4740+
appendMacroExpansion(expansion);
47034741
return finalize();
47044742
}
47054743

4744+
namespace {
4745+
4746+
/// Stores either a declaration or its enclosing context, for use in mangling
4747+
/// of macro expansion contexts.
4748+
struct DeclOrEnclosingContext: llvm::PointerUnion<const Decl *, const DeclContext *> {
4749+
using PointerUnion::PointerUnion;
4750+
4751+
const DeclContext *getEnclosingContext() const {
4752+
if (auto decl = dyn_cast<const Decl *>()) {
4753+
return decl->getDeclContext();
4754+
}
4755+
4756+
return get<const DeclContext *>();
4757+
}
4758+
};
4759+
4760+
}
4761+
4762+
/// Given a declaration, find the declaration or enclosing context that is
4763+
/// the innermost context that is not a local context, along with a
4764+
/// discriminator that identifies this given specific declaration (along
4765+
/// with its `name`) within that enclosing context. This is used to
4766+
/// mangle entities within local contexts before they are fully type-checked,
4767+
/// as is needed for macro expansions.
4768+
static std::pair<DeclOrEnclosingContext, std::optional<unsigned>>
4769+
getPrecheckedLocalContextDiscriminator(const Decl *decl, Identifier name) {
4770+
auto outermostLocal = getOutermostLocalContext(decl->getDeclContext());
4771+
if (!outermostLocal) {
4772+
return std::make_pair(
4773+
DeclOrEnclosingContext(decl),
4774+
std::optional<unsigned>()
4775+
);
4776+
}
4777+
DeclOrEnclosingContext declOrEnclosingContext;
4778+
if (decl->getDeclContext() == outermostLocal)
4779+
declOrEnclosingContext = decl;
4780+
else if (const Decl *fromDecl = outermostLocal->getAsDecl())
4781+
declOrEnclosingContext = fromDecl;
4782+
else
4783+
declOrEnclosingContext = outermostLocal->getParent();
4784+
4785+
DeclContext *enclosingDC = const_cast<DeclContext *>(
4786+
declOrEnclosingContext.getEnclosingContext());
4787+
ASTContext &ctx = enclosingDC->getASTContext();
4788+
auto discriminator = ctx.getNextMacroDiscriminator(enclosingDC, name);
4789+
return std::make_pair(declOrEnclosingContext, discriminator);
4790+
}
4791+
47064792
std::string ASTMangler::mangleAttachedMacroExpansion(
47074793
const Decl *decl, CustomAttr *attr, MacroRole role) {
47084794
// FIXME(kavon): using the decl causes a cycle. Is a null base fine?
47094795
BaseEntitySignature nullBase(nullptr);
47104796

47114797
beginMangling();
47124798

4799+
auto appendDeclWithName = [&](const Decl *decl, Identifier name) {
4800+
// Mangle the context.
4801+
auto precheckedMangleContext =
4802+
getPrecheckedLocalContextDiscriminator(decl, name);
4803+
if (auto mangleDecl = dyn_cast_or_null<ValueDecl>(
4804+
precheckedMangleContext.first.dyn_cast<const Decl *>())) {
4805+
appendContextOf(mangleDecl, nullBase);
4806+
} else {
4807+
appendContext(
4808+
precheckedMangleContext.first.getEnclosingContext(), nullBase,
4809+
StringRef());
4810+
}
4811+
4812+
// If we needed a local discriminator, stuff that into the name itself.
4813+
// This is hack, but these names aren't stable anyway.
4814+
if (auto discriminator = precheckedMangleContext.second) {
4815+
name = encodeLocalPrecheckedDiscriminator(
4816+
decl->getASTContext(), name, *discriminator);
4817+
}
4818+
4819+
if (auto valueDecl = dyn_cast<ValueDecl>(decl))
4820+
appendDeclName(valueDecl, name);
4821+
else if (!name.empty())
4822+
appendIdentifier(name.str());
4823+
else
4824+
appendIdentifier("_");
4825+
};
4826+
47134827
// Append the context and name of the declaration.
47144828
// We don't mangle the declaration itself because doing so requires semantic
47154829
// information (e.g., its interface type), which introduces cyclic
47164830
// dependencies.
47174831
const Decl *attachedTo = decl;
4718-
DeclBaseName attachedToName;
4832+
Identifier attachedToName;
47194833
if (auto accessor = dyn_cast<AccessorDecl>(decl)) {
47204834
auto storage = accessor->getStorage();
4721-
appendContextOf(storage, nullBase);
47224835

47234836
// Introduce an identifier mangling that includes var/subscript, accessor
47244837
// kind, and static.
@@ -4744,23 +4857,24 @@ std::string ASTMangler::mangleAttachedMacroExpansion(
47444857
attachedToName = decl->getASTContext().getIdentifier(name);
47454858
}
47464859

4747-
appendDeclName(storage, attachedToName);
4860+
appendDeclWithName(storage, attachedToName);
47484861

47494862
// For member attribute macros, the attribute is attached to the enclosing
47504863
// declaration.
47514864
if (role == MacroRole::MemberAttribute) {
47524865
attachedTo = storage->getDeclContext()->getAsDecl();
47534866
}
47544867
} else if (auto valueDecl = dyn_cast<ValueDecl>(decl)) {
4755-
appendContextOf(valueDecl, nullBase);
4756-
47574868
// Mangle the name, replacing special names with their user-facing names.
4758-
attachedToName = valueDecl->getName().getBaseName();
4759-
if (attachedToName.isSpecial()) {
4869+
auto name = valueDecl->getName().getBaseName();
4870+
if (name.isSpecial()) {
47604871
attachedToName =
4761-
decl->getASTContext().getIdentifier(attachedToName.userFacingName());
4872+
decl->getASTContext().getIdentifier(name.userFacingName());
4873+
} else {
4874+
attachedToName = name.getIdentifier();
47624875
}
4763-
appendDeclName(valueDecl, attachedToName);
4876+
4877+
appendDeclWithName(valueDecl, attachedToName);
47644878

47654879
// For member attribute macros, the attribute is attached to the enclosing
47664880
// declaration.

test/Macros/Inputs/syntax_macro_definitions.swift

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2807,3 +2807,19 @@ public struct HangingMacro: PeerMacro {
28072807
]
28082808
}
28092809
}
2810+
2811+
public struct BigEndianAccessorMacro: AccessorMacro {
2812+
public static func expansion(
2813+
of node: AttributeSyntax,
2814+
providingAccessorsOf declaration: some DeclSyntaxProtocol,
2815+
in context: some MacroExpansionContext
2816+
) throws -> [AccessorDeclSyntax] {
2817+
[
2818+
"""
2819+
get {
2820+
__value.bigEndian
2821+
}
2822+
"""
2823+
]
2824+
}
2825+
}

test/Macros/accessor_macros.swift

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,3 +174,36 @@ struct S {
174174
// expected-warning@-1 {{cannot expand accessor macro on variable declared with 'let'; this is an error in the Swift 6 language mode}}
175175
}
176176
#endif
177+
178+
func acceptAutoclosure(_ success: @autoclosure () -> Bool, message: @autoclosure () -> String) {
179+
}
180+
181+
@attached(accessor)
182+
macro BigEndianAccessorMacro() = #externalMacro(module: "MacroDefinition", type: "BigEndianAccessorMacro")
183+
184+
func testLocalWithAutoclosure(x: Int, y: Int) {
185+
struct Local {
186+
var __value: Int = 0
187+
188+
// CHECK-DUMP: @__swiftmacro_15accessor_macros9value_$l022BigEndianAccessorMacrofMa_.swift
189+
@BigEndianAccessorMacro
190+
var value: Int
191+
}
192+
193+
acceptAutoclosure(x == y, message: "they better be the same")
194+
195+
let local = Local(__value: 5)
196+
acceptAutoclosure(x + 1 == local.__value, message: "they better be the same")
197+
198+
if x == y {
199+
struct Nested {
200+
struct Local {
201+
var __value: Int = 0
202+
203+
// CHECK-DUMP: @__swiftmacro_15accessor_macros9value_$l122BigEndianAccessorMacrofMa_.swift
204+
@BigEndianAccessorMacro
205+
var value: Int
206+
}
207+
}
208+
}
209+
}

test/Macros/macro_expand.swift

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -138,11 +138,19 @@ macro AccidentalCodeItem() = #externalMacro(module: "MacroDefinition", type: "Fa
138138
func invalidDeclarationMacro() {
139139
#accidentalCodeItem
140140
// expected-note@-1 {{in expansion of macro 'accidentalCodeItem' here}}
141-
// CHECK-DIAGS: @__swiftmacro_9MacroUser0023macro_expandswift_elFCffMX{{.*}}_18accidentalCodeItemfMf_.swift:1:1: error: expected macro expansion to produce a declaration
141+
// CHECK-DIAGS: @__swiftmacro_9MacroUser0023macro_expandswift_elFCffMX138_2_18accidentalCodeItemfMf_.swift:1:1: error: expected macro expansion to produce a declaration
142142

143143
@AccidentalCodeItem struct S {}
144144
// expected-note@-1 {{in expansion of macro 'AccidentalCodeItem' on struct 'S' here}}
145-
// CHECK-DIAGS: @__swiftmacro_9MacroUser018invalidDeclarationA0yyF1SL_18AccidentalCodeItemfMp_.swift:1:1: error: expected macro expansion to produce a declaration
145+
// CHECK-DIAGS: @__swiftmacro_9MacroUser018invalidDeclarationA0yyF5S_$l0L_18AccidentalCodeItemfMp_.swift:1:1: error: expected macro expansion to produce a declaration
146+
147+
struct LocalThing1 {
148+
func f() {
149+
#accidentalCodeItem
150+
// expected-note@-1 {{in expansion of macro 'accidentalCodeItem' here}}
151+
// CHECK-DIAGS: @__swiftmacro_9MacroUser018invalidDeclarationA0yyF5S_$l0L_18AccidentalCodeItemfMp_.swift
152+
}
153+
}
146154
}
147155
#endif
148156

0 commit comments

Comments
 (0)