Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
51 commits
Select commit Hold shift + click to select a range
6ee6c4c
feat(compiler): add Swift gRPC service generation
yash-agarwa-l Jun 19, 2026
9d15a1b
feat(compiler): preflight Swift gRPC collisions
yash-agarwa-l Jun 19, 2026
e78e79e
test(compiler): cover Swift gRPC codegen
yash-agarwa-l Jun 19, 2026
b554dae
test(compiler): build and run the Swift gRPC fixture
yash-agarwa-l Jun 19, 2026
e865db5
test(grpc): generate Swift companions in the interop harness
yash-agarwa-l Jun 19, 2026
efdcad2
docs: document Swift gRPC support
yash-agarwa-l Jun 20, 2026
034861b
style(compiler): wrap long Swift gRPC handler closures
yash-agarwa-l Jun 20, 2026
7f3d888
style(compiler): make Swift gRPC companions swiftlint-clean
yash-agarwa-l Jun 20, 2026
1ab4cf8
test(compiler): document the Swift common-root package limit
yash-agarwa-l Jun 20, 2026
f746c2c
fix(compiler): use a per-thread Fory in Swift gRPC marshaller
yash-agarwa-l Jun 20, 2026
00f2657
docs: note Swift gRPC client and interceptor limits
yash-agarwa-l Jun 21, 2026
3c93b3e
test(compiler): prove Swift marshaller thread-safety under TSan
yash-agarwa-l Jun 21, 2026
dd40171
test(compiler): gate the Swift TSan test behind FORY_SWIFT_TSAN
yash-agarwa-l Jun 21, 2026
4922f77
feat(compiler): reserve inherited member names in Swift gRPC
yash-agarwa-l Jun 21, 2026
9cf3cee
Merge remote-tracking branch 'upstream/main' into grpc-swift
yash-agarwa-l Jun 21, 2026
4b5278e
refactor(grpc): move Swift toolchain tests into integration_tests
yash-agarwa-l Jun 21, 2026
6d44772
fix(compiler): make the Swift gRPC wire wrapper Sendable
yash-agarwa-l Jun 21, 2026
5c5c15c
test(grpc): run Swift toolchain gRPC tests in their package
yash-agarwa-l Jun 22, 2026
602183a
Merge remote-tracking branch 'upstream/main' into grpc-swift
yash-agarwa-l Jul 24, 2026
e21a9a4
chore(compiler): fix ruff lint in swift gRPC codegen
yash-agarwa-l Jul 26, 2026
de06611
fix(java): balance read ref id stack for NOT_NULL values
yash-agarwa-l Jul 26, 2026
f294ef3
Merge remote-tracking branch 'upstream/main' into grpc-swift
yash-agarwa-l Jul 27, 2026
3c50b8c
Merge remote-tracking branch 'upstream/main' into grpc-swift
yash-agarwa-l Aug 4, 2026
20047dc
Revert "fix(java): balance read ref id stack for NOT_NULL values"
yash-agarwa-l Aug 6, 2026
df15e7c
fix(swift): reserve a ref slot for tracked value roots
yash-agarwa-l Aug 7, 2026
f7ea499
Merge remote-tracking branch 'upstream/main' into grpc-swift
yash-agarwa-l Aug 7, 2026
574542c
fix(compiler): constrain Swift gRPC generics to self-provided seriali…
yash-agarwa-l Aug 7, 2026
84adfc4
docs(swift): align gRPC page front
yash-agarwa-l Aug 7, 2026
d97696a
docs(compiler): document Swift gRPC service companions
yash-agarwa-l Aug 7, 2026
9efe651
test(grpc): add Java<->Swift gRPC interop harness
yash-agarwa-l Jun 22, 2026
044dddc
ci: run Java/Swift gRPC interop tests
yash-agarwa-l Aug 7, 2026
36288fc
fix(compiler): require Sendable payloads in Swift gRPC companions
yash-agarwa-l Aug 11, 2026
ec24fa8
fix(compiler): keep the compiler importable on Python 3.8 and 3.9
yash-agarwa-l Aug 23, 2026
9d58d1e
test(compiler): use typing generics in the service codegen tests
yash-agarwa-l Aug 23, 2026
e39531b
fix(compiler): keep the IDL frontends importable on Python 3.8
yash-agarwa-l Aug 23, 2026
a8f2596
ci: import the compiler CLI on Python 3.8 and 3.9
yash-agarwa-l Aug 23, 2026
d2a47f2
fix(compiler): key the Swift gRPC runtime per generated module
yash-agarwa-l Aug 23, 2026
1832860
fix(compiler): record the Swift namespace enum in collision preflight
yash-agarwa-l Aug 23, 2026
f113142
fix(compiler): preserve duplicate Swift declaration owners in preflight
yash-agarwa-l Aug 23, 2026
83c68b2
fix(compiler): reserve the Swift marshaller symbol only for non-empty…
yash-agarwa-l Aug 23, 2026
06ce60f
docs(swift): describe the gRPC namespace preflight and Swift 6 status
yash-agarwa-l Aug 23, 2026
bcb2faf
test(swift): make the gRPC marshaller tests exercise what they claim
yash-agarwa-l Aug 23, 2026
af2fe1e
docs(swift): pin the gRPC install snippet to the release version plac…
yash-agarwa-l Aug 23, 2026
75e2854
fix(swift): use async cleanup in the gRPC interop async paths
yash-agarwa-l Aug 23, 2026
930b97c
Merge apache/main into grpc-swift
yash-agarwa-l Aug 23, 2026
be757b8
fix(swift): honor the selected test classes and narrow the namespace …
yash-agarwa-l Aug 23, 2026
be6eeb5
fix(compiler): reject an underscore-only Swift gRPC rpc name
yash-agarwa-l Aug 23, 2026
cf93d27
fix(compiler): collect nested Swift declarations in collision preflight
yash-agarwa-l Aug 23, 2026
c3eb9f3
docs(swift): state the Swift 5 language mode requirement for generate…
yash-agarwa-l Aug 23, 2026
e036c66
fix(swift): harden gRPC test lifecycle
chaokunyang Aug 24, 2026
869d1cd
ci(swift): reuse debug gRPC peer
chaokunyang Aug 24, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .agents/languages/swift.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ Load this file when changing `swift/` or Swift xlang behavior.
- Swift formatting uses `swift/.swift-format`; do not rely on SwiftLint for indentation or source
formatting.
- Use `ENABLE_FORY_DEBUG_OUTPUT=1` when debugging Swift tests.
- Generated Swift gRPC companions are compiler-owned files targeting grpc-swift 1.x. Keep grpc-swift out of the `swift/` runtime package; it belongs only to generated user code and the compiler build fixture.
- Prefer the user-requested or existing Foundation public value type when it is the intended Swift surface; do not invent Fory-prefixed wrappers only to avoid import ambiguity.
- Preserve distinct temporal semantics. Timestamp values and day-only local dates should have protocol-accurate helper names and no stale aliases after a refactor.
- When temporal or public-type refactors touch generated Swift code, sweep message fields, union payloads, macros, xlang harnesses, and integration fixtures together.
Expand Down
57 changes: 57 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
Expand Down Expand Up @@ -196,6 +196,24 @@
cd compiler
pytest -q fory_compiler/tests

compiler_import:
name: Compiler Import ${{ matrix.python-version }}
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ["3.8", "3.9"]
steps:
- uses: actions/checkout@v5
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
cache: "pip"
- name: Import the compiler CLI
run: |
cd compiler
python -c "import fory_compiler.cli"

java:
name: Java CI
needs: changes
Expand Down Expand Up @@ -1056,6 +1074,45 @@
cd integration_tests/grpc_tests/java
mvn -T16 --no-transfer-progress -Dtest=DartGrpcTest test

grpc_java_swift_tests:
name: Java/Swift gRPC Tests
needs: changes
if: needs.changes.outputs.grpc_tests == 'true' || needs.changes.outputs.swift == 'true'
runs-on: macos-latest
steps:
- uses: actions/checkout@v5
- name: Set up JDK 21
uses: actions/setup-java@v4
with:
java-version: 21
distribution: "temurin"
- name: Set up Python 3.11
uses: actions/setup-python@v5
with:
python-version: 3.11
cache: "pip"
- name: Cache Maven local repository
uses: actions/cache@v4
with:
path: ~/.m2/repository
key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}
restore-keys: |
${{ runner.os }}-maven-
- name: Install Java artifacts for gRPC tests
run: |
cd java
mvn -T16 --no-transfer-progress clean install -DskipTests -Dmaven.javadoc.skip=true -Dmaven.source.skip=true
- name: Generate gRPC test sources
run: python integration_tests/grpc_tests/generate_grpc.py
- name: Run Swift gRPC package tests
run: |
cd integration_tests/grpc_tests/swift/interop
swift test

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Run the concurrency regression under ThreadSanitizer

This runs only plain swift test. The sole --sanitize=thread command is behind FORY_SWIFT_TSAN=1 in another script that this workflow never invokes, so the test and PR description's TSan safety claim is not exercised by CI. Please add a sanitized invocation, preferably filtered to the marshaller concurrency test.

- name: Run Java/Swift gRPC Tests
run: |
cd integration_tests/grpc_tests/java
mvn -T16 --no-transfer-progress -Dtest=SwiftGrpcTest test

javascript:
name: JavaScript CI
needs: changes
Expand Down
59 changes: 43 additions & 16 deletions compiler/fory_compiler/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,8 @@

from fory_compiler.frontend.base import FrontendError
from fory_compiler.frontend.utils import parse_idl_file, resolve_import_path
from fory_compiler.ir.ast import Schema
from fory_compiler.ir.emitter import FDLEmitter
from fory_compiler.ir.validator import SchemaValidator
from fory_compiler.generators.base import GeneratorOptions
from fory_compiler.generators import GENERATORS
from fory_compiler.generators.base import GeneratorOptions
from fory_compiler.generators.csharp import validate_csharp_generation
from fory_compiler.generators.kotlin import (
kotlin_output_paths,
Expand All @@ -40,13 +37,15 @@
scala_output_paths,
scala_package_for_schema,
)
from fory_compiler.generators.swift import validate_swift_generation
from fory_compiler.ir.ast import Schema
from fory_compiler.ir.emitter import FDLEmitter
from fory_compiler.ir.validator import SchemaValidator


class ImportError(Exception):
"""Error during import resolution."""

pass


GENERATED_MARKER = "This file is generated by Apache Fory compiler."
SKIP_DIR_NAMES = {"build", "target"}
Expand Down Expand Up @@ -342,6 +341,27 @@ def validate_csharp_files(
return False


def validate_swift_files(
files: List[Path],
import_paths: List[Path],
namespace_style: Optional[str] = None,
grpc: bool = False,
) -> bool:
"""Preflight Swift output paths and top-level symbols before writing output."""
cache: Dict[Path, Schema] = {}
graph: List[Tuple[Path, Schema]] = []
for file_path in files:
file_graph = collect_schema_graph(file_path, import_paths, cache, set())
if file_graph is None:
return False
graph.extend(file_graph)
try:
return validate_swift_generation(graph, namespace_style, grpc=grpc)
except ValueError as e:
print(f"Error: {e}", file=sys.stderr)
return False


def validate_scala_import_packages(graph: List[Tuple[Path, Schema]]) -> bool:
"""Check package combinations that Scala source cannot compile."""
packages = {scala_package_for_schema(schema) for _, schema in graph}
Expand Down Expand Up @@ -1023,7 +1043,7 @@ def cmd_compile(args: argparse.Namespace) -> int:
return 1

# Validate that all languages are supported
invalid = [lang for lang in lang_output_dirs.keys() if lang not in GENERATORS]
invalid = [lang for lang in lang_output_dirs if lang not in GENERATORS]
if invalid:
print(f"Error: Unknown language(s): {', '.join(invalid)}", file=sys.stderr)
print(f"Available: {', '.join(GENERATORS.keys())}", file=sys.stderr)
Expand All @@ -1044,15 +1064,22 @@ def cmd_compile(args: argparse.Namespace) -> int:
)
import_paths.append(resolved)

if "kotlin" in lang_output_dirs:
if not validate_kotlin_generation(args.files, import_paths, grpc=args.grpc):
return 1
if "csharp" in lang_output_dirs:
if not validate_csharp_files(args.files, import_paths, grpc=args.grpc):
return 1
if "scala" in lang_output_dirs:
if not validate_scala_generation(args.files, import_paths, grpc=args.grpc):
return 1
if "kotlin" in lang_output_dirs and not validate_kotlin_generation(
args.files, import_paths, grpc=args.grpc
):
return 1
if "csharp" in lang_output_dirs and not validate_csharp_files(
args.files, import_paths, grpc=args.grpc
):
return 1
if "scala" in lang_output_dirs and not validate_scala_generation(
args.files, import_paths, grpc=args.grpc
):
return 1
if "swift" in lang_output_dirs and not validate_swift_files(
args.files, import_paths, args.swift_namespace_style, grpc=args.grpc
):
return 1

if args.grpc_web and "javascript" not in lang_output_dirs:
print(
Expand Down
4 changes: 2 additions & 2 deletions compiler/fory_compiler/frontend/fbs/lexer.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@

from dataclasses import dataclass
from enum import Enum, auto
from typing import List
from typing import List, Tuple


class TokenType(Enum):
Expand Down Expand Up @@ -217,7 +217,7 @@ def read_string(self) -> str:
self.advance() # closing quote
return value

def read_number(self) -> tuple[TokenType, str]:
def read_number(self) -> Tuple[TokenType, str]:
value = ""
if self.peek() == "-":
value += self.advance()
Expand Down
4 changes: 2 additions & 2 deletions compiler/fory_compiler/frontend/fdl/parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
"""Recursive descent parser for FDL."""

import warnings
from typing import List, Set, Optional
from typing import List, Set, Optional, Tuple

from fory_compiler.ir.ast import (
Schema,
Expand Down Expand Up @@ -258,7 +258,7 @@ def make_location(self, token: Token) -> SourceLocation:
source_format=self.source_format,
)

def parse_package(self) -> tuple[str, Optional[str]]:
def parse_package(self) -> Tuple[str, Optional[str]]:
"""Parse a package declaration: package foo.bar [alias baz];"""
self.consume(TokenType.PACKAGE)

Expand Down
4 changes: 2 additions & 2 deletions compiler/fory_compiler/frontend/proto/parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@

"""Recursive descent parser for proto3."""

from typing import List
from typing import List, Tuple

from fory_compiler.frontend.proto.ast import (
ProtoSchema,
Expand Down Expand Up @@ -162,7 +162,7 @@ def parse_import(self) -> str:
self.consume(TokenType.SEMI, "Expected ';' after import")
return path

def parse_option_statement(self) -> tuple[str, object]:
def parse_option_statement(self) -> Tuple[str, object]:
self.consume(TokenType.OPTION, "Expected 'option'")
name = self.parse_option_name()
self.consume(TokenType.EQUALS, "Expected '=' after option name")
Expand Down
Loading
Loading