//===--- Stmt.h - Swift Language Statement ASTs -----------------*- 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 Stmt class and subclasses. // //===----------------------------------------------------------------------===// #ifndef SWIFT_AST_STMT_H #define SWIFT_AST_STMT_H #include "swift/AST/ASTAllocated.h" #include "swift/AST/ASTNode.h" #include "swift/AST/AvailabilityQuery.h" #include "swift/AST/AvailabilityRange.h" #include "swift/AST/ConcreteDeclRef.h" #include "swift/AST/IfConfigClause.h" #include "swift/AST/ThrownErrorDestination.h" #include "swift/AST/TypeAlignments.h" #include "swift/AST/TypeLoc.h" #include "swift/Basic/Debug.h" #include "swift/Basic/NullablePtr.h" #include "llvm/Support/TrailingObjects.h" #include namespace swift { class AnyPattern; class ASTContext; class ASTWalker; class AvailabilitySpec; class Decl; class DeclContext; class Evaluator; class Expr; class FuncDecl; class AbstractFunctionDecl; class Pattern; class PatternBindingDecl; class SemanticAvailabilitySpecs; class VarDecl; class CaseStmt; class DoCatchStmt; class IsSingleValueStmtResult; class SwitchStmt; enum class StmtKind { #define STMT(ID, PARENT) ID, #define LAST_STMT(ID) Last_Stmt = ID, #define STMT_RANGE(Id, FirstId, LastId) \ First_##Id##Stmt = FirstId, Last_##Id##Stmt = LastId, #include "swift/AST/StmtNodes.def" }; enum : unsigned { NumStmtKindBits = countBitsUsed(static_cast(StmtKind::Last_Stmt)) }; /// Stmt - Base class for all statements in swift. class alignas(8) Stmt : public ASTAllocated { Stmt(const Stmt&) = delete; Stmt& operator=(const Stmt&) = delete; protected: // clang-format off union { uint64_t OpaqueBits; SWIFT_INLINE_BITFIELD_BASE(Stmt, bitmax(NumStmtKindBits,8) + 1, /// Kind - The subclass of Stmt that this is. Kind : bitmax(NumStmtKindBits,8), /// Implicit - Whether this statement is implicit. Implicit : 1 ); SWIFT_INLINE_BITFIELD_FULL(BraceStmt, Stmt, 32, : NumPadBits, NumElements : 32 ); SWIFT_INLINE_BITFIELD_FULL(CaseStmt, Stmt, 16+32, : NumPadBits, NumCaseBodyVars : 16, NumPatterns : 32 ); SWIFT_INLINE_BITFIELD_EMPTY(LabeledStmt, Stmt); SWIFT_INLINE_BITFIELD_FULL(DoCatchStmt, LabeledStmt, 32, : NumPadBits, NumCatches : 32 ); SWIFT_INLINE_BITFIELD_FULL(SwitchStmt, LabeledStmt, 32, : NumPadBits, CaseCount : 32 ); SWIFT_INLINE_BITFIELD(ReturnStmt, Stmt, 1, /// Whether the result is an implied return, e.g for an implicit single /// expression return. IsImplied : 1 ); SWIFT_INLINE_BITFIELD_FULL(YieldStmt, Stmt, 32, : NumPadBits, NumYields : 32 ); } Bits; // clang-format on /// Return the given value for the 'implicit' flag if present, or if None, /// return true if the location is invalid. static bool getDefaultImplicitFlag(std::optional implicit, SourceLoc keyLoc) { return implicit.has_value() ? *implicit : keyLoc.isInvalid(); } public: Stmt(StmtKind kind, bool implicit) { Bits.OpaqueBits = 0; Bits.Stmt.Kind = static_cast(kind); Bits.Stmt.Implicit = implicit; } StmtKind getKind() const { return StmtKind(Bits.Stmt.Kind); } /// Retrieve the name of the given statement kind. /// /// This name should only be used for debugging dumps and other /// developer aids, and should never be part of a diagnostic or exposed /// to the user of the compiler in any way. static StringRef getKindName(StmtKind kind); /// Retrieve the descriptive kind name for a given statement. This is suitable /// for use in diagnostics. static StringRef getDescriptiveKindName(StmtKind K); /// Return the location of the start of the statement. SourceLoc getStartLoc() const; /// Return the location of the end of the statement. SourceLoc getEndLoc() const; SourceRange getSourceRange() const; SourceLoc TrailingSemiLoc; /// Whether the statement can produce a single value, and as such may be /// treated as an expression. IsSingleValueStmtResult mayProduceSingleValue(ASTContext &ctx) const; /// isImplicit - Determines whether this statement was implicitly-generated, /// rather than explicitly written in the AST. bool isImplicit() const { return Bits.Stmt.Implicit; } /// walk - This recursively walks the AST rooted at this statement. Stmt *walk(ASTWalker &walker); Stmt *walk(ASTWalker &&walker) { return walk(walker); } SWIFT_DEBUG_DUMP; void dump(raw_ostream &OS, const ASTContext *Ctx = nullptr, unsigned Indent = 0) const; }; /// BraceStmt - A brace enclosed sequence of expressions, stmts, or decls, like /// { var x = 10; print(10) }. class BraceStmt final : public Stmt, private llvm::TrailingObjects { friend TrailingObjects; SourceLoc LBLoc; SourceLoc RBLoc; BraceStmt(SourceLoc lbloc, ArrayRef elements, SourceLoc rbloc, std::optional implicit); public: static BraceStmt *create(ASTContext &ctx, SourceLoc lbloc, ArrayRef elements, SourceLoc rbloc, std::optional implicit = std::nullopt); static BraceStmt *createImplicit(ASTContext &ctx, ArrayRef elements) { return create(ctx, /*lbloc=*/SourceLoc(), elements, /*rbloc=*/SourceLoc(), /*implicit=*/true); } SourceLoc getLBraceLoc() const { return LBLoc; } SourceLoc getRBraceLoc() const { return RBLoc; } SourceLoc getStartLoc() const; SourceLoc getEndLoc() const; SourceLoc getContentStartLoc() const; SourceLoc getContentEndLoc() const; /// The range of the brace statement without the braces. SourceRange getContentRange() const { return {getContentStartLoc(), getContentEndLoc()}; } bool empty() const { return getNumElements() == 0; } unsigned getNumElements() const { return Bits.BraceStmt.NumElements; } ASTNode getFirstElement() const { return getElements().front(); } ASTNode getLastElement() const { return getElements().back(); } void setFirstElement(ASTNode node) { getElements().front() = node; } void setLastElement(ASTNode node) { getElements().back() = node; } /// The elements contained within the BraceStmt. MutableArrayRef getElements() { return {getTrailingObjects(), static_cast(Bits.BraceStmt.NumElements)}; } /// The elements contained within the BraceStmt (const version). ArrayRef getElements() const { return {getTrailingObjects(), static_cast(Bits.BraceStmt.NumElements)}; } ASTNode findAsyncNode(); /// If this brace contains a single ASTNode, or a \c #if that has a single active /// element, returns it. This will always be the last element of the brace. /// Otherwise returns \c nullptr. ASTNode getSingleActiveElement() const; /// If this brace is wrapping a single active expression, returns it. This /// includes both a single expression element, or a single expression in an /// active \c #if. Otherwise returns \c nullptr. Expr *getSingleActiveExpression() const; /// If this brace is wrapping a single active statement, returns it. This /// includes both a single statement element, or a single statement in an /// active \c #if. Otherwise returns \c nullptr. Stmt *getSingleActiveStatement() const; static bool classof(const Stmt *S) { return S->getKind() == StmtKind::Brace; } }; /// ReturnStmt - A return statement. The result is optional; "return" without /// an expression is semantically equivalent to "return ()". /// return 42 class ReturnStmt : public Stmt { SourceLoc ReturnLoc; Expr *Result; ReturnStmt(SourceLoc returnLoc, Expr *result, bool isImplicit) : Stmt(StmtKind::Return, isImplicit), ReturnLoc(returnLoc), Result(result) { Bits.ReturnStmt.IsImplied = false; } public: static ReturnStmt *createParsed(ASTContext &ctx, SourceLoc returnLoc, Expr *result) { return new (ctx) ReturnStmt(returnLoc, result, /*isImplicit*/ false); } static ReturnStmt *createImplicit(ASTContext &ctx, SourceLoc returnLoc, Expr *result) { return new (ctx) ReturnStmt(returnLoc, result, /*isImplicit*/ true); } static ReturnStmt *createImplicit(ASTContext &ctx, Expr *result) { return createImplicit(ctx, SourceLoc(), result); } /// Create an implicit implied ReturnStmt for e.g a single expression body. static ReturnStmt *createImplied(ASTContext &ctx, Expr *result) { assert(result && "Result must be present to be implied"); auto *RS = createImplicit(ctx, result); RS->Bits.ReturnStmt.IsImplied = true; return RS; } SourceLoc getReturnLoc() const { return ReturnLoc; } SourceLoc getStartLoc() const; SourceLoc getEndLoc() const; /// Whether the result is an implied return, e.g for an implicit single /// expression return. bool isImplied() const { return Bits.ReturnStmt.IsImplied; } bool hasResult() const { return Result != 0; } Expr *getResult() const { assert(Result && "ReturnStmt doesn't have a result"); return Result; } void setResult(Expr *e) { Result = e; } static bool classof(const Stmt *S) { return S->getKind() == StmtKind::Return;} }; /// YieldStmt - A yield statement. The yield-values sequence is not optional, /// but the parentheses are. /// yield 42 class YieldStmt final : public Stmt, private llvm::TrailingObjects { friend TrailingObjects; SourceLoc YieldLoc; SourceLoc LPLoc; SourceLoc RPLoc; YieldStmt(SourceLoc yieldLoc, SourceLoc lpLoc, ArrayRef yields, SourceLoc rpLoc, std::optional implicit = std::nullopt) : Stmt(StmtKind::Yield, getDefaultImplicitFlag(implicit, yieldLoc)), YieldLoc(yieldLoc), LPLoc(lpLoc), RPLoc(rpLoc) { Bits.YieldStmt.NumYields = yields.size(); memcpy(getMutableYields().data(), yields.data(), yields.size() * sizeof(Expr*)); } public: static YieldStmt *create(const ASTContext &ctx, SourceLoc yieldLoc, SourceLoc lp, ArrayRef yields, SourceLoc rp, std::optional implicit = std::nullopt); SourceLoc getYieldLoc() const { return YieldLoc; } SourceLoc getLParenLoc() const { return LPLoc; } SourceLoc getRParenLoc() const { return RPLoc; } SourceLoc getStartLoc() const { return YieldLoc; } SourceLoc getEndLoc() const; ArrayRef getYields() const { return {getTrailingObjects(), static_cast(Bits.YieldStmt.NumYields)}; } MutableArrayRef getMutableYields() { return {getTrailingObjects(), static_cast(Bits.YieldStmt.NumYields)}; } static bool classof(const Stmt *S) { return S->getKind() == StmtKind::Yield; } }; /// The statement `then `. This is used within if/switch expressions to /// indicate the value being produced by a given branch. class ThenStmt : public Stmt { SourceLoc ThenLoc; Expr *Result; ThenStmt(SourceLoc thenLoc, Expr *result, bool isImplicit) : Stmt(StmtKind::Then, isImplicit), ThenLoc(thenLoc), Result(result) { assert(Result && "Must have non-null result"); } public: /// Create a new parsed ThenStmt. static ThenStmt *createParsed(ASTContext &ctx, SourceLoc thenLoc, Expr *result); /// Create an implicit ThenStmt. /// /// Note that such statements will be elided during the result builder /// transform. static ThenStmt *createImplicit(ASTContext &ctx, Expr *result); SourceLoc getThenLoc() const { return ThenLoc; } SourceRange getSourceRange() const; Expr *getResult() const { return Result; } void setResult(Expr *e) { Result = e; } static bool classof(const Stmt *S) { return S->getKind() == StmtKind::Then; } }; /// DeferStmt - A 'defer' statement. This runs the substatement it contains /// when the enclosing scope is exited. /// /// defer { cleanUp() } /// /// The AST representation for a defer statement is a bit weird. We model this /// as if they wrote: /// /// func tmpClosure() { body } /// tmpClosure() // This is emitted on each path that needs to run this. /// /// As such, the body of the 'defer' is actually type checked within the /// closure's DeclContext. We do this because of unfortunateness in SILGen, /// some expressions (e.g. OpenExistentialExpr) cannot be multiply emitted in a /// composable way. When this gets fixed, patches r27767 and r27768 can be /// reverted to go back to the simpler and more obvious representation. /// class DeferStmt : public Stmt { SourceLoc DeferLoc; /// This is the bound temp function. FuncDecl *tempDecl; /// This is the invocation of the closure, which is to be emitted on any error /// paths. Expr *callExpr; DeferStmt(SourceLoc DeferLoc, FuncDecl *tempDecl, Expr *callExpr) : Stmt(StmtKind::Defer, /*implicit*/false), DeferLoc(DeferLoc), tempDecl(tempDecl), callExpr(callExpr) {} public: /// Create a 'defer' statement. This automatically creates the "temp decl" and /// the call expression. It's the caller's responsibility to populate the /// body of the func decl. static DeferStmt *create(DeclContext *dc, SourceLoc deferLoc); SourceLoc getDeferLoc() const { return DeferLoc; } SourceLoc getStartLoc() const { return DeferLoc; } SourceLoc getEndLoc() const; FuncDecl *getTempDecl() const { return tempDecl; } Expr *getCallExpr() const { return callExpr; } void setCallExpr(Expr *E) { callExpr = E; } /// Dig the original user's body of the defer out for AST fidelity. BraceStmt *getBodyAsWritten() const; static bool classof(const Stmt *S) { return S->getKind() == StmtKind::Defer; } }; /// Represent `let`/`var` optional binding, or `case` pattern matching in /// conditional statements (i.e. `if`, `guard`, `while`). class alignas(8) ConditionalPatternBindingInfo : public ASTAllocated { /// Location of the var/let/case keyword. SourceLoc IntroducerLoc; /// Pattern being matched. In the case of an "implicit optional" pattern, the /// OptionalSome pattern is explicitly added to this as an 'implicit' pattern. Pattern *ThePattern; /// The value for matching. Expr *Initializer; ConditionalPatternBindingInfo(SourceLoc IntroducerLoc, Pattern *ThePattern, Expr *Initializer) : IntroducerLoc(IntroducerLoc), ThePattern(ThePattern), Initializer(Initializer) {} public: static ConditionalPatternBindingInfo *create(ASTContext &ctx, SourceLoc IntroducerLoc, Pattern *ThePattern, Expr *Initializer) { return new (ctx) ConditionalPatternBindingInfo(IntroducerLoc, ThePattern, Initializer); } SourceLoc getIntroducerLoc() const { return IntroducerLoc; } void setIntroducerLoc(SourceLoc Loc) { IntroducerLoc = Loc; } Pattern *getPattern() const { return ThePattern; } void setPattern(Pattern *P) { ThePattern = P; } Expr *getInitializer() const { return Initializer; } void setInitializer(Expr *E) { Initializer = E; } SourceRange getSourceRange() const; SourceLoc getStartLoc() const { return getSourceRange().Start; }; SourceLoc getEndLoc() const { return getSourceRange().End; }; }; /// An expression that guards execution based on whether the run-time /// configuration supports a given API, e.g., /// #available(OSX >= 10.9, iOS >= 7.0). class alignas(8) PoundAvailableInfo final : private llvm::TrailingObjects { friend TrailingObjects; SourceLoc PoundLoc; SourceLoc LParenLoc; SourceLoc RParenLoc; /// The number of queries tail allocated after this object. unsigned NumQueries; /// The type-checked availability query information. std::optional Query; struct { unsigned isInvalid : 1; /// Indicates that the expression is checking if a version range /// is **not** available. unsigned isUnavailability : 1; } Flags; PoundAvailableInfo(SourceLoc PoundLoc, SourceLoc LParenLoc, ArrayRef queries, SourceLoc RParenLoc, bool isUnavailability) : PoundLoc(PoundLoc), LParenLoc(LParenLoc), RParenLoc(RParenLoc), NumQueries(queries.size()), Flags() { Flags.isInvalid = false; Flags.isUnavailability = isUnavailability; std::uninitialized_copy(queries.begin(), queries.end(), getTrailingObjects()); } public: static PoundAvailableInfo *create(ASTContext &ctx, SourceLoc PoundLoc, SourceLoc LParenLoc, ArrayRef queries, SourceLoc RParenLoc, bool isUnavailability); bool isInvalid() const { return Flags.isInvalid; } void setInvalid() { Flags.isInvalid = true; } ArrayRef getQueries() const { return llvm::ArrayRef(getTrailingObjects(), NumQueries); } /// Returns an iterator for the statement's type-checked availability specs. SemanticAvailabilitySpecs getSemanticAvailabilitySpecs(const DeclContext *declContext) const; SourceLoc getLParenLoc() const { return LParenLoc; } SourceLoc getRParenLoc() const { return RParenLoc; } SourceLoc getStartLoc() const { return PoundLoc; } SourceLoc getEndLoc() const; SourceLoc getLoc() const { return PoundLoc; } SourceRange getSourceRange() const { return SourceRange(getStartLoc(), getEndLoc()); } std::optional getAvailabilityQuery() const { return Query; } void setAvailabilityQuery(const AvailabilityQuery &query) { Query.emplace(query); } bool isUnavailability() const { return Flags.isUnavailability; } }; /// An expression that guards execution based on whether the symbols for the /// declaration identified by the given expression are non-null at run-time, e.g. /// /// if #_hasSymbol(foo(_:)) { foo(42) } /// class PoundHasSymbolInfo final : public ASTAllocated { Expr *SymbolExpr; ConcreteDeclRef ReferencedDecl; bool Invalid; SourceLoc PoundLoc; SourceLoc LParenLoc; SourceLoc RParenLoc; PoundHasSymbolInfo(SourceLoc PoundLoc, SourceLoc LParenLoc, Expr *SymbolExpr, SourceLoc RParenLoc) : SymbolExpr(SymbolExpr), ReferencedDecl(), Invalid(), PoundLoc(PoundLoc), LParenLoc(LParenLoc), RParenLoc(RParenLoc){}; public: static PoundHasSymbolInfo *create(ASTContext &Ctx, SourceLoc PoundLoc, SourceLoc LParenLoc, Expr *SymbolExpr, SourceLoc RParenLoc); Expr *getSymbolExpr() const { return SymbolExpr; } void setSymbolExpr(Expr *E) { SymbolExpr = E; } ConcreteDeclRef getReferencedDecl() { return ReferencedDecl; } void setReferencedDecl(ConcreteDeclRef CDR) { ReferencedDecl = CDR; } /// Returns true if the referenced decl has been diagnosed as invalid. bool isInvalid() const { return Invalid; } void setInvalid() { Invalid = true; } SourceLoc getLParenLoc() const { return LParenLoc; } SourceLoc getRParenLoc() const { return RParenLoc; } SourceLoc getStartLoc() const { return PoundLoc; } SourceLoc getEndLoc() const { return RParenLoc; } SourceRange getSourceRange() const { return SourceRange(getStartLoc(), getEndLoc()); } }; /// This represents an entry in an "if" or "while" condition. /// Either a boolean expression, optional binding, pattern matching, /// `#available`, or `#_hasSymbol`. /// /// E.g. this 'if' statement has 5 'StmtConditionElement'. /// if /// list.count == 1, // CK_Boolean /// let firstElem = list.first, // CK_PatternBinding /// case .foo(let value?, "int") = firstElem, // CK_PatternBinding /// #available(myOS 13), // CK_Availability /// #_hasSymbol(MyStruct.peform(operation:)) // CK_HasSymbol /// { ... } /// class alignas(1 << PatternAlignInBits) StmtConditionElement { private: llvm::PointerUnion Condition; public: StmtConditionElement() : Condition(nullptr) {} StmtConditionElement(Expr *cond) : Condition(cond) {} StmtConditionElement(ConditionalPatternBindingInfo *Info) : Condition(Info) {} StmtConditionElement(PoundAvailableInfo *Info) : Condition(Info) {} StmtConditionElement(PoundHasSymbolInfo *Info) : Condition(Info) {} static StmtConditionElement fromOpaqueValue(void *opaque) { StmtConditionElement val; val.Condition = decltype(Condition)::getFromOpaqueValue(opaque); return val; } void *getOpaqueValue() const { return Condition.getOpaqueValue(); } /// ConditionKind - This indicates the sort of condition this is. enum ConditionKind { CK_Boolean, CK_PatternBinding, CK_Availability, CK_HasSymbol, }; ConditionKind getKind() const { if (isa(Condition)) return CK_Boolean; if (isa(Condition)) return CK_PatternBinding; if (isa(Condition)) return CK_Availability; if (isa(Condition)) return CK_HasSymbol; return CK_Boolean; } /// Boolean Condition Accessors. Expr *getBooleanOrNull() const { return Condition.dyn_cast(); } Expr *getBoolean() const { assert(getKind() == CK_Boolean && "Not a condition"); return cast(Condition); } void setBoolean(Expr *E) { assert(getKind() == CK_Boolean && "Not a condition"); Condition = E; } /// Pattern Binding Accessors. ConditionalPatternBindingInfo *getPatternBindingOrNull() const { return Condition.dyn_cast(); } ConditionalPatternBindingInfo *getPatternBinding() const { assert(getKind() == CK_PatternBinding && "Not a pattern binding condition"); return cast(Condition); } SourceLoc getIntroducerLoc() const { return getPatternBinding()->getIntroducerLoc(); } void setIntroducerLoc(SourceLoc loc) { getPatternBinding()->setIntroducerLoc(loc); } Pattern *getPatternOrNull() const { if (auto *binding = getPatternBindingOrNull()) return binding->getPattern(); return nullptr; } Pattern *getPattern() const { return getPatternBinding()->getPattern(); } void setPattern(Pattern *P) { getPatternBinding()->setPattern(P); } Expr *getInitializerOrNull() const { if (auto *binding = getPatternBindingOrNull()) return binding->getInitializer(); return nullptr; } Expr *getInitializer() const { return getPatternBinding()->getInitializer(); } void setInitializer(Expr *E) { getPatternBinding()->setInitializer(E); } // Availability Accessors PoundAvailableInfo *getAvailability() const { assert(getKind() == CK_Availability && "Not an #available condition"); return cast(Condition); } void setAvailability(PoundAvailableInfo *Info) { assert(getKind() == CK_Availability && "Not an #available condition"); Condition = Info; } // #_hasSymbol Accessors PoundHasSymbolInfo *getHasSymbolInfo() const { assert(getKind() == CK_HasSymbol && "Not a #_hasSymbol condition"); return cast(Condition); } void setHasSymbolInfo(PoundHasSymbolInfo *Info) { assert(getKind() == CK_HasSymbol && "Not a #_hasSymbol condition"); Condition = Info; } /// Whether or not this conditional stmt rebinds self with a `let self` /// or `let self = self` condition. /// - If `requiresCaptureListRef` is `true`, additionally requires that the /// RHS of the self condition references a var defined in a capture list. /// - If `requireLoadExpr` is `true`, additionally requires that the RHS of /// the self condition is a `LoadExpr`. /// TODO: Remove `requireLoadExpr` after full-on of the ImmutableWeakCaptures /// feature bool rebindsSelf(ASTContext &Ctx, bool requiresCaptureListRef = false, bool requireLoadExpr = false) const; SourceLoc getStartLoc() const; SourceLoc getEndLoc() const; SourceRange getSourceRange() const; /// Recursively walks the AST rooted at this statement condition element StmtConditionElement *walk(ASTWalker &walker); StmtConditionElement *walk(ASTWalker &&walker) { return walk(walker); } }; struct LabeledStmtInfo { Identifier Name; SourceLoc Loc; // Evaluates to true if set. operator bool() const { return !Name.empty(); } }; /// LabeledStmt - Common base class between the labeled statements (loops and /// switch). class LabeledStmt : public Stmt { LabeledStmtInfo LabelInfo; protected: SourceLoc getLabelLocOrKeywordLoc(SourceLoc L) const { return LabelInfo ? LabelInfo.Loc : L; } public: LabeledStmt(StmtKind Kind, bool Implicit, LabeledStmtInfo LabelInfo) : Stmt(Kind, Implicit), LabelInfo(LabelInfo) {} LabeledStmtInfo getLabelInfo() const { return LabelInfo; } void setLabelInfo(LabeledStmtInfo L) { LabelInfo = L; } /// Is this statement a valid target of "continue" if labeled? /// /// For the most part, non-looping constructs shouldn't be /// continue-able, but we threw in "do" as a sop. bool isPossibleContinueTarget() const; /// Is this statement a valid target of an unlabeled "break" or /// "continue"? /// /// The nice, consistent language rule is that unlabeled "break" and /// "continue" leave the innermost loop. We have to include /// "switch" (for "break") for consistency with C: Swift doesn't /// require "break" to leave a switch case, but it's still way too /// similar to C's switch to allow different behavior for "break". bool requiresLabelOnJump() const; static bool classof(const Stmt *S) { return S->getKind() >= StmtKind::First_LabeledStmt && S->getKind() <= StmtKind::Last_LabeledStmt; } }; /// DoStmt - do statement, without any trailing clauses. class DoStmt : public LabeledStmt { SourceLoc DoLoc; BraceStmt *Body; public: DoStmt(LabeledStmtInfo labelInfo, SourceLoc doLoc, BraceStmt *body, std::optional implicit = std::nullopt) : LabeledStmt(StmtKind::Do, getDefaultImplicitFlag(implicit, doLoc), labelInfo), DoLoc(doLoc), Body(body) {} static DoStmt *createImplicit(ASTContext &C, LabeledStmtInfo labelInfo, ArrayRef body); SourceLoc getDoLoc() const { return DoLoc; } SourceLoc getStartLoc() const; SourceLoc getEndLoc() const; BraceStmt *getBody() const { return Body; } void setBody(BraceStmt *s) { Body = s; } static bool classof(const Stmt *S) { return S->getKind() == StmtKind::Do; } }; /// Either an "if let" case or a simple boolean expression can appear as the /// condition of an 'if', 'guard', or 'while' statement. using StmtCondition = MutableArrayRef; /// This is the common base class between statements that can have labels, and /// also have complex "if let" style conditions: 'if' and 'while'. class LabeledConditionalStmt : public LabeledStmt { StmtCondition Cond; public: LabeledConditionalStmt(StmtKind Kind, bool Implicit, LabeledStmtInfo LabelInfo, StmtCondition Cond) : LabeledStmt(Kind, Implicit, LabelInfo) { setCond(Cond); } StmtCondition getCond() const { return Cond; } void setCond(StmtCondition e); /// FIXME: Find a better way to implement this. Allows conditions to be /// stored in \c ASTNode. StmtCondition *getCondPointer() { return &Cond; } /// Whether or not this conditional stmt rebinds self with a `let self` /// or `let self = self` condition. /// - If `requiresCaptureListRef` is `true`, additionally requires that the /// RHS of the self condition references a var defined in a capture list. bool rebindsSelf(ASTContext &Ctx, bool requiresCaptureListRef = false, bool requireLoadExpr = false) const; static bool classof(const Stmt *S) { return S->getKind() >= StmtKind::First_LabeledConditionalStmt && S->getKind() <= StmtKind::Last_LabeledConditionalStmt; } }; /// IfStmt - if/then/else statement. If no 'else' is specified, then the /// ElseLoc location is not specified and the Else statement is null. After /// type-checking, the condition is of type Builtin.Int1. class IfStmt : public LabeledConditionalStmt { SourceLoc IfLoc; SourceLoc ElseLoc; BraceStmt *Then; Stmt *Else; public: IfStmt(LabeledStmtInfo LabelInfo, SourceLoc IfLoc, StmtCondition Cond, BraceStmt *Then, SourceLoc ElseLoc, Stmt *Else, std::optional implicit = std::nullopt) : LabeledConditionalStmt(StmtKind::If, getDefaultImplicitFlag(implicit, IfLoc), LabelInfo, Cond), IfLoc(IfLoc), ElseLoc(ElseLoc), Then(Then), Else(Else) { assert(Then && "Must have non-null 'then' statement"); assert(!Else || isa(Else) || isa(Else) && "Else statement must either be BraceStmt or IfStmt"); } IfStmt(SourceLoc IfLoc, Expr *Cond, BraceStmt *Then, SourceLoc ElseLoc, Stmt *Else, std::optional implicit, ASTContext &Ctx); SourceLoc getIfLoc() const { return IfLoc; } SourceLoc getElseLoc() const { return ElseLoc; } SourceLoc getStartLoc() const { return getLabelLocOrKeywordLoc(IfLoc); } SourceLoc getEndLoc() const { return (Else ? Else->getEndLoc() : Then->getEndLoc()); } BraceStmt *getThenStmt() const { return Then; } void setThenStmt(BraceStmt *s) { Then = s; } Stmt *getElseStmt() const { return Else; } void setElseStmt(Stmt *s) { Else = s; } /// Retrieve the complete set of branches for this if statement, including /// else if statements. ArrayRef getBranches(SmallVectorImpl &scratch) const; /// Whether the if statement has an unconditional \c else. bool isSyntacticallyExhaustive() const; // Implement isa/cast/dyncast/etc. static bool classof(const Stmt *S) { return S->getKind() == StmtKind::If; } }; /// GuardStmt - 'guard' statement. Evaluate a condition and if it fails, run /// its body. The body is always guaranteed to exit the current scope (or /// abort), it never falls through. /// class GuardStmt : public LabeledConditionalStmt { SourceLoc GuardLoc; BraceStmt *Body; public: GuardStmt(SourceLoc GuardLoc, StmtCondition Cond, BraceStmt *Body, std::optional implicit = std::nullopt) : LabeledConditionalStmt(StmtKind::Guard, getDefaultImplicitFlag(implicit, GuardLoc), LabeledStmtInfo(), Cond), GuardLoc(GuardLoc), Body(Body) {} GuardStmt(SourceLoc GuardLoc, Expr *Cond, BraceStmt *Body, std::optional implicit, ASTContext &Ctx); SourceLoc getGuardLoc() const { return GuardLoc; } SourceLoc getStartLoc() const { return getLabelLocOrKeywordLoc(GuardLoc); } SourceLoc getEndLoc() const { return Body->getEndLoc(); } BraceStmt *getBody() const { return Body; } void setBody(BraceStmt *s) { Body = s; } // Implement isa/cast/dyncast/etc. static bool classof(const Stmt *S) { return S->getKind() == StmtKind::Guard; } }; /// WhileStmt - while statement. After type-checking, the condition is of /// type Builtin.Int1. class WhileStmt : public LabeledConditionalStmt { SourceLoc WhileLoc; StmtCondition Cond; Stmt *Body; public: WhileStmt(LabeledStmtInfo LabelInfo, SourceLoc WhileLoc, StmtCondition Cond, Stmt *Body, std::optional implicit = std::nullopt) : LabeledConditionalStmt(StmtKind::While, getDefaultImplicitFlag(implicit, WhileLoc), LabelInfo, Cond), WhileLoc(WhileLoc), Body(Body) {} SourceLoc getStartLoc() const { return getLabelLocOrKeywordLoc(WhileLoc); } SourceLoc getEndLoc() const { return Body->getEndLoc(); } SourceLoc getWhileLoc() const { return WhileLoc; } Stmt *getBody() const { return Body; } void setBody(Stmt *s) { Body = s; } static bool classof(const Stmt *S) { return S->getKind() == StmtKind::While; } }; /// RepeatWhileStmt - repeat/while statement. After type-checking, the /// condition is of type Builtin.Int1. class RepeatWhileStmt : public LabeledStmt { SourceLoc RepeatLoc, WhileLoc; Stmt *Body; Expr *Cond; public: RepeatWhileStmt(LabeledStmtInfo LabelInfo, SourceLoc RepeatLoc, Expr *Cond, SourceLoc WhileLoc, Stmt *Body, std::optional implicit = std::nullopt) : LabeledStmt(StmtKind::RepeatWhile, getDefaultImplicitFlag(implicit, RepeatLoc), LabelInfo), RepeatLoc(RepeatLoc), WhileLoc(WhileLoc), Body(Body), Cond(Cond) {} SourceLoc getStartLoc() const { return getLabelLocOrKeywordLoc(RepeatLoc); } SourceLoc getEndLoc() const; SourceLoc getRepeatLoc() const { return RepeatLoc; } Stmt *getBody() const { return Body; } void setBody(Stmt *s) { Body = s; } Expr *getCond() const { return Cond; } void setCond(Expr *e) { Cond = e; } static bool classof(const Stmt *S) {return S->getKind() == StmtKind::RepeatWhile;} }; /// ForEachStmt - foreach statement that iterates over the elements in a /// container. /// /// Example: /// \code /// for i in 0...10 { /// print(String(i)) /// } /// \endcode class ForEachStmt : public LabeledStmt { SourceLoc ForLoc; SourceLoc TryLoc; SourceLoc AwaitLoc; SourceLoc UnsafeLoc; Pattern *Pat; SourceLoc InLoc; Expr *Sequence; SourceLoc WhereLoc; Expr *WhereExpr = nullptr; BraceStmt *Body; // Set by Sema: ProtocolConformanceRef sequenceConformance = ProtocolConformanceRef(); Type sequenceType; PatternBindingDecl *iteratorVar = nullptr; Expr *nextCall = nullptr; OpaqueValueExpr *elementExpr = nullptr; Expr *convertElementExpr = nullptr; public: ForEachStmt(LabeledStmtInfo LabelInfo, SourceLoc ForLoc, SourceLoc TryLoc, SourceLoc AwaitLoc, SourceLoc UnsafeLoc, Pattern *Pat, SourceLoc InLoc, Expr *Sequence, SourceLoc WhereLoc, Expr *WhereExpr, BraceStmt *Body, std::optional implicit = std::nullopt) : LabeledStmt(StmtKind::ForEach, getDefaultImplicitFlag(implicit, ForLoc), LabelInfo), ForLoc(ForLoc), TryLoc(TryLoc), AwaitLoc(AwaitLoc), UnsafeLoc(UnsafeLoc), Pat(nullptr), InLoc(InLoc), Sequence(Sequence), WhereLoc(WhereLoc), WhereExpr(WhereExpr), Body(Body) { setPattern(Pat); } void setIteratorVar(PatternBindingDecl *var) { iteratorVar = var; } PatternBindingDecl *getIteratorVar() const { return iteratorVar; } void setNextCall(Expr *next) { nextCall = next; } Expr *getNextCall() const { return nextCall; } void setElementExpr(OpaqueValueExpr *expr) { elementExpr = expr; } OpaqueValueExpr *getElementExpr() const { return elementExpr; } void setConvertElementExpr(Expr *expr) { convertElementExpr = expr; } Expr *getConvertElementExpr() const { return convertElementExpr; } void setSequenceConformance(Type type, ProtocolConformanceRef conformance) { sequenceType = type; sequenceConformance = conformance; } Type getSequenceType() const { return sequenceType; } ProtocolConformanceRef getSequenceConformance() const { return sequenceConformance; } /// getForLoc - Retrieve the location of the 'for' keyword. SourceLoc getForLoc() const { return ForLoc; } /// getInLoc - Retrieve the location of the 'in' keyword. SourceLoc getInLoc() const { return InLoc; } /// getWhereLoc - Retrieve the location of the 'where' keyword. SourceLoc getWhereLoc() const { return WhereLoc; } SourceLoc getAwaitLoc() const { return AwaitLoc; } SourceLoc getTryLoc() const { return TryLoc; } SourceLoc getUnsafeLoc() const { return UnsafeLoc; } /// getPattern - Retrieve the pattern describing the iteration variables. /// These variables will only be visible within the body of the loop. Pattern *getPattern() const { return Pat; } void setPattern(Pattern *p); Expr *getWhere() const { return WhereExpr; } void setWhere(Expr *W) { WhereExpr = W; } /// getSequence - Retrieve the Sequence whose elements will be visited /// by this foreach loop, as it was written in the source code and /// subsequently type-checked. To determine the semantic behavior of this /// expression to extract a range, use \c getRangeInit(). Expr *getParsedSequence() const { return Sequence; } void setParsedSequence(Expr *S) { Sequence = S; } /// Type-checked version of the sequence or nullptr if this statement /// yet to be type-checked. Expr *getTypeCheckedSequence() const; /// getBody - Retrieve the body of the loop. BraceStmt *getBody() const { return Body; } void setBody(BraceStmt *B) { Body = B; } SourceLoc getStartLoc() const { return getLabelLocOrKeywordLoc(ForLoc); } SourceLoc getEndLoc() const { return Body->getEndLoc(); } static bool classof(const Stmt *S) { return S->getKind() == StmtKind::ForEach; } }; /// A pattern and an optional guard expression used in a 'case' statement. class alignas(1 << PatternAlignInBits) CaseLabelItem { enum class Kind { /// A normal pattern Normal = 0, /// `default` Default, }; llvm::PointerIntPair CasePatternAndResolved; SourceLoc WhereLoc; llvm::PointerIntPair GuardExprAndKind; CaseLabelItem(Kind kind, Pattern *casePattern, SourceLoc whereLoc, Expr *guardExpr) : CasePatternAndResolved(casePattern, false), WhereLoc(whereLoc), GuardExprAndKind(guardExpr, kind) {} public: CaseLabelItem(const CaseLabelItem &) = default; CaseLabelItem(Pattern *casePattern, SourceLoc whereLoc, Expr *guardExpr) : CaseLabelItem(Kind::Normal, casePattern, whereLoc, guardExpr) {} explicit CaseLabelItem(Pattern *casePattern) : CaseLabelItem(casePattern, SourceLoc(), nullptr) {} static CaseLabelItem getDefault(AnyPattern *pattern, SourceLoc whereLoc, Expr *guardExpr) { assert(pattern); return CaseLabelItem(Kind::Default, reinterpret_cast(pattern), whereLoc, guardExpr); } static CaseLabelItem getDefault(AnyPattern *pattern) { return getDefault(pattern, SourceLoc(), nullptr); } SourceLoc getWhereLoc() const { return WhereLoc; } SourceLoc getStartLoc() const; SourceLoc getEndLoc() const; SourceRange getSourceRange() const; Pattern *getPattern() { return CasePatternAndResolved.getPointer(); } const Pattern *getPattern() const { return CasePatternAndResolved.getPointer(); } bool isPatternResolved() const { return CasePatternAndResolved.getInt(); } void setPattern(Pattern *CasePattern, bool resolved) { this->CasePatternAndResolved.setPointer(CasePattern); this->CasePatternAndResolved.setInt(resolved); } /// Return the guard expression if present, or null if the case label has /// no guard. Expr *getGuardExpr() { return GuardExprAndKind.getPointer(); } const Expr *getGuardExpr() const { return GuardExprAndKind.getPointer(); } void setGuardExpr(Expr *e) { GuardExprAndKind.setPointer(e); } /// Returns true if this is syntactically a 'default' label. bool isDefault() const { return GuardExprAndKind.getInt() == Kind::Default; } bool isSyntacticallyExhaustive() const; }; /// FallthroughStmt - The keyword "fallthrough". class FallthroughStmt : public Stmt { SourceLoc Loc; DeclContext *DC; FallthroughStmt(SourceLoc Loc, DeclContext *DC, std::optional implicit = std::nullopt) : Stmt(StmtKind::Fallthrough, getDefaultImplicitFlag(implicit, Loc)), Loc(Loc), DC(DC) {} public: static FallthroughStmt *createParsed(SourceLoc Loc, DeclContext *DC); SourceLoc getLoc() const { return Loc; } SourceRange getSourceRange() const { return Loc; } DeclContext *getDeclContext() const { return DC; } void setDeclContext(DeclContext *newDC) { DC = newDC; } /// Get the CaseStmt block from which the fallthrough transfers control. /// Returns \c nullptr if the fallthrough is invalid. CaseStmt *getFallthroughSource() const; /// Get the CaseStmt block to which the fallthrough transfers control. /// Returns \c nullptr if the fallthrough is invalid. CaseStmt *getFallthroughDest() const; static bool classof(const Stmt *S) { return S->getKind() == StmtKind::Fallthrough; } }; enum CaseParentKind { Switch, DoCatch }; /// A 'case' or 'default' block of a switch statement, or a 'catch' clause of a /// do-catch statement. Only valid as the substatement of a SwitchStmt or /// DoCatchStmt. A case block begins either with one or more CaseLabelItems or /// a single 'default' label. /// /// Some examples: /// \code /// case 1: /// case 2, 3: /// case Foo(var x, var y) where x < y: /// case 2 where foo(), 3 where bar(): /// default: /// \endcode /// class CaseStmt final : public Stmt, private llvm::TrailingObjects { friend TrailingObjects; Stmt *ParentStmt = nullptr; SourceLoc UnknownAttrLoc; SourceLoc ItemIntroducerLoc; SourceLoc ItemTerminatorLoc; CaseParentKind ParentKind; llvm::PointerIntPair BodyAndHasFallthrough; CaseStmt(CaseParentKind ParentKind, SourceLoc ItemIntroducerLoc, ArrayRef CaseLabelItems, SourceLoc UnknownAttrLoc, SourceLoc ItemTerminatorLoc, BraceStmt *Body, ArrayRef CaseBodyVariables, std::optional Implicit, NullablePtr fallthroughStmt); MutableArrayRef getCaseBodyVariablesBuffer() { return {getTrailingObjects(), static_cast(Bits.CaseStmt.NumCaseBodyVars)}; } public: /// Create a parsed 'case'/'default' for 'switch' statement. static CaseStmt * createParsedSwitchCase(ASTContext &C, SourceLoc ItemIntroducerLoc, ArrayRef CaseLabelItems, SourceLoc UnknownAttrLoc, SourceLoc ColonLoc, BraceStmt *Body); /// Create a parsed 'catch' for 'do' statement. static CaseStmt *createParsedDoCatch(ASTContext &C, SourceLoc CatchLoc, ArrayRef CaseLabelItems, BraceStmt *Body); static CaseStmt * createImplicit(ASTContext &ctx, CaseParentKind parentKind, ArrayRef caseLabelItems, BraceStmt *body, NullablePtr fallthroughStmt = nullptr); static CaseStmt * create(ASTContext &C, CaseParentKind ParentKind, SourceLoc ItemIntroducerLoc, ArrayRef CaseLabelItems, SourceLoc UnknownAttrLoc, SourceLoc ItemTerminatorLoc, BraceStmt *Body, ArrayRef CaseBodyVariables, std::optional Implicit, NullablePtr fallthroughStmt); CaseParentKind getParentKind() const { return ParentKind; } Stmt *getParentStmt() const { return ParentStmt; } void setParentStmt(Stmt *S) { assert(S && "Parent statement must be SwitchStmt or DoCatchStmt"); assert((ParentKind == CaseParentKind::Switch && isa(S)) || (ParentKind == CaseParentKind::DoCatch && isa(S))); ParentStmt = S; } ArrayRef getCaseLabelItems() const { return {getTrailingObjects(), static_cast(Bits.CaseStmt.NumPatterns)}; } MutableArrayRef getMutableCaseLabelItems() { return {getTrailingObjects(), static_cast(Bits.CaseStmt.NumPatterns)}; } unsigned getNumCaseLabelItems() const { return Bits.CaseStmt.NumPatterns; } FallthroughStmt *getFallthroughStmt() const { return hasFallthroughDest() ? *getTrailingObjects() : nullptr; } NullablePtr getFallthroughDest() const { return const_cast(*this).getFallthroughDest(); } NullablePtr getFallthroughDest() { if (!hasFallthroughDest()) return nullptr; return (*getTrailingObjects())->getFallthroughDest(); } bool hasFallthroughDest() const { return BodyAndHasFallthrough.getInt(); } BraceStmt *getBody() const { return BodyAndHasFallthrough.getPointer(); } void setBody(BraceStmt *body) { BodyAndHasFallthrough.setPointer(body); } /// True if the case block declares any patterns with local variable bindings. bool hasCaseBodyVariables() const { return !getCaseBodyVariables().empty(); } /// Get the source location of the 'case', 'default', or 'catch' of the first /// label. SourceLoc getLoc() const { return ItemIntroducerLoc; } SourceLoc getStartLoc() const { if (UnknownAttrLoc.isValid()) return UnknownAttrLoc; if (ItemIntroducerLoc.isValid()) return ItemIntroducerLoc; return getBody()->getStartLoc(); } SourceLoc getEndLoc() const { return getBody()->getEndLoc(); } SourceLoc getItemTerminatorLoc() const { return ItemTerminatorLoc; } SourceRange getLabelItemsRange() const { switch (ParentKind) { case CaseParentKind::Switch: // The range extends from 'case' to the colon at the end. return ItemTerminatorLoc.isValid() ? SourceRange(getLoc(), ItemTerminatorLoc) : getSourceRange(); case CaseParentKind::DoCatch: { // The range extends from 'catch' to the end of the last non-implicit // item. auto items = getCaseLabelItems(); for (auto item = items.rbegin(), end = items.rend(); item != end; ++item) { auto itemEndLoc = item->getEndLoc(); if (itemEndLoc.isValid()) return SourceRange(getLoc(), itemEndLoc); } // Handle the 'catch {' case. return SourceRange(getLoc(), getLoc()); } } llvm_unreachable("invalid parent kind"); } bool isDefault() { return getCaseLabelItems()[0].isDefault(); } bool hasUnknownAttr() const { // Note: This representation doesn't allow for synthesized @unknown cases. // However, that's probably sensible; the purpose of @unknown is for // diagnosing otherwise-non-exhaustive switches, and the user can't edit // a synthesized case. return UnknownAttrLoc.isValid(); } /// Return an ArrayRef containing the case body variables of this CaseStmt. ArrayRef getCaseBodyVariables() const { return const_cast(this)->getCaseBodyVariablesBuffer(); } /// Find the next case statement within the same 'switch' or 'do-catch', /// if there is one. CaseStmt *findNextCaseStmt() const; static bool classof(const Stmt *S) { return S->getKind() == StmtKind::Case; } size_t numTrailingObjects(OverloadToken) const { return getNumCaseLabelItems(); } size_t numTrailingObjects(OverloadToken) const { return hasFallthroughDest() ? 1 : 0; } }; /// Switch statement. class SwitchStmt final : public LabeledStmt, private llvm::TrailingObjects { friend TrailingObjects; SourceLoc SwitchLoc, LBraceLoc, RBraceLoc; /// The location of the last token in the 'switch' statement. For valid /// 'switch' statements this is the same as \c RBraceLoc. If the '}' is /// missing this points to the last token before the '}' was expected. SourceLoc EndLoc; Expr *SubjectExpr; SwitchStmt(LabeledStmtInfo LabelInfo, SourceLoc SwitchLoc, Expr *SubjectExpr, SourceLoc LBraceLoc, unsigned CaseCount, SourceLoc RBraceLoc, SourceLoc EndLoc, std::optional implicit = std::nullopt) : LabeledStmt(StmtKind::Switch, getDefaultImplicitFlag(implicit, SwitchLoc), LabelInfo), SwitchLoc(SwitchLoc), LBraceLoc(LBraceLoc), RBraceLoc(RBraceLoc), EndLoc(EndLoc), SubjectExpr(SubjectExpr) { Bits.SwitchStmt.CaseCount = CaseCount; } public: /// Allocate a new SwitchStmt in the given ASTContext. static SwitchStmt *create(LabeledStmtInfo LabelInfo, SourceLoc SwitchLoc, Expr *SubjectExpr, SourceLoc LBraceLoc, ArrayRef Cases, SourceLoc RBraceLoc, SourceLoc EndLoc, ASTContext &C); static SwitchStmt *createImplicit(LabeledStmtInfo LabelInfo, Expr *SubjectExpr, ArrayRef Cases, ASTContext &C) { return SwitchStmt::create(LabelInfo, /*SwitchLoc=*/SourceLoc(), SubjectExpr, /*LBraceLoc=*/SourceLoc(), Cases, /*RBraceLoc=*/SourceLoc(), /*EndLoc=*/SourceLoc(), C); } /// Get the source location of the 'switch' keyword. SourceLoc getSwitchLoc() const { return SwitchLoc; } /// Get the source location of the opening brace. SourceLoc getLBraceLoc() const { return LBraceLoc; } /// Get the source location of the closing brace. SourceLoc getRBraceLoc() const { return RBraceLoc; } SourceLoc getLoc() const { return SwitchLoc; } SourceLoc getStartLoc() const { return getLabelLocOrKeywordLoc(SwitchLoc); } SourceLoc getEndLoc() const { return EndLoc; } /// Get the subject expression of the switch. Expr *getSubjectExpr() const { return SubjectExpr; } void setSubjectExpr(Expr *e) { SubjectExpr = e; } /// Get the list of case clauses. ArrayRef getCases() const { return {getTrailingObjects(), static_cast(Bits.SwitchStmt.CaseCount)}; } /// Retrieve the complete set of branches for this switch statement. ArrayRef getBranches(SmallVectorImpl &scratch) const; static bool classof(const Stmt *S) { return S->getKind() == StmtKind::Switch; } }; /// DoCatchStmt - do statement with trailing 'catch' clauses. class DoCatchStmt final : public LabeledStmt, private llvm::TrailingObjects { friend TrailingObjects; friend class ExplicitCaughtTypeRequest; DeclContext *DC; SourceLoc DoLoc; /// Location of the 'throws' token. SourceLoc ThrowsLoc; /// The error type that is being thrown. TypeLoc ThrownType; Stmt *Body; ThrownErrorDestination RethrowDest; DoCatchStmt(DeclContext *dc, LabeledStmtInfo labelInfo, SourceLoc doLoc, SourceLoc throwsLoc, TypeLoc thrownType, Stmt *body, ArrayRef catches, std::optional implicit) : LabeledStmt(StmtKind::DoCatch, getDefaultImplicitFlag(implicit, doLoc), labelInfo), DC(dc), DoLoc(doLoc), ThrowsLoc(throwsLoc), ThrownType(thrownType), Body(body) { Bits.DoCatchStmt.NumCatches = catches.size(); std::uninitialized_copy(catches.begin(), catches.end(), getTrailingObjects()); for (auto *catchStmt : getCatches()) catchStmt->setParentStmt(this); } public: static DoCatchStmt *create(DeclContext *dc, LabeledStmtInfo labelInfo, SourceLoc doLoc, SourceLoc throwsLoc, TypeLoc thrownType, Stmt *body, ArrayRef catches, std::optional implicit = std::nullopt); DeclContext *getDeclContext() const { return DC; } SourceLoc getDoLoc() const { return DoLoc; } /// Retrieve the location of the 'throws' keyword, if present. SourceLoc getThrowsLoc() const { return ThrowsLoc; } SourceLoc getStartLoc() const { return getLabelLocOrKeywordLoc(DoLoc); } SourceLoc getEndLoc() const { return getCatches().back()->getEndLoc(); } /// Retrieves the type representation for the caught type. TypeRepr *getCaughtTypeRepr() const { return ThrownType.getTypeRepr(); } // Get the explicitly-specified caught error type. Type getExplicitCaughtType() const; Stmt *getBody() const { return Body; } void setBody(Stmt *s) { Body = s; } ArrayRef getCatches() const { return {getTrailingObjects(), static_cast(Bits.DoCatchStmt.NumCatches)}; } MutableArrayRef getMutableCatches() { return {getTrailingObjects(), static_cast(Bits.DoCatchStmt.NumCatches)}; } /// Retrieve the complete set of branches for this do-catch statement. ArrayRef getBranches(SmallVectorImpl &scratch) const; /// Does this statement contain a syntactically exhaustive catch /// clause? /// /// Note that an exhaustive do/catch statement can still throw /// errors out of its catch block(s). bool isSyntacticallyExhaustive() const; // Determines the type of the error that is thrown out of the 'do' block // and caught by the various 'catch' clauses. If this the catch clauses // aren't exhausive, this is also the type of the error that is implicitly // rethrown. Type getCaughtErrorType() const; /// Retrieves the rethrown error and its conversion to the error type /// expected by the enclosing context. ThrownErrorDestination rethrows() const { return RethrowDest; } void setRethrows(ThrownErrorDestination rethrows) { assert(!RethrowDest); RethrowDest = rethrows; } static bool classof(const Stmt *S) { return S->getKind() == StmtKind::DoCatch; } }; /// BreakStmt - The "break" and "break label" statement. class BreakStmt : public Stmt { SourceLoc Loc; Identifier TargetName; // Named target statement, if specified in the source. SourceLoc TargetLoc; DeclContext *DC; public: BreakStmt(SourceLoc Loc, Identifier TargetName, SourceLoc TargetLoc, DeclContext *DC, std::optional implicit = std::nullopt) : Stmt(StmtKind::Break, getDefaultImplicitFlag(implicit, Loc)), Loc(Loc), TargetName(TargetName), TargetLoc(TargetLoc), DC(DC) {} SourceLoc getLoc() const { return Loc; } Identifier getTargetName() const { return TargetName; } void setTargetName(Identifier N) { TargetName = N; } SourceLoc getTargetLoc() const { return TargetLoc; } void setTargetLoc(SourceLoc L) { TargetLoc = L; } /// Retrieve the target statement being jumped out of. LabeledStmt *getTarget() const; SourceLoc getStartLoc() const { return Loc; } SourceLoc getEndLoc() const { return (TargetLoc.isValid() ? TargetLoc : Loc); } DeclContext *getDeclContext() const { return DC; } void setDeclContext(DeclContext *newDC) { DC = newDC; } static bool classof(const Stmt *S) { return S->getKind() == StmtKind::Break; } }; /// ContinueStmt - The "continue" and "continue label" statement. class ContinueStmt : public Stmt { SourceLoc Loc; Identifier TargetName; // Named target statement, if specified in the source. SourceLoc TargetLoc; DeclContext *DC; public: ContinueStmt(SourceLoc Loc, Identifier TargetName, SourceLoc TargetLoc, DeclContext *DC, std::optional implicit = std::nullopt) : Stmt(StmtKind::Continue, getDefaultImplicitFlag(implicit, Loc)), Loc(Loc), TargetName(TargetName), TargetLoc(TargetLoc), DC(DC) {} Identifier getTargetName() const { return TargetName; } void setTargetName(Identifier N) { TargetName = N; } SourceLoc getTargetLoc() const { return TargetLoc; } void setTargetLoc(SourceLoc L) { TargetLoc = L; } /// Retrieve the target statement being jumped to. LabeledStmt *getTarget() const; SourceLoc getLoc() const { return Loc; } SourceLoc getStartLoc() const { return Loc; } SourceLoc getEndLoc() const { return (TargetLoc.isValid() ? TargetLoc : Loc); } DeclContext *getDeclContext() const { return DC; } void setDeclContext(DeclContext *newDC) { DC = newDC; } static bool classof(const Stmt *S) { return S->getKind() == StmtKind::Continue; } }; /// FailStmt - A statement that indicates a failable, which is currently /// spelled as "return nil" and can only be used within failable initializers. class FailStmt : public Stmt { SourceLoc ReturnLoc; SourceLoc NilLoc; public: FailStmt(SourceLoc returnLoc, SourceLoc nilLoc, std::optional implicit = std::nullopt) : Stmt(StmtKind::Fail, getDefaultImplicitFlag(implicit, returnLoc)), ReturnLoc(returnLoc), NilLoc(nilLoc) {} SourceRange getSourceRange() const { return SourceRange::combine(ReturnLoc, NilLoc); } static bool classof(const Stmt *S) { return S->getKind() == StmtKind::Fail; } }; /// ThrowStmt - Throws an error. class ThrowStmt : public Stmt { Expr *SubExpr; SourceLoc ThrowLoc; public: explicit ThrowStmt(SourceLoc throwLoc, Expr *subExpr) : Stmt(StmtKind::Throw, /*Implicit=*/false), SubExpr(subExpr), ThrowLoc(throwLoc) {} SourceLoc getThrowLoc() const { return ThrowLoc; } SourceLoc getStartLoc() const { return ThrowLoc; } SourceLoc getEndLoc() const; SourceRange getSourceRange() const { return SourceRange(ThrowLoc, getEndLoc()); } Expr *getSubExpr() const { return SubExpr; } void setSubExpr(Expr *subExpr) { SubExpr = subExpr; } static bool classof(const Stmt *S) { return S->getKind() == StmtKind::Throw; } }; /// DiscardStmt - Consumes a noncopyable value and performs memberwise /// destruction of unconsumed fields, without invoking its deinit. Only /// supported form is "discard self". class DiscardStmt : public Stmt { Expr *SubExpr; SourceLoc DiscardLoc; AbstractFunctionDecl *InnermostMethod; public: explicit DiscardStmt(SourceLoc discardLoc, Expr *subExpr) : Stmt(StmtKind::Discard, /*Implicit=*/false), SubExpr(subExpr), DiscardLoc(discardLoc), InnermostMethod(nullptr) {} /// Location of the 'discard' keyword. SourceLoc getDiscardLoc() const { return DiscardLoc; } SourceLoc getStartLoc() const { return DiscardLoc; } SourceLoc getEndLoc() const; SourceRange getSourceRange() const { return SourceRange(DiscardLoc, getEndLoc()); } Expr *getSubExpr() const { return SubExpr; } void setSubExpr(Expr *subExpr) { SubExpr = subExpr; } /// Retrieves the saved innermost method / accessor decl in which this Stmt /// resides. Corresponds to \c DeclContext::getInnermostMethodContext. /// Must be saved with \c setInnermostMethodContext during typechecking. AbstractFunctionDecl *getInnermostMethodContext() { return InnermostMethod; } void setInnermostMethodContext(AbstractFunctionDecl *afd) { assert(afd); // shouldn't be clearing this InnermostMethod = afd; } static bool classof(const Stmt *S) { return S->getKind() == StmtKind::Discard; } }; /// PoundAssertStmt - Asserts that a condition is true, at compile time. class PoundAssertStmt : public Stmt { SourceRange Range; Expr *Condition; StringRef Message; public: PoundAssertStmt(SourceRange Range, Expr *condition, StringRef message) : Stmt(StmtKind::PoundAssert, /*Implicit=*/false), Range(Range), Condition(condition), Message(message) {} SourceRange getSourceRange() const { return Range; } Expr *getCondition() const { return Condition; } StringRef getMessage() const { return Message; } void setCondition(Expr *condition) { Condition = condition; } static bool classof(const Stmt *S) { return S->getKind() == StmtKind::PoundAssert; } }; SourceLoc extractNearestSourceLoc(const Stmt *stmt); inline void simple_display(llvm::raw_ostream &out, const Stmt *S) { if (S) out << Stmt::getKindName(S->getKind()); else out << "(null)"; } } // end namespace swift #endif // SWIFT_AST_STMT_H