-
Notifications
You must be signed in to change notification settings - Fork 10.5k
[Macros] Use source locations to determine whether to suppress macro expansions. #66532
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
[Macros] Use source locations to determine whether to suppress macro expansions. #66532
Conversation
// macro expansion buffers for freestanding macros are children of | ||
// MacroExpansionDeclScope, and child scopes of freestanding macros | ||
// are otherwise inside the macro argument. | ||
if (scope->getClassName() == "ASTSourceFileScope") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there a better way to check the ASTScope
kind?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, and it looks like you're not the first to lament that this is the only way to check:
// FIXME: Proper isa/dyn_cast support would be better than a string
// comparison here.
while (parent->getClassName() == "GenericParamScope")
parent = parent->getLookupParent().get();
@swift-ci please smoke test |
Re: Using freestanding & arbitrary peer names now being banned at the global level. Are you sure that’s what you want to do? I feel like plenty of I’m sure I don’t understand the code at this level though, so please disregard if I don’t make much sense. |
This change does not affect the |
@swift-ci please smoke test |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks fantastic. The restriction on arbitrary names at global scope is going to be a bit of a shock at first, but I think we can find suitable alternative approaches. I think I'm convincing myself that it's better overall for readability to limit the introduction of globals.
@@ -3891,7 +3891,7 @@ class NominalTypeDecl : public GenericTypeDecl, public IterableDeclContext { | |||
/// protocols to which the nominal type conforms. Furthermore, the resulting | |||
/// set of declarations has not been filtered for visibility, nor have | |||
/// overridden declarations been removed. | |||
TinyPtrVector<ValueDecl *> lookupDirect(DeclName name, | |||
TinyPtrVector<ValueDecl *> lookupDirect(DeclName name, SourceLoc loc = SourceLoc(), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we plan to take away that default argument at some point, once we've managed to update all of the callers?
// macro expansion buffers for freestanding macros are children of | ||
// MacroExpansionDeclScope, and child scopes of freestanding macros | ||
// are otherwise inside the macro argument. | ||
if (scope->getClassName() == "ASTSourceFileScope") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, and it looks like you're not the first to lament that this is the only way to check:
// FIXME: Proper isa/dyn_cast support would be better than a string
// comparison here.
while (parent->getClassName() == "GenericParamScope")
parent = parent->getLookupParent().get();
TypeChecker::performTypoCorrection(DC, UDRE->getRefKind(), Type(), | ||
lookupOptions, corrections); | ||
|
||
// FIXME: Don't perform typo correction inside macro arguments, because it |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Argh, this is a tricky one. Maybe typo correction should avoid ever synthesizing declarations or expanding macros.
Ah! This reassures me/addresses my fear. I thought this was a flat ban on any new names (introduced by macros) at global scope. Thanks for clarifying! |
@swift-ci please smoke test |
…omAttributeScope so that it can represent the scope for any custom attribute and its arguments. This commit is NFC, but CustomAttributeScope is now applicable to attached macros.
…s inside a macro argument.
…APIs. This source location will be used to determine whether to add a name lookup option to exclude macro expansions when the name lookup request is constructed. Currently, the source location argument is unused.
expansions. Evaluator::hasActiveResolveMacroRequest is now unused.
checker while type checking a freestanding macro argument in the same scope.
…arbitrary names at global scope. Freestanding and peer macros applied at top-level scope cannot introduce arbitrary names. Introducing arbitrary names means that any lookup into this scope must expand the macro. This is a problem, because resolving the macro can invoke type checking other declarations, e.g. anything that the macro arguments depend on. If _anything_ the macro depends on performs name unqualified name lookup, e.g. type resolution, we'll get circularity errors. It's better to prevent this by banning these macros at global scope if any of the macro candidates introduce arbitrary names.
… of a macro argument.
…ass. IDE inspection can delay parsing of particular declarations, so expanding ASTScopes during the first pass will miss those declarations. Clear any expanded scopes to force re-expansion during the second pass.
…ookup namespace. This moves the `isInMacroArgument` predicate and `lookupMacros` into `namelookup`. ASTScope still encapsulates the scope tree and contains the operation to lookup the enclosing macro scope, which then invokes a callback to determine whether a potential macro scope is indeed a macro, because answering this question requires name lookup.
2ae6396
to
2cc1204
Compare
@swift-ci please smoke test |
@swift-ci please smoke test Windows |
1 similar comment
@swift-ci please smoke test Windows |
If I understand it correctly, does this mean something like the |
That's correct. However, that example is not producing truly arbitrary names, and it could be expressed if a freestanding macro could use |
Ok this seems reasonable to me. At least, |
This PR is a follow-up to #64968 that replaces the request evaluator stack approach to macro expansion suppression in macro arguments with a source-location-based approach using ASTScope.
Background
SE-0389 states that code in a macro argument does not have visibility into macro-generated names for macros at the same scope or enclosing scopes, because the semantics of the program would depend on the ordering of those macro expansions. Arbitrary macro names also make name lookup extremely prone to request cycles, because any lookup into that scope will need to expand macros that can produce arbitrary names. For these reasons, macro resolution cannot be dependent on macro expansion, so macro-generated names are excluded from name lookup when lookup happens within a macro argument.
The old request-evaluator-stack approach
Previously, answering the question of "are we performing name lookup from within a macro argument?" used a heuristic that checked whether there is an active
ResolveMacroRequest
in the current request-evaluator stack. This heuristic was not totally correct. For example, if type checking a macro argument was the first bit of type checker work that invoked conformance checking for a type, name lookups performed by the conformance checker would exclude macro-generated names even though the conforming type is not inside of a macro argument. If the conformance checker happens to be invoked before type checking the macro argument, macro-generated names would be included. Because macro-generated names in a conforming type can fulfill protocol requirements, this makes the type checking semantics dependent on the order of requests, which is Not Good.The new
ASTScope
approachInstead, this change uses source locations to answer the question of "are we performing name lookup from within a macro argument?". Source locations are now plumbed through all of the name lookup APIs. When constructing the various name lookup requests, the source location is used to determine whether lookup is being done from within a macro argument. We use ASTScopes to find the innermost scope enclosing that source location, and walk up the scope tree to look for either a freestanding macro expansion scope or an attached macro scope. If we find one of those scopes, that source location is contained within a macro argument, and the appropriate flag is added to the name lookup options to exclude macro-generated names from the name lookup results.
Arbitrary names at global scope
As part of this, I needed to ban freestanding and peer macro expansions at global scope that can generate arbitrary names. Introducing arbitrary names means that any lookup into this scope must expand the macro. This is a problem, because resolving the macro can invoke type checking other declarations. If anything the macro depends on performs unqualified name lookup, e.g. type resolution, we'll get circularity errors. It's better to prevent this by banning these macros at global scope if any of the macro candidates introduce arbitrary names.
Resolves rdar://109214279.