Skip to content

Commit f96240d

Browse files
committed
[Macros] Do not look into macro expansions while resolving macro arguments.
Macros introduced a significant wrinkle into Swift's name lookup mechanism. Specifically, when resolving names (and, really, anything else) within the arguments to a macro expansion, name lookup must not try to expand any macros, because doing so trivially creates a cyclic dependency amongst the macro expansions that will be detected by the request-evaluator. Our lookup requests don't always have enough information to answer the question "is this part of an argument to a macro?", so we do a much simpler, more efficient, and not-entirely-sound hack based on the request-evaluator. Specifically, if we are in the process of resolving a macro (which is determined by checking for the presence of a `ResolveMacroRequest` in the request-evaluator stack), then we adjust the options used for the name lookup request we are forming to exclude macro expansions. The evaluation of that request will then avoid expanding any macros, and not produce any results that involve entries in already-expanded macros. By adjusting the request itself, we still distinguish between requests that can and cannot look into macro expansions, so it doesn't break caching for those immediate requests. Over time, we should seek to replace this heuristic with a location-based check, where we use ASTScope to determine whether we are inside a macro argument. This existing check might still be useful because it's going to be faster than a location-based query, but the location-based query can be fully correct. This addresses a class of cyclic dependencies that we've been seeing with macros, and aligns the lookup behavior for module-level lookups with that specified in the macros proposals. It is not fully complete because lookup until nominal types does not yet support excluding results from macro expansions.
1 parent 6854bbe commit f96240d

File tree

4 files changed

+145
-8
lines changed

4 files changed

+145
-8
lines changed

include/swift/AST/Evaluator.h

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,18 @@ class Evaluator {
208208
/// is treated as a stack and is used to detect cycles.
209209
llvm::SetVector<ActiveRequest> activeRequests;
210210

211+
/// How many `ResolveMacroRequest` requests are active.
212+
///
213+
/// This allows us to quickly determine whether there is any
214+
/// `ResolveMacroRequest` active in the active request stack.
215+
/// It saves us from a linear scan through `activeRequests` when
216+
/// we need to determine this information.
217+
///
218+
/// Why on earth would we need to determine this information?
219+
/// Please see the extended comment that goes with the constructor
220+
/// of `UnqualifiedLookupRequest`.
221+
unsigned numActiveResolveMacroRequests = 0;
222+
211223
/// A cache that stores the results of requests.
212224
evaluator::RequestCache cache;
213225

@@ -324,6 +336,16 @@ class Evaluator {
324336
return activeRequests.count(ActiveRequest(request));
325337
}
326338

339+
/// Determine whether there is any active "resolve macro" request
340+
/// on the request stack.
341+
///
342+
/// Why on earth would we need to determine this information?
343+
/// Please see the extended comment that goes with the constructor
344+
/// of `UnqualifiedLookupRequest`.
345+
bool hasActiveResolveMacroRequest() const {
346+
return numActiveResolveMacroRequests > 0;
347+
}
348+
327349
private:
328350
/// Diagnose a cycle detected in the evaluation of the given
329351
/// request.
@@ -337,6 +359,10 @@ class Evaluator {
337359
/// request to the \c activeRequests stack.
338360
bool checkDependency(const ActiveRequest &request);
339361

362+
/// Note that we have finished this request, popping it from the
363+
/// \c activeRequests stack.
364+
void finishedRequest(const ActiveRequest &request);
365+
340366
/// Produce the result of the request without caching.
341367
template<typename Request>
342368
llvm::Expected<typename Request::OutputType>
@@ -366,8 +392,7 @@ class Evaluator {
366392

367393
// Make sure we remove this from the set of active requests once we're
368394
// done.
369-
assert(activeRequests.back() == activeReq);
370-
activeRequests.pop_back();
395+
finishedRequest(activeReq);
371396

372397
return std::move(result);
373398
}

include/swift/AST/NameLookupRequests.h

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -430,7 +430,7 @@ class UnqualifiedLookupRequest
430430
RequestFlags::Uncached |
431431
RequestFlags::DependencySink> {
432432
public:
433-
using SimpleRequest::SimpleRequest;
433+
UnqualifiedLookupRequest(UnqualifiedLookupDescriptor);
434434

435435
private:
436436
friend SimpleRequest;
@@ -456,7 +456,10 @@ class LookupInModuleRequest
456456
NLOptions),
457457
RequestFlags::Uncached | RequestFlags::DependencySink> {
458458
public:
459-
using SimpleRequest::SimpleRequest;
459+
LookupInModuleRequest(
460+
const DeclContext *, DeclName, NLKind,
461+
namelookup::ResolutionKind, const DeclContext *,
462+
NLOptions);
460463

461464
private:
462465
friend SimpleRequest;
@@ -504,7 +507,9 @@ class ModuleQualifiedLookupRequest
504507
RequestFlags::Uncached |
505508
RequestFlags::DependencySink> {
506509
public:
507-
using SimpleRequest::SimpleRequest;
510+
ModuleQualifiedLookupRequest(const DeclContext *,
511+
ModuleDecl *, DeclNameRef,
512+
NLOptions);
508513

509514
private:
510515
friend SimpleRequest;
@@ -528,7 +533,9 @@ class QualifiedLookupRequest
528533
DeclNameRef, NLOptions),
529534
RequestFlags::Uncached> {
530535
public:
531-
using SimpleRequest::SimpleRequest;
536+
QualifiedLookupRequest(const DeclContext *,
537+
SmallVector<NominalTypeDecl *, 4>,
538+
DeclNameRef, NLOptions);
532539

533540
private:
534541
friend SimpleRequest;
@@ -579,7 +586,7 @@ class DirectLookupRequest
579586
TinyPtrVector<ValueDecl *>(DirectLookupDescriptor),
580587
RequestFlags::Uncached|RequestFlags::DependencySink> {
581588
public:
582-
using SimpleRequest::SimpleRequest;
589+
DirectLookupRequest(DirectLookupDescriptor);
583590

584591
private:
585592
friend SimpleRequest;

lib/AST/Evaluator.cpp

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
#include "swift/AST/Evaluator.h"
1818
#include "swift/AST/DeclContext.h"
1919
#include "swift/AST/DiagnosticEngine.h"
20+
#include "swift/AST/TypeCheckRequests.h" // for ResolveMacroRequest
2021
#include "swift/Basic/LangOptions.h"
2122
#include "swift/Basic/Range.h"
2223
#include "swift/Basic/SourceManager.h"
@@ -61,14 +62,25 @@ Evaluator::Evaluator(DiagnosticEngine &diags, const LangOptions &opts)
6162

6263
bool Evaluator::checkDependency(const ActiveRequest &request) {
6364
// Record this as an active request.
64-
if (activeRequests.insert(request))
65+
if (activeRequests.insert(request)) {
66+
if (request.getAs<ResolveMacroRequest>())
67+
++numActiveResolveMacroRequests;
6568
return false;
69+
}
6670

6771
// Diagnose cycle.
6872
diagnoseCycle(request);
6973
return true;
7074
}
7175

76+
void Evaluator::finishedRequest(const ActiveRequest &request) {
77+
if (request.getAs<ResolveMacroRequest>())
78+
--numActiveResolveMacroRequests;
79+
80+
assert(activeRequests.back() == request);
81+
activeRequests.pop_back();
82+
}
83+
7284
void Evaluator::diagnoseCycle(const ActiveRequest &request) {
7385
if (debugDumpCycles) {
7486
const auto printIndent = [](llvm::raw_ostream &OS, unsigned indent) {

lib/AST/NameLookupRequests.cpp

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -508,6 +508,99 @@ swift::extractNearestSourceLoc(CustomRefCountingOperationDescriptor desc) {
508508
return SourceLoc();
509509
}
510510

511+
//----------------------------------------------------------------------------//
512+
// Macro-related adjustments to name lookup requests.
513+
//----------------------------------------------------------------------------//
514+
//
515+
// Macros introduced a significant wrinkle into Swift's name lookup mechanism.
516+
// Specifically, when resolving names (and, really, anything else) within the
517+
// arguments to a macro expansion, name lookup must not try to expand any
518+
// macros, because doing so trivially creates a cyclic dependency amongst the
519+
// macro expansions that will be detected by the request-evaluator.
520+
//
521+
// Our lookup requests don't always have enough information to answer the
522+
// question "is this part of an argument to a macro?", so we do a much simpler,
523+
// more efficient, and not-entirely-sound hack based on the request-evaluator.
524+
// Specifically, if we are in the process of resolving a macro (which is
525+
// determined by checking for the presence of a `ResolveMacroRequest` in the
526+
// request-evaluator stack), then we adjust the options used for the name
527+
// lookup request we are forming to exclude macro expansions. The evaluation
528+
// of that request will then avoid expanding any macros, and not produce any
529+
// results that involve entries in already-expanded macros. By adjusting the
530+
// request itself, we still distinguish between requests that can and cannot
531+
// look into macro expansions, so it doesn't break caching for those immediate
532+
// requests.
533+
//
534+
// Over time, we should seek to replace this heuristic with a location-based
535+
// check, where we use ASTScope to determine whether we are inside a macro
536+
// argument. This existing check might still be useful because it's going to
537+
// be faster than a location-based query, but the location-based query can be
538+
// fully correct.
539+
540+
/// Exclude macros in the unqualified lookup descriptor if we need to.
541+
static UnqualifiedLookupDescriptor excludeMacrosIfNeeded(
542+
UnqualifiedLookupDescriptor descriptor) {
543+
if (descriptor.Options.contains(
544+
UnqualifiedLookupFlags::ExcludeMacroExpansions))
545+
return descriptor;
546+
547+
auto &evaluator = descriptor.DC->getASTContext().evaluator;
548+
if (!evaluator.hasActiveResolveMacroRequest())
549+
return descriptor;
550+
551+
descriptor.Options |= UnqualifiedLookupFlags::ExcludeMacroExpansions;
552+
return descriptor;
553+
}
554+
555+
/// Exclude macros in the direct lookup descriptor if we need to.
556+
static DirectLookupDescriptor excludeMacrosIfNeeded(
557+
DirectLookupDescriptor descriptor) {
558+
// FIXME: Direct lookups don't currently have an "exclude macro expansions"
559+
// flag.
560+
return descriptor;
561+
}
562+
563+
/// Exclude macros in the name lookup options if we need to.
564+
static NLOptions
565+
excludeMacrosIfNeeded(const DeclContext *dc, NLOptions options) {
566+
if (options & NL_ExcludeMacroExpansions)
567+
return options;
568+
569+
auto &evaluator = dc->getASTContext().evaluator;
570+
if (!evaluator.hasActiveResolveMacroRequest())
571+
return options;
572+
573+
return options | NL_ExcludeMacroExpansions;
574+
}
575+
576+
UnqualifiedLookupRequest::UnqualifiedLookupRequest(
577+
UnqualifiedLookupDescriptor descriptor
578+
) : SimpleRequest(excludeMacrosIfNeeded(descriptor)) { }
579+
580+
LookupInModuleRequest::LookupInModuleRequest(
581+
const DeclContext *moduleOrFile, DeclName name, NLKind lookupKind,
582+
namelookup::ResolutionKind resolutionKind,
583+
const DeclContext *moduleScopeContext,
584+
NLOptions options
585+
) : SimpleRequest(moduleOrFile, name, lookupKind, resolutionKind,
586+
moduleScopeContext,
587+
excludeMacrosIfNeeded(moduleOrFile, options)) { }
588+
589+
ModuleQualifiedLookupRequest::ModuleQualifiedLookupRequest(
590+
const DeclContext *dc, ModuleDecl *module, DeclNameRef name,
591+
NLOptions options
592+
) : SimpleRequest(dc, module, name, excludeMacrosIfNeeded(dc, options)) { }
593+
594+
QualifiedLookupRequest::QualifiedLookupRequest(
595+
const DeclContext *dc,
596+
SmallVector<NominalTypeDecl *, 4> decls,
597+
DeclNameRef name, NLOptions options
598+
) : SimpleRequest(dc, std::move(decls), name,
599+
excludeMacrosIfNeeded(dc, options)) { }
600+
601+
DirectLookupRequest::DirectLookupRequest(DirectLookupDescriptor descriptor)
602+
: SimpleRequest(excludeMacrosIfNeeded(descriptor)) { }
603+
511604
// Implement the clang importer type zone.
512605
#define SWIFT_TYPEID_ZONE ClangImporter
513606
#define SWIFT_TYPEID_HEADER "swift/ClangImporter/ClangImporterTypeIDZone.def"

0 commit comments

Comments
 (0)