//===--- Stmt.cpp - Swift Language Statement ASTs -------------------------===// // // This source file is part of the Swift.org open source project // // Copyright (c) 2014 - 2015 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) { memcpy(getElementsStorage(), elts.data(), elts.size() * sizeof(ASTNode)); } 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(sizeof(BraceStmt) + elts.size() * sizeof(ASTNode), 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 DeferStmt::getEndLoc() const { return getPatternBinding()->getEndLoc(); } /// Dig the original users's body of the defer out for AST fidelity. BraceStmt *DeferStmt::getBodyAsWritten() const { return cast(tempDecl->getParentInitializer())->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!"); } DoCatchStmt *DoCatchStmt::create(ASTContext &ctx, LabeledStmtInfo labelInfo, SourceLoc doLoc, Stmt *body, ArrayRef catches, Optional implicit) { void *mem = ctx.Allocate(sizeof(DoCatchStmt) + catches.size() * sizeof(catches[0]), 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; } bool CatchStmt::isSyntacticallyExhaustive() const { // It cannot have a guard expression. if (getGuardExpr()) return false; // Ignore 'var', 'let', and parens. auto pattern = getErrorPattern()->getSemanticsProvidingPattern(); // We might be calling this on an non-type-checked pattern. // Deal with some untranslated cases here. if (auto exprPattern = dyn_cast(pattern)) { // If the pattern has a registered match expression, it's // a type-checked ExprPattern. if (exprPattern->getMatchExpr()) return false; auto expr = exprPattern->getSubExpr(); while (true) { // Drill into parens. if (auto parens = dyn_cast(expr)) { expr = parens->getSubExpr(); // A '_' is an untranslated AnyPattern. } else if (isa(expr)) { return true; // Everything else is non-exhaustive. } else { return false; } } } // Must be '_' or a variable binding. We do not want to allow tuple // patterns: in an existential context, those are potentially // refutable patterns (assuming tuples can someday conform to // protocols). return (isa(pattern) || isa(pattern)); } SourceRange StmtConditionElement::getSourceRange() const { if (auto *E = getConditionOrNull()) return E->getSourceRange(); SourceLoc Start; if (IntroducerLoc.isValid()) Start = IntroducerLoc; else Start = getPattern()->getStartLoc(); return SourceRange(Start, getInitializer()->getEndLoc()); } SourceLoc StmtConditionElement::getStartLoc() const { if (auto *E = getConditionOrNull()) return E->getStartLoc(); if (IntroducerLoc.isValid()) return IntroducerLoc; return getPattern()->getStartLoc(); } SourceLoc StmtConditionElement::getEndLoc() const { if (auto *E = getConditionOrNull()) return E->getEndLoc(); 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{ getCaseLabelItemsBuffer(), NumPatterns }; for (unsigned i = 0; i < NumPatterns; ++i) { new (&Items[i]) CaseLabelItem(CaseLabelItems[i]); } } CaseStmt *CaseStmt::create(ASTContext &C, SourceLoc CaseLoc, ArrayRef CaseLabelItems, bool HasBoundDecls, SourceLoc ColonLoc, Stmt *Body, Optional Implicit) { void *Mem = C.Allocate(sizeof(CaseStmt) + CaseLabelItems.size() * sizeof(CaseLabelItem), 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(sizeof(SwitchStmt) + Cases.size() * sizeof(SwitchStmt*), alignof(SwitchStmt)); SwitchStmt *theSwitch = ::new (p) SwitchStmt(LabelInfo, SwitchLoc, SubjectExpr, LBraceLoc, Cases.size(), RBraceLoc); memcpy(theSwitch->getCaseBuffer(), Cases.data(), Cases.size() * sizeof(CaseStmt*)); return theSwitch; }