Skip to content

Commit adc777c

Browse files
authored
Merge pull request swiftlang#78001 from DougGregor/local-macro-expansion-mangling-6.1
[6.1] Rework mangling of macro expansions in local contexts to not trigger type checking
2 parents 6af1d18 + dd91756 commit adc777c

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
@@ -394,7 +394,9 @@ class ASTMangler : public Mangler {
394394
std::string mangleAttachedMacroExpansion(
395395
const Decl *decl, CustomAttr *attr, MacroRole role);
396396

397-
void appendMacroExpansionContext(SourceLoc loc, DeclContext *origDC);
397+
void appendMacroExpansion(const FreestandingMacroExpansion *expansion);
398+
void appendMacroExpansionContext(SourceLoc loc, DeclContext *origDC,
399+
const FreestandingMacroExpansion *expansion);
398400
void appendMacroExpansionOperator(
399401
StringRef macroName, MacroRole role, unsigned discriminator);
400402

lib/AST/ASTMangler.cpp

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

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

@@ -4515,14 +4510,51 @@ std::string ASTMangler::mangleDistributedThunk(const AbstractFunctionDecl *thunk
45154510
return finalize();
45164511
}
45174512

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

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

45274559
ASTContext &ctx = origDC->getASTContext();
45284560
SourceManager &sourceMgr = ctx.SourceMgr;
@@ -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)