Skip to content

Commit 64fb19b

Browse files
authored
Merge pull request #77999 from DougGregor/local-macro-expansion-mangling
Rework mangling of macro expansions in local contexts to not trigger type checking
2 parents d2cdf0f + 65a9bff commit 64fb19b

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)