12 Commits 12a2aebeb6 ... 67848a8c51

Tác giả SHA1 Thông báo Ngày
  Andrew Kaster 67848a8c51 CI: Add nightly Swift jobs for both Linux and macOS 1 tuần trước cách đây
  Andrew Kaster 382b574946 CI: Rework Swift build to be a separate job from normal macOS builds 1 tuần trước cách đây
  Andrew Kaster 345cd6b9c9 CI: Add setup steps to install swift toolchain from swiftly 1 tuần trước cách đây
  Andrew Kaster 195f0106a4 Meta: Add swiftly .swift-version to .gitignore 1 tuần trước cách đây
  Andrew Kaster 1ed4e64575 CMake: Refactor FindSwiftTesting to find TestingMacros dir on macOS 2 tuần trước cách đây
  Andrew Kaster 9ee2473aa4 LibWeb+LibGC: Import GC swift module into LibWeb and an initial user 2 tuần trước cách đây
  Andrew Kaster 8554ee386e LibGC: Teach Swift bindings about Cell and Cell::Visitor 2 tuần trước cách đây
  Andrew Kaster 4ab89d8bbb AK: Add cxxCast template to allow Swift to perform simple checked casts 2 tuần trước cách đây
  Andrew Kaster e4c88915ab LibGC+LibJS+LibWeb: Add workaround for Swift boolean bitfield issue 2 tuần trước cách đây
  Andrew Kaster 08b27f7b6e AK: Mark Function as SWIFT_UNSAFE_REFERENCE 2 tuần trước cách đây
  Andrew Kaster c85043f2e1 CI: Bump CI Xcode version to 16.2 2 tuần trước cách đây
  Timothy Flynn d403f02988 AK: Remove unused capacity field from StringData 1 tuần trước cách đây

+ 57 - 6
.github/actions/setup/action.yml

@@ -8,8 +8,12 @@ inputs:
     default: 'Linux'
   arch:
     description: 'Target Architecture to set up'
-    required: false
+    required: true
     default: 'x86_64'
+  toolchain:
+    description: 'Toolchain to set up'
+    required: true
+    default: 'Clang'
 runs:
   using: "composite"
   steps:
@@ -31,12 +35,15 @@ runs:
         sudo add-apt-repository 'deb http://apt.llvm.org/noble/ llvm-toolchain-noble-19 main'
 
         sudo apt-get update -y
-        sudo apt-get install -y autoconf autoconf-archive automake build-essential ccache clang-19 clang++-19 cmake curl fonts-liberation2 \
-            gcc-13 g++-13 libegl1-mesa-dev libgl1-mesa-dev libpulse-dev libssl-dev \
+        sudo apt-get install -y autoconf autoconf-archive automake build-essential ccache cmake curl fonts-liberation2 \
+            gcc-13 g++-13 libcurl4-openssl-dev libegl1-mesa-dev libgl1-mesa-dev libpulse-dev libssl-dev \
             libstdc++-13-dev lld-19 nasm ninja-build qt6-base-dev qt6-tools-dev-tools tar unzip zip
 
-        sudo update-alternatives --install /usr/bin/clang clang /usr/bin/clang-19 100
-        sudo update-alternatives --install /usr/bin/clang++ clang++ /usr/bin/clang++-19 100
+        if ${{ inputs.toolchain == 'Clang' }} ; then
+          sudo apt-get install -y clang-19 clang++-19
+          sudo update-alternatives --install /usr/bin/clang clang /usr/bin/clang-19 100
+          sudo update-alternatives --install /usr/bin/clang++ clang++ /usr/bin/clang++-19 100
+        fi
         sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-13 100
         sudo update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-13 100
 
@@ -51,7 +58,51 @@ runs:
       if: ${{ inputs.os == 'macOS' || inputs.os == 'Android' }}
       uses: maxim-lobanov/setup-xcode@v1
       with:
-        xcode-version: 16.1
+        xcode-version: 16.2
+
+    - name: 'Install Swift toolchain'
+      if: ${{ inputs.toolchain == 'Swift' }}
+      shell: bash
+      run: |
+        set -e
+
+        export SWIFTLY_HOME_DIR=${{ github.workspace }}/.swiftly/share
+        export SWIFTLY_BIN_DIR=${{ github.workspace }}/.swiftly/bin
+
+        echo "$SWIFTLY_BIN_DIR" >> $GITHUB_PATH
+        echo "SWIFTLY_HOME_DIR=$SWIFTLY_HOME_DIR" >> $GITHUB_ENV
+        echo "SWIFTLY_BIN_DIR=$SWIFTLY_BIN_DIR" >> $GITHUB_ENV
+
+        export PATH=$SWIFTLY_BIN_DIR:$PATH
+
+        mkdir -p $SWIFTLY_HOME_DIR
+        mkdir -p $SWIFTLY_BIN_DIR
+
+        if ${{ inputs.os == 'Linux' }} ; then
+          curl -O https://download.swift.org/swiftly/linux/swiftly-${{ inputs.arch }}.tar.gz
+          file swiftly-${{ inputs.arch }}.tar.gz
+          tar -xzf swiftly-${{ inputs.arch }}.tar.gz -C $SWIFTLY_BIN_DIR
+          rm swiftly-${{ inputs.arch }}.tar.gz
+        else
+          # FIXME: https://github.com/swiftlang/swiftly/issues/271
+          #    Why does this drop files in $HOME? That's not very CI-friendly
+          curl -O https://download.swift.org/swiftly/darwin/swiftly.pkg
+          installer -pkg swiftly.pkg -target CurrentUserHomeDirectory
+          cp ~/.swiftly/bin/swiftly $SWIFTLY_BIN_DIR
+          rm swiftly.pkg
+        fi
+
+        swiftly init \
+          --no-modify-profile \
+          --quiet-shell-followup \
+          --assume-yes \
+          --skip-install \
+          --verbose
+
+        echo "swiftly version: $(swiftly --version)" >&2
+
+        swiftly install --use main-snapshot-2025-04-02
+        swiftly list
 
     - name: 'Install Dependencies'
       if: ${{ inputs.os == 'macOS' || inputs.os == 'Android' }}

+ 15 - 18
.github/workflows/lagom-template.yml

@@ -58,13 +58,19 @@ jobs:
         with:
           os: ${{ inputs.os_name }}
           arch: ${{ inputs.arch }}
+          toolchain: ${{ inputs.toolchain }}
 
       # === PREPARE FOR BUILDING ===
 
       - name: Assign Build Parameters
         id: 'build-parameters'
         run: |
-          if ${{ inputs.os_name == 'Linux' }} ; then
+          CMAKE_OPTIONS="-DENABLE_QT=ON"
+          if ${{ inputs.toolchain == 'Swift' }} ; then
+            echo "host_cc=$(swiftly use --print-location)/usr/bin/clang" >> "$GITHUB_OUTPUT"
+            echo "host_cxx=$(swiftly use --print-location)/usr/bin/clang++" >> "$GITHUB_OUTPUT"
+            CMAKE_OPTIONS="$CMAKE_OPTIONS -DENABLE_SWIFT=ON"
+          elif ${{ inputs.os_name == 'Linux' }} ; then
             if ${{ inputs.toolchain == 'Clang' }} ; then
               echo "host_cc=clang-19" >> "$GITHUB_OUTPUT"
               echo "host_cxx=clang++-19" >> "$GITHUB_OUTPUT"
@@ -79,19 +85,21 @@ jobs:
 
           if ${{ inputs.clang_plugins }} ; then
             echo "ccache_key=${{ inputs.build_preset }}-CLANG_PLUGINS" >> "$GITHUB_OUTPUT"
-            echo "cmake_options=-DENABLE_CLANG_PLUGINS=ON" >> "$GITHUB_OUTPUT"
+            CMAKE_OPTIONS="$CMAKE_OPTIONS -DENABLE_CLANG_PLUGINS=ON"
           else
             echo "ccache_key=${{ inputs.build_preset }}" >> "$GITHUB_OUTPUT"
             if ${{ inputs.os_name == 'Linux' && inputs.arch == 'arm64' }} ; then
                 # FIXME: https://github.com/WebAssembly/wabt/issues/2533
                 #        wabt doesn't have binary releases for arm64 Linux
                 PKGCONFIG=$(which pkg-config)
-                echo "cmake_options=-DPKG_CONFIG_EXECUTABLE=$PKGCONFIG" >> "$GITHUB_OUTPUT"
+                CMAKE_OPTIONS="$CMAKE_OPTIONS -DPKG_CONFIG_EXECUTABLE=$PKGCONFIG"
             else
-              echo "cmake_options=-DINCLUDE_WASM_SPEC_TESTS=ON -DWASM_SPEC_TEST_SKIP_FORMATTING=ON" >> "$GITHUB_OUTPUT"
+              CMAKE_OPTIONS="$CMAKE_OPTIONS -DINCLUDE_WASM_SPEC_TESTS=ON -DWASM_SPEC_TEST_SKIP_FORMATTING=ON"
             fi
           fi
 
+          echo "cmake_options=$CMAKE_OPTIONS" >> "$GITHUB_OUTPUT"
+
       - name: Restore Caches
         uses: ./.github/actions/cache-restore
         id: 'cache-restore'
@@ -156,23 +164,12 @@ jobs:
           cmake --build .
           cmake --install . --strip --prefix ${{ github.workspace }}/Install
 
-      - name: Enable the Ladybird Qt chrome
-        if: ${{ inputs.os_name == 'macOS' && inputs.build_preset == 'Sanitizer_CI' }}
-        working-directory: ${{ github.workspace }}
-        run: cmake -B Build -DENABLE_QT=ON
-
-      - name: Build the Ladybird Qt chrome
-        if: ${{ inputs.os_name == 'macOS' && inputs.build_preset == 'Sanitizer_CI' }}
-        working-directory: ${{ github.workspace }}/Build
-        run: cmake --build .
-
-      - name: Enable the AppKit chrome with Swift files
+      - name: Enable the Ladybird AppKit chrome
         if: ${{ inputs.os_name == 'macOS' && inputs.build_preset == 'Sanitizer_CI' }}
         working-directory: ${{ github.workspace }}
-        # FIXME: Don't force release build after https://github.com/LadybirdBrowser/ladybird/issues/1101 is fixed
-        run: cmake -B Build -DENABLE_QT=OFF -DENABLE_SWIFT=ON -DCMAKE_BUILD_TYPE=RelWithDebInfo
+        run: cmake -B Build -DENABLE_QT=OFF
 
-      - name: Build the AppKit chrome with Swift files
+      - name: Build the Ladybird AppKit chrome
         if: ${{ inputs.os_name == 'macOS' && inputs.build_preset == 'Sanitizer_CI' }}
         working-directory: ${{ github.workspace }}/Build
         run: cmake --build .

+ 14 - 0
.github/workflows/nightly-lagom.yml

@@ -46,6 +46,20 @@ jobs:
             toolchain: 'Clang'
             clang_plugins: false
 
+          - os_name: 'Linux'
+            os: ubuntu-24.04
+            arch: 'x86_64'
+            build_preset: 'Sanitizer_CI'
+            toolchain: 'Swift'
+            clang_plugins: false
+
+          - os_name: 'macOS'
+            os: macos-15
+            arch: 'arm64'
+            build_preset: 'Sanitizer_CI'
+            toolchain: 'Swift'
+            clang_plugins: false
+
     uses: ./.github/workflows/lagom-template.yml
     with:
       toolchain: ${{ matrix.toolchain }}

+ 1 - 0
.gitignore

@@ -27,6 +27,7 @@ output/
 .vim/
 .exrc
 .helix/
+.swift-version
 
 # Environments
 .venv/

+ 2 - 1
AK/Function.h

@@ -35,6 +35,7 @@
 #include <AK/ScopeGuard.h>
 #include <AK/Span.h>
 #include <AK/StdLibExtras.h>
+#include <AK/Swift.h>
 #include <AK/TypeCasts.h>
 #include <AK/Types.h>
 
@@ -382,7 +383,7 @@ private:
     static constexpr size_t inline_capacity = 4 * sizeof(void*);
 
     alignas(inline_alignment) u8 m_storage[inline_capacity];
-};
+} SWIFT_UNSAFE_REFERENCE;
 
 }
 

+ 5 - 8
AK/StringData.h

@@ -30,7 +30,7 @@ public:
         if (!slot)
             return Error::from_errno(ENOMEM);
 
-        auto new_string_data = adopt_ref(*new (slot) StringData(byte_count, capacity));
+        auto new_string_data = adopt_ref(*new (slot) StringData(byte_count));
         buffer = const_cast<u8*>(new_string_data->bytes().data());
         return new_string_data;
     }
@@ -43,7 +43,7 @@ public:
         auto buffer = builder.leak_buffer_for_string_construction({});
         VERIFY(buffer.has_value()); // We should only arrive here if the buffer is outlined.
 
-        return adopt_ref(*new (buffer->buffer.data()) StringData(byte_count, buffer->capacity));
+        return adopt_ref(*new (buffer->buffer.data()) StringData(byte_count));
     }
 
     static ErrorOr<NonnullRefPtr<StringData>> create_substring(StringData const& superstring, size_t start, size_t byte_count)
@@ -56,7 +56,7 @@ public:
         if (!slot)
             return Error::from_errno(ENOMEM);
 
-        return adopt_ref(*new (slot) StringData(superstring, start, byte_count, capacity));
+        return adopt_ref(*new (slot) StringData(superstring, start, byte_count));
     }
 
     struct SubstringData {
@@ -117,15 +117,13 @@ private:
         return sizeof(StringData) + (sizeof(char) * length);
     }
 
-    StringData(size_t byte_count, size_t capacity)
+    explicit StringData(size_t byte_count)
         : m_byte_count(byte_count)
-        , m_capacity(capacity)
     {
     }
 
-    StringData(StringData const& superstring, size_t start, size_t byte_count, size_t capacity)
+    StringData(StringData const& superstring, size_t start, size_t byte_count)
         : m_byte_count(byte_count)
-        , m_capacity(capacity)
         , m_substring(true)
     {
         auto& data = const_cast<SubstringData&>(substring_data());
@@ -145,7 +143,6 @@ private:
     }
 
     u32 m_byte_count { 0 };
-    u32 m_capacity { 0 };
 
     mutable unsigned m_hash { 0 };
     mutable bool m_has_hash { false };

+ 8 - 0
AK/Swift.h

@@ -37,3 +37,11 @@
 #    define SWIFT_RETURNS_RETAINED
 #    define SWIFT_RETURNS_UNRETAINED
 #endif
+
+// FIXME: This needs to be in the global namespace for reasons
+//  https://github.com/swiftlang/swift/issues/80231
+template<class From, class To>
+To cxxCast(From i)
+{
+    return static_cast<To>(i);
+}

+ 6 - 2
Libraries/LibGC/CMakeLists.txt

@@ -17,8 +17,12 @@ target_link_libraries(LibGC PRIVATE LibCore)
 if (ENABLE_SWIFT)
     generate_clang_module_map(LibGC)
     target_sources(LibGC PRIVATE
-            Heap+Swift.swift
+        Heap+Swift.swift
     )
     target_link_libraries(LibGC PRIVATE AK)
-    add_swift_target_properties(LibGC LAGOM_LIBRARIES AK)
+
+    # FIXME: https://github.com/swiftlang/swift/issues/80182
+    target_compile_options(LibGC PUBLIC $<$<COMPILE_LANGUAGE:C,CXX>:-DLIBGC_WORKAROUND_BOOL_BITFIELD>
+                                        SHELL:$<$<COMPILE_LANGUAGE:Swift>:-Xcc -DLIBGC_WORKAROUND_BOOL_BITFIELD>)
+    add_swift_target_properties(LibGC LAGOM_LIBRARIES AK COMPILE_DEFINITIONS LIBGC_WORKAROUND_BOOL_BITFIELD)
 endif()

+ 17 - 9
Libraries/LibGC/Cell.h

@@ -12,6 +12,7 @@
 #include <AK/HashMap.h>
 #include <AK/Noncopyable.h>
 #include <AK/StringView.h>
+#include <AK/Swift.h>
 #include <AK/Weakable.h>
 #include <LibGC/Forward.h>
 #include <LibGC/Internals.h>
@@ -27,6 +28,13 @@ namespace GC {
 #    define IGNORE_GC
 #endif
 
+// https://github.com/swiftlang/swift/issues/80182
+#if defined(LIBGC_WORKAROUND_BOOL_BITFIELD)
+#    define BOOL_BITFIELD
+#else
+#    define BOOL_BITFIELD : 1
+#endif
+
 #define GC_CELL(class_, base_class)                \
 public:                                            \
     using Base = base_class;                       \
@@ -64,17 +72,17 @@ public:
                 visit_impl(*cell);
         }
 
-        void visit(Cell& cell)
+        void visit(Cell& cell) SWIFT_NAME(visitRef(_:))
         {
             visit_impl(cell);
         }
 
-        void visit(Cell const* cell)
+        void visit(Cell const* cell) SWIFT_NAME(visitConst(_:))
         {
             visit(const_cast<Cell*>(cell));
         }
 
-        void visit(Cell const& cell)
+        void visit(Cell const& cell) SWIFT_NAME(visitConstRef(_:))
         {
             visit(const_cast<Cell&>(cell));
         }
@@ -149,7 +157,7 @@ public:
             }
         }
 
-        void visit(NanBoxedValue const& value);
+        void visit(NanBoxedValue const& value) SWIFT_NAME(visitValue(_:));
 
         // Allow explicitly ignoring a GC-allocated member in a visit_edges implementation instead
         // of just not using it.
@@ -163,7 +171,7 @@ public:
     protected:
         virtual void visit_impl(Cell&) = 0;
         virtual ~Visitor() = default;
-    };
+    } SWIFT_UNSAFE_REFERENCE;
 
     virtual void visit_edges(Visitor&) { }
 
@@ -187,10 +195,10 @@ protected:
     void set_overrides_must_survive_garbage_collection(bool b) { m_overrides_must_survive_garbage_collection = b; }
 
 private:
-    bool m_mark { false };
-    bool m_overrides_must_survive_garbage_collection { false };
-    State m_state { State::Live };
-};
+    bool m_mark BOOL_BITFIELD { false };
+    bool m_overrides_must_survive_garbage_collection BOOL_BITFIELD { false };
+    State m_state BOOL_BITFIELD { State::Live };
+} SWIFT_UNSAFE_REFERENCE;
 
 }
 

+ 6 - 29
Libraries/LibGC/Heap+Swift.swift

@@ -15,38 +15,15 @@ extension GC.Heap {
     }
 }
 
-// FIXME: Cell and Cell::Visitor are not imported properly, so we have to treat them as OpaquePointer
 public protocol HeapAllocatable {
     static func allocate(on heap: GC.Heap) -> UnsafeMutablePointer<Self>
 
-    init(cell: OpaquePointer)
+    init(cell: GC.Cell)
 
     func finalize()
-    func visitEdges(_ visitor: OpaquePointer)
+    func visitEdges(_ visitor: GC.Cell.Visitor)
 
-    var cell: OpaquePointer { get }
-}
-
-// FIXME: Figure out why other modules can't conform to HeapAllocatable
-public struct HeapString: HeapAllocatable {
-    public var string: Swift.String
-
-    public init(cell: OpaquePointer) {
-        self.cell = cell
-        self.string = ""
-    }
-
-    // FIXME: HeapAllocatable cannot be exposed to C++ yet, so we're off to void* paradise
-    public static func create(on heap: GC.Heap, string: Swift.String) -> OpaquePointer {
-        // NOTE: GC must be deferred so that a collection during allocation doesn't get tripped
-        //   up looking for the Cell pointer on the stack or in a register when it might only exist in the heap
-        precondition(heap.is_gc_deferred())
-        let heapString = allocate(on: heap)
-        heapString.pointee.string = string
-        return heapString.pointee.cell
-    }
-
-    public var cell: OpaquePointer
+    var cell: GC.Cell { get }
 }
 
 // Here be dragons
@@ -64,7 +41,7 @@ func asHeapAllocatableType(_ typeMetadata: UnsafeMutableRawPointer) -> any HeapA
 }
 
 extension HeapAllocatable {
-    fileprivate static func initializeFromFFI(at this: UnsafeMutableRawPointer, cell: OpaquePointer) {
+    fileprivate static func initializeFromFFI(at this: UnsafeMutableRawPointer, cell: GC.Cell) {
         this.assumingMemoryBound(to: Self.self).initialize(to: Self.self.init(cell: cell))
     }
 
@@ -76,7 +53,7 @@ extension HeapAllocatable {
         this.assumingMemoryBound(to: Self.self).pointee.finalize()
     }
 
-    fileprivate static func visitEdgesFromFFI(at this: UnsafeMutableRawPointer, visitor: OpaquePointer) {
+    fileprivate static func visitEdgesFromFFI(at this: UnsafeMutableRawPointer, visitor: GC.Cell.Visitor) {
         this.assumingMemoryBound(to: Self.self).pointee.visitEdges(visitor)
     }
 
@@ -101,5 +78,5 @@ extension HeapAllocatable {
     }
 
     public func finalize() {}
-    public func visitEdges(_ visitor: OpaquePointer) {}
+    public func visitEdges(_ visitor: GC.Cell.Visitor) {}
 }

+ 3 - 3
Libraries/LibJS/Runtime/Shape.h

@@ -142,9 +142,9 @@ private:
     PropertyAttributes m_attributes { 0 };
     TransitionType m_transition_type { TransitionType::Invalid };
 
-    bool m_dictionary : 1 { false };
-    bool m_cacheable : 1 { true };
-    bool m_is_prototype_shape : 1 { false };
+    bool m_dictionary BOOL_BITFIELD { false };
+    bool m_cacheable BOOL_BITFIELD { true };
+    bool m_is_prototype_shape BOOL_BITFIELD { false };
 };
 
 }

+ 11 - 11
Libraries/LibWeb/Bindings/PlatformObject.h

@@ -49,17 +49,17 @@ protected:
     explicit PlatformObject(JS::Object& prototype, MayInterfereWithIndexedPropertyAccess = MayInterfereWithIndexedPropertyAccess::No);
 
     struct LegacyPlatformObjectFlags {
-        u16 supports_indexed_properties : 1 = false;
-        u16 supports_named_properties : 1 = false;
-        u16 has_indexed_property_setter : 1 = false;
-        u16 has_named_property_setter : 1 = false;
-        u16 has_named_property_deleter : 1 = false;
-        u16 has_legacy_unenumerable_named_properties_interface_extended_attribute : 1 = false;
-        u16 has_legacy_override_built_ins_interface_extended_attribute : 1 = false;
-        u16 has_global_interface_extended_attribute : 1 = false;
-        u16 indexed_property_setter_has_identifier : 1 = false;
-        u16 named_property_setter_has_identifier : 1 = false;
-        u16 named_property_deleter_has_identifier : 1 = false;
+        u16 supports_indexed_properties BOOL_BITFIELD = false;
+        u16 supports_named_properties BOOL_BITFIELD = false;
+        u16 has_indexed_property_setter BOOL_BITFIELD = false;
+        u16 has_named_property_setter BOOL_BITFIELD = false;
+        u16 has_named_property_deleter BOOL_BITFIELD = false;
+        u16 has_legacy_unenumerable_named_properties_interface_extended_attribute BOOL_BITFIELD = false;
+        u16 has_legacy_override_built_ins_interface_extended_attribute BOOL_BITFIELD = false;
+        u16 has_global_interface_extended_attribute BOOL_BITFIELD = false;
+        u16 indexed_property_setter_has_identifier BOOL_BITFIELD = false;
+        u16 named_property_setter_has_identifier BOOL_BITFIELD = false;
+        u16 named_property_deleter_has_identifier BOOL_BITFIELD = false;
     };
     Optional<LegacyPlatformObjectFlags> m_legacy_platform_object_flags = {};
 

+ 2 - 1
Libraries/LibWeb/CMakeLists.txt

@@ -999,7 +999,8 @@ if (ENABLE_SWIFT)
         HTML/Parser/HTMLToken.swift
         HTML/Parser/HTMLTokenizer.swift
         HTML/Parser/HTMLTokenizerHelpers.cpp
+        HTML/Parser/SpeculativeHTMLParser.swift
     )
     target_link_libraries(LibWeb PRIVATE AK Collections)
-    add_swift_target_properties(LibWeb LAGOM_LIBRARIES AK LibGfx)
+    add_swift_target_properties(LibWeb LAGOM_LIBRARIES AK LibGfx LibGC COMPILE_DEFINITIONS LIBGC_WORKAROUND_BOOL_BITFIELD)
 endif()

+ 4 - 4
Libraries/LibWeb/CSS/StyleInvalidation.h

@@ -11,10 +11,10 @@
 namespace Web::CSS {
 
 struct RequiredInvalidationAfterStyleChange {
-    bool repaint : 1 { false };
-    bool rebuild_stacking_context_tree : 1 { false };
-    bool relayout : 1 { false };
-    bool rebuild_layout_tree : 1 { false };
+    bool repaint BOOL_BITFIELD { false };
+    bool rebuild_stacking_context_tree BOOL_BITFIELD { false };
+    bool relayout BOOL_BITFIELD { false };
+    bool rebuild_layout_tree BOOL_BITFIELD { false };
 
     void operator|=(RequiredInvalidationAfterStyleChange const& other)
     {

+ 10 - 10
Libraries/LibWeb/DOM/Element.h

@@ -553,16 +553,16 @@ private:
 
     Array<CSSPixelPoint, 3> m_scroll_offset;
 
-    bool m_in_top_layer : 1 { false };
-    bool m_rendered_in_top_layer : 1 { false };
-    bool m_style_uses_css_custom_properties { false };
-    bool m_affected_by_has_pseudo_class_in_subject_position : 1 { false };
-    bool m_affected_by_has_pseudo_class_in_non_subject_position : 1 { false };
-    bool m_affected_by_direct_sibling_combinator : 1 { false };
-    bool m_affected_by_indirect_sibling_combinator : 1 { false };
-    bool m_affected_by_first_or_last_child_pseudo_class : 1 { false };
-    bool m_affected_by_nth_child_pseudo_class : 1 { false };
-    bool m_affected_by_has_pseudo_class_with_relative_selector_that_has_sibling_combinator : 1 { false };
+    bool m_in_top_layer BOOL_BITFIELD { false };
+    bool m_rendered_in_top_layer BOOL_BITFIELD { false };
+    bool m_style_uses_css_custom_properties BOOL_BITFIELD { false };
+    bool m_affected_by_has_pseudo_class_in_subject_position BOOL_BITFIELD { false };
+    bool m_affected_by_has_pseudo_class_in_non_subject_position BOOL_BITFIELD { false };
+    bool m_affected_by_direct_sibling_combinator BOOL_BITFIELD { false };
+    bool m_affected_by_indirect_sibling_combinator BOOL_BITFIELD { false };
+    bool m_affected_by_first_or_last_child_pseudo_class BOOL_BITFIELD { false };
+    bool m_affected_by_nth_child_pseudo_class BOOL_BITFIELD { false };
+    bool m_affected_by_has_pseudo_class_with_relative_selector_that_has_sibling_combinator BOOL_BITFIELD { false };
 
     size_t m_sibling_invalidation_distance { 0 };
 

+ 5 - 1
Libraries/LibWeb/HTML/Parser/HTMLParser.h

@@ -214,10 +214,14 @@ private:
 
     GC::Ptr<DOM::Text> m_character_insertion_node;
     StringBuilder m_character_insertion_builder;
-};
+} SWIFT_UNSAFE_REFERENCE;
 
 RefPtr<CSS::CSSStyleValue> parse_dimension_value(StringView);
 RefPtr<CSS::CSSStyleValue> parse_nonzero_dimension_value(StringView);
 Optional<Color> parse_legacy_color_value(StringView);
 
+// Swift interop
+using HTMLParserGCPtr = GC::Ptr<HTMLParser>;
+using HTMLParserGCRef = GC::Ref<HTMLParser>;
+
 }

+ 60 - 0
Libraries/LibWeb/HTML/Parser/SpeculativeHTMLParser.swift

@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2025, Andrew Kaster <andrew@ladybird.org>>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+import AK
+import Collections
+import Foundation
+import GC
+@_exported import WebCxx
+
+// Workaround for https://github.com/swiftlang/swift/issues/80231
+// If any line of this changes, the whole thing breaks though
+extension GC.Cell.Visitor {
+    public func visit(_ parser: Web.HTML.HTMLParserGCPtr) {
+        if let parser = parser.ptr() {
+            let cell: GC.Cell = cxxCast(parser)
+            visit(cell)
+        }
+    }
+}
+
+struct SpeculativeMockElement {
+    let name: Swift.String
+    let localName: Swift.String
+    let attributes: [HTMLToken.Attribute]
+    var children: [SpeculativeMockElement]
+
+    init(name: Swift.String, localName: Swift.String, attributes: [HTMLToken.Attribute]) {
+        self.name = name
+        self.localName = localName
+        self.attributes = attributes
+        self.children = []
+    }
+
+    mutating func appendChild(_ child: consuming SpeculativeMockElement) {
+        children.append(child)
+    }
+}
+
+public final class SpeculativeHTMLParser: HeapAllocatable {
+    var parser = Web.HTML.HTMLParserGCPtr()  // FIXME: Want HTMLParserGCRef here, but how to initialize it?
+
+    public init(cell: GC.Cell) {
+        self.cell = cell
+    }
+    public var cell: GC.Cell
+
+    public static func create(on heap: GC.Heap, `for` parser: Web.HTML.HTMLParserGCPtr) -> GC.Cell {
+        precondition(heap.is_gc_deferred())
+        let _self = allocate(on: heap)
+        _self.pointee.parser = parser
+        return _self.pointee.cell
+    }
+
+    public func visitEdges(_ visitor: GC.Cell.Visitor) {
+        visitor.visit(parser)
+    }
+}

+ 2 - 1
Libraries/LibWeb/Loader/FileRequest.h

@@ -9,6 +9,7 @@
 #include <AK/ByteString.h>
 #include <AK/Error.h>
 #include <AK/Function.h>
+#include <AK/Swift.h>
 
 namespace Web {
 
@@ -22,6 +23,6 @@ public:
 
 private:
     ByteString m_path {};
-};
+} SWIFT_UNSAFE_REFERENCE; // FIXME: This type is actually move-only, not unsafe
 
 }

+ 6 - 6
Libraries/LibWeb/Painting/Paintable.h

@@ -168,12 +168,12 @@ private:
 
     SelectionState m_selection_state { SelectionState::None };
 
-    bool m_positioned : 1 { false };
-    bool m_fixed_position : 1 { false };
-    bool m_sticky_position : 1 { false };
-    bool m_absolutely_positioned : 1 { false };
-    bool m_floating : 1 { false };
-    bool m_inline : 1 { false };
+    bool m_positioned BOOL_BITFIELD { false };
+    bool m_fixed_position BOOL_BITFIELD { false };
+    bool m_sticky_position BOOL_BITFIELD { false };
+    bool m_absolutely_positioned BOOL_BITFIELD { false };
+    bool m_floating BOOL_BITFIELD { false };
+    bool m_inline BOOL_BITFIELD { false };
 };
 
 inline DOM::Node* HitTestResult::dom_node()

+ 53 - 17
Meta/CMake/FindSwiftTesting.cmake

@@ -1,24 +1,60 @@
 # Finds the swift-testing library
 # On Apple platforms, this is a framework included in the Xcode release
+include_guard()
 
-find_library(SWIFT_TESTING NAMES Testing
-  PATHS ${SWIFT_LIBRARY_SEARCH_PATHS}
-)
-if (SWIFT_TESTING)
-    if (NOT TARGET SwiftTesting::SwiftTesting)
-      add_library(SwiftTesting::SwiftTesting IMPORTED UNKNOWN)
-      message(STATUS "Found SwiftTesting: ${SWIFT_TESTING}")
-      cmake_path(GET SWIFT_TESTING PARENT_PATH _SWIFT_TESTING_DIR)
-      set_target_properties(SwiftTesting::SwiftTesting PROPERTIES
-        IMPORTED_LOCATION "${SWIFT_TESTING}"
-        INTERFACE_LINK_DIRECTORIES "${_SWIFT_TESTING_DIR}"
-      )
-      if (UNIX AND NOT APPLE)
-        cmake_path(GET _SWIFT_TESTING_DIR PARENT_PATH _SWIFT_TESTING_TARGETLESS_DIR)
-        set_target_properties(SwiftTesting::SwiftTesting PROPERTIES
-          INTERFACE_COMPILE_OPTIONS "$<$<COMPILE_LANGUAGE:Swift>:SHELL:-load-plugin-library ${_SWIFT_TESTING_TARGETLESS_DIR}/host/plugins/libTestingMacros.so>"
-        )
+if (NOT TARGET SwiftTesting::SwiftTesting)
+  cmake_policy(PUSH)
+  if (POLICY CMP0152)
+    cmake_policy(SET CMP0152 NEW)
+  endif()
+
+  set(_SEARCH_PATHS "")
+  set(_PLUGIN_PATHS "")
+  foreach(path IN LISTS SWIFT_LIBRARY_SEARCH_PATHS)
+    file(REAL_PATH ${path} real_path)
+    if (EXISTS ${real_path})
+      list(APPEND _SEARCH_PATHS ${real_path})
+      if (EXISTS "${real_path}/testing")
+        list(APPEND _SEARCH_PATHS "${real_path}/testing")
+      endif()
+    endif()
+    if (EXISTS "${real_path}/../host/plugins")
+      file(REAL_PATH "${real_path}/../host/plugins" plugin_path)
+      list(APPEND _PLUGIN_PATHS ${plugin_path})
+      if (EXISTS "${plugin_path}/testing")
+        list(APPEND _PLUGIN_PATHS "${plugin_path}/testing")
       endif()
     endif()
+  endforeach()
+  list(REMOVE_DUPLICATES _SEARCH_PATHS)
+  list(REMOVE_DUPLICATES _PLUGIN_PATHS)
+
+  find_library(SWIFT_TESTING NAMES Testing
+    PATHS ${_SEARCH_PATHS}
+  )
+  if (SWIFT_TESTING)
+    add_library(SwiftTesting::SwiftTesting IMPORTED UNKNOWN)
+    message(STATUS "Found SwiftTesting: ${SWIFT_TESTING}")
+    cmake_path(GET SWIFT_TESTING PARENT_PATH _SWIFT_TESTING_DIR)
+
+    find_library(SWIFT_TESTING_MACROS NAMES TestingMacros
+      PATHS ${_PLUGIN_PATHS}
+      NO_DEFAULT_PATH
+    )
+    if (NOT SWIFT_TESTING_MACROS)
+      message(FATAL_ERROR "Could not find associated TestingMacros plugin for ${SWIFT_TESTING}")
+    else()
+      message(VERBOSE "Found SwiftTesting macros: ${SWIFT_TESTING_MACROS}")
+    endif()
+
+    set_target_properties(SwiftTesting::SwiftTesting PROPERTIES
+      IMPORTED_LOCATION "${SWIFT_TESTING}"
+      INTERFACE_LINK_DIRECTORIES "${_SWIFT_TESTING_DIR}"
+      INTERFACE_INCLUDE_DIRECTORIES "${_SWIFT_TESTING_DIR}"
+      INTERFACE_COMPILE_OPTIONS "$<$<COMPILE_LANGUAGE:Swift>:SHELL:-load-plugin-library ${SWIFT_TESTING_MACROS}>"
+      INTERFACE_LINK_OPTIONS "-load-plugin-library;${SWIFT_TESTING_MACROS}"
+    )
     set(SwiftTesting_FOUND TRUE)
+  endif()
+  cmake_policy(POP)
 endif()

+ 17 - 2
Meta/CMake/Swift/swift-settings.cmake

@@ -17,6 +17,8 @@ endif()
 include(${CMAKE_CURRENT_LIST_DIR}/InitializeSwift.cmake)
 include(${CMAKE_CURRENT_LIST_DIR}/GenerateSwiftHeader.cmake)
 
+find_package(SwiftTesting REQUIRED)
+
 # FIXME: https://gitlab.kitware.com/cmake/cmake/-/issues/26174
 if (APPLE)
     set(CMAKE_Swift_COMPILER_TARGET "${SWIFT_TARGET_TRIPLE}")
@@ -40,7 +42,7 @@ function(swizzle_target_properties_for_swift target_name)
 endfunction()
 
 function(add_swift_target_properties target_name)
-    cmake_parse_arguments(PARSE_ARGV 1 SWIFT_TARGET "" "" "LAGOM_LIBRARIES")
+    cmake_parse_arguments(PARSE_ARGV 1 SWIFT_TARGET "" "" "LAGOM_LIBRARIES;COMPILE_DEFINITIONS;COMPILE_OPTIONS")
 
     target_compile_features(${target_name} PUBLIC cxx_std_${CMAKE_CXX_STANDARD})
     target_compile_options(${target_name} PUBLIC "SHELL:$<$<COMPILE_LANGUAGE:Swift>:-Xcc -std=c++23 -cxx-interoperability-mode=default>")
@@ -60,8 +62,21 @@ function(add_swift_target_properties target_name)
     get_target_property(_NATIVE_DIRS ${target_name} INCLUDE_DIRECTORIES)
     list(APPEND _NATIVE_DIRS ${CMAKE_Swift_MODULE_DIRECTORY})
 
+    # Swift-testing in swift.org toolchains on macOS has its .swiftmodule in a testing/ subdirectory of
+    # the swift compiler's built-in lib dirs.
+    get_target_property(DEPENDENCIES ${target_name} LINK_LIBRARIES)
+    if (SwiftTesting::SwiftTesting IN_LIST DEPENDENCIES)
+        get_target_property(SWIFT_TESTING_INCLUDE_DIRS SwiftTesting::SwiftTesting INTERFACE_INCLUDE_DIRECTORIES)
+        list(APPEND _NATIVE_DIRS ${SWIFT_TESTING_INCLUDE_DIRS})
+    endif()
+
+    set(EXTRA_COMPILE_DEFINITIONS "")
+    foreach (compile_definition IN LISTS SWIFT_TARGET_COMPILE_DEFINITIONS)
+        list(APPEND EXTRA_COMPILE_DEFINITIONS "-Xcc" "-D${compile_definition}")
+    endforeach()
+
     _swift_generate_cxx_header(${target_name} "${target_name}-Swift.h"
         SEARCH_PATHS ${_NATIVE_DIRS}
-        COMPILE_OPTIONS ${VFS_OVERLAY_OPTIONS}
+        COMPILE_OPTIONS ${VFS_OVERLAY_OPTIONS} ${EXTRA_COMPILE_DEFINITIONS} ${SWIFT_TARGET_COMPILE_OPTIONS}
     )
 endfunction()

+ 10 - 1
Tests/LibGC/CMakeLists.txt

@@ -6,12 +6,21 @@ if (ENABLE_SWIFT)
         TestHeap.cpp
         TestInterop.cpp
     )
-
     # FIXME: Swift doesn't seem to like object libraries for @main
     target_sources(TestGCSwift PRIVATE ../Resources/SwiftTestMain.swift)
 
+    generate_clang_module_map(TestGCSwift)
+
     set_target_properties(TestGCSwift PROPERTIES SUFFIX .swift-testing)
     target_include_directories(TestGCSwift PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
     target_link_libraries(TestGCSwift PRIVATE AK LibGC SwiftTesting::SwiftTesting)
+
+    get_property(testing_compile_options TARGET SwiftTesting::SwiftTesting PROPERTY INTERFACE_LINK_OPTIONS)
+
+    add_swift_target_properties(TestGCSwift
+        LAGOM_LIBRARIES AK LibGC
+        COMPILE_DEFINITIONS LIBGC_WORKAROUND_BOOL_BITFIELD
+        COMPILE_OPTIONS ${testing_compile_options} -enable-experimental-feature Extern
+    )
     add_test(NAME TestGCSwift COMMAND TestGCSwift)
 endif()

+ 20 - 6
Tests/LibGC/TestGCBindings.swift

@@ -6,14 +6,28 @@
 
 import AK
 import GC
-import GCTesting
+@_exported import TestGCSwiftCxx
 import Testing
 
-// FIXME: We want a type declared *here* for HeapString, but it gives a compiler warning:
-//  error: type 'GCString' cannot conform to protocol 'HeapAllocatable' because it has requirements that cannot be satisfied
-//  Even using the same exact code from LibGC/Heap+Swift.swift
-//  This is likely because one of the required types for HeapAllocatable is not fully imported from C++ and thus can't
-//  be re-exported by the GC module.
+public struct HeapString: HeapAllocatable {
+    public var string: Swift.String
+
+    public init(cell: GC.Cell) {
+        self.cell = cell
+        self.string = ""
+    }
+
+    public static func create(on heap: GC.Heap, string: Swift.String) -> GC.Cell {
+        // NOTE: GC must be deferred so that a collection during allocation doesn't get tripped
+        //   up looking for the Cell pointer on the stack or in a register when it might only exist in the heap
+        precondition(heap.is_gc_deferred())
+        let heapString = allocate(on: heap)
+        heapString.pointee.string = string
+        return heapString.pointee.cell
+    }
+
+    public var cell: GC.Cell
+}
 
 @Suite(.serialized)
 struct TestGCSwiftBindings {

+ 2 - 2
Tests/LibGC/TestInterop.cpp

@@ -7,9 +7,9 @@
 #include "TestInterop.h"
 #include "TestHeap.h"
 #include <AK/TypeCasts.h>
-#include <LibGC-Swift.h>
 #include <LibGC/ForeignCell.h>
 #include <LibGC/Heap.h>
+#include <TestGCSwift-Swift.h>
 
 #define COLLECT heap.collect_garbage(GC::Heap::CollectionType::CollectGarbage)
 #define COLLECT_ALL heap.collect_garbage(GC::Heap::CollectionType::CollectEverything)
@@ -20,7 +20,7 @@ void test_interop()
 
     COLLECT_ALL;
 
-    auto string = GC::ForeignRef<GC::HeapString>::allocate(heap, "Hello, World!");
+    auto string = GC::ForeignRef<TestGCSwift::HeapString>::allocate(heap, "Hello, World!");
 
     COLLECT;
 

+ 0 - 6
Tests/LibGC/module.modulemap

@@ -1,6 +0,0 @@
-module GCTesting {
-    header "TestHeap.h"
-    header "TestInterop.h"
-    requires cplusplus
-    export *
-}

+ 1 - 1
Tests/LibWeb/CMakeLists.txt

@@ -29,6 +29,6 @@ if (ENABLE_SWIFT)
     target_sources(TestLibWebSwift PRIVATE ../Resources/SwiftTestMain.swift)
 
     set_target_properties(TestLibWebSwift PROPERTIES SUFFIX .swift-testing)
-    target_link_libraries(TestLibWebSwift PRIVATE AK LibWeb SwiftTesting::SwiftTesting)
+    target_link_libraries(TestLibWebSwift PRIVATE AK LibWeb LibGC SwiftTesting::SwiftTesting)
     add_test(NAME TestLibWebSwift COMMAND TestLibWebSwift)
 endif()