mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
My recent refactoring of top-level lookup replaced the old shadowing done as part of top-level lookup with two separate rules: - If all paths from the current source file to a module 'B' go through a module 'A', then declarations in 'A' shadow declarations in 'B'. - If a declaration in module 'A' was found via a scoped import and a declaration with the same name in module 'B' was found via an unscoped import, prefer the declaration from module 'A'. However this caused a source break when you have a scenario like the following: - A source file imports 'A', 'B', and 'B.Foo'. - 'A' re-exports 'B'. - Both 'A' and 'B' define a type named 'Foo'. The problem is that the scoped import 'B.Foo' can actually find both 'A.Foo' and 'B.Foo', since 'B' re-exports 'A'. Furthermore, since the source file explicitly imports 'A', 'B' does not shadow 'A' in the import graph. As a result neither shadowing rule would eliminate the ambiguity. The new rule combines the scoped import check and the shadowing check by considering all access paths to 'A' that are not shadowed by 'B'. Using this rule, 'A.Foo' is only seen via the access path 'A', whereas 'B.Foo' is seen under both 'B' and 'B.Foo'. Since 'B.Foo' is seen via a scoped import and 'A.Foo' is only seen via an unscoped import, we can conclude that 'B.Foo' shadows 'A.Foo'. Fixes <rdar://problem/55205050>.
164 lines
5.5 KiB
C++
164 lines
5.5 KiB
C++
//===--- ImportCache.h - Caching the import graph ---------------*- C++ -*-===//
|
|
//
|
|
// This source file is part of the Swift.org open source project
|
|
//
|
|
// Copyright (c) 2014 - 2019 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
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
//
|
|
// This file defines interfaces for querying the module import graph in an
|
|
// efficient manner.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#ifndef SWIFT_AST_IMPORT_CACHE_H
|
|
#define SWIFT_AST_IMPORT_CACHE_H
|
|
|
|
#include "swift/AST/Module.h"
|
|
#include "llvm/ADT/DenseMap.h"
|
|
#include "llvm/ADT/FoldingSet.h"
|
|
|
|
namespace swift {
|
|
class DeclContext;
|
|
|
|
namespace namelookup {
|
|
|
|
/// An object describing a set of modules visible from a DeclContext.
|
|
///
|
|
/// This consists of two arrays of modules; the top-level imports and the
|
|
/// transitive imports.
|
|
///
|
|
/// The top-level imports contains all public imports of the parent module
|
|
/// of 'dc', together with any private imports in the source file containing
|
|
/// 'dc', if there is one.
|
|
///
|
|
/// The transitive imports contains all public imports reachable from the
|
|
/// set of top-level imports.
|
|
///
|
|
/// Both sets only contain unique elements. The top-level imports always
|
|
/// include the parent module of 'dc' explicitly.
|
|
///
|
|
/// The set of transitive imports does not contain any elements found in
|
|
/// the top-level imports.
|
|
///
|
|
/// The Swift standard library module is not included in either set unless
|
|
/// it was explicitly imported (or re-exported).
|
|
class ImportSet final :
|
|
public llvm::FoldingSetNode,
|
|
private llvm::TrailingObjects<ImportSet, ModuleDecl::ImportedModule> {
|
|
friend TrailingObjects;
|
|
friend class ImportCache;
|
|
|
|
unsigned HasHeaderImportModule : 1;
|
|
unsigned NumTopLevelImports : 31;
|
|
unsigned NumTransitiveImports;
|
|
|
|
ImportSet(bool hasHeaderImportModule,
|
|
ArrayRef<ModuleDecl::ImportedModule> topLevelImports,
|
|
ArrayRef<ModuleDecl::ImportedModule> transitiveImports);
|
|
|
|
ImportSet(const ImportSet &) = delete;
|
|
void operator=(const ImportSet &) = delete;
|
|
|
|
public:
|
|
void Profile(llvm::FoldingSetNodeID &ID) {
|
|
Profile(ID, getTopLevelImports());
|
|
}
|
|
static void Profile(
|
|
llvm::FoldingSetNodeID &ID,
|
|
ArrayRef<ModuleDecl::ImportedModule> topLevelImports);
|
|
|
|
size_t numTrailingObjects(OverloadToken<ModuleDecl::ImportedModule>) const {
|
|
return NumTopLevelImports + NumTransitiveImports;
|
|
}
|
|
|
|
/// This is a bit of a hack to make module name lookup work properly.
|
|
/// If our import set includes the ClangImporter's special header import
|
|
/// module, we have to check it first, before any other imported module.
|
|
bool hasHeaderImportModule() const {
|
|
return HasHeaderImportModule;
|
|
}
|
|
|
|
ArrayRef<ModuleDecl::ImportedModule> getTopLevelImports() const {
|
|
return {getTrailingObjects<ModuleDecl::ImportedModule>(),
|
|
NumTopLevelImports};
|
|
}
|
|
|
|
ArrayRef<ModuleDecl::ImportedModule> getTransitiveImports() const {
|
|
return {getTrailingObjects<ModuleDecl::ImportedModule>() +
|
|
NumTopLevelImports,
|
|
NumTransitiveImports};
|
|
}
|
|
|
|
ArrayRef<ModuleDecl::ImportedModule> getAllImports() const {
|
|
return {getTrailingObjects<ModuleDecl::ImportedModule>(),
|
|
NumTopLevelImports + NumTransitiveImports};
|
|
}
|
|
};
|
|
|
|
class alignas(ModuleDecl::ImportedModule) ImportCache {
|
|
ImportCache(const ImportCache &) = delete;
|
|
void operator=(const ImportCache &) = delete;
|
|
|
|
llvm::FoldingSet<ImportSet> ImportSets;
|
|
llvm::DenseMap<const DeclContext *, ImportSet *> ImportSetForDC;
|
|
llvm::DenseMap<std::tuple<const ModuleDecl *,
|
|
const DeclContext *>,
|
|
ArrayRef<ModuleDecl::AccessPathTy>> VisibilityCache;
|
|
llvm::DenseMap<std::tuple<const ModuleDecl *,
|
|
const ModuleDecl *,
|
|
const DeclContext *>,
|
|
ArrayRef<ModuleDecl::AccessPathTy>> ShadowCache;
|
|
|
|
ModuleDecl::AccessPathTy EmptyAccessPath;
|
|
|
|
ArrayRef<ModuleDecl::AccessPathTy> allocateArray(
|
|
ASTContext &ctx,
|
|
SmallVectorImpl<ModuleDecl::AccessPathTy> &results);
|
|
|
|
ImportSet &getImportSet(ASTContext &ctx,
|
|
ArrayRef<ModuleDecl::ImportedModule> topLevelImports);
|
|
|
|
public:
|
|
ImportCache() {}
|
|
|
|
/// Returns an object descripting all modules transtiively imported
|
|
/// from 'dc'.
|
|
ImportSet &getImportSet(const DeclContext *dc);
|
|
|
|
/// Returns all access paths into 'mod' that are visible from 'dc',
|
|
/// including transitively, via re-exports.
|
|
ArrayRef<ModuleDecl::AccessPathTy>
|
|
getAllVisibleAccessPaths(const ModuleDecl *mod, const DeclContext *dc);
|
|
|
|
bool isImportedBy(const ModuleDecl *mod,
|
|
const DeclContext *dc) {
|
|
return !getAllVisibleAccessPaths(mod, dc).empty();
|
|
}
|
|
|
|
/// Returns all access paths in 'mod' that are visible from 'dc' if we
|
|
/// subtract imports of 'other'.
|
|
ArrayRef<ModuleDecl::AccessPathTy>
|
|
getAllAccessPathsNotShadowedBy(const ModuleDecl *mod,
|
|
const ModuleDecl *other,
|
|
const DeclContext *dc);
|
|
|
|
/// This is a hack to cope with main file parsing and REPL parsing, where
|
|
/// we can add ImportDecls after name binding.
|
|
void clear() {
|
|
ImportSetForDC.clear();
|
|
}
|
|
};
|
|
|
|
ArrayRef<ModuleDecl::ImportedModule> getAllImports(const DeclContext *dc);
|
|
|
|
} // namespace namelookup
|
|
|
|
} // namespace swift
|
|
|
|
#endif
|