Skip to content

Function body macros #70034

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 2 commits into from
Nov 28, 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 docs/ABI/Mangling.rst
Original file line number Diff line number Diff line change
Expand Up @@ -407,6 +407,9 @@ Entities
macro-expansion-operator ::= decl-name identifier 'fMm' // attached member macro
macro-expansion-operator ::= decl-name identifier 'fMp' // attached peer macro
macro-expansion-operator ::= decl-name identifier 'fMc' // attached conformance macro
macro-expansion-operator ::= decl-name identifier 'fMe' // attached extension macro
macro-expansion-operator ::= decl-name identifier 'fMq' // attached preamble macro
macro-expansion-operator ::= decl-name identifier 'fMb' // attached body macro
macro-expansion-operator ::= decl-name identifier 'fMu' // uniquely-named entity

file-discriminator ::= identifier 'Ll' // anonymous file-discriminated declaration
Expand Down
31 changes: 30 additions & 1 deletion include/swift/AST/Decl.h
Original file line number Diff line number Diff line change
Expand Up @@ -464,13 +464,16 @@ class alignas(1 << DeclAlignInBits) Decl : public ASTAllocated<Decl> {
SWIFT_INLINE_BITFIELD(SubscriptDecl, VarDecl, 2,
StaticSpelling : 2
);
SWIFT_INLINE_BITFIELD(AbstractFunctionDecl, ValueDecl, 3+2+2+8+1+1+1+1+1+1+1,
SWIFT_INLINE_BITFIELD(AbstractFunctionDecl, ValueDecl, 3+2+2+2+8+1+1+1+1+1+1+1,
/// \see AbstractFunctionDecl::BodyKind
BodyKind : 3,

/// \see AbstractFunctionDecl::BodySkippedStatus
BodySkippedStatus : 2,

/// \see AbstractFunctionDecl::BodyExpandedStatus
BodyExpandedStatus : 2,

/// \see AbstractFunctionDecl::SILSynthesizeKind
SILSynthesizeKind : 2,

Expand Down Expand Up @@ -7003,6 +7006,19 @@ class AbstractFunctionDecl : public GenericContext, public ValueDecl {
// This enum needs to fit in a 2-bit bitfield.
};

enum class BodyExpandedStatus {
/// We haven't tried to expand any body macros.
NotExpanded,

/// We tried to expand body macros, and there weren't any.
NoMacros,

/// The body was expanded from a body macro.
Expanded,

// This enum needs to fit in a 2-bit bitfield.
};

BodyKind getBodyKind() const {
return BodyKind(Bits.AbstractFunctionDecl.BodyKind);
}
Expand Down Expand Up @@ -7089,6 +7105,7 @@ class AbstractFunctionDecl : public GenericContext, public ValueDecl {
ValueDecl(Kind, Parent, Name, NameLoc), BodyAndFP(), AsyncLoc(AsyncLoc),
ThrowsLoc(ThrowsLoc), ThrownType(ThrownTy) {
setBodyKind(BodyKind::None);
setBodyExpandedStatus(BodyExpandedStatus::NotExpanded);
Bits.AbstractFunctionDecl.HasImplicitSelfDecl = HasImplicitSelfDecl;
Bits.AbstractFunctionDecl.Overridden = false;
Bits.AbstractFunctionDecl.Async = Async;
Expand All @@ -7110,6 +7127,14 @@ class AbstractFunctionDecl : public GenericContext, public ValueDecl {
Bits.AbstractFunctionDecl.BodySkippedStatus = unsigned(status);
}

BodyExpandedStatus getBodyExpandedStatus() const {
return BodyExpandedStatus(Bits.AbstractFunctionDecl.BodyExpandedStatus);
}

void setBodyExpandedStatus(BodyExpandedStatus status) {
Bits.AbstractFunctionDecl.BodyExpandedStatus = unsigned(status);
}

void setSILSynthesizeKind(SILSynthesizeKind K) {
Bits.AbstractFunctionDecl.SILSynthesizeKind = unsigned(K);
}
Expand Down Expand Up @@ -7258,6 +7283,10 @@ class AbstractFunctionDecl : public GenericContext, public ValueDecl {
/// \sa hasBody()
BraceStmt *getBody(bool canSynthesize = true) const;

/// Retrieve the body after macro expansion, which might also have been
/// type-checked.
BraceStmt *getMacroExpandedBody() const;

/// Retrieve the type-checked body of the given function, or \c nullptr if
/// there's no body available.
BraceStmt *getTypecheckedBody() const;
Expand Down
38 changes: 38 additions & 0 deletions include/swift/AST/TypeCheckRequests.h
Original file line number Diff line number Diff line change
Expand Up @@ -4397,6 +4397,44 @@ class ExpandPeerMacroRequest
void noteCycleStep(DiagnosticEngine &diags) const;
};

class ExpandPreambleMacroRequest
: public SimpleRequest<ExpandPreambleMacroRequest,
ArrayRef<unsigned>(AbstractFunctionDecl *),
RequestFlags::Cached> {
public:
using SimpleRequest::SimpleRequest;

private:
friend SimpleRequest;

ArrayRef<unsigned> evaluate(
Evaluator &evaluator, AbstractFunctionDecl *fn) const;

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

class ExpandBodyMacroRequest
: public SimpleRequest<ExpandBodyMacroRequest,
llvm::Optional<unsigned>(AbstractFunctionDecl *),
RequestFlags::Cached> {
public:
using SimpleRequest::SimpleRequest;

private:
friend SimpleRequest;

llvm::Optional<unsigned> evaluate(
Evaluator &evaluator, AbstractFunctionDecl *fn) const;

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

/// Resolve an external macro given its module and type name.
class ExternalMacroDefinitionRequest
: public SimpleRequest<ExternalMacroDefinitionRequest,
Expand Down
6 changes: 6 additions & 0 deletions include/swift/AST/TypeCheckerTypeIDZone.def
Original file line number Diff line number Diff line change
Expand Up @@ -493,6 +493,12 @@ SWIFT_REQUEST(TypeChecker, ExpandSynthesizedMemberMacroRequest,
SWIFT_REQUEST(TypeChecker, ExpandPeerMacroRequest,
ArrayRef<unsigned>(Decl *),
Cached, NoLocationInfo)
SWIFT_REQUEST(TypeChecker, ExpandPreambleMacroRequest,
ArrayRef<unsigned>(AbstractFunctionDecl *),
Cached, NoLocationInfo)
SWIFT_REQUEST(TypeChecker, ExpandBodyMacroRequest,
llvm::Optional<unsigned>(AbstractFunctionDecl *),
Cached, NoLocationInfo)
SWIFT_REQUEST(TypeChecker, LocalDiscriminatorsRequest,
unsigned(DeclContext *),
Cached, NoLocationInfo)
Expand Down
1 change: 1 addition & 0 deletions include/swift/Basic/Features.def
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,7 @@ EXPERIMENTAL_FEATURE(StaticAssert, false)
EXPERIMENTAL_FEATURE(NamedOpaqueTypes, false)
EXPERIMENTAL_FEATURE(FlowSensitiveConcurrencyCaptures, false)
EXPERIMENTAL_FEATURE(CodeItemMacros, false)
EXPERIMENTAL_FEATURE(BodyMacros, true)
EXPERIMENTAL_FEATURE(TupleConformances, false)

SUPPRESSIBLE_LANGUAGE_FEATURE(ExtensionMacroAttr, 0, "@attached(extension)", true)
Expand Down
6 changes: 6 additions & 0 deletions include/swift/Basic/MacroRoles.def
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,12 @@ EXPERIMENTAL_FREESTANDING_MACRO_ROLE(CodeItem, "codeItem", CodeItemMacros)
/// macro is attached to.
ATTACHED_MACRO_ROLE(Extension, "extension", "e")

/// An attached macro that expands to a preamble to a function.
EXPERIMENTAL_ATTACHED_MACRO_ROLE(Preamble, "preamble", "q", BodyMacros)

/// An attached macro that expands to a function body.
EXPERIMENTAL_ATTACHED_MACRO_ROLE(Body, "body", "b", BodyMacros)

#undef ATTACHED_MACRO_ROLE
#undef FREESTANDING_MACRO_ROLE
#undef EXPERIMENTAL_ATTACHED_MACRO_ROLE
Expand Down
2 changes: 2 additions & 0 deletions include/swift/Demangling/DemangleNodes.def
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ NODE(AccessorAttachedMacroExpansion)
NODE(AssociatedTypeWitnessTableAccessor)
NODE(BaseWitnessTableAccessor)
NODE(AutoClosureType)
NODE(BodyAttachedMacroExpansion)
NODE(BoundGenericClass)
NODE(BoundGenericEnum)
NODE(BoundGenericStructure)
Expand Down Expand Up @@ -189,6 +190,7 @@ NODE(PartialApplyForwarder)
NODE(PartialApplyObjCForwarder)
NODE(PeerAttachedMacroExpansion)
NODE(PostfixOperator)
NODE(PreambleAttachedMacroExpansion)
NODE(PrefixOperator)
NODE(PrivateDeclName)
NODE(PropertyDescriptor)
Expand Down
1 change: 1 addition & 0 deletions lib/AST/ASTContext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6133,6 +6133,7 @@ void AbstractFunctionDecl::keepOriginalBodySourceRange() {
auto result =
impl.OriginalBodySourceRanges.insert({this, getBodySourceRange()});
assert((!result.second ||
result.first->getSecond().isInvalid() ||
isSourceLocInOrignalBuffer(this, result.first->getSecond().Start)) &&
"This function must be called before setting new body range");
(void)result;
Expand Down
4 changes: 4 additions & 0 deletions lib/AST/ASTPrinter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2940,6 +2940,10 @@ static bool usesFeatureExtensionMacros(Decl *decl) {
return macro->getMacroRoles().contains(MacroRole::Extension);
}

static bool usesFeatureBodyMacros(Decl *decl) {
return false;
}

static bool usesFeatureExtensionMacroAttr(Decl *decl) {
return usesFeatureExtensionMacros(decl);
}
Expand Down
19 changes: 18 additions & 1 deletion lib/AST/ASTScopeCreation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -266,6 +266,7 @@ ASTSourceFileScope::ASTSourceFileScope(SourceFile *SF,
auto expansion = SF->getMacroExpansion();

// Determine the parent source location based on the macro role.
AbstractFunctionDecl *bodyForDecl = nullptr;
switch (*macroRole) {
case MacroRole::Expression:
case MacroRole::Declaration:
Expand All @@ -276,6 +277,16 @@ ASTSourceFileScope::ASTSourceFileScope(SourceFile *SF,
case MacroRole::Extension:
parentLoc = expansion.getStartLoc();
break;
case MacroRole::Preamble: {
// Preamble macro roles start at the beginning of the macro body.
auto func = cast<AbstractFunctionDecl>(expansion.get<Decl *>());
parentLoc = func->getMacroExpandedBody()->getStartLoc();
break;
}
case MacroRole::Body:
parentLoc = expansion.getEndLoc();
bodyForDecl = cast<AbstractFunctionDecl>(expansion.get<Decl *>());
break;
case MacroRole::Peer: {
ASTContext &ctx = SF->getASTContext();
SourceManager &sourceMgr = ctx.SourceMgr;
Expand All @@ -297,6 +308,12 @@ ASTSourceFileScope::ASTSourceFileScope(SourceFile *SF,
}

if (auto parentScope = findStartingScopeForLookup(enclosingSF, parentLoc)) {
if (bodyForDecl) {
auto bodyScope = new (bodyForDecl->getASTContext()) FunctionBodyScope(bodyForDecl);
bodyScope->parentAndWasExpanded.setPointer(const_cast<ASTScopeImpl *>(parentScope));
parentScope = bodyScope;
}

parentAndWasExpanded.setPointer(const_cast<ASTScopeImpl *>(parentScope));
}
}
Expand Down Expand Up @@ -986,7 +1003,7 @@ void AbstractFunctionDeclScope::expandAScopeThatDoesNotCreateANewInsertionPoint(
// Create scope for the body.
// We create body scopes when there is no body for source kit to complete
// erroneous code in bodies.
if (decl->getBodySourceRange().isValid()) {
if (decl->getOriginalBodySourceRange().isValid()) {
scopeCreator.constructExpandAndInsert<FunctionBodyScope>(leaf, decl);
}
}
Expand Down
7 changes: 7 additions & 0 deletions lib/AST/ASTScopeSourceRange.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,13 @@ SourceRange DifferentiableAttributeScope::getSourceRangeOfThisASTNode(

SourceRange FunctionBodyScope::getSourceRangeOfThisASTNode(
const bool omitAssertions) const {
// If this function body scope is synthesized for a body macro, use the
// real source range.
if (getChildren().size() == 1 &&
getChildren()[0]->getClassName() == "ASTSourceFileScope") {
return decl->getBodySourceRange();
}

return decl->getOriginalBodySourceRange();
}

Expand Down
19 changes: 18 additions & 1 deletion lib/AST/ASTVerifier.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -258,6 +258,13 @@ class Verifier : public ASTWalker {
abort();
}

ModuleDecl *getModuleContext() const {
if (auto sourceFile = M.dyn_cast<SourceFile *>())
return sourceFile->getParentModule();

return M.get<ModuleDecl *>();
}

public:
Verifier(ModuleDecl *M, DeclContext *DC)
: Verifier(PointerUnion<ModuleDecl *, SourceFile *>(M), DC) {}
Expand Down Expand Up @@ -3813,7 +3820,17 @@ class Verifier : public ASTWalker {
} else {
llvm_unreachable("impossible parent node");
}


if (AltEnclosing.isInvalid()) {
// A preamble macro introduces child nodes directly into the tree.
auto *sourceFile =
getModuleContext()->getSourceFileContainingLocation(Current.Start);
if (sourceFile &&
sourceFile->getFulfilledMacroRole() == MacroRole::Preamble) {
AltEnclosing = Current;
}
}

if (!Ctx.SourceMgr.rangeContains(Enclosing, Current) &&
!(AltEnclosing.isValid() &&
Ctx.SourceMgr.rangeContains(AltEnclosing, Current))) {
Expand Down
61 changes: 60 additions & 1 deletion lib/AST/Decl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1029,7 +1029,7 @@ Expr *AbstractFunctionDecl::getSingleExpressionBody() const {

void AbstractFunctionDecl::setSingleExpressionBody(Expr *NewBody) {
assert(hasSingleExpressionBody() && "Not a single-expression body");
auto body = getBody()->getLastElement();
auto body = getBody(/*canSynthesize=*/false)->getLastElement();
if (auto *stmt = body.dyn_cast<Stmt *>()) {
if (auto *returnStmt = dyn_cast<ReturnStmt>(stmt)) {
returnStmt->setResult(NewBody);
Expand Down Expand Up @@ -9118,6 +9118,63 @@ bool AbstractFunctionDecl::hasBody() const {
}


static BraceStmt *expandBodyMacro(AbstractFunctionDecl *fn) {
ASTContext &ctx = fn->getASTContext();

auto bufferID = evaluateOrDefault(
ctx.evaluator, ExpandBodyMacroRequest{fn}, llvm::None);
if (!bufferID) {
return nullptr;
}

CharSourceRange bufferRange = ctx.SourceMgr.getRangeForBuffer(*bufferID);
auto bufferStart = bufferRange.getStart();
auto module = fn->getParentModule();
auto macroSourceFile = module->getSourceFileContainingLocation(bufferStart);

return BraceStmt::create(
ctx, bufferRange.getStart(), macroSourceFile->getTopLevelItems(),
bufferRange.getEnd());
}

BraceStmt *AbstractFunctionDecl::getMacroExpandedBody() const {
auto mutableThis = const_cast<AbstractFunctionDecl *>(this);
switch (getBodyKind()) {
case BodyKind::None:
case BodyKind::Unparsed:
case BodyKind::Parsed:
switch (getBodyExpandedStatus()) {
case BodyExpandedStatus::NotExpanded:
if (auto expandedBody = expandBodyMacro(mutableThis)) {
// Save the original body's source range.
mutableThis->keepOriginalBodySourceRange();

// Cache the expanded macro body as the parsed body of the function.
mutableThis->setBodyExpandedStatus(BodyExpandedStatus::Expanded);
mutableThis->setBodyParsed(expandedBody);

return expandedBody;
}

mutableThis->setBodyExpandedStatus(BodyExpandedStatus::NoMacros);
break;

case BodyExpandedStatus::NoMacros:
case BodyExpandedStatus::Expanded:
break;
}

// Fall through to get the body.
LLVM_FALLTHROUGH;

case BodyKind::Synthesize:
case BodyKind::TypeChecked:
case BodyKind::SILSynthesize:
case BodyKind::Deserialized:
return getBody(/*canSynthesize=*/true);
}
}

BraceStmt *AbstractFunctionDecl::getBody(bool canSynthesize) const {
if ((getBodyKind() == BodyKind::Synthesize ||
getBodyKind() == BodyKind::Unparsed) &&
Expand Down Expand Up @@ -11325,6 +11382,8 @@ void MacroDecl::getIntroducedNames(MacroRole role, ValueDecl *attachedTo,
case MacroRole::Accessor:
case MacroRole::Conformance:
case MacroRole::MemberAttribute:
case MacroRole::Preamble:
case MacroRole::Body:
break;
}
}
Expand Down
8 changes: 4 additions & 4 deletions lib/AST/Module.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -851,7 +851,6 @@ SourceFile *ModuleDecl::getSourceFileContainingLocation(SourceLoc loc) {
if (loc.isInvalid())
return nullptr;


// Check whether this location is in a "replaced" range, in which case
// we want to use the original source file.
auto &sourceMgr = getASTContext().SourceMgr;
Expand Down Expand Up @@ -1210,11 +1209,12 @@ llvm::Optional<MacroRole> SourceFile::getFulfilledMacroRole() const {
}

SourceFile *SourceFile::getEnclosingSourceFile() const {
auto macroExpansion = getMacroExpansion();
if (!macroExpansion)
if (Kind != SourceFileKind::MacroExpansion)
return nullptr;

auto sourceLoc = macroExpansion.getStartLoc();
auto genInfo =
*getASTContext().SourceMgr.getGeneratedSourceInfo(*getBufferID());
auto sourceLoc = genInfo.originalSourceRange.getStart();
return getParentModule()->getSourceFileContainingLocation(sourceLoc);
}

Expand Down
Loading