// swan_sil_parser.hpp — SWAN-inspired SIL parser for fold-swift // // This is a focused C++ reimplementation of the SIL parser concepts from // SWAN (themaplelab/swan, ESEC/FSE '20). It parses canonical SIL text // (output of `swiftc -emit-sil`) into a lightweight AST and exports // RustExprNode-compatible JSON for the fold symbolic IR encoder. // // Architecture: // SIL text → recursive-descent parser → SIL AST nodes // SIL AST → SWIRL-like operator extractor → RustExprNode JSON // // The parser focuses on security-relevant SIL operations: // - unchecked casts (ref_cast, addr_cast, trivial_bit_cast) // - load/store through pointer types // - function calls (apply) // - cond_fail (force-unwrap) // - struct/tuple/enum access patterns // - reference-counting operations // // The existing regex-based parser is kept as a fallback for SIL constructs // that this parser doesn't yet handle. #pragma once #include #include #include #include #include #include #include #include namespace fold_swift { // ========================================================================= // SIL Lexer / Source Location // ========================================================================= struct SilLoc { std::string file; std::uint32_t line = 0; std::uint32_t column = 0; [[nodiscard]] bool valid() const { return !file.empty() && line != 0; } }; struct SilSourceInfo { std::uint32_t scope_ref = 0; SilLoc loc; [[nodiscard]] bool valid() const { return loc.valid(); } }; // ========================================================================= // SIL Type (simplified — only what we need for security analysis) // ========================================================================= enum class SilTypeKind : std::uint8_t { Unknown, Named, // e.g. Int, String, UnsafePointer Address, // @inout or *T Function, // function type Tuple, // tuple type Optional, // Optional }; struct SilType { SilTypeKind kind = SilTypeKind::Unknown; std::string name; // for Named std::vector params; // generic params / tuple elements / function params bool is_optional = false; bool is_pointer = false; // UnsafePointer, UnsafeMutablePointer, etc. bool is_raw_pointer = false; // UnsafeRawPointer }; // ========================================================================= // SIL Operand / Result // ========================================================================= struct SilOperand { std::string value; // %0, %1, etc. SilType type; }; struct SilResult { std::vector value_names; // %0 = ... }; // ========================================================================= // SIL DeclRef (declaration reference — class/struct member, etc.) // ========================================================================= struct SilDeclRef { std::vector parts; // e.g. ["MyClass", "myMethod"] std::string kind; // func, allocator, enumelt, etc. }; // ========================================================================= // SIL Operators (the ~75 SIL instructions) // We model the security-relevant subset explicitly; the rest go through // a generic "Other" path. // ========================================================================= enum class SilOperatorKind : std::uint8_t { // Allocation AllocStack, AllocRef, AllocBox, DeallocStack, DeallocRef, DeallocBox, ProjectBox, // Memory Access Load, Store, LoadBorrow, BeginBorrow, EndBorrow, EndLifetime, CopyAddr, DestroyAddr, IndexAddr, IndexRawPointer, BeginAccess, EndAccess, // Reference Counting StrongRetain, StrongRelease, SetDeallocating, UnownedRetain, UnownedRelease, LoadWeak, StoreWeak, FixLifetime, IsUnique, IsEscapingClosure, // Literals FunctionRef, DynamicFunctionRef, GlobalAddr, IntegerLiteral, FloatLiteral, StringLiteral, // Dynamic Dispatch ClassMethod, ObjcMethod, SuperMethod, WitnessMethod, // Function Application Apply, BeginApply, AbortApply, EndApply, PartialApply, Builtin, // Aggregate Types RetainValue, ReleaseValue, CopyValue, DestroyValue, Tuple, TupleExtract, TupleElementAddr, DestructureTuple, Struct, StructExtract, StructElementAddr, DestructureStruct, RefElementAddr, RefTailAddr, // Enums Enum, UncheckedEnumData, InitEnumDataAddr, InjectEnumAddr, UncheckedTakeEnumDataAddr, SelectEnum, SelectEnumAddr, // Protocol / Existential InitExistentialAddr, DeinitExistentialAddr, OpenExistentialAddr, OpenExistentialRef, // Unchecked Conversions (security-critical) Upcast, AddressToPointer, PointerToAddress, UncheckedRefCast, UncheckedRefCastAddr, UncheckedAddrCast, UncheckedTrivialBitCast, UncheckedBitwiseCast, UncheckedOwnershipConversion, RefToRawPointer, RawPointerToRef, RefToUnowned, UnownedToRef, RefToUnmanaged, UnmanagedToRef, ConvertFunction, ConvertEscapeToNoescape, // Checked Conversions UnconditionalCheckedCast, UnconditionalCheckedCastAddr, // Runtime Failures CondFail, // Other Metatype, ValueMetatype, ExistentialMetatype, SelectValue, Keypath, DebugValue, DebugValueAddr, Unknown, }; struct SilOperator { SilOperatorKind kind = SilOperatorKind::Unknown; SilResult result; std::vector operands; SilType type; // result type or target type for casts SilType from_type; // source type for addr casts SilDeclRef decl_ref; // for struct_extract, enum, etc. std::int64_t int_value = 0; std::string string_value; std::string callee_name; // for function_ref, apply std::vector apply_args; // value operands for apply bool nothrow = false; SilSourceInfo source_info; // Metadata bool is_unchecked_cast = false; bool is_load_through_pointer = false; bool is_cond_fail = false; std::string raw_text; // original SIL text line for fallback }; // ========================================================================= // SIL Terminators // ========================================================================= enum class SilTerminatorKind : std::uint8_t { Unreachable, Return, Throw, Yield, Unwind, Branch, CondBranch, SwitchValue, SwitchEnum, SwitchEnumAddr, DynamicMethodBranch, CheckedCastBranch, CheckedCastAddrBranch, TryApply, Unknown, }; struct SilTerminator { SilTerminatorKind kind = SilTerminatorKind::Unknown; SilOperand operand; // ret operand, switch operand, etc. std::vector args; // branch args std::string target_label; // branch / cond_br target std::string true_label; // cond_br true std::string false_label; // cond_br false std::string normal_label; // try_apply normal std::string error_label; // try_apply error SilSourceInfo source_info; }; // ========================================================================= // SIL Basic Block // ========================================================================= struct SilBlock { std::string identifier; std::vector arguments; std::vector operators; SilTerminator terminator; }; // ========================================================================= // SIL Function // ========================================================================= struct SilFunction { std::string name; // demangled std::string mangled_name; SilType type; std::vector blocks; int start_line = 0; }; // ========================================================================= // SIL Module (full parse result) // ========================================================================= struct SilModule { std::string source_file; std::vector functions; std::vector imports; bool parse_success = false; std::string parse_error; // Statistics std::uint32_t total_blocks = 0; std::uint32_t total_operators = 0; }; // ========================================================================= // SIL Parser (recursive-descent, character-at-a-time) // ========================================================================= class SilTextParser { public: explicit SilTextParser(std::string text, std::string source_path = ""); SilModule parseModule(); void setDemangleHook(std::function hook); private: // --- Lexer --- char peek() const; char peekAt(int offset) const; bool peekStr(std::string_view s) const; void skipWhitespace(); void skipWhitespaceAndComments(); bool skipToken(std::string_view s); void takeToken(std::string_view s); std::string takeIdentifier(); std::string takeUntil(char delim); std::string takeWhile(int (*pred)(int)); bool tryTake(const std::string& s); bool atEnd() const; [[noreturn]] void parseError(const std::string& msg) const; // --- Parser helpers --- std::int64_t parseInt(); std::string parseMangledName(); std::string parseValueOperand(); int parseScopeRef(); SilLoc parseLoc(); SilSourceInfo parseSourceInfo(); SilType parseType(); SilType parseFunctionType(); SilType parseNamedType(); SilType parseOptionalType(SilType base); SilType parseAddressType(); SilType parseTupleType(); SilType parseAttributedType(); SilDeclRef parseDeclRef(); std::string parseQuotedString(); void resolveSourceInfo(SilSourceInfo& info) const; // --- Module-level --- void parseImport(); SilFunction parseFunction(); SilBlock parseBlock(); SilOperator parseOperator(); SilTerminator parseTerminator(); SilOperand parseOperand(); SilResult parseResult(); // --- Operator parsers (grouped by category) --- SilOperator parseAllocationOp(const std::string& name); SilOperator parseMemoryAccessOp(const std::string& name); SilOperator parseRefCountingOp(const std::string& name); SilOperator parseLiteralOp(const std::string& name); SilOperator parseDispatchOp(const std::string& name); SilOperator parseApplyOp(const std::string& name); SilOperator parseAggregateOp(const std::string& name); SilOperator parseEnumOp(const std::string& name); SilOperator parseConversionOp(const std::string& name); SilOperator parseMiscOp(const std::string& name); // --- Source location tracking --- // SIL embeds source locations in comments: // file:line:col // Track the most recently seen location to attach to next operator. void trackCommentLoc(); // --- State --- std::string source_; std::string text_; std::size_t cursor_ = 0; std::size_t len_ = 0; SilLoc last_comment_loc_; // tracked from // file:line:col comments std::unordered_map scope_map_; // scope_id -> loc std::function demangle_hook_; }; // ========================================================================= // SIL → RustExprNode JSON Converter // ========================================================================= // Converts parsed SIL operators/terminators to RustExprNode-compatible JSON // trees. Also produces finding NDJSON lines for security-relevant operations. class SilToJsonConverter { public: // Convert a full SIL module to fold NDJSON output lines. // Each line is a JSON object written to `out`. void convertModule(const SilModule& mod, std::ostream& out); private: // Emit fold NDJSON record kinds void emitInfo(const std::string& unit, std::ostream& out); void emitFunction(const SilFunction& fn, const std::string& file, std::ostream& out); void emitInterface(const SilFunction& fn, const std::string& file, std::ostream& out); void emitIr(const SilFunction& fn, const std::string& file, std::ostream& out); void emitFinding(const std::string& file, int line, int col, const std::string& function, const std::string& kind, const std::string& hazard, const std::string& message, std::ostream& out); // Build RustExprNode JSON from a SIL operator nlohmann::json operatorToExpr(const SilOperator& op, const std::string& file); nlohmann::json buildCallExpr(const std::string& callee, const std::vector& args, int line, int col, const std::string& file); nlohmann::json buildLiteralExpr(std::int64_t val, int width, bool signedness, int line, int col, const std::string& file); nlohmann::json buildStringLiteralExpr(const std::string& val, int line, int col, const std::string& file); nlohmann::json buildDeclRefExpr(const std::string& symbol, int line, int col, const std::string& file); nlohmann::json buildUnaryExpr(const std::string& op_name, nlohmann::json sub, int line, int col, const std::string& file); nlohmann::json buildBinaryExpr(const std::string& op_name, nlohmann::json lhs, nlohmann::json rhs, int line, int col, const std::string& file); nlohmann::json buildCastExpr(const std::string& target_type, nlohmann::json sub, int line, int col, const std::string& file); nlohmann::json buildMemberExpr(nlohmann::json base, const std::string& field, int line, int col, const std::string& file); // Build a source location object nlohmann::json makeLoc(int line, int col, const std::string& file); }; } // namespace fold_swift