mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
328 lines
12 KiB
C++
328 lines
12 KiB
C++
//===--- AvailabilityScope.h - Swift Availability Scopes ----*- C++ -----*-===//
|
|
//
|
|
// 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
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
//
|
|
// This file defines the AvailabilityScope class. An AvailabilityScope
|
|
// is the semantic construct that refines a source range with constraints
|
|
// declared using @available and if #available.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#ifndef SWIFT_AVAILABILITYSCOPE_H
|
|
#define SWIFT_AVAILABILITYSCOPE_H
|
|
|
|
#include "swift/AST/AvailabilityContext.h"
|
|
#include "swift/AST/AvailabilityRange.h"
|
|
#include "swift/AST/Identifier.h"
|
|
#include "swift/AST/Stmt.h" // for PoundAvailableInfo
|
|
#include "swift/Basic/Debug.h"
|
|
#include "swift/Basic/LLVM.h"
|
|
#include "swift/Basic/STLExtras.h"
|
|
#include "swift/Basic/SourceLoc.h"
|
|
#include "llvm/Support/ErrorHandling.h"
|
|
|
|
namespace swift {
|
|
class BraceStmt;
|
|
class Decl;
|
|
class IfStmt;
|
|
class GuardStmt;
|
|
class SourceFile;
|
|
class Stmt;
|
|
class Expr;
|
|
class StmtConditionElement;
|
|
|
|
/// Represents a lexical context in which availability is refined. These scopes
|
|
/// form a lexical tree parallel to the AST but much more sparse: we only
|
|
/// introduce availability scopes when there is something to refine.
|
|
class AvailabilityScope : public ASTAllocated<AvailabilityScope> {
|
|
|
|
public:
|
|
/// Describes the reason an availability scope was introduced.
|
|
enum class Reason {
|
|
/// The root availability scope.
|
|
Root,
|
|
|
|
/// The context was introduced by a declaration with an explicit
|
|
/// availability attribute. The context contains both the signature and the
|
|
/// body of the declaration.
|
|
Decl,
|
|
|
|
/// The context was introduced implicitly by a declaration. The context may
|
|
/// cover the entire declaration or it may cover a subset of it. For
|
|
/// example, a public, non-inlinable function declaration in an API module
|
|
/// will have at least two associated contexts: one for the entire
|
|
/// declaration at the declared availability of the API and a nested
|
|
/// implicit context for the body of the function, which will always run at
|
|
/// the deployment target of the library.
|
|
DeclImplicit,
|
|
|
|
/// The context was introduced for the Then branch of an IfStmt.
|
|
IfStmtThenBranch,
|
|
|
|
/// The context was introduced for the Else branch of an IfStmt.
|
|
IfStmtElseBranch,
|
|
|
|
/// The context was introduced for the remaining StmtConditionElements
|
|
/// following an #available(...) query in a StmtCondition.
|
|
/// For example, in the IfStmt below, the optional binding let x = expr()
|
|
/// would be contained in this kind of context:
|
|
///
|
|
/// if #available(...),
|
|
/// let x = expr() {
|
|
/// }
|
|
ConditionFollowingAvailabilityQuery,
|
|
|
|
/// The context was introduced for the fallthrough flow of a guard
|
|
/// statement.
|
|
GuardStmtFallthrough,
|
|
|
|
/// The context was introduced for the else flow of a guard
|
|
/// statement.
|
|
GuardStmtElseBranch,
|
|
|
|
// The context was introduced for the body of a while statement.
|
|
WhileStmtBody
|
|
};
|
|
|
|
private:
|
|
friend class ExpandChildAvailabilityScopesRequest;
|
|
|
|
/// Represents the AST node that introduced an availability scope.
|
|
class IntroNode {
|
|
Reason IntroReason;
|
|
const DeclContext *DC;
|
|
union {
|
|
SourceFile *SF;
|
|
Decl *D;
|
|
IfStmt *IS;
|
|
PoundAvailableInfo *PAI;
|
|
GuardStmt *GS;
|
|
WhileStmt *WS;
|
|
};
|
|
|
|
public:
|
|
IntroNode(SourceFile *SF);
|
|
IntroNode(Decl *D, Reason introReason = Reason::Decl);
|
|
IntroNode(IfStmt *IS, const DeclContext *DC, bool IsThen)
|
|
: IntroReason(IsThen ? Reason::IfStmtThenBranch
|
|
: Reason::IfStmtElseBranch),
|
|
DC(DC), IS(IS) {}
|
|
IntroNode(PoundAvailableInfo *PAI, const DeclContext *DC)
|
|
: IntroReason(Reason::ConditionFollowingAvailabilityQuery), DC(DC),
|
|
PAI(PAI) {}
|
|
IntroNode(GuardStmt *GS, const DeclContext *DC, bool IsFallthrough)
|
|
: IntroReason(IsFallthrough ? Reason::GuardStmtFallthrough
|
|
: Reason::GuardStmtElseBranch),
|
|
DC(DC), GS(GS) {}
|
|
IntroNode(WhileStmt *WS, const DeclContext *DC)
|
|
: IntroReason(Reason::WhileStmtBody), DC(DC), WS(WS) {}
|
|
|
|
Reason getReason() const { return IntroReason; }
|
|
|
|
const DeclContext *getDeclContext() const { return DC; }
|
|
|
|
SourceFile *getAsSourceFile() const {
|
|
assert(IntroReason == Reason::Root);
|
|
return SF;
|
|
}
|
|
|
|
Decl *getAsDecl() const {
|
|
assert(IntroReason == Reason::Decl ||
|
|
IntroReason == Reason::DeclImplicit);
|
|
return D;
|
|
}
|
|
|
|
IfStmt *getAsIfStmt() const {
|
|
assert(IntroReason == Reason::IfStmtThenBranch ||
|
|
IntroReason == Reason::IfStmtElseBranch);
|
|
return IS;
|
|
}
|
|
|
|
PoundAvailableInfo *getAsPoundAvailableInfo() const {
|
|
assert(IntroReason == Reason::ConditionFollowingAvailabilityQuery);
|
|
return PAI;
|
|
}
|
|
|
|
GuardStmt *getAsGuardStmt() const {
|
|
assert(IntroReason == Reason::GuardStmtFallthrough ||
|
|
IntroReason == Reason::GuardStmtElseBranch);
|
|
return GS;
|
|
}
|
|
|
|
WhileStmt *getAsWhileStmt() const {
|
|
assert(IntroReason == Reason::WhileStmtBody);
|
|
return WS;
|
|
}
|
|
};
|
|
|
|
/// The AST node that introduced this context.
|
|
IntroNode Node;
|
|
|
|
SourceRange SrcRange;
|
|
|
|
/// A canonical availability info for this context, computed top-down from the
|
|
/// root context.
|
|
const AvailabilityContext AvailabilityInfo;
|
|
|
|
std::vector<AvailabilityScope *> Children;
|
|
|
|
struct {
|
|
/// Whether this node has child nodes that have not yet been expanded.
|
|
unsigned needsExpansion : 1;
|
|
} LazyInfo = {};
|
|
|
|
void verify(const AvailabilityScope *parent, ASTContext &ctx) const;
|
|
|
|
AvailabilityScope(ASTContext &Ctx, IntroNode Node, AvailabilityScope *Parent,
|
|
SourceRange SrcRange, const AvailabilityContext Info);
|
|
|
|
public:
|
|
/// Constructs the root availability scope for the given file and builds out
|
|
/// the scope tree for the top level contents of the file.
|
|
static AvailabilityScope *getOrBuildForSourceFile(SourceFile &SF);
|
|
|
|
/// Create the root availability scope for the given SourceFile.
|
|
static AvailabilityScope *createForSourceFile(SourceFile *SF,
|
|
const AvailabilityContext Info);
|
|
|
|
/// Create an availability scope for the given declaration.
|
|
static AvailabilityScope *createForDecl(ASTContext &Ctx, Decl *D,
|
|
AvailabilityScope *Parent,
|
|
const AvailabilityContext Info,
|
|
SourceRange SrcRange);
|
|
|
|
/// Create an availability scope for the given declaration.
|
|
static AvailabilityScope *
|
|
createForDeclImplicit(ASTContext &Ctx, Decl *D, AvailabilityScope *Parent,
|
|
const AvailabilityContext Info, SourceRange SrcRange);
|
|
|
|
/// Create an availability scope for the Then branch of the given IfStmt.
|
|
static AvailabilityScope *createForIfStmtThen(ASTContext &Ctx, IfStmt *S,
|
|
const DeclContext *DC,
|
|
AvailabilityScope *Parent,
|
|
const AvailabilityContext Info);
|
|
|
|
/// Create an availability scope for the Else branch of the given IfStmt.
|
|
static AvailabilityScope *createForIfStmtElse(ASTContext &Ctx, IfStmt *S,
|
|
const DeclContext *DC,
|
|
AvailabilityScope *Parent,
|
|
const AvailabilityContext Info);
|
|
|
|
/// Create an availability scope for the true-branch control flow to
|
|
/// further StmtConditionElements following a #available() query in
|
|
/// a StmtCondition.
|
|
static AvailabilityScope *createForConditionFollowingQuery(
|
|
ASTContext &Ctx, PoundAvailableInfo *PAI,
|
|
const StmtConditionElement &LastElement, const DeclContext *DC,
|
|
AvailabilityScope *Parent, const AvailabilityContext Info);
|
|
|
|
/// Create an availability scope for the fallthrough of a GuardStmt.
|
|
static AvailabilityScope *createForGuardStmtFallthrough(
|
|
ASTContext &Ctx, GuardStmt *RS, BraceStmt *ContainingBraceStmt,
|
|
const DeclContext *DC, AvailabilityScope *Parent,
|
|
const AvailabilityContext Info);
|
|
|
|
/// Create an availability scope for the else branch of a GuardStmt.
|
|
static AvailabilityScope *
|
|
createForGuardStmtElse(ASTContext &Ctx, GuardStmt *RS, const DeclContext *DC,
|
|
AvailabilityScope *Parent,
|
|
const AvailabilityContext Info);
|
|
|
|
/// Create an availability scope for the body of a WhileStmt.
|
|
static AvailabilityScope *
|
|
createForWhileStmtBody(ASTContext &Ctx, WhileStmt *WS, const DeclContext *DC,
|
|
AvailabilityScope *Parent,
|
|
const AvailabilityContext Info);
|
|
|
|
Decl *getDeclOrNull() const {
|
|
auto IntroReason = getReason();
|
|
if (IntroReason == Reason::Decl || IntroReason == Reason::DeclImplicit)
|
|
return getIntroductionNode().getAsDecl();
|
|
return nullptr;
|
|
}
|
|
|
|
/// Returns the reason this scope was introduced.
|
|
Reason getReason() const;
|
|
|
|
/// Returns the AST node that introduced this availability scope. Note that
|
|
/// this node may be different than the refined range. For example, an
|
|
/// availability scope covering an IfStmt Then branch will have the
|
|
/// IfStmt as the introduction node (and its reason as IfStmtThenBranch)
|
|
/// but its source range will cover the Then branch.
|
|
IntroNode getIntroductionNode() const { return Node; }
|
|
|
|
/// Returns the location of the node that introduced this availability scope
|
|
/// or an invalid location if the context reflects the minimum deployment
|
|
/// target.
|
|
SourceLoc getIntroductionLoc() const;
|
|
|
|
/// Returns the source range covering a _single_ decl-attribute or statement
|
|
/// condition that introduced the availability scope for a given platform
|
|
/// version; if zero or multiple such responsible attributes or statements
|
|
/// exist, returns an invalid SourceRange.
|
|
SourceRange getAvailabilityConditionVersionSourceRange(
|
|
AvailabilityDomain Domain, const llvm::VersionTuple &Version) const;
|
|
|
|
/// Returns the availability version range that was explicitly written in
|
|
/// source for the given domain, if applicable. Otherwise, returns
|
|
/// `std::nullopt`.
|
|
std::optional<const AvailabilityRange>
|
|
getExplicitAvailabilityRange(AvailabilityDomain Domain,
|
|
ASTContext &Ctx) const;
|
|
|
|
/// Returns the source range this scope represents.
|
|
SourceRange getSourceRange() const { return SrcRange; }
|
|
|
|
/// Returns the availability context of code contained in this scope.
|
|
const AvailabilityContext getAvailabilityContext() const {
|
|
return AvailabilityInfo;
|
|
}
|
|
|
|
/// Returns the platform version range that can be assumed present at run
|
|
/// time when running code contained in this scope.
|
|
const AvailabilityRange getPlatformAvailabilityRange() const {
|
|
return AvailabilityInfo.getPlatformRange();
|
|
}
|
|
|
|
/// Adds a child availability scope.
|
|
void addChild(AvailabilityScope *Child, ASTContext &Ctx);
|
|
|
|
/// Returns the innermost AvailabilityScope descendant of this scope
|
|
/// for the given source location.
|
|
AvailabilityScope *findMostRefinedSubContext(SourceLoc Loc, ASTContext &Ctx);
|
|
|
|
bool getNeedsExpansion() const { return LazyInfo.needsExpansion; }
|
|
|
|
void setNeedsExpansion(bool needsExpansion) {
|
|
LazyInfo.needsExpansion = needsExpansion;
|
|
}
|
|
|
|
/// Recursively check the tree for integrity. If any errors are found, emits
|
|
/// diagnostics to stderr and aborts.
|
|
void verify(ASTContext &ctx);
|
|
|
|
SWIFT_DEBUG_DUMPER(dump(SourceManager &SrcMgr));
|
|
void dump(raw_ostream &OS, SourceManager &SrcMgr) const;
|
|
void print(raw_ostream &OS, SourceManager &SrcMgr, unsigned Indent = 0) const;
|
|
|
|
static StringRef getReasonName(Reason R);
|
|
};
|
|
|
|
void simple_display(llvm::raw_ostream &out, const AvailabilityScope *scope);
|
|
|
|
inline SourceLoc extractNearestSourceLoc(const AvailabilityScope *scope) {
|
|
return scope->getIntroductionLoc();
|
|
}
|
|
|
|
} // end namespace swift
|
|
|
|
#endif
|