//===--- Stmt.cpp - Swift Language Statement ASTs -------------------------===// // // This source file is part of the Swift.org open source project // // Copyright (c) 2014 - 2016 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See http://swift.org/LICENSE.txt for license information // See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors // //===----------------------------------------------------------------------===// // // This file implements the Stmt class and subclasses. // //===----------------------------------------------------------------------===// #include "swift/AST/Stmt.h" #include "swift/AST/ASTContext.h" #include "swift/AST/Decl.h" #include "swift/AST/Expr.h" #include "swift/AST/Pattern.h" #include "llvm/ADT/PointerUnion.h" using namespace swift; //===----------------------------------------------------------------------===// // Stmt methods. //===----------------------------------------------------------------------===// // Only allow allocation of Stmts using the allocator in ASTContext. void *Stmt::operator new(size_t Bytes, ASTContext &C, unsigned Alignment) { return C.Allocate(Bytes, Alignment); } StringRef Stmt::getKindName(StmtKind K) { switch (K) { #define STMT(Id, Parent) case StmtKind::Id: return #Id; #include "swift/AST/StmtNodes.def" } llvm_unreachable("bad StmtKind"); } // Helper functions to check statically whether a method has been // overridden from its implementation in Stmt. The sort of thing you // need when you're avoiding v-tables. namespace { template constexpr bool isOverriddenFromStmt(ReturnType (Class::*)() const) { return true; } template constexpr bool isOverriddenFromStmt(ReturnType (Stmt::*)() const) { return false; } template struct Dispatch; /// Dispatch down to a concrete override. template <> struct Dispatch { template static SourceLoc getStartLoc(const T *S) { return S->getStartLoc(); } template static SourceLoc getEndLoc(const T *S) { return S->getEndLoc(); } template static SourceRange getSourceRange(const T *S) { return S->getSourceRange(); } }; /// Default implementations for when a method isn't overridden. template <> struct Dispatch { template static SourceLoc getStartLoc(const T *S) { return S->getSourceRange().Start; } template static SourceLoc getEndLoc(const T *S) { return S->getSourceRange().End; } template static SourceRange getSourceRange(const T *S) { return { S->getStartLoc(), S->getEndLoc() }; } }; } template static SourceRange getSourceRangeImpl(const T *S) { static_assert(isOverriddenFromStmt(&T::getSourceRange) || (isOverriddenFromStmt(&T::getStartLoc) && isOverriddenFromStmt(&T::getEndLoc)), "Stmt subclass must implement either getSourceRange() " "or getStartLoc()/getEndLoc()"); return Dispatch::getSourceRange(S); } SourceRange Stmt::getSourceRange() const { switch (getKind()) { #define STMT(ID, PARENT) \ case StmtKind::ID: return getSourceRangeImpl(cast(this)); #include "swift/AST/StmtNodes.def" } llvm_unreachable("statement type not handled!"); } template static SourceLoc getStartLocImpl(const T *S) { return Dispatch::getStartLoc(S); } SourceLoc Stmt::getStartLoc() const { switch (getKind()) { #define STMT(ID, PARENT) \ case StmtKind::ID: return getStartLocImpl(cast(this)); #include "swift/AST/StmtNodes.def" } llvm_unreachable("statement type not handled!"); } template static SourceLoc getEndLocImpl(const T *S) { return Dispatch::getEndLoc(S); } SourceLoc Stmt::getEndLoc() const { switch (getKind()) { #define STMT(ID, PARENT) \ case StmtKind::ID: return getEndLocImpl(cast(this)); #include "swift/AST/StmtNodes.def" } llvm_unreachable("statement type not handled!"); } BraceStmt::BraceStmt(SourceLoc lbloc, ArrayRef elts, SourceLoc rbloc, Optional implicit) : Stmt(StmtKind::Brace, getDefaultImplicitFlag(implicit, lbloc)), NumElements(elts.size()), LBLoc(lbloc), RBLoc(rbloc) { std::uninitialized_copy(elts.begin(), elts.end(), getTrailingObjects()); } BraceStmt *BraceStmt::create(ASTContext &ctx, SourceLoc lbloc, ArrayRef elts, SourceLoc rbloc, Optional implicit) { assert(std::none_of(elts.begin(), elts.end(), [](ASTNode node) -> bool { return node.isNull(); }) && "null element in BraceStmt"); void *Buffer = ctx.Allocate(totalSizeToAlloc(elts.size()), alignof(BraceStmt)); return ::new(Buffer) BraceStmt(lbloc, elts, rbloc, implicit); } SourceLoc ReturnStmt::getStartLoc() const { if (ReturnLoc.isInvalid() && Result) return Result->getStartLoc(); return ReturnLoc; } SourceLoc ReturnStmt::getEndLoc() const { return (Result ? Result->getEndLoc() : ReturnLoc); } SourceLoc ThrowStmt::getEndLoc() const { return SubExpr->getEndLoc(); } SourceLoc DeferStmt::getEndLoc() const { return tempDecl->getBody()->getEndLoc(); } /// Dig the original user's body of the defer out for AST fidelity. BraceStmt *DeferStmt::getBodyAsWritten() const { return tempDecl->getBody(); } bool LabeledStmt::isPossibleContinueTarget() const { switch (getKind()) { #define LABELED_STMT(ID, PARENT) #define STMT(ID, PARENT) case StmtKind::ID: #include "swift/AST/StmtNodes.def" llvm_unreachable("not a labeled statement"); // Sema has diagnostics with hard-coded expectations about what // statements return false from this method. case StmtKind::If: case StmtKind::Guard: case StmtKind::Switch: return false; case StmtKind::Do: case StmtKind::DoCatch: case StmtKind::RepeatWhile: case StmtKind::For: case StmtKind::ForEach: case StmtKind::While: return true; } llvm_unreachable("statement kind unhandled!"); } bool LabeledStmt::requiresLabelOnJump() const { switch (getKind()) { #define LABELED_STMT(ID, PARENT) #define STMT(ID, PARENT) case StmtKind::ID: #include "swift/AST/StmtNodes.def" llvm_unreachable("not a labeled statement"); case StmtKind::If: case StmtKind::Do: case StmtKind::DoCatch: case StmtKind::Guard: // Guard doesn't allow labels, so no break/continue. return true; case StmtKind::RepeatWhile: case StmtKind::For: case StmtKind::ForEach: case StmtKind::Switch: case StmtKind::While: return false; } llvm_unreachable("statement kind unhandled!"); } void ForEachStmt::setPattern(Pattern *p) { Pat = p; Pat->markOwnedByStatement(this); } void CatchStmt::setErrorPattern(Pattern *pattern) { ErrorPattern = pattern; ErrorPattern->markOwnedByStatement(this); } DoCatchStmt *DoCatchStmt::create(ASTContext &ctx, LabeledStmtInfo labelInfo, SourceLoc doLoc, Stmt *body, ArrayRef catches, Optional implicit) { void *mem = ctx.Allocate(totalSizeToAlloc(catches.size()), alignof(DoCatchStmt)); return ::new (mem) DoCatchStmt(labelInfo, doLoc, body, catches, implicit); } bool DoCatchStmt::isSyntacticallyExhaustive() const { for (auto clause : getCatches()) { if (clause->isSyntacticallyExhaustive()) return true; } return false; } void LabeledConditionalStmt::setCond(StmtCondition e) { // When set a condition into a Conditional Statement, inform each of the // variables bound in any patterns that this is the owning statement for the // pattern. for (auto &elt : e) if (auto pat = elt.getPatternOrNull()) pat->markOwnedByStatement(this); Cond = e; } bool CatchStmt::isSyntacticallyExhaustive() const { // It cannot have a guard expression and the pattern cannot be refutable. return getGuardExpr() == nullptr && !getErrorPattern()->isRefutablePattern(); } PoundAvailableInfo *PoundAvailableInfo::create(ASTContext &ctx, SourceLoc PoundLoc, ArrayRef queries, SourceLoc RParenLoc) { unsigned size = totalSizeToAlloc(queries.size()); void *Buffer = ctx.Allocate(size, alignof(PoundAvailableInfo)); return ::new (Buffer) PoundAvailableInfo(PoundLoc, queries, RParenLoc); } SourceLoc PoundAvailableInfo::getEndLoc() const { if (RParenLoc.isInvalid()) { if (NumQueries == 0) { return PoundLoc; } return getQueries()[NumQueries - 1]->getSourceRange().End; } return RParenLoc; } void PoundAvailableInfo:: getPlatformKeywordRanges(SmallVectorImpl &PlatformRanges) { for (unsigned i = 0; i < NumQueries; i++) { auto *VersionSpec = dyn_cast(getQueries()[i]); if (!VersionSpec) continue; auto Loc = VersionSpec->getPlatformLoc(); auto Platform = VersionSpec->getPlatform(); switch (Platform) { case PlatformKind::none: break; #define AVAILABILITY_PLATFORM(X, PrettyName) \ case PlatformKind::X: \ PlatformRanges.push_back(CharSourceRange(Loc, strlen(#X))); \ break; #include "swift/AST/PlatformKinds.def" } } } SourceRange StmtConditionElement::getSourceRange() const { switch (getKind()) { case StmtConditionElement::CK_Boolean: return getBoolean()->getSourceRange(); case StmtConditionElement::CK_Availability: return getAvailability()->getSourceRange(); case StmtConditionElement::CK_PatternBinding: SourceLoc Start; if (IntroducerLoc.isValid()) Start = IntroducerLoc; else Start = getPattern()->getStartLoc(); return SourceRange(Start, getInitializer()->getEndLoc()); } } SourceLoc StmtConditionElement::getStartLoc() const { switch (getKind()) { case StmtConditionElement::CK_Boolean: return getBoolean()->getStartLoc(); case StmtConditionElement::CK_Availability: return getAvailability()->getStartLoc(); case StmtConditionElement::CK_PatternBinding: if (IntroducerLoc.isValid()) return IntroducerLoc; return getPattern()->getStartLoc(); } } SourceLoc StmtConditionElement::getEndLoc() const { switch (getKind()) { case StmtConditionElement::CK_Boolean: return getBoolean()->getEndLoc(); case StmtConditionElement::CK_Availability: return getAvailability()->getEndLoc(); case StmtConditionElement::CK_PatternBinding: return getInitializer()->getEndLoc(); } } static StmtCondition exprToCond(Expr *C, ASTContext &Ctx) { StmtConditionElement Arr[] = { StmtConditionElement(C) }; return Ctx.AllocateCopy(Arr); } IfStmt::IfStmt(SourceLoc IfLoc, Expr *Cond, Stmt *Then, SourceLoc ElseLoc, Stmt *Else, Optional implicit, ASTContext &Ctx) : IfStmt(LabeledStmtInfo(), IfLoc, exprToCond(Cond, Ctx), Then, ElseLoc, Else, implicit) { } GuardStmt::GuardStmt(SourceLoc GuardLoc, Expr *Cond, Stmt *Body, Optional implicit, ASTContext &Ctx) : GuardStmt(GuardLoc, exprToCond(Cond, Ctx), Body, implicit) { } SourceLoc RepeatWhileStmt::getEndLoc() const { return Cond->getEndLoc(); } SourceRange CaseLabelItem::getSourceRange() const { if (auto *E = getGuardExpr()) return { CasePattern->getStartLoc(), E->getEndLoc() }; return CasePattern->getSourceRange(); } SourceLoc CaseLabelItem::getStartLoc() const { return CasePattern->getStartLoc(); } SourceLoc CaseLabelItem::getEndLoc() const { if (auto *E = getGuardExpr()) return E->getEndLoc(); return CasePattern->getEndLoc(); } CaseStmt::CaseStmt(SourceLoc CaseLoc, ArrayRef CaseLabelItems, bool HasBoundDecls, SourceLoc ColonLoc, Stmt *Body, Optional Implicit) : Stmt(StmtKind::Case, getDefaultImplicitFlag(Implicit, CaseLoc)), CaseLoc(CaseLoc), ColonLoc(ColonLoc), BodyAndHasBoundDecls(Body, HasBoundDecls), NumPatterns(CaseLabelItems.size()) { assert(NumPatterns > 0 && "case block must have at least one pattern"); MutableArrayRef Items{ getTrailingObjects(), NumPatterns }; for (unsigned i = 0; i < NumPatterns; ++i) { new (&Items[i]) CaseLabelItem(CaseLabelItems[i]); Items[i].getPattern()->markOwnedByStatement(this); } } CaseStmt *CaseStmt::create(ASTContext &C, SourceLoc CaseLoc, ArrayRef CaseLabelItems, bool HasBoundDecls, SourceLoc ColonLoc, Stmt *Body, Optional Implicit) { void *Mem = C.Allocate(totalSizeToAlloc(CaseLabelItems.size()), alignof(CaseStmt)); return ::new (Mem) CaseStmt(CaseLoc, CaseLabelItems, HasBoundDecls, ColonLoc, Body, Implicit); } SwitchStmt *SwitchStmt::create(LabeledStmtInfo LabelInfo, SourceLoc SwitchLoc, Expr *SubjectExpr, SourceLoc LBraceLoc, ArrayRef Cases, SourceLoc RBraceLoc, ASTContext &C) { void *p = C.Allocate(totalSizeToAlloc(Cases.size()), alignof(SwitchStmt)); SwitchStmt *theSwitch = ::new (p) SwitchStmt(LabelInfo, SwitchLoc, SubjectExpr, LBraceLoc, Cases.size(), RBraceLoc); std::uninitialized_copy(Cases.begin(), Cases.end(), theSwitch->getTrailingObjects()); return theSwitch; }