Skip to content

[Macros] Initial implementation of conformance macros. #63853

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 3 commits into from
Feb 24, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions include/swift/AST/MacroDeclaration.h
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,9 @@ enum class MacroRole: uint32_t {
/// An attached macro that generates declarations that are peers
/// of the declaration the macro is attached to.
Peer = 0x20,
/// An attached macro that adds conformances to the declaration the
/// macro is attached to.
Conformance = 0x40,
};

/// The contexts in which a particular macro declaration can be used.
Expand Down
20 changes: 20 additions & 0 deletions include/swift/AST/TypeCheckRequests.h
Original file line number Diff line number Diff line change
Expand Up @@ -3848,6 +3848,26 @@ class ExpandAccessorMacros
bool isCached() const { return true; }
};

/// Expand all conformance macros attached to the given declaration.
///
/// Produces the set of macro expansion buffer IDs.
class ExpandConformanceMacros
: public SimpleRequest<ExpandConformanceMacros,
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; }
};

/// 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 @@ -437,6 +437,9 @@ SWIFT_REQUEST(TypeChecker, ExpandMemberAttributeMacros,
SWIFT_REQUEST(TypeChecker, ExpandAccessorMacros,
ArrayRef<unsigned>(AbstractStorageDecl *),
Cached, NoLocationInfo)
SWIFT_REQUEST(TypeChecker, ExpandConformanceMacros,
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/SourceManager.h
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,9 @@ class GeneratedSourceInfo {
/// The expansion of an attached peer macro.
PeerMacroExpansion,

/// The expansion of an attached conformance macro.
ConformanceMacroExpansion,

/// 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 @@ -49,6 +49,7 @@ NODE(ClangType)
CONTEXT_NODE(Class)
NODE(ClassMetadataBaseOffset)
NODE(ConcreteProtocolConformance)
NODE(ConformanceAttachedMacroExpansion)
CONTEXT_NODE(Constructor)
NODE(CoroutineContinuationPrototype)
CONTEXT_NODE(Deallocator)
Expand Down
11 changes: 10 additions & 1 deletion lib/AST/ASTMangler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3771,7 +3771,8 @@ void ASTMangler::appendMacroExpansionContext(
case GeneratedSourceInfo::AccessorMacroExpansion:
case GeneratedSourceInfo::MemberAttributeMacroExpansion:
case GeneratedSourceInfo::MemberMacroExpansion:
case GeneratedSourceInfo::PeerMacroExpansion: {
case GeneratedSourceInfo::PeerMacroExpansion:
case GeneratedSourceInfo::ConformanceMacroExpansion: {
auto decl = ASTNode::getFromOpaqueValue(generatedSourceInfo->astNode)
.get<Decl *>();
auto attr = generatedSourceInfo->attachedMacroCustomAttr;
Expand All @@ -3793,6 +3794,10 @@ void ASTMangler::appendMacroExpansionContext(
role = MacroRole::Peer;
break;

case GeneratedSourceInfo::ConformanceMacroExpansion:
role = MacroRole::Conformance;
break;

default:
llvm_unreachable("Unhandled macro role");
}
Expand Down Expand Up @@ -3851,6 +3856,10 @@ void ASTMangler::appendMacroExpansionOperator(
case MacroRole::Peer:
appendOperator("fMp", Index(discriminator));
break;

case MacroRole::Conformance:
appendOperator("fMc", Index(discriminator));
break;
}
}

Expand Down
1 change: 1 addition & 0 deletions lib/AST/ASTScopeCreation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -259,6 +259,7 @@ ASTSourceFileScope::ASTSourceFileScope(SourceFile *SF,
case MacroRole::Accessor:
case MacroRole::MemberAttribute:
case MacroRole::Peer:
case MacroRole::Conformance:
parentLoc = expansion.getStartLoc();
break;
case MacroRole::Member: {
Expand Down
26 changes: 23 additions & 3 deletions lib/AST/Decl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -383,13 +383,14 @@ DeclAttributes Decl::getSemanticAttrs() const {
void Decl::visitAuxiliaryDecls(AuxiliaryDeclCallback callback) const {
auto &ctx = getASTContext();
auto *mutableThis = const_cast<Decl *>(this);
SourceManager &sourceMgr = ctx.SourceMgr;
auto *moduleDecl = getModuleContext();

auto peerBuffers =
evaluateOrDefault(ctx.evaluator,
ExpandPeerMacroRequest{mutableThis},
{});

SourceManager &sourceMgr = ctx.SourceMgr;
auto *moduleDecl = getModuleContext();
for (auto bufferID : peerBuffers) {
auto startLoc = sourceMgr.getLocForBufferStart(bufferID);
auto *sourceFile = moduleDecl->getSourceFileContainingLocation(startLoc);
Expand All @@ -398,6 +399,20 @@ void Decl::visitAuxiliaryDecls(AuxiliaryDeclCallback callback) const {
}
}

if (auto *nominal = dyn_cast<NominalTypeDecl>(mutableThis)) {
auto conformanceBuffers =
evaluateOrDefault(ctx.evaluator,
ExpandConformanceMacros{nominal},
{});
for (auto bufferID : conformanceBuffers) {
auto startLoc = sourceMgr.getLocForBufferStart(bufferID);
auto *sourceFile = moduleDecl->getSourceFileContainingLocation(startLoc);
for (auto *extension : sourceFile->getTopLevelDecls()) {
callback(extension);
}
}
}

// FIXME: fold VarDecl::visitAuxiliaryDecls into this.
}

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

case MacroRole::Peer:
return "peer";

case MacroRole::Conformance:
return "conformance";
}
}

Expand Down Expand Up @@ -9856,7 +9874,8 @@ static MacroRoles attachedMacroRoles = (MacroRoles() |
MacroRole::Accessor |
MacroRole::MemberAttribute |
MacroRole::Member |
MacroRole::Peer);
MacroRole::Peer |
MacroRole::Conformance);

bool swift::isFreestandingMacro(MacroRoles contexts) {
return bool(contexts & freestandingMacroRoles);
Expand Down Expand Up @@ -10035,6 +10054,7 @@ MacroDiscriminatorContext MacroDiscriminatorContext::getParentOf(
case GeneratedSourceInfo::MemberAttributeMacroExpansion:
case GeneratedSourceInfo::MemberMacroExpansion:
case GeneratedSourceInfo::PeerMacroExpansion:
case GeneratedSourceInfo::ConformanceMacroExpansion:
case GeneratedSourceInfo::PrettyPrinted:
case GeneratedSourceInfo::ReplacedFunctionBody:
return origDC;
Expand Down
3 changes: 2 additions & 1 deletion lib/AST/DiagnosticEngine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1313,7 +1313,8 @@ std::vector<Diagnostic> DiagnosticEngine::getGeneratedSourceBufferNotes(
case GeneratedSourceInfo::AccessorMacroExpansion:
case GeneratedSourceInfo::MemberAttributeMacroExpansion:
case GeneratedSourceInfo::MemberMacroExpansion:
case GeneratedSourceInfo::PeerMacroExpansion: {
case GeneratedSourceInfo::PeerMacroExpansion:
case GeneratedSourceInfo::ConformanceMacroExpansion: {
SourceRange origRange = expansionNode.getSourceRange();
DeclName macroName;
if (auto customAttr = generatedInfo->attachedMacroCustomAttr) {
Expand Down
3 changes: 3 additions & 0 deletions lib/AST/Module.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -931,6 +931,9 @@ Optional<MacroRole> SourceFile::getFulfilledMacroRole() const {
case GeneratedSourceInfo::PeerMacroExpansion:
return MacroRole::Peer;

case GeneratedSourceInfo::ConformanceMacroExpansion:
return MacroRole::Conformance;

case GeneratedSourceInfo::ReplacedFunctionBody:
case GeneratedSourceInfo::PrettyPrinted:
return None;
Expand Down
36 changes: 36 additions & 0 deletions lib/ASTGen/Sources/ASTGen/Macros.swift
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ enum MacroRole: UInt8 {
case MemberAttribute = 0x08
case Member = 0x10
case Peer = 0x20
case Conformance = 0x40
}

extension String {
Expand Down Expand Up @@ -514,6 +515,8 @@ func expandAttachedMacro(
expandedSource = expandedSources.joined(separator: " ")
case .Peer:
expandedSource = expandedSources.joined(separator: "\n\n")
case .Conformance:
expandedSource = expandedSources.joined(separator: "\n\n")
case .Expression,
.FreestandingDeclaration:
fatalError("unreachable")
Expand Down Expand Up @@ -739,6 +742,39 @@ func expandAttachedMacroInProcess(
$0.trimmedDescription
}

case (let attachedMacro as ConformanceMacro.Type, .Conformance):
guard let declGroup = declarationNode.asProtocol(DeclGroupSyntax.self),
let identified = declarationNode.asProtocol(IdentifiedDeclSyntax.self) else {
return nil
}

// Local function to expand a conformance macro once we've opened up
// the existential.
func expandConformanceMacro<Node: DeclGroupSyntax>(
_ node: Node
) throws -> [(TypeSyntax, GenericWhereClauseSyntax?)] {
return try attachedMacro.expansion(
of: sourceManager.detach(
customAttrNode,
foldingWith: OperatorTable.standardOperators
),
providingConformancesOf: sourceManager.detach(node),
in: context
)
}

let conformances = try _openExistential(
declGroup, do: expandConformanceMacro
)

// Form a buffer of extension declarations to return to the caller.
expandedSources = conformances.map { typeSyntax, whereClause in
let typeName = identified.identifier.trimmedDescription
let protocolName = typeSyntax.trimmedDescription
let whereClause = whereClause?.trimmedDescription ?? ""
return "extension \(typeName) : \(protocolName) \(whereClause) {}"
}

default:
print("\(macroPtr) does not conform to any known attached macro protocol")
return nil
Expand Down
1 change: 1 addition & 0 deletions lib/Basic/SourceLoc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -379,6 +379,7 @@ void SourceManager::setGeneratedSourceInfo(
case GeneratedSourceInfo::MemberAttributeMacroExpansion:
case GeneratedSourceInfo::MemberMacroExpansion:
case GeneratedSourceInfo::PeerMacroExpansion:
case GeneratedSourceInfo::ConformanceMacroExpansion:
case GeneratedSourceInfo::PrettyPrinted:
break;

Expand Down
7 changes: 6 additions & 1 deletion lib/Demangling/Demangler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3934,7 +3934,8 @@ static bool isMacroExpansionNodeKind(Node::Kind kind) {
kind == Node::Kind::MemberAttributeAttachedMacroExpansion ||
kind == Node::Kind::FreestandingMacroExpansion ||
kind == Node::Kind::MemberAttachedMacroExpansion ||
kind == Node::Kind::PeerAttachedMacroExpansion;
kind == Node::Kind::PeerAttachedMacroExpansion ||
kind == Node::Kind::ConformanceAttachedMacroExpansion;
}

NodePointer Demangler::demangleMacroExpansion() {
Expand All @@ -3960,6 +3961,10 @@ NodePointer Demangler::demangleMacroExpansion() {
kind = Node::Kind::PeerAttachedMacroExpansion;
break;

case 'c':
kind = Node::Kind::ConformanceAttachedMacroExpansion;
break;

case 'u':
kind = Node::Kind::MacroExpansionUniqueName;
break;
Expand Down
5 changes: 5 additions & 0 deletions lib/Demangling/NodePrinter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -356,6 +356,7 @@ class NodePrinter {
case Node::Kind::ClangType:
case Node::Kind::ClassMetadataBaseOffset:
case Node::Kind::CFunctionPointer:
case Node::Kind::ConformanceAttachedMacroExpansion:
case Node::Kind::Constructor:
case Node::Kind::CoroutineContinuationPrototype:
case Node::Kind::CurryThunk:
Expand Down Expand Up @@ -1364,6 +1365,10 @@ NodePointer NodePrinter::print(NodePointer Node, unsigned depth,
return printEntity(Node, depth, asPrefixContext, TypePrinting::NoType,
/*hasName*/true, "peer macro expansion #",
(int)Node->getChild(2)->getIndex() + 1);
case Node::Kind::ConformanceAttachedMacroExpansion:
return printEntity(Node, depth, asPrefixContext, TypePrinting::NoType,
/*hasName*/true, "conformance macro expansion #",
(int)Node->getChild(2)->getIndex() + 1);
case Node::Kind::MacroExpansionUniqueName:
return printEntity(Node, depth, asPrefixContext, TypePrinting::NoType,
/*hasName*/true, "unique name #",
Expand Down
7 changes: 7 additions & 0 deletions lib/Demangling/OldRemangler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1098,6 +1098,13 @@ ManglingError Remangler::manglePeerAttachedMacroExpansion(
return mangleChildNodes(node, depth + 1);
}

ManglingError Remangler::mangleConformanceAttachedMacroExpansion(
Node *node, unsigned depth) {
Buffer << "fMc";
RETURN_IF_ERROR(mangleIndex(node, depth + 1));
return mangleChildNodes(node, depth + 1);
}

ManglingError Remangler::mangleMacroExpansionUniqueName(
Node *node, unsigned depth) {
Buffer << "fMu";
Expand Down
8 changes: 8 additions & 0 deletions lib/Demangling/Remangler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2924,6 +2924,14 @@ ManglingError Remangler::manglePeerAttachedMacroExpansion(
return mangleChildNode(node, 2, depth + 1);
}

ManglingError Remangler::mangleConformanceAttachedMacroExpansion(
Node *node, unsigned depth) {
RETURN_IF_ERROR(mangleChildNode(node, 0, depth + 1));
RETURN_IF_ERROR(mangleChildNode(node, 1, depth + 1));
Buffer << "fMc";
return mangleChildNode(node, 2, depth + 1);
}

ManglingError Remangler::mangleMacroExpansionUniqueName(
Node *node, unsigned depth) {
RETURN_IF_ERROR(mangleChildNode(node, 0, depth + 1));
Expand Down
1 change: 1 addition & 0 deletions lib/Parse/ParseDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2198,6 +2198,7 @@ static Optional<MacroRole> getMacroRole(
.Case("memberAttribute", MacroRole::MemberAttribute)
.Case("member", MacroRole::Member)
.Case("peer", MacroRole::Peer)
.Case("conformance", MacroRole::Conformance)
.Default(None);

if (!role) {
Expand Down
3 changes: 3 additions & 0 deletions lib/Parse/ParseRequests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,10 @@ SourceFileParsingResult ParseSourceFileRequest::evaluate(Evaluator &evaluator,
} else {
parser.parseTopLevelItems(items);
}
}

case GeneratedSourceInfo::ConformanceMacroExpansion: {
parser.parseTopLevelItems(items);
break;
}
}
Expand Down
5 changes: 4 additions & 1 deletion lib/Parse/ParseStmt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
#include "swift/AST/ASTWalker.h"
#include "swift/AST/Attr.h"
#include "swift/AST/Decl.h"
#include "swift/AST/FileUnit.h"
#include "swift/Basic/Defer.h"
#include "swift/Basic/Version.h"
#include "swift/Parse/IDEInspectionCallbacks.h"
Expand Down Expand Up @@ -299,7 +300,9 @@ ParserStatus Parser::parseBraceItems(SmallVectorImpl<ASTNode> &Entries,
BraceItemListKind ConditionalBlockKind,
bool &IsFollowingGuard) {
bool IsTopLevel = (Kind == BraceItemListKind::TopLevelCode) ||
(Kind == BraceItemListKind::TopLevelLibrary);
(Kind == BraceItemListKind::TopLevelLibrary ||
(Kind == BraceItemListKind::MacroExpansion &&
isa<FileUnit>(CurDeclContext)));
bool isActiveConditionalBlock =
ConditionalBlockKind == BraceItemListKind::ActiveConditionalBlock;
bool isConditionalBlock = isActiveConditionalBlock ||
Expand Down
3 changes: 2 additions & 1 deletion lib/Refactoring/Refactoring.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8625,7 +8625,8 @@ static StringRef adjustMacroExpansionWhitespace(
return scratch;

case GeneratedSourceInfo::PeerMacroExpansion:
// For peers, add a newline to create some separation.
case GeneratedSourceInfo::ConformanceMacroExpansion:
// For peers and conformances, add a newline to create some separation.
scratch += "\n";
LLVM_FALLTHROUGH;

Expand Down
Loading