//===--- SILLocation.h - Location information for SIL nodes -----*- C++ -*-===// // // 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 // //===----------------------------------------------------------------------===// #ifndef SWIFT_SIL_LOCATION_H #define SWIFT_SIL_LOCATION_H #include "llvm/ADT/PointerUnion.h" #include "swift/AST/Decl.h" #include "swift/AST/Expr.h" #include "swift/AST/Pattern.h" #include "swift/AST/Stmt.h" #include #include namespace swift { class SourceLoc; /// This is a pointer to the AST node that a SIL instruction was /// derived from. This may be null if AST information is unavailable or /// stripped. /// /// FIXME: This should eventually include inlining history, generics /// instantiation info, etc (when we get to it). /// class SILLocation { private: template struct base_type; template struct base_type::value>::type> { using type = Decl; }; template struct base_type::value>::type> { using type = Expr; }; template struct base_type::value>::type> { using type = Stmt; }; template struct base_type::value>::type> { using type = Pattern; }; typedef llvm::PointerUnion4 ASTNodeTy; public: enum LocationKind : unsigned { // FIXME: NoneKind is to be removed. NoneKind = 0, RegularKind = 1, ReturnKind = 2, ImplicitReturnKind = 3, InlinedKind = 4, MandatoryInlinedKind = 5, CleanupKind = 6, ArtificialUnreachableKind = 7 }; enum StorageKind : unsigned { UnknownKind = 0, ASTNodeKind = 1 << 3, SILFileKind = 1 << 4, DebugInfoKind = 1 << 3 | 1 << 4 }; struct DebugLoc { unsigned Line; unsigned Column; const char *Filename; DebugLoc(unsigned Line = 0, unsigned Column = 0, const char *Filename = nullptr) : Line(Line), Column(Column), Filename(Filename) { } }; protected: union UnderlyingLocation { UnderlyingLocation() : DebugInfoLoc({}) {} UnderlyingLocation(ASTNodeTy N) : ASTNode(N) {} UnderlyingLocation(SourceLoc L) : SILFileLoc(L) {} UnderlyingLocation(DebugLoc D) : DebugInfoLoc(D) {} struct ASTNodeLoc { ASTNodeLoc(ASTNodeTy N) : Primary(N) {} /// Primary AST location, always used for diagnostics. ASTNodeTy Primary; /// Sometimes the location for diagnostics needs to be /// different than the one used to emit the line table. If /// HasDebugLoc is set, this is used for the debug info. ASTNodeTy ForDebugger; } ASTNode; /// A location inside a textual .sil file. SourceLoc SILFileLoc; /// A deserialized source location. DebugLoc DebugInfoLoc; } Loc; /// The kind of this SIL location. unsigned KindData; enum { LocationKindBits = 3, LocationKindMask = 7, StorageKindBits = 2, StorageKindMask = (1 << 3) | (1 << 4), SpecialFlagsMask = ~ (LocationKindMask | StorageKindMask), /// Used to mark this instruction as part of auto-generated /// code block. AutoGeneratedBit = 5, /// Used to redefine the default source location used to /// represent this SILLocation. For example, when the host instruction /// is known to correspond to the beginning or the end of the source /// range of the ASTNode. PointsToStartBit = 6, PointsToEndBit = 7, /// Used to notify that this instruction belongs to the top- /// level (module) scope. /// /// FIXME: If Module becomes a Decl, this could be removed. IsInTopLevel = 8, /// Marks this instruction as belonging to the function prologue. IsInPrologue = 9 }; template T *getNodeAs(ASTNodeTy Node) const { using base = typename base_type::type*; return dyn_cast_or_null(Node.dyn_cast()); } template bool isNode(ASTNodeTy Node) const { assert(isASTNode()); if (Loc.ASTNode.Primary.is::type*>()) return isa(Node.get::type*>()); return false; } template T *castNodeTo(ASTNodeTy Node) const { return cast(Node.get::type*>()); } /// \defgroup SILLocation constructors. /// @{ /// This constructor is used to support getAs operation. SILLocation() { assert(Loc.DebugInfoLoc.Line == 0); } SILLocation(LocationKind K, unsigned Flags = 0) : KindData(K | Flags) { assert(Loc.DebugInfoLoc.Line == 0); } SILLocation(Stmt *S, LocationKind K, unsigned Flags = 0) : Loc(S), KindData(K | Flags) { setStorageKind(ASTNodeKind); assert(isASTNode()); } SILLocation(Expr *E, LocationKind K, unsigned Flags = 0) : Loc(E), KindData(K | Flags) { setStorageKind(ASTNodeKind); assert(isASTNode()); } SILLocation(Decl *D, LocationKind K, unsigned Flags = 0) : Loc(D), KindData(K | Flags) { setStorageKind(ASTNodeKind); assert(isASTNode()); } SILLocation(Pattern *P, LocationKind K, unsigned Flags = 0) : Loc(P), KindData(K | Flags) { setStorageKind(ASTNodeKind); assert(isASTNode()); } SILLocation(SourceLoc L, LocationKind K, unsigned Flags = 0) : Loc(L), KindData(K | Flags) { setStorageKind(SILFileKind); assert(isSILFile()); } SILLocation(DebugLoc L, LocationKind K, unsigned Flags = 0) : Loc(L), KindData(K | Flags) { setStorageKind(DebugInfoKind); assert(isDebugInfoLoc()); } /// @} private: friend class ImplicitReturnLocation; friend class MandatoryInlinedLocation; friend class InlinedLocation; friend class CleanupLocation; void setLocationKind(LocationKind K) { KindData |= (K & LocationKindMask); } void setStorageKind(StorageKind K) { KindData |= (K & StorageKindMask); } unsigned getSpecialFlags() const { return KindData & SpecialFlagsMask; } void setSpecialFlags(unsigned Flags) { KindData |= (Flags & SpecialFlagsMask); } SourceLoc getSourceLoc(ASTNodeTy N) const; SourceLoc getStartSourceLoc(ASTNodeTy N) const; SourceLoc getEndSourceLoc(ASTNodeTy N) const; public: /// When an ASTNode gets implicitly converted into a SILLocation we /// construct a RegularLocation. Since RegularLocations represent the majority /// of locations, this greatly simplifies the user code. SILLocation(Stmt *S) : Loc(S), KindData(RegularKind) { setStorageKind(ASTNodeKind); assert(isASTNode()); } SILLocation(Expr *E) : Loc(E), KindData(RegularKind) { setStorageKind(ASTNodeKind); assert(isASTNode()); } SILLocation(Decl *D) : Loc(D), KindData(RegularKind) { setStorageKind(ASTNodeKind); assert(isASTNode()); } SILLocation(Pattern *P) : Loc(P), KindData(RegularKind) { setStorageKind(ASTNodeKind); assert(isASTNode()); } /// Check if the location wraps an AST node or a valid SIL file /// location. /// /// Artificial locations and the top-level module locations will be null. bool isNull() const { switch (getStorageKind()) { case ASTNodeKind: return Loc.ASTNode.Primary.isNull(); case DebugInfoKind: return Loc.DebugInfoLoc.Filename; case SILFileKind: return Loc.SILFileLoc.isInvalid(); default: return true; } } explicit operator bool() const { return !isNull(); } /// Return whether this location is backed by an AST node. bool isASTNode() const { return getStorageKind() == ASTNodeKind; } /// Return whether this location came from a SIL file. bool isSILFile() const { return getStorageKind() == SILFileKind; } /// Return whether this location came from a textual SIL file. bool isDebugInfoLoc() const { return getStorageKind() == DebugInfoKind; } /// Marks the location as coming from auto-generated body. void markAutoGenerated() { KindData |= (1 << AutoGeneratedBit); } /// Returns true if the location represents an artificially generated /// body, such as thunks or default destructors. /// /// These locations should not be included in the debug line table. /// These might also need special handling by the debugger since they might /// contain calls, which the debugger could be able to step into. bool isAutoGenerated() const { return KindData & (1 << AutoGeneratedBit); } /// Changes the default source location position to point to start of /// the AST node. void pointToStart() { KindData |= (1 << PointsToStartBit); } /// Changes the default source location position to point to the end of /// the AST node. void pointToEnd() { KindData |= (1 << PointsToEndBit); } /// Mark this location as the location corresponding to the top-level /// (module-level) code. void markAsInTopLevel() { KindData |= (1 << IsInTopLevel); } /// Check is this location is associated with the top level/module. bool isInTopLevel() const { return KindData & (1 << IsInTopLevel); } /// Mark this location as being part of the function /// prologue, which means that it deals with setting up the stack /// frame. The first breakpoint location in a function is at the end /// of the prologue. void markAsPrologue() { KindData |= (1 << IsInPrologue); } /// Check is this location is part of a function's implicit prologue. bool isInPrologue() const { return KindData & (1 << IsInPrologue); } /// Add an ASTNode to use as the location for debugging /// purposes if this location is different from the location used /// for diagnostics. template void setDebugLoc(T *ASTNodeForDebugging) { assert(!hasDebugLoc() && "DebugLoc already present"); assert(isASTNode() && "not an AST location"); Loc.ASTNode.ForDebugger = ASTNodeForDebugging; } bool hasDebugLoc() const { return isASTNode() && !Loc.ASTNode.ForDebugger.isNull(); } /// Populate this empty SILLocation with a DebugLoc. void setDebugInfoLoc(DebugLoc L) { Loc.DebugInfoLoc = L; setStorageKind(DebugInfoKind); } /// Check if the corresponding source code location definitely points /// to the start of the AST node. bool alwaysPointsToStart() const { return KindData & (1 << PointsToStartBit);} /// Check if the corresponding source code location definitely points /// to the end of the AST node. bool alwaysPointsToEnd() const { return KindData & (1 << PointsToEndBit); } LocationKind getKind() const { return LocationKind(KindData & LocationKindMask); } StorageKind getStorageKind() const { return StorageKind(KindData & StorageKindMask); } template bool is() const { return T::isKind(*this); } template T castTo() const { assert(T::isKind(*this)); T t; SILLocation& tr = t; tr = *this; return t; } template Optional getAs() const { if (!T::isKind(*this)) return Optional(); T t; SILLocation& tr = t; tr = *this; return t; } /// If the current value is of the specified AST unit type T, /// return it, otherwise return null. template T *getAsASTNode() const { return isASTNode() ? getNodeAs(Loc.ASTNode.Primary) : nullptr; } /// Returns true if the Location currently points to the AST node /// matching type T. template bool isASTNode() const { return isASTNode() && isNode(Loc.ASTNode.Primary); } /// Returns the primary value as the specified AST node type. If the /// specified type is incorrect, asserts. template T *castToASTNode() const { assert(isASTNode()); return castNodeTo(Loc.ASTNode.Primary); } /// If the DebugLoc is of the specified AST unit type T, /// return it, otherwise return null. template T *getDebugLocAsASTNode() const { assert(hasDebugLoc() && "no debug location"); return getNodeAs(Loc.ASTNode.ForDebugger); } SourceLoc getDebugSourceLoc() const; SourceLoc getSourceLoc() const; SourceLoc getStartSourceLoc() const; SourceLoc getEndSourceLoc() const; SourceRange getSourceRange() const { return { getStartSourceLoc(), getEndSourceLoc() }; } /// Fingerprint a DebugLoc for use in a DenseMap. typedef std::pair, const void *> DebugLocKey; struct DebugLocHash : public DebugLocKey { DebugLocHash(DebugLoc L) : DebugLocKey({{L.Line, L.Column}, L.Filename}) {} }; /// Extract the line, column, and filename. static DebugLoc decode(SourceLoc Loc, const SourceManager &SM); /// Return the decoded debug location. DebugLoc decodeDebugLoc(const SourceManager &SM) const { return isDebugInfoLoc() ? Loc.DebugInfoLoc : decode(getDebugSourceLoc(), SM); } /// Pretty-print the value. void dump(const SourceManager &SM) const; void print(raw_ostream &OS, const SourceManager &SM) const; /// Returns an opaque pointer value for the debug location that may /// be used to unique debug locations. const void *getOpaquePointerValue() const { if (isSILFile()) return Loc.SILFileLoc.getOpaquePointerValue(); if (isASTNode()) return Loc.ASTNode.Primary.getOpaqueValue(); else return 0; } unsigned getOpaqueKind() const { return KindData; } inline bool operator==(const SILLocation& R) const { return KindData == R.KindData && Loc.ASTNode.Primary.getOpaqueValue() == R.Loc.ASTNode.Primary.getOpaqueValue() && Loc.ASTNode.ForDebugger.getOpaqueValue() == R.Loc.ASTNode.ForDebugger.getOpaqueValue(); } }; /// Allowed on any instruction. class RegularLocation : public SILLocation { public: RegularLocation(Stmt *S) : SILLocation(S, RegularKind) {} RegularLocation(Expr *E) : SILLocation(E, RegularKind) {} RegularLocation(Decl *D) : SILLocation(D, RegularKind) {} RegularLocation(Pattern *P) : SILLocation(P, RegularKind) {} RegularLocation(SourceLoc L) : SILLocation(L, RegularKind) {} RegularLocation(DebugLoc L) : SILLocation(L, RegularKind) {} /// Returns a location representing the module. static RegularLocation getModuleLocation() { RegularLocation Loc; Loc.markAsInTopLevel(); return Loc; } /// If the current value is of the specified AST unit type T, /// return it, otherwise return null. template T *getAs() const { return getNodeAs(Loc.ASTNode); } /// Returns true if the Location currently points to the AST node /// matching type T. template bool is() const { return isNode(Loc.ASTNode); } /// Returns the primary value as the specified AST node type. If the /// specified type is incorrect, asserts. template T *castTo() const { return castNodeTo(Loc.ASTNode); } static RegularLocation getAutoGeneratedLocation() { RegularLocation AL; AL.markAutoGenerated(); return AL; } private: RegularLocation() : SILLocation(RegularKind) {} friend class SILLocation; static bool isKind(const SILLocation& L) { return L.getKind() == RegularKind; } }; /// Used to represent a return instruction in user code. /// /// Allowed on an BranchInst, ReturnInst. class ReturnLocation : public SILLocation { public: ReturnLocation(ReturnStmt *RS) : SILLocation(RS, ReturnKind) {} /// Construct the return location for a constructor or a destructor. ReturnLocation(BraceStmt *BS) : SILLocation(BS, ReturnKind) {} ReturnStmt *get() { return castToASTNode(); } private: friend class SILLocation; static bool isKind(const SILLocation& L) { return L.getKind() == ReturnKind; } }; /// Used on the instruction that was generated to represent an implicit /// return from a function. /// /// Allowed on an BranchInst, ReturnInst. class ImplicitReturnLocation : public SILLocation { public: ImplicitReturnLocation(AbstractClosureExpr *E) : SILLocation(E, ImplicitReturnKind) { } ImplicitReturnLocation(ReturnStmt *S) : SILLocation(S, ImplicitReturnKind) { } ImplicitReturnLocation(AbstractFunctionDecl *AFD) : SILLocation(AFD, ImplicitReturnKind) { } /// Construct from a RegularLocation; preserve all special bits. /// /// Note, this can construct an implicit return for an arbitrary expression /// (specifically, in case of auto-generated bodies). static SILLocation getImplicitReturnLoc(SILLocation L) { assert(L.isASTNode() || L.isASTNode() || L.isASTNode() || (L.isNull() && L.isInTopLevel())); L.setLocationKind(ImplicitReturnKind); return L; } AbstractClosureExpr *get() { return castToASTNode(); } private: friend class SILLocation; static bool isKind(const SILLocation& L) { return L.getKind() == ImplicitReturnKind; } ImplicitReturnLocation() : SILLocation(ImplicitReturnKind) {} }; /// Marks instructions that correspond to inlined function body and /// setup code. This location should not be used for inlined transparent /// bodies, see MandatoryInlinedLocation. /// /// This location wraps the call site ASTNode. /// /// Allowed on any instruction except for ReturnInst. class InlinedLocation : public SILLocation { public: InlinedLocation(Expr *CallSite) : SILLocation(CallSite, InlinedKind) {} InlinedLocation(Stmt *S) : SILLocation(S, InlinedKind) {} InlinedLocation(Pattern *P) : SILLocation(P, InlinedKind) {} InlinedLocation(Decl *D) : SILLocation(D, InlinedKind) {} /// Constructs an inlined location when the call site is represented by a /// SILFile location. InlinedLocation(SourceLoc L) : SILLocation(InlinedKind) { setStorageKind(SILFileKind); Loc.SILFileLoc = L; } static InlinedLocation getInlinedLocation(SILLocation L); private: friend class SILLocation; static bool isKind(const SILLocation& L) { return L.getKind() == InlinedKind; } InlinedLocation() : SILLocation(InlinedKind) {} InlinedLocation(Expr *E, unsigned F) : SILLocation(E, InlinedKind, F) {} InlinedLocation(Stmt *S, unsigned F) : SILLocation(S, InlinedKind, F) {} InlinedLocation(Pattern *P, unsigned F) : SILLocation(P, InlinedKind, F) {} InlinedLocation(Decl *D, unsigned F) : SILLocation(D, InlinedKind, F) {} InlinedLocation(SourceLoc L, unsigned F) : SILLocation(L, InlinedKind, F) {} static InlinedLocation getModuleLocation(unsigned Flags) { auto L = InlinedLocation(); L.setSpecialFlags(Flags); return L; } }; /// Marks instructions that correspond to inlined function body and /// setup code for transparent functions, inlined as part of mandatory inlining /// pass. /// /// This location wraps the call site ASTNode. /// /// Allowed on any instruction except for ReturnInst. class MandatoryInlinedLocation : public SILLocation { public: MandatoryInlinedLocation(Expr *CallSite) : SILLocation(CallSite, MandatoryInlinedKind) {} MandatoryInlinedLocation(Stmt *S) : SILLocation(S, MandatoryInlinedKind) {} MandatoryInlinedLocation(Pattern *P) : SILLocation(P, MandatoryInlinedKind) {} MandatoryInlinedLocation(Decl *D) : SILLocation(D, MandatoryInlinedKind) {} /// Constructs an inlined location when the call site is represented by a /// SILFile location. MandatoryInlinedLocation(SourceLoc L) : SILLocation(L, MandatoryInlinedKind) {} static MandatoryInlinedLocation getMandatoryInlinedLocation(SILLocation L); static MandatoryInlinedLocation getModuleLocation(unsigned Flags) { auto L = MandatoryInlinedLocation(); L.setSpecialFlags(Flags); return L; } private: friend class SILLocation; static bool isKind(const SILLocation& L) { return L.getKind() == MandatoryInlinedKind; } MandatoryInlinedLocation() : SILLocation(MandatoryInlinedKind) {} MandatoryInlinedLocation(Expr *E, unsigned F) : SILLocation(E, MandatoryInlinedKind, F) {} MandatoryInlinedLocation(Stmt *S, unsigned F) : SILLocation(S, MandatoryInlinedKind, F) {} MandatoryInlinedLocation(Pattern *P, unsigned F) : SILLocation(P, MandatoryInlinedKind, F) {} MandatoryInlinedLocation(Decl *D, unsigned F) : SILLocation(D, MandatoryInlinedKind, F) {} MandatoryInlinedLocation(SourceLoc L, unsigned F) : SILLocation(L, MandatoryInlinedKind, F) {} }; /// Used on the instruction performing auto-generated cleanup such as /// deallocs, destructor calls. /// /// The cleanups are performed after completing the evaluation of the AST Node /// wrapped inside the SILLocation. This location wraps the statement /// representing the enclosing scope, for example, FuncDecl, ParenExpr. The /// scope's end location points to the SourceLoc that shows when the operation /// is performed at runtime. /// /// Allowed on any instruction except for ReturnInst. /// Locations of an inlined destructor should also be represented by this. class CleanupLocation : public SILLocation { public: CleanupLocation(Expr *E) : SILLocation(E, CleanupKind) {} CleanupLocation(Stmt *S) : SILLocation(S, CleanupKind) {} CleanupLocation(Pattern *P) : SILLocation(P, CleanupKind) {} CleanupLocation(Decl *D) : SILLocation(D, CleanupKind) {} static CleanupLocation get(SILLocation L); /// Returns a location representing a cleanup on the module level. static CleanupLocation getModuleCleanupLocation() { CleanupLocation Loc; Loc.markAsInTopLevel(); return Loc; } private: friend class SILLocation; static bool isKind(const SILLocation& L) { return L.getKind() == CleanupKind; } CleanupLocation() : SILLocation(CleanupKind) {} CleanupLocation(Expr *E, unsigned F) : SILLocation(E, CleanupKind, F) {} CleanupLocation(Stmt *S, unsigned F) : SILLocation(S, CleanupKind, F) {} CleanupLocation(Pattern *P, unsigned F) : SILLocation(P, CleanupKind, F) {} CleanupLocation(Decl *D, unsigned F) : SILLocation(D, CleanupKind, F) {} }; /// Used to represent an unreachable location that was /// auto-generated and has no correspondence to user code. It should /// not be used in diagnostics or for debugging. /// /// Differentiates an unreachable instruction, which is generated by /// DCE, from an unreachable instruction in user code (output of SILGen). /// Allowed on an unreachable instruction. class ArtificialUnreachableLocation : public SILLocation { public: ArtificialUnreachableLocation() : SILLocation(ArtificialUnreachableKind) {} private: friend class SILLocation; static bool isKind(const SILLocation& L) { return (L.getKind() == ArtificialUnreachableKind); } }; class SILDebugScope; /// A SILLocation paired with a SILDebugScope. class SILDebugLocation { const SILDebugScope *Scope = nullptr; SILLocation Location; public: SILDebugLocation() : Scope(nullptr), Location(RegularLocation(SourceLoc())) {} SILDebugLocation(SILLocation Loc, const SILDebugScope *DS) : Scope(DS), Location(Loc) {} SILLocation getLocation() const { return Location; } const SILDebugScope *getScope() const { return Scope; } }; } // end swift namespace #endif