Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
1a41b05
fix(cli): the failure exit kind is returned by CompileToBinary, not r…
antonsynd Oct 15, 2026
28235b5
test(stdlib): the closed-port socket cells retry a re-taken ephemeral…
antonsynd Oct 15, 2026
3c1ea0d
test(server): {no -r, -r, no -r} on one server must answer as three f…
antonsynd Oct 15, 2026
3c4c4c1
test(conformance): every process-wide type-resolution site and every …
antonsynd Oct 15, 2026
522a19e
test(diagnostics): crash bundles under 8 concurrent writers at one cl…
antonsynd Oct 15, 2026
a584522
fix(diagnostics): a crash bundle directory is claimed with a create-e…
antonsynd Oct 15, 2026
083029b
test(stdlib): module globals are rostered by a written-after-init cen…
antonsynd Oct 15, 2026
97d0b5d
test(conformance): in-process isolation sweep — compile/analyse × sha…
antonsynd Oct 15, 2026
385199b
ci: run the in-process isolation sweep as its own GapDiscovery step (…
antonsynd Oct 15, 2026
e0a2f5f
test(registry): the TPA namespace gate and the name-keyed type cache …
antonsynd Oct 15, 2026
e3a987e
test(lsp): warm-sequential and warm-concurrent CompilerApi parity aga…
antonsynd Oct 15, 2026
59a51d5
test(conformance): static auto-property backing fields are subject to…
antonsynd Oct 15, 2026
cbabbf7
fix(registry): the master BuiltinRegistry is frozen after constructio…
antonsynd Oct 15, 2026
ff61019
fix(stdlib): random and socket module globals are read and written un…
antonsynd Oct 15, 2026
dae06b7
fix(stdlib): xml.register_namespace and numpy.random share one proces…
antonsynd Oct 15, 2026
333de07
fix(discovery): ONE assembly-visibility seam — every CLR name resolve…
antonsynd Oct 15, 2026
535f5a2
test(server): the request-3 KnownRed row reported stale under the vis…
antonsynd Oct 15, 2026
32ea542
fix(registry): each compilation loads its references into a load cont…
antonsynd Oct 15, 2026
4fe1ca6
fix(stdlib): the random and socket NuGet module projects reference th…
antonsynd Oct 15, 2026
62558b7
test(lsp): the code-action sweep shares one CompilerApi again — pins …
antonsynd Oct 15, 2026
b7ff795
docs(design): roster the in-process isolation class — visibility seam…
antonsynd Oct 15, 2026
effba77
perf(discovery): record the warm-compile cost of the P54 seams — with…
antonsynd Oct 15, 2026
f5fb7f8
fix(discovery): the framework a compilation sees is the shared framew…
antonsynd Oct 15, 2026
7ae6ed7
fix(semantic): every type definition declares its own symbol and a fr…
antonsynd Oct 15, 2026
59ed0a8
chore(spec-blocks): two wrapped overload examples are refused, not co…
antonsynd Oct 15, 2026
045ce6b
test(build-tools): the stdlib-docs type roster and mapping citations …
antonsynd Oct 15, 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
23 changes: 20 additions & 3 deletions .github/workflows/dotnet10.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
#
# build ─┬─ test-fast CLI + Core + Stdlib + LSP (one step each)
# ├─ compiler (matrix 1..7) the pre-#2178 "Test Compiler" step, sharded by namespace
# ├─ sweep (matrix of 5) one job per GapDiscovery sweep, filters unchanged
# ├─ sweep (matrix of 6) one job per GapDiscovery sweep, filters unchanged
# ├─ stdlib-spy-gates .spy native compile + generated-C# staleness gates
# └─ compiler-shard-partition proves the shards partition the main-step filter
# ci-summary (needs all, always runs) the single check to require: fails unless every job
Expand Down Expand Up @@ -52,7 +52,12 @@ env:
# (MetamorphicCorpusInvarianceTests, which spawns a dotnet subprocess per pair) out of CI.
# The CPython execution oracle likewise earned its own job when #1202 took it from a
# 60-program sample to the full ~565-cell pool.
COMPILER_MAIN_FILTER: "Category!=Benchmark&FullyQualifiedName!~InteropConformance&FullyQualifiedName!~MetamorphicCorpus&FullyQualifiedName!~DifferentialExecution&FullyQualifiedName!~FormatSpecDifferential&FullyQualifiedName!~FormatterEmitInvariance"
# The in-process isolation sweep (#2250) is excluded by Category AND name, not by name alone:
# InProcessIsolationSweepTests also carries a regular-suite slice (threads {1, 2} x 1
# repetition) and its controls, which must keep running in the shards — a bare
# `FullyQualifiedName!~InProcessIsolation` would leave them selected by no step
# (CiFilterCoverageConformanceTests).
COMPILER_MAIN_FILTER: "Category!=Benchmark&FullyQualifiedName!~InteropConformance&FullyQualifiedName!~MetamorphicCorpus&FullyQualifiedName!~DifferentialExecution&FullyQualifiedName!~FormatSpecDifferential&FullyQualifiedName!~FormatterEmitInvariance&(Category!=GapDiscovery|FullyQualifiedName!~InProcessIsolation)"
# Compiler shard buckets (#2178). Shard n runs COMPILER_MAIN_FILTER&COMPILER_SHARD_<n>.
# PARTITION INVARIANT: shards 1–6 are positive namespace buckets (shard 5 excludes shard 4's
# sub-bucket by negation); shard 7 is "everything else" — the negation of every other bucket
Expand Down Expand Up @@ -322,6 +327,18 @@ jobs:
- name: formatter-emit-invariance
filter: "Category=GapDiscovery&FullyQualifiedName~FormatterEmitInvariance"
python: false
# In-process isolation sweep (P54, #2250). A compile is a function of its inputs alone:
# seeded per-thread schedules over a corpus (the #2250 cell programs + a stride-16
# fixture sample) x instance sharing {compiler-per-thread, api-per-thread, api-shared}
# x 8 threads x 8 repetitions x {analyze, compile-to-IL}, each answer compared with the
# same (program, operation) run alone on a fresh instance. Emits
# .claude/tmp/in-process-isolation-report.json and ratchets
# src/Sharpy.Compiler.Tests/Conformance/in-process-isolation-allowlist.txt (drain-on-fix;
# `probabilistic` rows are reported, not judged). Only the GapDiscovery mode runs here:
# the class's regular slice (threads {1, 2} x 1) stays in the Compiler shards.
- name: in-process-isolation
filter: "Category=GapDiscovery&FullyQualifiedName~InProcessIsolation"
python: false

steps:
- uses: actions/checkout@v7
Expand Down Expand Up @@ -539,7 +556,7 @@ jobs:
NS = "{http://microsoft.com/schemas/VisualStudio/TeamTest/2010}"
shards = sorted(int(k[len("COMPILER_SHARD_"):]) for k in os.environ if k.startswith("COMPILER_SHARD_"))
sweeps = ["interop-conformance", "metamorphic-corpus", "differential-execution",
"format-spec-differential", "formatter-emit-invariance"]
"format-spec-differential", "formatter-emit-invariance", "in-process-isolation"]
expected = (["cli", "core", "stdlib", "lsp", "windows"] + [f"compiler-shard-{n}" for n in shards]
+ [f"sweep-{s}" for s in sweeps])
found = {os.path.basename(p)[:-4]: p for p in glob.glob("trx/**/*.trx", recursive=True)}
Expand Down
14 changes: 7 additions & 7 deletions build_tools/generate_stdlib_docs.py
Original file line number Diff line number Diff line change
Expand Up @@ -193,24 +193,24 @@ def sharpy_field_name(cs_name: str, attr_lines: Optional[list] = None) -> str:
"Sharpy.Bytes": "bytes",
"Slice": "slice",
"Sharpy.Slice": "slice",
# `RegisterType(BuiltinNames.Complex, typeof(Sharpy.Complex))` — Python's spelling (#1362, #2163).
# `Register(BuiltinNames.Complex, typeof(Sharpy.Complex))` — Python's spelling (#1362, #2163).
"Complex": "complex",
"Sharpy.Complex": "complex",
}

# Generic types, keyed by the C# outer name. Every value is the name the compiler resolves for that
# CLR type (#2163), cited by where it is registered — a page may only spell a type the way a Sharpy
# user can write it:
# list / dict / set BuiltinRegistry `RegisterType("list", typeof(Sharpy.List<>))`, etc.
# IEnumerable `RegisterType(BuiltinNames.IEnumerable, typeof(IEnumerable<>))`; the spec's
# list / dict / set BuiltinRegistry `Register("list", typeof(Sharpy.List<>))`, etc.
# IEnumerable `Register(BuiltinNames.IEnumerable, typeof(IEnumerable<>))`; the spec's
# spelling (collection_types.md "`IEnumerable[T]` for a read-only ...",
# dunder_methods.md `__iter__`). NOT `Iterable`: no Sharpy surface declares
# it (`def f(xs: Iterable[int])` is SPY0202 "Generic type 'Iterable' not found").
# IEnumerator `RegisterType(BuiltinNames.IEnumerator, typeof(IEnumerator<>))`. NOT
# IEnumerator `Register(BuiltinNames.IEnumerator, typeof(IEnumerator<>))`. NOT
# `Iterator`: that is the registered abstract `Sharpy.Iterator<T>`, another type.
# Iterator `RegisterType(BuiltinNames.Iterator, typeof(Sharpy.Iterator<>))`.
# Optional / Result `RegisterType("Optional", ...)`, `RegisterType("Result", ...)`.
# tuple `RegisterType(BuiltinNames.Tuple, typeof(System.ValueTuple))`.
# Iterator `Register(BuiltinNames.Iterator, typeof(Sharpy.Iterator<>))`.
# Optional / Result `Register("Optional", ...)`, `Register("Result", ...)`.
# tuple `Register(BuiltinNames.Tuple, typeof(System.ValueTuple))`.
# `IComparer<T>` has no entry: it renders as itself, a System.Collections.Generic interface the
# compiler resolves bare. The `Comparer` it used to render as is `SCG.Comparer<T>`, another type.
_GENERIC_TYPE_MAP: dict[str, str] = {
Expand Down
3 changes: 2 additions & 1 deletion build_tools/spec_blocks_allowlist.txt
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,7 @@ function_parameters.md::4903c78545 # SPY0200 first #1952
function_parameters.md::61cf31491b # SPY0353 first #1952
function_parameters.md::62211a9973 # SPY0234 first #1952
function_parameters.md::679a768603 # SPY0109 first #1952
function_parameters.md::695c88ab70 # SPY0220 first #2314
function_types.md::24acd11b25 # SPY0104 first #1977
function_types.md::e891fe1fdc # SPY0104 first #1977
function_variadic_arguments.md::07cd79d7a4 # SPY0202 first #1952
Expand Down Expand Up @@ -309,10 +310,10 @@ operator_precedence.md::742e989df5 # SPY0200 first #1952
operator_precedence.md::96a81f8939 # SPY0200 first #1952
operator_precedence.md::a7c0baf4de # SPY0200 first #1952
operator_precedence.md::dcb61c1e61 # SPY0462 first #1952
overload_resolution.md::4270141db4 # SPY0220 first #2314
overload_resolution.md::8ea322663a # SPY0353 first #1952
overload_resolution.md::9c88e1a396 # SPY0220 first #1952
overload_resolution.md::b4729efa50 # SPY0220 first #1952
overload_resolution.md::e5437cb11f # SPY0220 first #1952
parameter_modifiers.md::4579cd7cd3 # SPY0204 first #2145
parameter_modifiers.md::b8922611be # SPY0600 first #2144
partial_application.md::2625b8a12d # SPY0343 first #1952
Expand Down
36 changes: 18 additions & 18 deletions build_tools/tests/test_generate_stdlib_docs.py
Original file line number Diff line number Diff line change
Expand Up @@ -2877,13 +2877,13 @@ def _spec_primitive_roster() -> set[str]:


def _registered_builtin_types() -> dict[str, str]:
"""Every `RegisterType(<name>, typeof(<clr>))` in BuiltinRegistry.cs: Sharpy name -> CLR short name."""
"""Every `Register(<name>, typeof(<clr>))` in BuiltinRegistry.LoadBuiltins: Sharpy name -> CLR short name."""
names = (_COMPILER_SRC / "Shared" / "BuiltinNames.cs").read_text(encoding="utf-8")
consts = dict(re.findall(r'public const string (\w+) = "([^"]+)";', names))
registry = _REGISTRY_CS.read_text(encoding="utf-8")
registered = {}
for m in re.finditer(
r'RegisterType\(\s*(?:"(\w+)"|BuiltinNames\.(\w+))\s*,\s*typeof\(([^()]*)\)', registry
r'\bRegister\(\s*(?:"(\w+)"|BuiltinNames\.(\w+))\s*,\s*typeof\(([^()]*)\)', registry
):
sharpy = m.group(1) or consts[m.group(2)]
clr = re.sub(r"<[^<>]*>$", "", m.group(3)).replace("::", ".").rsplit(".", 1)[-1]
Expand All @@ -2892,15 +2892,15 @@ def _registered_builtin_types() -> dict[str, str]:


def _registered_generic_builtins() -> set[str]:
"""The `RegisterType(..., isGeneric: true, ...)` names: written without type arguments, such a
"""The `Register(..., isGeneric: true, ...)` names: written without type arguments, such a
name does not denote the non-generic CLR type the C# meant (`System.Collections.IEnumerable`)."""
names = (_COMPILER_SRC / "Shared" / "BuiltinNames.cs").read_text(encoding="utf-8")
consts = dict(re.findall(r'public const string (\w+) = "([^"]+)";', names))
registry = _REGISTRY_CS.read_text(encoding="utf-8")
return {
m.group(1) or consts[m.group(2)]
for m in re.finditer(
r'RegisterType\(\s*(?:"(\w+)"|BuiltinNames\.(\w+))\s*,[^;]*isGeneric:\s*true', registry
r'\bRegister\(\s*(?:"(\w+)"|BuiltinNames\.(\w+))\s*,[^;]*isGeneric:\s*true', registry
)
}

Expand Down Expand Up @@ -3416,13 +3416,13 @@ def test_second_file_partial_members_attach_to_the_type(self, tmp_path: Path):
_MAPPING_IDENTITY = [
# (C# spelling, rendered Sharpy, cited file, text that must be in it)
("IEnumerable<int>", "IEnumerable[int]", "src/Sharpy.Compiler/Semantic/Registry/BuiltinRegistry.cs",
"RegisterType(BuiltinNames.IEnumerable, typeof(IEnumerable<>)"),
"Register(BuiltinNames.IEnumerable, typeof(IEnumerable<>)"),
("System.Collections.Generic.IEnumerable<int>", "IEnumerable[int]",
"docs/language_specification/collection_types.md", "`IEnumerable[T]` for a read-only"),
("IEnumerator<int>", "IEnumerator[int]", "src/Sharpy.Compiler/Semantic/Registry/BuiltinRegistry.cs",
"RegisterType(BuiltinNames.IEnumerator, typeof(IEnumerator<>)"),
"Register(BuiltinNames.IEnumerator, typeof(IEnumerator<>)"),
("Iterator<int>", "Iterator[int]", "src/Sharpy.Compiler/Semantic/Registry/BuiltinRegistry.cs",
"RegisterType(BuiltinNames.Iterator, typeof(SharpyRT::Sharpy.Iterator<>)"),
"Register(BuiltinNames.Iterator, typeof(SharpyRT::Sharpy.Iterator<>)"),
("IComparer<int>", "IComparer[int]", "src/Sharpy.Compiler/Semantic/Registry/BuiltinRegistry.cs",
'"System.Collections.Generic",'),
("int[]", "array[int]", "docs/language_specification/primitive_types.md", "| `array[T]` | `T[]` |"),
Expand All @@ -3440,27 +3440,27 @@ def test_second_file_partial_members_attach_to_the_type(self, tmp_path: Path):
"docs/language_specification/nullable_types.md", "T?"),
("int?", "int | None", "docs/language_specification/nullable_types.md", "T?"),
("List<int>", "list[int]", "src/Sharpy.Compiler/Semantic/Registry/BuiltinRegistry.cs",
'RegisterType("list", typeof(SharpyRT::Sharpy.List<>)'),
'Register("list", typeof(SharpyRT::Sharpy.List<>)'),
("Dict<string, int>", "dict[str, int]", "src/Sharpy.Compiler/Semantic/Registry/BuiltinRegistry.cs",
'RegisterType("dict", typeof(SharpyRT::Sharpy.Dict<,>)'),
'Register("dict", typeof(SharpyRT::Sharpy.Dict<,>)'),
("Set<int>", "set[int]", "src/Sharpy.Compiler/Semantic/Registry/BuiltinRegistry.cs",
'RegisterType("set", typeof(SharpyRT::Sharpy.Set<>)'),
'Register("set", typeof(SharpyRT::Sharpy.Set<>)'),
("Bytes", "bytes", "src/Sharpy.Compiler/Semantic/Registry/BuiltinRegistry.cs",
'RegisterType("bytes", typeof(SharpyRT::Sharpy.Bytes)'),
'Register("bytes", typeof(SharpyRT::Sharpy.Bytes)'),
("Complex", "complex", "src/Sharpy.Compiler/Semantic/Registry/BuiltinRegistry.cs",
"RegisterType(BuiltinNames.Complex, typeof(SharpyRT::Sharpy.Complex)"),
"Register(BuiltinNames.Complex, typeof(SharpyRT::Sharpy.Complex)"),
("Slice", "slice", "src/Sharpy.Compiler/Semantic/Registry/BuiltinRegistry.cs",
'RegisterType("slice", typeof(SharpyRT::Sharpy.Slice)'),
'Register("slice", typeof(SharpyRT::Sharpy.Slice)'),
("Optional<int>", "Optional[int]", "src/Sharpy.Compiler/Semantic/Registry/BuiltinRegistry.cs",
'RegisterType("Optional", typeof(SharpyRT::Sharpy.Optional<>)'),
'Register("Optional", typeof(SharpyRT::Sharpy.Optional<>)'),
("Result<int, string>", "Result[int, str]", "src/Sharpy.Compiler/Semantic/Registry/BuiltinRegistry.cs",
'RegisterType("Result", typeof(SharpyRT::Sharpy.Result<,>)'),
'Register("Result", typeof(SharpyRT::Sharpy.Result<,>)'),
("ValueTuple<int, string>", "tuple[int, str]", "src/Sharpy.Compiler/Semantic/Registry/BuiltinRegistry.cs",
"RegisterType(BuiltinNames.Tuple, typeof(System.ValueTuple)"),
"Register(BuiltinNames.Tuple, typeof(System.ValueTuple)"),
("(int, string)", "tuple[int, str]", "src/Sharpy.Compiler/Semantic/Registry/BuiltinRegistry.cs",
"RegisterType(BuiltinNames.Tuple, typeof(System.ValueTuple)"),
"Register(BuiltinNames.Tuple, typeof(System.ValueTuple)"),
("void", "None", "src/Sharpy.Compiler/Semantic/Registry/BuiltinRegistry.cs",
'RegisterType("None", typeof(void)'),
'Register("None", typeof(void)'),
("System.Runtime.CompilerServices.ITuple", "system.runtime.compiler_services.ITuple",
"src/Sharpy.Compiler/Semantic/Registry/ModuleRegistry.cs", '"system.text.regular_expressions"'),
("System.Net.Sockets.SocketException", "SocketException",
Expand Down
2 changes: 1 addition & 1 deletion docs/architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ Sharpy is a statically-typed Pythonic language for .NET. Source `.spy` files com

**Pass 3 — Type Checking** (`TypeChecker.cs`, split into partial files by area: Definitions, Expressions, Expressions.Access/.Calls/.Lambdas, Literals, Operators, Statements, Patterns, Utilities): Traverses the AST, infers types, records them in `SemanticInfo`, then runs the `ValidationPipeline`.

**Key registries**: `OperatorRegistry`, `ProtocolRegistry`, `BuiltinRegistry`, `ModuleRegistry`, `PrimitiveCatalog` (source of truth for primitive types and CLR mappings).
**Key registries**: `OperatorRegistry`, `ProtocolRegistry`, `BuiltinRegistry`, `ModuleRegistry`, `PrimitiveCatalog` (source of truth for primitive types and CLR mappings). A compilation sees exactly `{framework} ∪ {its own references}`: every CLR name/namespace question goes through the `ModuleRegistry`'s `AssemblyVisibility` (`Discovery/AssemblyVisibility.cs` — a process-constant index of the trusted-platform framework assemblies plus the assemblies this registry loaded; `FrameworkOnly` for a compile with no registry), never through `AppDomain` or `Type.GetType`; references load into a collectible `ReferenceLoadContext` keyed by the reference set, so a same-named assembly from another path is its own copy (`ProcessWideResolutionScanTests` keeps every process-wide resolution or load site in that one seam). The master `BuiltinRegistry` a `CompilerApi` shares across analyses is frozen after construction — a write to a master symbol throws (#2250).

**Store checks** go through `TypeChecker.CheckStore` (`TypeChecker.StoreConversion.cs`) — one entry point for every store position (declaration, assignment, return, yield, argument, default, collection element, walrus, lambda body, etc.). Value-shape predicates (integer constant conversion, float32/decimal literal narrowing, literal-derived string into `LiteralString`) live in `ImplicitConversions.cs`.

Expand Down
Loading
Loading