Skip to content

[Macros] Generalize conformance macros as extension macros #66967

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 8 commits into from
Jun 29, 2023
11 changes: 10 additions & 1 deletion include/swift/AST/Attr.h
Original file line number Diff line number Diff line change
Expand Up @@ -2394,36 +2394,45 @@ class ObjCImplementationAttr final : public DeclAttribute {
/// which declares one of the roles that a given macro can inhabit.
class MacroRoleAttr final
: public DeclAttribute,
private llvm::TrailingObjects<MacroRoleAttr, MacroIntroducedDeclName> {
private llvm::TrailingObjects<MacroRoleAttr, MacroIntroducedDeclName,
TypeExpr *> {
friend TrailingObjects;

MacroSyntax syntax;
MacroRole role;
unsigned numNames;
unsigned numConformances;
SourceLoc lParenLoc, rParenLoc;

MacroRoleAttr(SourceLoc atLoc, SourceRange range, MacroSyntax syntax,
SourceLoc lParenLoc, MacroRole role,
ArrayRef<MacroIntroducedDeclName> names,
ArrayRef<TypeExpr *> conformances,
SourceLoc rParenLoc, bool implicit);

public:
static MacroRoleAttr *create(ASTContext &ctx, SourceLoc atLoc,
SourceRange range, MacroSyntax syntax,
SourceLoc lParenLoc, MacroRole role,
ArrayRef<MacroIntroducedDeclName> names,
ArrayRef<TypeExpr *> conformances,
SourceLoc rParenLoc, bool implicit);

size_t numTrailingObjects(OverloadToken<MacroIntroducedDeclName>) const {
return numNames;
}

size_t numTrailingObjects(OverloadToken<TypeExpr *>) const {
return numConformances;
}

SourceLoc getLParenLoc() const { return lParenLoc; }
SourceLoc getRParenLoc() const { return rParenLoc; }

MacroSyntax getMacroSyntax() const { return syntax; }
MacroRole getMacroRole() const { return role; }
ArrayRef<MacroIntroducedDeclName> getNames() const;
ArrayRef<TypeExpr *> getConformances() const;
bool hasNameKind(MacroIntroducedDeclNameKind kind) const;

static bool classof(const DeclAttribute *DA) {
Expand Down
3 changes: 3 additions & 0 deletions include/swift/AST/DiagnosticsSema.def
Original file line number Diff line number Diff line change
Expand Up @@ -7220,6 +7220,9 @@ ERROR(global_arbitrary_name,none,
"'%0' macros are not allowed to introduce arbitrary names "
"at global scope",
(StringRef))
ERROR(local_extension_macro,none,
"local type cannot have attached extension macro",
())

ERROR(macro_resolve_circular_reference, none,
"circular reference resolving %select{freestanding|attached}0 macro %1",
Expand Down
3 changes: 3 additions & 0 deletions include/swift/AST/MacroDeclaration.h
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,9 @@ enum class MacroRole: uint32_t {
/// A freestanding macro that expands to expressions, statements and
/// declarations in a code block.
CodeItem = 0x80,
/// An attached macro that adds extensions to the declaration the
/// macro is attached to.
Extension = 0x100,

// NOTE: When adding a new macro role, also add it to `getAllMacroRoles`.
};
Expand Down
5 changes: 5 additions & 0 deletions include/swift/AST/PrintOptions.h
Original file line number Diff line number Diff line change
Expand Up @@ -578,6 +578,11 @@ struct PrintOptions {
/// If false, we print them as ordinary associated types.
bool PrintPrimaryAssociatedTypes = true;

/// Whether or not to print \c @attached(extension) attributes on
/// macro declarations. This is used for feature suppression in
/// Swift interface printing.
bool PrintExtensionMacroAttributes = true;

/// If this is not \c nullptr then function bodies (including accessors
/// and constructors) will be printed by this function.
std::function<void(const ValueDecl *, ASTPrinter &)> FunctionBody;
Expand Down
22 changes: 22 additions & 0 deletions include/swift/AST/TypeCheckRequests.h
Original file line number Diff line number Diff line change
Expand Up @@ -4053,6 +4053,28 @@ class ExpandConformanceMacros
void noteCycleStep(DiagnosticEngine &diags) const;
};

/// Expand all extension macros attached to the given declaration.
///
/// Produces the set of macro expansion buffer IDs.
class ExpandExtensionMacros
: public SimpleRequest<ExpandExtensionMacros,
ArrayRef<unsigned>(NominalTypeDecl *),
RequestFlags::Cached> {
public:
using SimpleRequest::SimpleRequest;

private:
friend SimpleRequest;

ArrayRef<unsigned> evaluate(Evaluator &evaluator,
NominalTypeDecl *nominal) const;

public:
bool isCached() const { return true; }
void diagnoseCycle(DiagnosticEngine &diags) const;
void noteCycleStep(DiagnosticEngine &diags) const;
};

/// Expand all member attribute macros attached to the given
/// declaration.
///
Expand Down
3 changes: 3 additions & 0 deletions include/swift/AST/TypeCheckerTypeIDZone.def
Original file line number Diff line number Diff line change
Expand Up @@ -460,6 +460,9 @@ SWIFT_REQUEST(TypeChecker, ExpandAccessorMacros,
SWIFT_REQUEST(TypeChecker, ExpandConformanceMacros,
ArrayRef<unsigned>(NominalTypeDecl *),
Cached, NoLocationInfo)
SWIFT_REQUEST(TypeChecker, ExpandExtensionMacros,
ArrayRef<unsigned>(NominalTypeDecl *),
Cached, NoLocationInfo)
SWIFT_REQUEST(TypeChecker, ExpandSynthesizedMemberMacroRequest,
ArrayRef<unsigned>(Decl *),
Cached, NoLocationInfo)
Expand Down
3 changes: 3 additions & 0 deletions include/swift/Basic/Features.def
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,9 @@ EXPERIMENTAL_FEATURE(CodeItemMacros, true)
EXPERIMENTAL_FEATURE(TupleConformances, false)
EXPERIMENTAL_FEATURE(InitAccessors, false)

EXPERIMENTAL_FEATURE(ExtensionMacros, false)
SUPPRESSIBLE_LANGUAGE_FEATURE(ExtensionMacroAttr, 0, "@attached(extension)", true)

// Whether to enable @_used and @_section attributes
EXPERIMENTAL_FEATURE(SymbolLinkageMarkers, true)

Expand Down
3 changes: 3 additions & 0 deletions include/swift/Basic/SourceManager.h
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,9 @@ class GeneratedSourceInfo {
/// The expansion of an attached conformance macro.
ConformanceMacroExpansion,

/// The expansion of an attached extension macro.
ExtensionMacroExpansion,

/// A new function body that is replacing an existing function body.
ReplacedFunctionBody,

Expand Down
1 change: 1 addition & 0 deletions include/swift/Demangling/DemangleNodes.def
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ NODE(DifferentiableFunctionType)
NODE(ExistentialMetatype)
CONTEXT_NODE(ExplicitClosure)
CONTEXT_NODE(Extension)
NODE(ExtensionAttachedMacroExpansion)
NODE(FieldOffset)
NODE(FreestandingMacroExpansion)
NODE(FullTypeMetadata)
Expand Down
11 changes: 10 additions & 1 deletion lib/AST/ASTMangler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3914,7 +3914,8 @@ void ASTMangler::appendMacroExpansionContext(
case GeneratedSourceInfo::MemberAttributeMacroExpansion:
case GeneratedSourceInfo::MemberMacroExpansion:
case GeneratedSourceInfo::PeerMacroExpansion:
case GeneratedSourceInfo::ConformanceMacroExpansion: {
case GeneratedSourceInfo::ConformanceMacroExpansion:
case GeneratedSourceInfo::ExtensionMacroExpansion: {
auto decl = ASTNode::getFromOpaqueValue(generatedSourceInfo->astNode)
.get<Decl *>();
auto attr = generatedSourceInfo->attachedMacroCustomAttr;
Expand All @@ -3940,6 +3941,10 @@ void ASTMangler::appendMacroExpansionContext(
role = MacroRole::Conformance;
break;

case GeneratedSourceInfo::ExtensionMacroExpansion:
role = MacroRole::Extension;
break;

default:
llvm_unreachable("Unhandled macro role");
}
Expand Down Expand Up @@ -4004,6 +4009,10 @@ void ASTMangler::appendMacroExpansionOperator(
case MacroRole::Conformance:
appendOperator("fMc", Index(discriminator));
break;

case MacroRole::Extension:
appendOperator("fMe", Index(discriminator));
break;
}
}

Expand Down
20 changes: 20 additions & 0 deletions lib/AST/ASTPrinter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2852,6 +2852,26 @@ static bool usesFeatureCodeItemMacros(Decl *decl) {
return macro->getMacroRoles().contains(MacroRole::CodeItem);
}

static bool usesFeatureExtensionMacros(Decl *decl) {
auto macro = dyn_cast<MacroDecl>(decl);
if (!macro)
return false;

return macro->getMacroRoles().contains(MacroRole::Extension);
}

static bool usesFeatureExtensionMacroAttr(Decl *decl) {
return usesFeatureExtensionMacros(decl);
}

static void suppressingFeatureExtensionMacroAttr(PrintOptions &options,
llvm::function_ref<void()> action) {
bool originalPrintExtensionMacroAttrs = options.PrintExtensionMacroAttributes;
options.PrintExtensionMacroAttributes = false;
action();
options.PrintExtensionMacroAttributes = originalPrintExtensionMacroAttrs;
}

static bool usesFeatureAttachedMacros(Decl *decl) {
auto macro = dyn_cast<MacroDecl>(decl);
if (!macro)
Expand Down
1 change: 1 addition & 0 deletions lib/AST/ASTScopeCreation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -272,6 +272,7 @@ ASTSourceFileScope::ASTSourceFileScope(SourceFile *SF,
case MacroRole::Accessor:
case MacroRole::MemberAttribute:
case MacroRole::Conformance:
case MacroRole::Extension:
parentLoc = expansion.getStartLoc();
break;
case MacroRole::Peer: {
Expand Down
29 changes: 26 additions & 3 deletions lib/AST/Attr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1371,6 +1371,13 @@ bool DeclAttribute::printImpl(ASTPrinter &Printer, const PrintOptions &Options,

case DAK_MacroRole: {
auto Attr = cast<MacroRoleAttr>(this);

// Suppress @attached(extension) if needed.
if (!Options.PrintExtensionMacroAttributes &&
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fantastic, thank you!

Attr->getMacroRole() == MacroRole::Extension) {
break;
}

switch (Attr->getMacroSyntax()) {
case MacroSyntax::Freestanding:
Printer.printAttrName("@freestanding");
Expand Down Expand Up @@ -2491,23 +2498,32 @@ MacroRoleAttr::MacroRoleAttr(SourceLoc atLoc, SourceRange range,
MacroSyntax syntax, SourceLoc lParenLoc,
MacroRole role,
ArrayRef<MacroIntroducedDeclName> names,
ArrayRef<TypeExpr *> conformances,
SourceLoc rParenLoc, bool implicit)
: DeclAttribute(DAK_MacroRole, atLoc, range, implicit),
syntax(syntax), role(role), numNames(names.size()), lParenLoc(lParenLoc),
syntax(syntax), role(role), numNames(names.size()),
numConformances(conformances.size()), lParenLoc(lParenLoc),
rParenLoc(rParenLoc) {
auto *trailingNamesBuffer = getTrailingObjects<MacroIntroducedDeclName>();
std::uninitialized_copy(names.begin(), names.end(), trailingNamesBuffer);

auto *trailingConformancesBuffer = getTrailingObjects<TypeExpr *>();
std::uninitialized_copy(conformances.begin(), conformances.end(),
trailingConformancesBuffer);
}

MacroRoleAttr *
MacroRoleAttr::create(ASTContext &ctx, SourceLoc atLoc, SourceRange range,
MacroSyntax syntax, SourceLoc lParenLoc, MacroRole role,
ArrayRef<MacroIntroducedDeclName> names,
ArrayRef<TypeExpr *> conformances,
SourceLoc rParenLoc, bool implicit) {
unsigned size = totalSizeToAlloc<MacroIntroducedDeclName>(names.size());
unsigned size =
totalSizeToAlloc<MacroIntroducedDeclName, TypeExpr *>(
names.size(), conformances.size());
auto *mem = ctx.Allocate(size, alignof(MacroRoleAttr));
return new (mem) MacroRoleAttr(atLoc, range, syntax, lParenLoc, role, names,
rParenLoc, implicit);
conformances, rParenLoc, implicit);
}

ArrayRef<MacroIntroducedDeclName> MacroRoleAttr::getNames() const {
Expand All @@ -2517,6 +2533,13 @@ ArrayRef<MacroIntroducedDeclName> MacroRoleAttr::getNames() const {
};
}

ArrayRef<TypeExpr *> MacroRoleAttr::getConformances() const {
return {
getTrailingObjects<TypeExpr *>(),
numConformances
};
}

bool MacroRoleAttr::hasNameKind(MacroIntroducedDeclNameKind kind) const {
return llvm::find_if(getNames(), [kind](MacroIntroducedDeclName name) {
return name.getKind() == kind;
Expand Down
4 changes: 4 additions & 0 deletions lib/AST/ConformanceLookupTable.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -286,6 +286,10 @@ void ConformanceLookupTable::updateLookupTable(NominalTypeDecl *nominal,
ASTContext &ctx = nominal->getASTContext();
(void)evaluateOrDefault(
ctx.evaluator, ExpandConformanceMacros{nominal}, { });

// Expand extension macros.
(void)evaluateOrDefault(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This expansion is very "eager", in that we're expanding all extension macros even if they don't provide any conformances. I think it'll be okay for now.

ctx.evaluator, ExpandExtensionMacros{nominal}, { });
},
[&](ExtensionDecl *ext,
ArrayRef<ConformanceConstructionInfo> protos) {
Expand Down
12 changes: 10 additions & 2 deletions lib/AST/Decl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10359,7 +10359,7 @@ std::vector<MacroRole> swift::getAllMacroRoles() {
return {
MacroRole::Expression, MacroRole::Declaration, MacroRole::Accessor,
MacroRole::MemberAttribute, MacroRole::Member, MacroRole::Peer,
MacroRole::Conformance, MacroRole::CodeItem,
MacroRole::Conformance, MacroRole::CodeItem, MacroRole::Extension,
};
}

Expand Down Expand Up @@ -10388,6 +10388,9 @@ StringRef swift::getMacroRoleString(MacroRole role) {

case MacroRole::CodeItem:
return "codeItem";

case MacroRole::Extension:
return "extension";
}
}

Expand Down Expand Up @@ -10447,7 +10450,8 @@ static MacroRoles attachedMacroRoles = (MacroRoles() |
MacroRole::MemberAttribute |
MacroRole::Member |
MacroRole::Peer |
MacroRole::Conformance);
MacroRole::Conformance |
MacroRole::Extension);

bool swift::isFreestandingMacro(MacroRoles contexts) {
return bool(contexts & freestandingMacroRoles);
Expand Down Expand Up @@ -10477,6 +10481,8 @@ bool swift::isMacroSupported(MacroRole role, ASTContext &ctx) {
return true;
case MacroRole::CodeItem:
return ctx.LangOpts.hasFeature(Feature::CodeItemMacros);
case MacroRole::Extension:
return ctx.LangOpts.hasFeature(Feature::ExtensionMacros);
}
}

Expand Down Expand Up @@ -10675,6 +10681,7 @@ void MacroDecl::getIntroducedNames(MacroRole role, ValueDecl *attachedTo,
case MacroRole::Member:
case MacroRole::Peer:
case MacroRole::CodeItem:
case MacroRole::Extension:
names.push_back(MacroDecl::getUniqueNamePlaceholder(getASTContext()));
break;

Expand Down Expand Up @@ -10870,6 +10877,7 @@ MacroDiscriminatorContext MacroDiscriminatorContext::getParentOf(
case GeneratedSourceInfo::MemberMacroExpansion:
case GeneratedSourceInfo::PeerMacroExpansion:
case GeneratedSourceInfo::ConformanceMacroExpansion:
case GeneratedSourceInfo::ExtensionMacroExpansion:
case GeneratedSourceInfo::PrettyPrinted:
case GeneratedSourceInfo::ReplacedFunctionBody:
return origDC;
Expand Down
7 changes: 5 additions & 2 deletions lib/AST/DiagnosticEngine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1288,6 +1288,7 @@ DiagnosticEngine::diagnosticInfoForDiagnostic(const Diagnostic &diagnostic) {
case GeneratedSourceInfo::MemberMacroExpansion:
case GeneratedSourceInfo::PeerMacroExpansion:
case GeneratedSourceInfo::ConformanceMacroExpansion:
case GeneratedSourceInfo::ExtensionMacroExpansion:
case GeneratedSourceInfo::PrettyPrinted:
fixIts = {};
break;
Expand Down Expand Up @@ -1335,7 +1336,8 @@ DiagnosticEngine::getGeneratedSourceBufferNotes(SourceLoc loc) {
case GeneratedSourceInfo::MemberAttributeMacroExpansion:
case GeneratedSourceInfo::MemberMacroExpansion:
case GeneratedSourceInfo::PeerMacroExpansion:
case GeneratedSourceInfo::ConformanceMacroExpansion: {
case GeneratedSourceInfo::ConformanceMacroExpansion:
case GeneratedSourceInfo::ExtensionMacroExpansion: {
SourceRange origRange = expansionNode.getSourceRange();
DeclName macroName = getGeneratedSourceInfoMacroName(*generatedInfo);

Expand Down Expand Up @@ -1533,7 +1535,8 @@ swift::getGeneratedSourceInfoMacroName(const GeneratedSourceInfo &info) {
case GeneratedSourceInfo::MemberAttributeMacroExpansion:
case GeneratedSourceInfo::MemberMacroExpansion:
case GeneratedSourceInfo::PeerMacroExpansion:
case GeneratedSourceInfo::ConformanceMacroExpansion: {
case GeneratedSourceInfo::ConformanceMacroExpansion:
case GeneratedSourceInfo::ExtensionMacroExpansion: {
DeclName macroName;
if (auto customAttr = info.attachedMacroCustomAttr) {
// FIXME: How will we handle deserialized custom attributes like this?
Expand Down
6 changes: 5 additions & 1 deletion lib/AST/Module.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -848,7 +848,8 @@ ModuleDecl::getOriginalLocation(SourceLoc loc) const {
case GeneratedSourceInfo::MemberAttributeMacroExpansion:
case GeneratedSourceInfo::MemberMacroExpansion:
case GeneratedSourceInfo::PeerMacroExpansion:
case GeneratedSourceInfo::ConformanceMacroExpansion: {
case GeneratedSourceInfo::ConformanceMacroExpansion:
case GeneratedSourceInfo::ExtensionMacroExpansion: {
// Location was within a macro expansion, return the expansion site, not
// the insertion location.
if (info->attachedMacroCustomAttr) {
Expand Down Expand Up @@ -1158,6 +1159,9 @@ llvm::Optional<MacroRole> SourceFile::getFulfilledMacroRole() const {
case GeneratedSourceInfo::ConformanceMacroExpansion:
return MacroRole::Conformance;

case GeneratedSourceInfo::ExtensionMacroExpansion:
return MacroRole::Extension;

case GeneratedSourceInfo::ReplacedFunctionBody:
case GeneratedSourceInfo::PrettyPrinted:
return llvm::None;
Expand Down
Loading