mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
Sema now type-checks the alternate ABI-providing decls inside of @abi attributes. Making this work—particularly, making redeclaration checking work—required making name lookup aware of ABI decls. Name lookup now evaluates both API-providing and ABI-providing declarations. In most cases, it will filter ABI-only decls out unless a specific flag is passed, in which case it will filter API-only decls out instead. Calls that simply retrieve a list of declarations, like `IterableDeclContext::getMembers()` and friends, typically only return API-providing decls; you have to access the ABI-providing ones through those. As part of that work, I have also added some basic compiler interfaces for working with the API-providing and ABI-providing variants. `ABIRole` encodes whether a declaration provides only API, only ABI, or both, and `ABIRoleInfo` combines that with a pointer to the counterpart providing the other role (for a declaration that provides both, that’ll just be a pointer to `this`). Decl checking of behavior specific to @abi will come in a future commit. Note that this probably doesn’t properly exercise some of the new code (ASTScope::lookupEnclosingABIAttributeScope(), for instance); I expect that to happen only once we can rename types using an @abi attribute, since that will create distinguishable behavior differences when resolving TypeReprs in other @abi attributes.
347 lines
12 KiB
C++
347 lines
12 KiB
C++
//===--- ModuleNameLookup.cpp - Name lookup within a module ---------------===//
|
|
//
|
|
// This source file is part of the Swift.org open source project
|
|
//
|
|
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
|
|
// Licensed under Apache License v2.0 with Runtime Library Exception
|
|
//
|
|
// See https://swift.org/LICENSE.txt for license information
|
|
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "swift/AST/ModuleNameLookup.h"
|
|
#include "swift/AST/ASTContext.h"
|
|
#include "swift/AST/ClangModuleLoader.h"
|
|
#include "swift/AST/ImportCache.h"
|
|
#include "swift/AST/NameLookup.h"
|
|
#include "swift/AST/NameLookupRequests.h"
|
|
#include "swift/Basic/Assertions.h"
|
|
#include "llvm/Support/raw_ostream.h"
|
|
|
|
using namespace swift;
|
|
using namespace namelookup;
|
|
|
|
namespace {
|
|
|
|
/// Encapsulates the work done for a recursive qualified lookup into a module.
|
|
///
|
|
/// The \p LookupStrategy handles the non-recursive part of the lookup. It
|
|
/// must be a subclass of ModuleNameLookup. It should provide a
|
|
/// \c doLocalLookup() method; this method will be passed appropriate
|
|
/// \c NLOptions but does not necessarily need to honor them.
|
|
template <typename LookupStrategy>
|
|
class ModuleNameLookup {
|
|
ASTContext &ctx;
|
|
const ResolutionKind resolutionKind;
|
|
const bool respectAccessControl;
|
|
|
|
LookupStrategy *getDerived() {
|
|
static_assert(std::is_base_of<ModuleNameLookup<LookupStrategy>,
|
|
LookupStrategy>::value,
|
|
"ModuleNameLookup is a CRTP class");
|
|
return static_cast<LookupStrategy *>(this);
|
|
}
|
|
|
|
public:
|
|
ModuleNameLookup(ASTContext &ctx, ResolutionKind resolutionKind)
|
|
: ctx(ctx),
|
|
resolutionKind(resolutionKind),
|
|
respectAccessControl(!ctx.isAccessControlDisabled()) {}
|
|
|
|
/// Performs a qualified lookup into the given module and, if necessary, its
|
|
/// reexports.
|
|
///
|
|
/// The results are appended to \p decls.
|
|
void lookupInModule(SmallVectorImpl<ValueDecl *> &decls,
|
|
const DeclContext *moduleOrFile,
|
|
ImportPath::Access accessPath,
|
|
const DeclContext *moduleScopeContext,
|
|
NLOptions options);
|
|
};
|
|
|
|
// Exclude names introduced by macro expansions.
|
|
enum class LocalLookupFlags {
|
|
ExcludeMacroExpansions,
|
|
};
|
|
|
|
/// Encapsulates the work done for a recursive qualified lookup into a module
|
|
/// by full name.
|
|
class LookupByName : public ModuleNameLookup<LookupByName> {
|
|
using Super = ModuleNameLookup<LookupByName>;
|
|
friend Super;
|
|
|
|
const DeclName name;
|
|
const NLKind lookupKind;
|
|
|
|
public:
|
|
LookupByName(ASTContext &ctx, ResolutionKind resolutionKind,
|
|
DeclName name, NLKind lookupKind)
|
|
: Super(ctx, resolutionKind), name(name),
|
|
lookupKind(lookupKind) {}
|
|
|
|
private:
|
|
/// Returns whether it's okay to stop recursively searching imports, given
|
|
/// that we found something non-overloadable.
|
|
static bool canReturnEarly() {
|
|
return true;
|
|
}
|
|
|
|
void doLocalLookup(ModuleDecl *module, ImportPath::Access path,
|
|
OptionSet<ModuleLookupFlags> flags,
|
|
SmallVectorImpl<ValueDecl *> &localDecls) {
|
|
// If this import is specific to some named decl ("import Swift.Int")
|
|
// then filter out any lookups that don't match.
|
|
if (!path.matches(name))
|
|
return;
|
|
module->lookupValue(name, lookupKind, flags, localDecls);
|
|
}
|
|
};
|
|
|
|
/// Encapsulates the work done for a recursive qualified lookup into a module
|
|
/// to find all visible decls.
|
|
class LookupVisibleDecls : public ModuleNameLookup<LookupVisibleDecls> {
|
|
using Super = ModuleNameLookup<LookupVisibleDecls>;
|
|
friend Super;
|
|
|
|
const NLKind lookupKind;
|
|
|
|
public:
|
|
LookupVisibleDecls(ASTContext &ctx, ResolutionKind resolutionKind,
|
|
NLKind lookupKind)
|
|
: ModuleNameLookup(ctx, resolutionKind),
|
|
lookupKind(lookupKind) {}
|
|
|
|
private:
|
|
/// Returns whether it's okay to stop recursively searching imports, given
|
|
/// that we found something non-overloadable.
|
|
static bool canReturnEarly() {
|
|
return false;
|
|
}
|
|
|
|
void doLocalLookup(ModuleDecl *module, ImportPath::Access path,
|
|
OptionSet<ModuleLookupFlags> flags,
|
|
SmallVectorImpl<ValueDecl *> &localDecls) {
|
|
VectorDeclConsumer consumer(localDecls);
|
|
module->lookupVisibleDecls(path, consumer, lookupKind);
|
|
}
|
|
};
|
|
|
|
} // end anonymous namespace
|
|
|
|
bool swift::declIsVisibleToNameLookup(
|
|
const ValueDecl *decl, const DeclContext *moduleScopeContext,
|
|
NLOptions options) {
|
|
// NL_IgnoreAccessControl only applies to the current module. If
|
|
// it applies here, the declaration is visible.
|
|
if ((options & NL_IgnoreAccessControl) &&
|
|
moduleScopeContext &&
|
|
moduleScopeContext->getParentModule() ==
|
|
decl->getDeclContext()->getParentModule())
|
|
return true;
|
|
|
|
bool includeUsableFromInline = options & NL_IncludeUsableFromInline;
|
|
return decl->isAccessibleFrom(moduleScopeContext, false,
|
|
includeUsableFromInline);
|
|
}
|
|
|
|
template <typename LookupStrategy>
|
|
void ModuleNameLookup<LookupStrategy>::lookupInModule(
|
|
SmallVectorImpl<ValueDecl *> &decls,
|
|
const DeclContext *moduleOrFile,
|
|
ImportPath::Access accessPath,
|
|
const DeclContext *moduleScopeContext,
|
|
NLOptions options) {
|
|
assert(moduleOrFile->isModuleScopeContext());
|
|
|
|
// Does the module scope have any separately-imported overlays shadowing
|
|
// the module we're looking into?
|
|
SmallVector<ModuleDecl *, 4> overlays;
|
|
moduleScopeContext->getSeparatelyImportedOverlays(
|
|
moduleOrFile->getParentModule(), overlays);
|
|
if (!overlays.empty()) {
|
|
// If so, look in each of those overlays.
|
|
for (auto overlay : overlays)
|
|
lookupInModule(decls, overlay, accessPath, moduleScopeContext, options);
|
|
// FIXME: This may not work gracefully if more than one of these lookups
|
|
// finds something.
|
|
return;
|
|
}
|
|
|
|
const size_t initialCount = decls.size();
|
|
size_t currentCount = decls.size();
|
|
|
|
auto updateNewDecls = [&](const DeclContext *moduleScopeContext) {
|
|
if (decls.size() == currentCount)
|
|
return;
|
|
|
|
auto newEnd = std::remove_if(
|
|
decls.begin() + currentCount, decls.end(),
|
|
[&](ValueDecl *VD) {
|
|
if (resolutionKind == ResolutionKind::TypesOnly && !isa<TypeDecl>(VD))
|
|
return true;
|
|
if (resolutionKind == ResolutionKind::MacrosOnly && !isa<MacroDecl>(VD))
|
|
return true;
|
|
if (respectAccessControl &&
|
|
!declIsVisibleToNameLookup(VD, moduleScopeContext, options))
|
|
return true;
|
|
if (!ABIRoleInfo(VD).matchesOptions(options))
|
|
return true;
|
|
return false;
|
|
});
|
|
decls.erase(newEnd, decls.end());
|
|
|
|
currentCount = decls.size();
|
|
};
|
|
|
|
OptionSet<ModuleLookupFlags> currentModuleLookupFlags = {};
|
|
if (options & NL_ExcludeMacroExpansions)
|
|
currentModuleLookupFlags |= ModuleLookupFlags::ExcludeMacroExpansions;
|
|
if (options & NL_ABIProviding)
|
|
currentModuleLookupFlags |= ModuleLookupFlags::ABIProviding;
|
|
|
|
// Do the lookup into the current module.
|
|
auto *module = moduleOrFile->getParentModule();
|
|
getDerived()->doLocalLookup(
|
|
module, accessPath, currentModuleLookupFlags, decls);
|
|
updateNewDecls(moduleScopeContext);
|
|
|
|
bool canReturnEarly = (initialCount != decls.size() &&
|
|
getDerived()->canReturnEarly());
|
|
if (canReturnEarly &&
|
|
resolutionKind == ResolutionKind::Overloadable) {
|
|
// If we only found top-level functions or macros, keep looking, since
|
|
// we may find additional overloads.
|
|
if (std::all_of(decls.begin() + initialCount, decls.end(),
|
|
[](ValueDecl *VD) {
|
|
return isa<FuncDecl>(VD) || isa<MacroDecl>(VD);
|
|
}))
|
|
canReturnEarly = false;
|
|
}
|
|
|
|
// If needed, search for decls in re-exported modules as well.
|
|
if (!canReturnEarly) {
|
|
auto &imports = ctx.getImportCache().getImportSet(moduleOrFile);
|
|
|
|
OptionSet<ModuleLookupFlags> importedModuleLookupFlags = {};
|
|
if (options & NL_ABIProviding)
|
|
currentModuleLookupFlags |= ModuleLookupFlags::ABIProviding;
|
|
|
|
auto visitImport = [&](ImportedModule import,
|
|
const DeclContext *moduleScopeContext) {
|
|
if (import.accessPath.empty())
|
|
import.accessPath = accessPath;
|
|
else if (!accessPath.empty() &&
|
|
!import.accessPath.isSameAs(accessPath))
|
|
return;
|
|
|
|
getDerived()->doLocalLookup(import.importedModule, import.accessPath,
|
|
importedModuleLookupFlags, decls);
|
|
updateNewDecls(moduleScopeContext);
|
|
};
|
|
|
|
// If the ClangImporter's special header import module appears in the
|
|
// import set, we must visit it first.
|
|
ModuleDecl *headerImportModule = nullptr;
|
|
if (imports.hasHeaderImportModule()) {
|
|
if (auto *loader = ctx.getClangModuleLoader()) {
|
|
headerImportModule = loader->getImportedHeaderModule();
|
|
if (headerImportModule) {
|
|
visitImport(ImportedModule(headerImportModule), nullptr);
|
|
}
|
|
}
|
|
}
|
|
|
|
for (auto import : imports.getTopLevelImports()) {
|
|
// A module appears in its own top-level import list; since we checked
|
|
// it already, skip it.
|
|
if (import.importedModule == module)
|
|
continue;
|
|
|
|
// Skip the special import set module; we've already visited it.
|
|
if (import.importedModule == headerImportModule)
|
|
continue;
|
|
|
|
visitImport(import, moduleScopeContext);
|
|
}
|
|
|
|
for (auto import : imports.getTransitiveImports()) {
|
|
// Skip the special import set module; we've already visited it.
|
|
if (import.importedModule == headerImportModule)
|
|
continue;
|
|
|
|
visitImport(import, moduleScopeContext);
|
|
}
|
|
}
|
|
|
|
// Nothing more to do if we don't have ambiguous results.
|
|
if (decls.size() - initialCount <= 1)
|
|
return;
|
|
|
|
// Remove duplicated declarations, which can happen when the same module is
|
|
// imported with multiple access paths.
|
|
llvm::SmallPtrSet<ValueDecl *, 4> knownDecls;
|
|
decls.erase(std::remove_if(decls.begin() + initialCount, decls.end(),
|
|
[&](ValueDecl *d) -> bool {
|
|
return !knownDecls.insert(d).second;
|
|
}),
|
|
decls.end());
|
|
}
|
|
|
|
QualifiedLookupResult
|
|
LookupInModuleRequest::evaluate(
|
|
Evaluator &evaluator, const DeclContext *moduleOrFile, DeclName name,
|
|
NLKind lookupKind, ResolutionKind resolutionKind,
|
|
const DeclContext *moduleScopeContext, NLOptions options) const {
|
|
assert(moduleScopeContext->isModuleScopeContext());
|
|
|
|
QualifiedLookupResult decls;
|
|
LookupByName lookup(moduleOrFile->getASTContext(), resolutionKind,
|
|
name, lookupKind);
|
|
lookup.lookupInModule(decls, moduleOrFile, {}, moduleScopeContext, options);
|
|
return decls;
|
|
}
|
|
|
|
void namelookup::lookupInModule(const DeclContext *moduleOrFile,
|
|
DeclName name,
|
|
SmallVectorImpl<ValueDecl *> &decls,
|
|
NLKind lookupKind,
|
|
ResolutionKind resolutionKind,
|
|
const DeclContext *moduleScopeContext,
|
|
SourceLoc loc, NLOptions options) {
|
|
auto &ctx = moduleOrFile->getASTContext();
|
|
LookupInModuleRequest req(moduleOrFile, name, lookupKind, resolutionKind,
|
|
moduleScopeContext, loc, options);
|
|
auto results = evaluateOrDefault(ctx.evaluator, req, {});
|
|
decls.append(results.begin(), results.end());
|
|
}
|
|
|
|
void namelookup::lookupVisibleDeclsInModule(
|
|
const DeclContext *moduleOrFile,
|
|
ImportPath::Access accessPath,
|
|
SmallVectorImpl<ValueDecl *> &decls,
|
|
NLKind lookupKind,
|
|
ResolutionKind resolutionKind,
|
|
const DeclContext *moduleScopeContext) {
|
|
assert(moduleScopeContext->isModuleScopeContext());
|
|
auto &ctx = moduleOrFile->getASTContext();
|
|
LookupVisibleDecls lookup(ctx, resolutionKind, lookupKind);
|
|
lookup.lookupInModule(decls, moduleOrFile, accessPath, moduleScopeContext,
|
|
NL_QualifiedDefault);
|
|
}
|
|
|
|
void namelookup::simple_display(llvm::raw_ostream &out, ResolutionKind kind) {
|
|
switch (kind) {
|
|
case ResolutionKind::Overloadable:
|
|
out << "Overloadable";
|
|
return;
|
|
case ResolutionKind::TypesOnly:
|
|
out << "TypesOnly";
|
|
return;
|
|
case ResolutionKind::MacrosOnly:
|
|
out << "MacrosOnly";
|
|
return;
|
|
}
|
|
llvm_unreachable("Unhandled case in switch");
|
|
}
|