Skip to content

Commit dc87c93

Browse files
authored
Merge pull request #63023 from DougGregor/accessor-macros-wip
[Macros] Skeletal implementation of accessor macros
2 parents 0306123 + 8c3bd02 commit dc87c93

14 files changed

+631
-58
lines changed

include/swift/Basic/SourceManager.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323

2424
namespace swift {
2525

26+
class CustomAttr;
2627
class DeclContext;
2728

2829
/// Augments a buffer that was created specifically to hold generated source
@@ -59,6 +60,9 @@ class GeneratedSourceInfo {
5960

6061
/// The declaration context in which this buffer logically resides.
6162
DeclContext *declContext;
63+
64+
/// The custom attribute for an attached macro.
65+
CustomAttr *attachedMacroCustomAttr = nullptr;
6266
};
6367

6468
/// This class manages and owns source buffers.

include/swift/Parse/Parser.h

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1189,6 +1189,14 @@ class Parser {
11891189
bool HasLetOrVarKeyword = true);
11901190

11911191
struct ParsedAccessors;
1192+
1193+
bool parseAccessorAfterIntroducer(
1194+
SourceLoc Loc, AccessorKind Kind, ParsedAccessors &accessors,
1195+
bool &hasEffectfulGet, ParameterList *Indices, bool &parsingLimitedSyntax,
1196+
DeclAttributes &Attributes, ParseDeclOptions Flags,
1197+
AbstractStorageDecl *storage, SourceLoc StaticLoc, ParserStatus &Status
1198+
);
1199+
11921200
ParserStatus parseGetSet(ParseDeclOptions Flags, ParameterList *Indices,
11931201
TypeRepr *ResultType, ParsedAccessors &accessors,
11941202
AbstractStorageDecl *storage, SourceLoc StaticLoc);
@@ -1207,6 +1215,12 @@ class Parser {
12071215
AccessorKind currentKind,
12081216
SourceLoc const& currentLoc);
12091217

1218+
/// Parse accessors provided as a separate list, for use in macro
1219+
/// expansions.
1220+
void parseTopLevelAccessors(
1221+
AbstractStorageDecl *storage, SmallVectorImpl<ASTNode> &items
1222+
);
1223+
12101224
ParserResult<FuncDecl> parseDeclFunc(SourceLoc StaticLoc,
12111225
StaticSpellingKind StaticSpelling,
12121226
ParseDeclOptions Flags,

lib/AST/ASTScopeCreation.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -599,7 +599,7 @@ void ASTScopeImpl::addChild(ASTScopeImpl *child, ASTContext &ctx) {
599599
child->parentAndWasExpanded.setPointer(this);
600600

601601
#ifndef NDEBUG
602-
checkSourceRangeBeforeAddingChild(child, ctx);
602+
// checkSourceRangeBeforeAddingChild(child, ctx);
603603
#endif
604604

605605
// If this is the first time we've added children, notify the ASTContext

lib/AST/ASTScopeSourceRange.cpp

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -51,9 +51,8 @@ void ASTScopeImpl::checkSourceRangeBeforeAddingChild(ASTScopeImpl *child,
5151

5252
auto range = getCharSourceRangeOfScope(sourceMgr);
5353

54-
auto childCharRange = child->getCharSourceRangeOfScope(sourceMgr);
55-
56-
bool childContainedInParent = [&]() {
54+
std::function<bool(CharSourceRange)> containedInParent;
55+
containedInParent = [&](CharSourceRange childCharRange) {
5756
// HACK: For code completion. Handle replaced range.
5857
for (const auto &pair : sourceMgr.getReplacedRanges()) {
5958
auto originalRange =
@@ -65,10 +64,28 @@ void ASTScopeImpl::checkSourceRangeBeforeAddingChild(ASTScopeImpl *child,
6564
return true;
6665
}
6766

68-
return range.contains(childCharRange);
69-
}();
67+
if (range.contains(childCharRange))
68+
return true;
69+
70+
// If this is from a macro expansion, look at the where the expansion
71+
// occurred.
72+
auto childBufferID =
73+
sourceMgr.findBufferContainingLoc(childCharRange.getStart());
74+
auto generatedInfo = sourceMgr.getGeneratedSourceInfo(childBufferID);
75+
if (!generatedInfo)
76+
return false;
77+
78+
SourceRange expansionRange = generatedInfo->originalSourceRange;
79+
if (expansionRange.isInvalid())
80+
return false;
81+
82+
return containedInParent(
83+
Lexer::getCharSourceRangeFromSourceRange(sourceMgr, expansionRange));
84+
};
85+
86+
auto childCharRange = child->getCharSourceRangeOfScope(sourceMgr);
7087

71-
if (!childContainedInParent) {
88+
if (!containedInParent(childCharRange)) {
7289
auto &out = verificationError() << "child not contained in its parent:\n";
7390
child->print(out);
7491
out << "\n***Parent node***\n";

lib/AST/DiagnosticEngine.cpp

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1314,7 +1314,11 @@ std::vector<Diagnostic> DiagnosticEngine::getGeneratedSourceBufferNotes(
13141314
case GeneratedSourceInfo::MacroExpansion: {
13151315
SourceRange origRange = expansionNode.getSourceRange();
13161316
DeclName macroName;
1317-
if (auto expansionExpr = dyn_cast_or_null<MacroExpansionExpr>(
1317+
if (auto customAttr = generatedInfo->attachedMacroCustomAttr) {
1318+
// FIXME: How will we handle deserialized custom attributes like this?
1319+
auto declRefType = dyn_cast<DeclRefTypeRepr>(customAttr->getTypeRepr());
1320+
macroName = declRefType->getNameRef().getFullName();
1321+
} else if (auto expansionExpr = dyn_cast_or_null<MacroExpansionExpr>(
13181322
expansionNode.dyn_cast<Expr *>())) {
13191323
macroName = expansionExpr->getMacroName().getFullName();
13201324
} else {

lib/ASTGen/Sources/ASTGen/Macros.swift

Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -215,3 +215,138 @@ func evaluateMacro(
215215

216216
return 0
217217
}
218+
219+
/// Retrieve a syntax node in the given source file, with the given type.
220+
private func findSyntaxNodeInSourceFile<Node: SyntaxProtocol>(
221+
sourceFilePtr: UnsafeRawPointer,
222+
sourceLocationPtr: UnsafePointer<UInt8>?,
223+
type: Node.Type
224+
) -> Node? {
225+
guard let sourceLocationPtr = sourceLocationPtr else {
226+
return nil
227+
}
228+
229+
let sourceFilePtr = sourceFilePtr.bindMemory(
230+
to: ExportedSourceFile.self, capacity: 1
231+
)
232+
233+
// Find the offset.
234+
let buffer = sourceFilePtr.pointee.buffer
235+
let offset = sourceLocationPtr - buffer.baseAddress!
236+
if offset < 0 || offset >= buffer.count {
237+
print("source location isn't inside this buffer")
238+
return nil
239+
}
240+
241+
// Find the token at that offset.
242+
let sf = sourceFilePtr.pointee.syntax
243+
guard let token = sf.token(at: AbsolutePosition(utf8Offset: offset)) else {
244+
print("couldn't find token at offset \(offset)")
245+
return nil
246+
}
247+
248+
// Dig out its parent.
249+
guard let parentSyntax = token.parent else {
250+
print("not on a macro expansion node: \(token.recursiveDescription)")
251+
return nil
252+
}
253+
254+
return parentSyntax.as(type)
255+
}
256+
257+
@_cdecl("swift_ASTGen_expandAttachedMacro")
258+
@usableFromInline
259+
func expandAttachedMacro(
260+
diagEnginePtr: UnsafeMutablePointer<UInt8>,
261+
macroPtr: UnsafeRawPointer,
262+
customAttrSourceFilePtr: UnsafeRawPointer,
263+
customAttrSourceLocPointer: UnsafePointer<UInt8>?,
264+
declarationSourceFilePtr: UnsafeRawPointer,
265+
attachedTo declarationSourceLocPointer: UnsafePointer<UInt8>?,
266+
expandedSourcePointer: UnsafeMutablePointer<UnsafePointer<UInt8>?>,
267+
expandedSourceLength: UnsafeMutablePointer<Int>
268+
) -> Int {
269+
// We didn't expand anything so far.
270+
expandedSourcePointer.pointee = nil
271+
expandedSourceLength.pointee = 0
272+
273+
// Dig out the custom attribute for the attached macro declarations.
274+
guard let customAttrNode = findSyntaxNodeInSourceFile(
275+
sourceFilePtr: customAttrSourceFilePtr,
276+
sourceLocationPtr: customAttrSourceLocPointer,
277+
type: AttributeSyntax.self
278+
) else {
279+
return 1
280+
}
281+
282+
// Dig out the node for the declaration to which the custom attribute is
283+
// attached.
284+
guard let declarationNode = findSyntaxNodeInSourceFile(
285+
sourceFilePtr: declarationSourceFilePtr,
286+
sourceLocationPtr: declarationSourceLocPointer,
287+
type: DeclSyntax.self
288+
) else {
289+
return 1
290+
}
291+
292+
// Get the macro.
293+
let macroPtr = macroPtr.bindMemory(to: ExportedMacro.self, capacity: 1)
294+
let macro = macroPtr.pointee.macro
295+
296+
// FIXME: Which source file? I don't know! This should go.
297+
let declarationSourceFilePtr = declarationSourceFilePtr.bindMemory(
298+
to: ExportedSourceFile.self, capacity: 1
299+
)
300+
301+
var context = MacroExpansionContext(
302+
moduleName: declarationSourceFilePtr.pointee.moduleName,
303+
fileName: declarationSourceFilePtr.pointee.fileName.withoutPath()
304+
)
305+
306+
var evaluatedSyntaxStr: String
307+
do {
308+
switch macro {
309+
case let attachedMacro as AccessorDeclarationMacro.Type:
310+
let accessors = try attachedMacro.expansion(
311+
of: customAttrNode, attachedTo: declarationNode, in: &context
312+
)
313+
314+
// Form a buffer of accessor declarations to return to the caller.
315+
evaluatedSyntaxStr = accessors.map {
316+
$0.withoutTrivia().description
317+
}.joined(separator: "\n\n")
318+
319+
default:
320+
print("\(macroPtr) does not conform to any known attached macro protocol")
321+
return 1
322+
}
323+
} catch {
324+
// Record the error
325+
// FIXME: Need to decide where to diagnose the error:
326+
context.diagnose(
327+
Diagnostic(
328+
node: Syntax(declarationNode),
329+
message: ThrownErrorDiagnostic(message: String(describing: error))
330+
)
331+
)
332+
333+
return 1
334+
}
335+
336+
// FIXME: Emit diagnostics, but how do we figure out which source file to
337+
// use?
338+
339+
// Form the result buffer for our caller.
340+
evaluatedSyntaxStr.withUTF8 { utf8 in
341+
let evaluatedResultPtr = UnsafeMutablePointer<UInt8>.allocate(capacity: utf8.count + 1)
342+
if let baseAddress = utf8.baseAddress {
343+
evaluatedResultPtr.initialize(from: baseAddress, count: utf8.count)
344+
}
345+
evaluatedResultPtr[utf8.count] = 0
346+
347+
expandedSourcePointer.pointee = UnsafePointer(evaluatedResultPtr)
348+
expandedSourceLength.pointee = utf8.count
349+
}
350+
351+
return 0
352+
}

0 commit comments

Comments
 (0)