blob: e794daf155e5dbf7df6b3c4f7c4469ed5f1a14ce [file] [log] [blame]
//===--- TypeRefinementContext.cpp - Swift Refinement Context -------------===//
//
// 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 implements the TypeRefinementContext class.
//
//===----------------------------------------------------------------------===//
#include "swift/AST/ASTContext.h"
#include "swift/AST/Decl.h"
#include "swift/AST/Module.h"
#include "swift/AST/Stmt.h"
#include "swift/AST/Expr.h"
#include "swift/AST/TypeRefinementContext.h"
#include "swift/Basic/SourceManager.h"
using namespace swift;
TypeRefinementContext::TypeRefinementContext(ASTContext &Ctx, IntroNode Node,
TypeRefinementContext *Parent,
SourceRange SrcRange,
const AvailabilityContext &Info)
: Node(Node), SrcRange(SrcRange), AvailabilityInfo(Info) {
if (Parent) {
assert(SrcRange.isValid());
Parent->addChild(this);
assert(Info.isContainedIn(Parent->getAvailabilityInfo()));
}
Ctx.addDestructorCleanup(Children);
}
TypeRefinementContext *
TypeRefinementContext::createRoot(SourceFile *SF,
const AvailabilityContext &Info) {
assert(SF);
ASTContext &Ctx = SF->getASTContext();
return new (Ctx)
TypeRefinementContext(Ctx, SF,
/*Parent=*/nullptr, SourceRange(), Info);
}
TypeRefinementContext *
TypeRefinementContext::createForDecl(ASTContext &Ctx, Decl *D,
TypeRefinementContext *Parent,
const AvailabilityContext &Info,
SourceRange SrcRange) {
assert(D);
assert(Parent);
return new (Ctx)
TypeRefinementContext(Ctx, D, Parent, SrcRange, Info);
}
TypeRefinementContext *
TypeRefinementContext::createForIfStmtThen(ASTContext &Ctx, IfStmt *S,
TypeRefinementContext *Parent,
const AvailabilityContext &Info) {
assert(S);
assert(Parent);
return new (Ctx)
TypeRefinementContext(Ctx, IntroNode(S, /*IsThen=*/true), Parent,
S->getThenStmt()->getSourceRange(), Info);
}
TypeRefinementContext *
TypeRefinementContext::createForIfStmtElse(ASTContext &Ctx, IfStmt *S,
TypeRefinementContext *Parent,
const AvailabilityContext &Info) {
assert(S);
assert(Parent);
return new (Ctx)
TypeRefinementContext(Ctx, IntroNode(S, /*IsThen=*/false), Parent,
S->getElseStmt()->getSourceRange(), Info);
}
TypeRefinementContext *
TypeRefinementContext::createForConditionFollowingQuery(ASTContext &Ctx,
PoundAvailableInfo *PAI,
const StmtConditionElement &LastElement,
TypeRefinementContext *Parent,
const AvailabilityContext &Info) {
assert(PAI);
assert(Parent);
SourceRange Range(PAI->getEndLoc(), LastElement.getEndLoc());
return new (Ctx) TypeRefinementContext(Ctx, PAI, Parent, Range,
Info);
}
TypeRefinementContext *
TypeRefinementContext::createForGuardStmtFallthrough(ASTContext &Ctx,
GuardStmt *RS,
BraceStmt *ContainingBraceStmt,
TypeRefinementContext *Parent,
const AvailabilityContext &Info) {
assert(RS);
assert(ContainingBraceStmt);
assert(Parent);
SourceRange Range(RS->getEndLoc(), ContainingBraceStmt->getEndLoc());
return new (Ctx) TypeRefinementContext(Ctx,
IntroNode(RS, /*IsFallthrough=*/true),
Parent, Range, Info);
}
TypeRefinementContext *
TypeRefinementContext::createForGuardStmtElse(ASTContext &Ctx, GuardStmt *RS,
TypeRefinementContext *Parent,
const AvailabilityContext &Info) {
assert(RS);
assert(Parent);
return new (Ctx)
TypeRefinementContext(Ctx, IntroNode(RS, /*IsFallthrough=*/false), Parent,
RS->getBody()->getSourceRange(), Info);
}
TypeRefinementContext *
TypeRefinementContext::createForWhileStmtBody(ASTContext &Ctx, WhileStmt *S,
TypeRefinementContext *Parent,
const AvailabilityContext &Info) {
assert(S);
assert(Parent);
return new (Ctx) TypeRefinementContext(
Ctx, S, Parent, S->getBody()->getSourceRange(), Info);
}
// Only allow allocation of TypeRefinementContext using the allocator in
// ASTContext.
void *TypeRefinementContext::operator new(size_t Bytes, ASTContext &C,
unsigned Alignment) {
return C.Allocate(Bytes, Alignment);
}
TypeRefinementContext *
TypeRefinementContext::findMostRefinedSubContext(SourceLoc Loc,
SourceManager &SM) {
assert(Loc.isValid());
if (SrcRange.isValid() && !SM.rangeContainsTokenLoc(SrcRange, Loc))
return nullptr;
// For the moment, we perform a linear search here, but we can and should
// do something more efficient.
for (TypeRefinementContext *Child : Children) {
if (auto *Found = Child->findMostRefinedSubContext(Loc, SM)) {
return Found;
}
}
// Loc is in this context's range but not in any child's, so this context
// must be the inner-most context.
return this;
}
void TypeRefinementContext::dump(SourceManager &SrcMgr) const {
dump(llvm::errs(), SrcMgr);
}
void TypeRefinementContext::dump(raw_ostream &OS, SourceManager &SrcMgr) const {
print(OS, SrcMgr, 0);
OS << '\n';
}
SourceLoc TypeRefinementContext::getIntroductionLoc() const {
switch (getReason()) {
case Reason::Decl:
return Node.getAsDecl()->getLoc();
case Reason::IfStmtThenBranch:
case Reason::IfStmtElseBranch:
return Node.getAsIfStmt()->getIfLoc();
case Reason::ConditionFollowingAvailabilityQuery:
return Node.getAsPoundAvailableInfo()->getStartLoc();
case Reason::GuardStmtFallthrough:
case Reason::GuardStmtElseBranch:
return Node.getAsGuardStmt()->getGuardLoc();
case Reason::WhileStmtBody:
return Node.getAsWhileStmt()->getStartLoc();
case Reason::Root:
return SourceLoc();
}
llvm_unreachable("Unhandled Reason in switch.");
}
static SourceRange
getAvailabilityConditionVersionSourceRange(const PoundAvailableInfo *PAI,
PlatformKind Platform,
const llvm::VersionTuple &Version) {
SourceRange Range;
for (auto *S : PAI->getQueries()) {
if (auto *V = dyn_cast<PlatformVersionConstraintAvailabilitySpec>(S)) {
if (V->getPlatform() == Platform && V->getVersion() == Version) {
// More than one: return invalid range, no unique choice.
if (Range.isValid())
return SourceRange();
else
Range = V->getVersionSrcRange();
}
}
}
return Range;
}
static SourceRange
getAvailabilityConditionVersionSourceRange(
const MutableArrayRef<StmtConditionElement> &Conds,
PlatformKind Platform,
const llvm::VersionTuple &Version) {
SourceRange Range;
for (auto const& C : Conds) {
if (C.getKind() == StmtConditionElement::CK_Availability) {
SourceRange R = getAvailabilityConditionVersionSourceRange(
C.getAvailability(), Platform, Version);
// More than one: return invalid range.
if (Range.isValid())
return SourceRange();
else
Range = R;
}
}
return Range;
}
static SourceRange
getAvailabilityConditionVersionSourceRange(const DeclAttributes &DeclAttrs,
PlatformKind Platform,
const llvm::VersionTuple &Version) {
SourceRange Range;
for (auto *Attr : DeclAttrs) {
if (auto *AA = dyn_cast<AvailableAttr>(Attr)) {
if (AA->Introduced.hasValue() &&
AA->Introduced.getValue() == Version &&
AA->Platform == Platform) {
// More than one: return invalid range.
if (Range.isValid())
return SourceRange();
else
Range = AA->IntroducedRange;
}
}
}
return Range;
}
SourceRange
TypeRefinementContext::getAvailabilityConditionVersionSourceRange(
PlatformKind Platform,
const llvm::VersionTuple &Version) const {
switch (getReason()) {
case Reason::Decl:
return ::getAvailabilityConditionVersionSourceRange(
Node.getAsDecl()->getAttrs(), Platform, Version);
case Reason::IfStmtThenBranch:
case Reason::IfStmtElseBranch:
return ::getAvailabilityConditionVersionSourceRange(
Node.getAsIfStmt()->getCond(), Platform, Version);
case Reason::ConditionFollowingAvailabilityQuery:
return ::getAvailabilityConditionVersionSourceRange(
Node.getAsPoundAvailableInfo(), Platform, Version);
case Reason::GuardStmtFallthrough:
case Reason::GuardStmtElseBranch:
return ::getAvailabilityConditionVersionSourceRange(
Node.getAsGuardStmt()->getCond(), Platform, Version);
case Reason::WhileStmtBody:
return ::getAvailabilityConditionVersionSourceRange(
Node.getAsWhileStmt()->getCond(), Platform, Version);
case Reason::Root:
return SourceRange();
}
llvm_unreachable("Unhandled Reason in switch.");
}
void TypeRefinementContext::print(raw_ostream &OS, SourceManager &SrcMgr,
unsigned Indent) const {
OS.indent(Indent);
OS << "(" << getReasonName(getReason());
OS << " versions=" << AvailabilityInfo.getOSVersion().getAsString();
if (getReason() == Reason::Decl) {
Decl *D = Node.getAsDecl();
OS << " decl=";
if (auto VD = dyn_cast<ValueDecl>(D)) {
OS << VD->getFullName();
} else if (auto *ED = dyn_cast<ExtensionDecl>(D)) {
OS << "extension." << ED->getExtendedType().getString();
}
}
auto R = getSourceRange();
if (R.isValid()) {
OS << " src_range=";
R.print(OS, SrcMgr, /*PrintText=*/false);
}
for (TypeRefinementContext *Child : Children) {
OS << '\n';
Child->print(OS, SrcMgr, Indent + 2);
}
OS.indent(Indent);
OS << ")";
}
TypeRefinementContext::Reason TypeRefinementContext::getReason() const {
return Node.getReason();
}
StringRef TypeRefinementContext::getReasonName(Reason R) {
switch (R) {
case Reason::Root:
return "root";
case Reason::Decl:
return "decl";
case Reason::IfStmtThenBranch:
return "if_then";
case Reason::IfStmtElseBranch:
return "if_else";
case Reason::ConditionFollowingAvailabilityQuery:
return "condition_following_availability";
case Reason::GuardStmtFallthrough:
return "guard_fallthrough";
case Reason::GuardStmtElseBranch:
return "guard_else";
case Reason::WhileStmtBody:
return "while_body";
}
llvm_unreachable("Unhandled Reason in switch.");
}