Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
30 changes: 1 addition & 29 deletions include/swift/AST/ASTScope.h
Original file line number Diff line number Diff line change
Expand Up @@ -417,10 +417,6 @@ class ASTScopeImpl {
static llvm::SmallVector<LabeledStmt *, 4>
lookupLabeledStmts(SourceFile *sourceFile, SourceLoc loc);

static Optional<bool>
computeIsCascadingUse(ArrayRef<const ASTScopeImpl *> history,
Optional<bool> initialIsCascadingUse);

static std::pair<CaseStmt *, CaseStmt *>
lookupFallthroughSourceAndDest(SourceFile *sourceFile, SourceLoc loc);

Expand Down Expand Up @@ -448,7 +444,6 @@ class ASTScopeImpl {
/// The main (recursive) lookup function:
/// Tell DeclConsumer about all names found in this scope and if not done,
/// recurse for enclosing scopes. Stop lookup if about to look in limit.
/// Return final value for isCascadingUse
///
/// If the lookup depends on implicit self, selfDC is its context.
/// (Names in extensions never depend on self.)
Expand Down Expand Up @@ -508,9 +503,6 @@ class ASTScopeImpl {

#pragma mark - - lookup- local bindings
protected:
virtual Optional<bool>
resolveIsCascadingUseForThisScope(Optional<bool>) const;

// A local binding is a basically a local variable defined in that very scope
// It is not an instance variable or inherited type.

Expand Down Expand Up @@ -611,11 +603,11 @@ class Portion {
virtual ASTScopeImpl *expandScope(GenericTypeOrExtensionScope *,
ScopeCreator &) const = 0;

/// \Returns \c true if this lookup is done looking for results, else \c false.
virtual SourceRange
getChildlessSourceRangeOf(const GenericTypeOrExtensionScope *scope,
bool omitAssertions) const = 0;

/// Returns isDone and isCascadingUse
virtual bool lookupMembersOf(const GenericTypeOrExtensionScope *scope,
ArrayRef<const ASTScopeImpl *>,
ASTScopeImpl::DeclConsumer consumer) const;
Expand Down Expand Up @@ -780,10 +772,6 @@ class GenericTypeOrExtensionScope : public ASTScopeImpl {
virtual bool doesDeclHaveABody() const;
const char *portionName() const { return portion->portionName; }

protected:
Optional<bool> resolveIsCascadingUseForThisScope(
Optional<bool> isCascadingUse) const override;

public:
// Only for DeclScope, not BodyScope
// Returns the where clause scope, or the parent if none
Expand Down Expand Up @@ -969,8 +957,6 @@ class GenericParamScope final : public ASTScopeImpl {
protected:
bool lookupLocalsOrMembers(ArrayRef<const ASTScopeImpl *>,
DeclConsumer) const override;
Optional<bool>
resolveIsCascadingUseForThisScope(Optional<bool>) const override;
};

/// Concrete class for a function/initializer/deinitializer
Expand Down Expand Up @@ -1014,9 +1000,6 @@ class AbstractFunctionDeclScope final : public ASTScopeImpl {

protected:
NullablePtr<const GenericParamList> genericParams() const override;

Optional<bool>
resolveIsCascadingUseForThisScope(Optional<bool>) const override;
};

/// The parameters for an abstract function (init/func/deinit)., subscript, and
Expand Down Expand Up @@ -1087,8 +1070,6 @@ class AbstractFunctionBodyScope : public ASTScopeImpl {
protected:
bool lookupLocalsOrMembers(ArrayRef<const ASTScopeImpl *>,
DeclConsumer) const override;
Optional<bool>
resolveIsCascadingUseForThisScope(Optional<bool>) const override;

public:
NullablePtr<ASTScopeImpl> insertionPointForDeferredExpansion() override;
Expand Down Expand Up @@ -1123,10 +1104,6 @@ class DefaultArgumentInitializerScope final : public ASTScopeImpl {
virtual NullablePtr<DeclContext> getDeclContext() const override;
virtual NullablePtr<Decl> getDeclIfAny() const override { return decl; }
Decl *getDecl() const { return decl; }

protected:
Optional<bool>
resolveIsCascadingUseForThisScope(Optional<bool>) const override;
};

/// Consider:
Expand Down Expand Up @@ -1274,9 +1251,6 @@ class PatternEntryInitializerScope final : public AbstractPatternEntryScope {
protected:
bool lookupLocalsOrMembers(ArrayRef<const ASTScopeImpl *>,
DeclConsumer) const override;

Optional<bool>
resolveIsCascadingUseForThisScope(Optional<bool>) const override;
};

/// The scope introduced by a conditional clause in an if/guard/while
Expand Down Expand Up @@ -1402,8 +1376,6 @@ class ClosureParametersScope final : public ASTScopeImpl {
protected:
bool lookupLocalsOrMembers(ArrayRef<const ASTScopeImpl *>,
DeclConsumer) const override;
Optional<bool> resolveIsCascadingUseForThisScope(
Optional<bool> isCascadingUse) const override;
};

class TopLevelCodeScope final : public ASTScopeImpl {
Expand Down
8 changes: 0 additions & 8 deletions include/swift/AST/DeclContext.h
Original file line number Diff line number Diff line change
Expand Up @@ -508,14 +508,6 @@ class alignas(1 << DeclContextAlignInBits) DeclContext {
/// FIXME: do this for Protocols, too someday
bool canBeParentOfExtension() const;

/// Returns true if lookups within this context could affect downstream files.
///
/// \param functionsAreNonCascading If true, functions are considered non-
/// cascading contexts. If false, functions are considered non-cascading only
/// if implicitly or explicitly marked private. When concerned only with a
/// function's body, pass true.
bool isCascadingContextForLookup(bool functionsAreNonCascading) const;

/// Look for the set of declarations with the given name within a type,
/// its extensions and, optionally, its supertypes.
///
Expand Down
153 changes: 19 additions & 134 deletions include/swift/AST/EvaluatorDependencies.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
#include "swift/AST/AnyRequest.h"
#include "swift/AST/AttrKind.h"
#include "swift/AST/SourceFile.h"
#include "swift/Basic/NullablePtr.h"
#include "llvm/ADT/PointerIntPair.h"

namespace swift {
Expand All @@ -32,80 +33,13 @@ namespace detail {
template <typename...> using void_t = void;
} // namespace detail

/// The "scope" of a dependency edge tracked by the evaluator.
///
/// Dependency scopes come in two flavors: cascading and private. A private
/// edge captures dependencies discovered in contexts that are not visible to
/// to other files. For example, a conformance to a private protocol, or the use
/// of any names inside of a function body. A cascading edge, by contrast,
/// captures dependencies discovered in the remaining visible contexts. These
/// are types with at least \c internal visibility, names defined or used
/// outside of function bodies with at least \c internal visibility, etc. A
/// dependency that has cascading scope is so-named because upon traversing the
/// edge, a reader such as the driver should continue transitively evaluating
/// further dependency edges.
///
/// A cascading edge is always conservatively correct to produce, but it comes
/// at the cost of increased resources spent (and possibly even wasted!) during
/// incremental compilation. A private edge, by contrast, is more efficient for
/// incremental compilation but it is harder to safely use.
///
/// To ensure that these edges are registered consistently with the correct
/// scopes, requests that act as the source of dependency edges are required
/// to specify a \c DependencyScope under which all evaluated sub-requests will
/// register their dependency edges. In this way, \c DependencyScope values
/// form a stack-like structure and are pushed and popped by the evaluator
/// during the course of request evaluation.
///
/// When determining the kind of scope a request should use, always err on the
/// side of a cascading scope unless there is absolute proof any discovered
/// dependencies will be private. Inner requests may also defensively choose to
/// flip the dependency scope from private to cascading in the name of safety.
enum class DependencyScope : bool {
Private = false,
Cascading = true,
};

/// Returns a \c DependencyScope appropriate for the given (formal) access level.
///
/// :warning: This function exists to bridge the old manual reference
/// dependencies code to the new evaluator-based reference dependencies code.
/// The manual code often made private/cascading scope judgements based on the
/// access level of a declaration. While this makes some sense intuitively, it
/// does not necessarily capture an accurate picture of where real incremental
/// dependencies lie. For example, references to formally private types can
/// "escape" to contexts that have no reference to the private name if, say,
/// the layout of that private type is taken into consideration by
/// SILGen or IRGen in a separate file that references the declaration
/// transitively. However, due to the density of the current dependency
/// graph, redundancy in registered dependency edges, and the liberal use of
/// cascading edges, we may be saved from the worst consequences of this
/// modelling choice.
///
/// The use of access-levels for dependency decisions is an anti-pattern that
/// should be revisited once finer-grained dependencies are explored more
/// thoroughly.
inline DependencyScope getScopeForAccessLevel(AccessLevel l) {
switch (l) {
case AccessLevel::Private:
case AccessLevel::FilePrivate:
return DependencyScope::Private;
case AccessLevel::Internal:
case AccessLevel::Public:
case AccessLevel::Open:
return DependencyScope::Cascading;
}
llvm_unreachable("invalid access level kind");
}

// A \c DependencySource is currently defined to be a parent source file and
// an associated dependency scope.
// A \c DependencySource is currently defined to be a primary source file.
//
// The \c SourceFile instance is an artifact of the current dependency system,
// and should be scrapped if possible. It currently encodes the idea that
// edges in the incremental dependency graph invalidate entire files instead
// of individual contexts.
using DependencySource = llvm::PointerIntPair<SourceFile *, 1, DependencyScope>;
using DependencySource = swift::NullablePtr<SourceFile>;

struct DependencyRecorder;

Expand Down Expand Up @@ -133,41 +67,38 @@ struct DependencyCollector {

NominalTypeDecl *subject;
DeclBaseName name;
bool cascades;

private:
Reference(Kind kind, NominalTypeDecl *subject, DeclBaseName name,
bool cascades)
: kind(kind), subject(subject), name(name), cascades(cascades) {}
Reference(Kind kind, NominalTypeDecl *subject, DeclBaseName name)
: kind(kind), subject(subject), name(name) {}

public:
static Reference empty() {
return {Kind::Empty, llvm::DenseMapInfo<NominalTypeDecl *>::getEmptyKey(),
llvm::DenseMapInfo<DeclBaseName>::getEmptyKey(), false};
llvm::DenseMapInfo<DeclBaseName>::getEmptyKey()};
}

static Reference tombstone() {
return {Kind::Tombstone,
llvm::DenseMapInfo<NominalTypeDecl *>::getTombstoneKey(),
llvm::DenseMapInfo<DeclBaseName>::getTombstoneKey(), false};
llvm::DenseMapInfo<DeclBaseName>::getTombstoneKey()};
}

public:
static Reference usedMember(NominalTypeDecl *subject, DeclBaseName name,
bool cascades) {
return {Kind::UsedMember, subject, name, cascades};
static Reference usedMember(NominalTypeDecl *subject, DeclBaseName name) {
return {Kind::UsedMember, subject, name};
}

static Reference potentialMember(NominalTypeDecl *subject, bool cascades) {
return {Kind::PotentialMember, subject, DeclBaseName(), cascades};
static Reference potentialMember(NominalTypeDecl *subject) {
return {Kind::PotentialMember, subject, DeclBaseName()};
}

static Reference topLevel(DeclBaseName name, bool cascades) {
return {Kind::TopLevel, nullptr, name, cascades};
static Reference topLevel(DeclBaseName name) {
return {Kind::TopLevel, nullptr, name};
}

static Reference dynamic(DeclBaseName name, bool cascades) {
return {Kind::Dynamic, nullptr, name, cascades};
static Reference dynamic(DeclBaseName name) {
return {Kind::Dynamic, nullptr, name};
}

public:
Expand Down Expand Up @@ -248,34 +179,17 @@ struct DependencyCollector {
struct DependencyRecorder {
friend DependencyCollector;

enum class Mode {
// Enables the status quo of recording direct dependencies.
//
// This mode restricts the dependency collector to ignore changes of
// scope. This has practical effect of charging all unqualified lookups to
// the primary file being acted upon instead of to the destination file.
DirectDependencies,
// Enables a legacy mode of dependency tracking that makes a distinction
// between private and cascading edges, and does not directly capture
// transitive dependencies.
//
// By default, the dependency collector moves to register dependencies in
// the referenced name trackers at the top of the active dependency stack.
LegacyCascadingDependencies,
};

private:
/// A stack of dependency sources in the order they were evaluated.
llvm::SmallVector<evaluator::DependencySource, 8> dependencySources;
llvm::DenseMap<SourceFile *, DependencyCollector::ReferenceSet>
fileReferences;
llvm::DenseMap<AnyRequest, DependencyCollector::ReferenceSet>
requestReferences;
Mode mode;
bool isRecording;

public:
explicit DependencyRecorder(Mode mode) : mode{mode}, isRecording{false} {};
explicit DependencyRecorder() : isRecording{false} {};

private:
/// Records the given \c Reference as a dependency of the current dependency
Expand Down Expand Up @@ -338,32 +252,17 @@ struct DependencyRecorder {
ReferenceEnumerator f) const ;

public:
/// Returns the scope of the current active scope.
///
/// If there is no active scope, the result always cascades.
evaluator::DependencyScope getActiveSourceScope() const {
if (dependencySources.empty()) {
return evaluator::DependencyScope::Cascading;
}
return dependencySources.back().getInt();
}

/// Returns the active dependency's source file, or \c nullptr if no
/// dependency source is active.
///
/// The use of this accessor is strongly discouraged, as it implies that a
/// dependency sink is seeking to filter out names based on the files they
/// come from. Existing callers are being migrated to more reasonable ways
/// of judging the relevancy of a dependency.
SourceFile *getActiveDependencySourceOrNull() const {
evaluator::DependencySource getActiveDependencySourceOrNull() const {
if (dependencySources.empty())
return nullptr;
switch (mode) {
case Mode::LegacyCascadingDependencies:
return dependencySources.back().getPointer();
case Mode::DirectDependencies:
return dependencySources.front().getPointer();
}
return dependencySources.front();
}

public:
Expand All @@ -382,7 +281,7 @@ struct DependencyRecorder {
auto Source = Req.readDependencySource(coll);
// If there is no source to introduce, bail. This can occur if
// a request originates in the context of a module.
if (!Source.getPointer()) {
if (Source.isNull() || !Source.get()->isPrimary()) {
return;
}
coll.dependencySources.emplace_back(Source);
Expand All @@ -394,20 +293,6 @@ struct DependencyRecorder {
Coll.get()->dependencySources.pop_back();
}
};

private:
/// Returns \c true if the scope of the current active source cascades.
///
/// If there is no active scope, the result always cascades.
bool isActiveSourceCascading() const {
switch (mode) {
case Mode::LegacyCascadingDependencies:
return getActiveSourceScope() == evaluator::DependencyScope::Cascading;
case Mode::DirectDependencies:
return false;
}
llvm_unreachable("invalid mode");
}
};
} // end namespace evaluator

Expand Down
Loading