Sema: Try to bind extensions without expanding macros

Macro expansion can call typeCheckExpr(), which performs qualified
lookups. So if we expand macros while binding extensions, these
qualified lookups can fail because they cannot find members of
extensions that have not been bound yet.

To fix this, try binding extensions without performing macro
expansion first. If any extensions remain at the end, we fall back
to the old behavior, and try to bind them again, this time
performing macro expansion.

Fixes rdar://149798059.
This commit is contained in:
Slava Pestov
2025-06-11 16:59:38 -04:00
parent d4280d4f98
commit 63a5731da1

View File

@@ -191,12 +191,14 @@ ModuleDecl *TypeChecker::getStdlibModule(const DeclContext *dc) {
} }
void swift::bindExtensions(ModuleDecl &mod) { void swift::bindExtensions(ModuleDecl &mod) {
bool excludeMacroExpansions = true;
// Utility function to try and resolve the extended type without diagnosing. // Utility function to try and resolve the extended type without diagnosing.
// If we succeed, we go ahead and bind the extension. Otherwise, return false. // If we succeed, we go ahead and bind the extension. Otherwise, return false.
auto tryBindExtension = [&](ExtensionDecl *ext) -> bool { auto tryBindExtension = [&](ExtensionDecl *ext) -> bool {
assert(!ext->canNeverBeBound() && assert(!ext->canNeverBeBound() &&
"Only extensions that can ever be bound get here."); "Only extensions that can ever be bound get here.");
if (auto nominal = ext->computeExtendedNominal()) { if (auto nominal = ext->computeExtendedNominal(excludeMacroExpansions)) {
nominal->addExtension(ext); nominal->addExtension(ext);
return true; return true;
} }
@@ -228,20 +230,28 @@ void swift::bindExtensions(ModuleDecl &mod) {
visitTopLevelDecl(D); visitTopLevelDecl(D);
} }
// Phase 2 - repeatedly go through the worklist and attempt to bind each auto tryBindExtensions = [&]() {
// extension there, removing it from the worklist if we succeed. // Phase 2 - repeatedly go through the worklist and attempt to bind each
bool changed; // extension there, removing it from the worklist if we succeed.
do { bool changed;
changed = false; do {
changed = false;
auto last = std::remove_if(worklist.begin(), worklist.end(), auto last = std::remove_if(worklist.begin(), worklist.end(),
tryBindExtension); tryBindExtension);
if (last != worklist.end()) { if (last != worklist.end()) {
worklist.erase(last, worklist.end()); worklist.erase(last, worklist.end());
changed = true; changed = true;
} }
} while(changed); } while(changed);
};
tryBindExtensions();
// If that fails, try again, but this time expand macros.
excludeMacroExpansions = false;
tryBindExtensions();
// Any remaining extensions are invalid. They will be diagnosed later by // Any remaining extensions are invalid. They will be diagnosed later by
// typeCheckDecl(). // typeCheckDecl().
} }