blob: 2dca10739444e283a3f087093fdfc529529c1ef6 [file] [log] [blame]
//===--- TypeRefinementContext.h - Swift Refinement Context -----*- 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 TypeRefinementContext class. A TypeRefinementContext
// is the semantic construct that refines a type within its lexical scope.
//
//===----------------------------------------------------------------------===//
#ifndef SWIFT_TYPEREFINEMENTCONTEXT_H
#define SWIFT_TYPEREFINEMENTCONTEXT_H
#include "swift/AST/Identifier.h"
#include "swift/AST/Availability.h"
#include "swift/AST/Stmt.h" // for PoundAvailableInfo
#include "swift/Basic/Debug.h"
#include "swift/Basic/LLVM.h"
#include "swift/Basic/SourceLoc.h"
#include "swift/Basic/STLExtras.h"
#include "llvm/ADT/PointerUnion.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 types are refined. For now,
/// types are refined solely for API availability checking, based on
/// the operating system versions that the refined context may execute
/// upon.
///
/// These refinement contexts form a lexical tree parallel to the AST but much
/// more sparse: we only introduce refinement contexts when there is something
/// to refine.
class TypeRefinementContext {
public:
/// Describes the reason a type refinement context was introduced.
enum class Reason {
/// The root refinement context.
Root,
/// The context was introduced by a declaration (e.g., the body of a
/// function declaration or the contents of a class declaration).
Decl,
/// 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:
/// Represents the AST node that introduced a refinement context.
class IntroNode {
Reason IntroReason;
union {
SourceFile *SF;
Decl *D;
IfStmt *IS;
PoundAvailableInfo *PAI;
GuardStmt *GS;
WhileStmt *WS;
};
public:
IntroNode(SourceFile *SF) : IntroReason(Reason::Root), SF(SF) {}
IntroNode(Decl *D) : IntroReason(Reason::Decl), D(D) {}
IntroNode(IfStmt *IS, bool IsThen) :
IntroReason(IsThen ? Reason::IfStmtThenBranch : Reason::IfStmtElseBranch),
IS(IS) {}
IntroNode(PoundAvailableInfo *PAI)
: IntroReason(Reason::ConditionFollowingAvailabilityQuery), PAI(PAI) {}
IntroNode(GuardStmt *GS, bool IsFallthrough)
: IntroReason(IsFallthrough ? Reason::GuardStmtFallthrough
: Reason::GuardStmtElseBranch),
GS(GS) {}
IntroNode(WhileStmt *WS) : IntroReason(Reason::WhileStmtBody), WS(WS) {}
Reason getReason() const { return IntroReason; }
SourceFile *getAsSourceFile() const {
assert(IntroReason == Reason::Root);
return SF;
}
Decl *getAsDecl() const {
assert(IntroReason == Reason::Decl);
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;
AvailabilityContext AvailabilityInfo;
std::vector<TypeRefinementContext *> Children;
TypeRefinementContext(ASTContext &Ctx, IntroNode Node,
TypeRefinementContext *Parent, SourceRange SrcRange,
const AvailabilityContext &Info);
public:
/// Create the root refinement context for the given SourceFile.
static TypeRefinementContext *createRoot(SourceFile *SF,
const AvailabilityContext &Info);
/// Create a refinement context for the given declaration.
static TypeRefinementContext *createForDecl(ASTContext &Ctx, Decl *D,
TypeRefinementContext *Parent,
const AvailabilityContext &Info,
SourceRange SrcRange);
/// Create a refinement context for the Then branch of the given IfStmt.
static TypeRefinementContext *
createForIfStmtThen(ASTContext &Ctx, IfStmt *S, TypeRefinementContext *Parent,
const AvailabilityContext &Info);
/// Create a refinement context for the Else branch of the given IfStmt.
static TypeRefinementContext *
createForIfStmtElse(ASTContext &Ctx, IfStmt *S, TypeRefinementContext *Parent,
const AvailabilityContext &Info);
/// Create a refinement context for the true-branch control flow to
/// further StmtConditionElements following a #available() query in
/// a StmtCondition.
static TypeRefinementContext *
createForConditionFollowingQuery(ASTContext &Ctx, PoundAvailableInfo *PAI,
const StmtConditionElement &LastElement,
TypeRefinementContext *Parent,
const AvailabilityContext &Info);
/// Create a refinement context for the fallthrough of a GuardStmt.
static TypeRefinementContext *
createForGuardStmtFallthrough(ASTContext &Ctx, GuardStmt *RS,
BraceStmt *ContainingBraceStmt,
TypeRefinementContext *Parent,
const AvailabilityContext &Info);
/// Create a refinement context for the else branch of a GuardStmt.
static TypeRefinementContext *
createForGuardStmtElse(ASTContext &Ctx, GuardStmt *RS,
TypeRefinementContext *Parent,
const AvailabilityContext &Info);
/// Create a refinement context for the body of a WhileStmt.
static TypeRefinementContext *
createForWhileStmtBody(ASTContext &Ctx, WhileStmt *WS,
TypeRefinementContext *Parent,
const AvailabilityContext &Info);
/// Returns the reason this context was introduced.
Reason getReason() const;
/// Returns the AST node that introduced this refinement context. Note that
/// this node may be different than the refined range. For example, a
/// refinement context 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 refinement context
/// 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 refinement context for a given platform
/// version; if zero or multiple such responsible attributes or statements
/// exist, returns an invalid SourceRange.
SourceRange
getAvailabilityConditionVersionSourceRange(
PlatformKind Platform,
const llvm::VersionTuple &Version) const;
/// Returns the source range on which this context refines types.
SourceRange getSourceRange() const { return SrcRange; }
/// Returns the information on what can be assumed present at run time when
/// running code contained in this context.
const AvailabilityContext &getAvailabilityInfo() const {
return AvailabilityInfo;
}
/// Adds a child refinement context.
void addChild(TypeRefinementContext *Child) {
assert(Child->getSourceRange().isValid());
Children.push_back(Child);
}
/// Returns the inner-most TypeRefinementContext descendant of this context
/// for the given source location.
TypeRefinementContext *findMostRefinedSubContext(SourceLoc Loc,
SourceManager &SM);
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);
// Only allow allocation of TypeRefinementContext using the allocator in
// ASTContext.
void *operator new(size_t Bytes, ASTContext &C,
unsigned Alignment = alignof(TypeRefinementContext));
};
} // end namespace swift
#endif