Glossary
Terms the go2cs documentation uses for how the converter is tested and validated, the names that appear in converted code, and the .NET and tooling acronyms the conversion pages assume. For conversion terminology (how Go constructs map to C#:
ж<T>, heap boxes, adapters, direct-ж, shadow renames) seeConversionStrategies.md(summary) andConversionStrategies-Reference/(full detail). Companion docs:Architecture.mdandBaseline-vs-FullConversion.md.
Testing and validation
Behavioral test.
A small Go program under src/tests/Behavioral/ that exercises one construct, together with its converted C#.
The behavioral suite converts each one, compares the converted C# with its golden (below), compiles both the
Go and the C#, and, for the tests that opt in, runs both and compares their output.
Guard.
A behavioral test that locks in a fix: a minimal Go program exercising the exact construct, its converted
.cs, and a golden. Where it can, a guard is shown to fail on the converter from before the fix, and a
guard for a pointer or box fix observes a write through the fixed path in its output, compared against Go, so
a conversion that compiles but silently copies cannot pass it.
Golden / re-baseline.
The committed expected converter output (*.cs.target), compared byte for byte (ignoring line endings) with
what the converter emits now. When a change is meant to alter emission, the affected goldens are
re-baselined: regenerated from the converter’s output, never edited by hand, while the run-and-compare
phase stays green to show the behavior is unchanged.
Census.
Building the whole converted standard library (dotnet build src/go2cs-stdlib.slnx) and counting the projects
that emit their own assembly (bin/Debug/net10.0/<AssemblyName>.dll exists after the build). It counts
packages that compile, not errors: fixing one family of errors can raise the error count by uncovering the
errors behind it.
Banked.
A standard-library package whose converted Go test suite validates — every test reaches the same verdict as
go test -json on the same sources, or carries a disclosed reason — has its converted test artifacts committed
into src/core/<pkg> (test sources, host, package_test_info.cs, <pkg>.tests.csproj and any disclosures
manifest) together with its row in ValidatedTestPackages.md. From then on those
artifacts are the package’s own regression check.
Banked roster.
The set of banked packages: whatever ValidatedTestPackages.md’s table lists, each
row carrying the package’s matching-verdict and disclosed counts. The table is the single source: the scripts
that re-validate the roster read it rather than keeping a list of their own.
Validated sweep.
The re-validation run (src/run-validated-sweep.ps1) that takes every package on the banked roster through
the whole test pipeline again with the current converter (convert the tests, build the test host, run it,
compare the verdicts with go test -json) and requires each package to land on exactly its recorded counts.
A package that passes with different counts fails the sweep, because the table and the converter no longer
agree. It is the check that re-proves converted test code, not just converted library code.
MVP — minimum viable [increment].
The smallest correct and fully tested first version of a converter or runtime feature: the narrowest scope
that proves the whole mechanism end to end, with the broader coverage added later in separately tested steps.
For example, the stack-string sstring emission (s := string([]byte) → a zero-copy view instead of a heap
@string copy) first fired on exactly one standard-library site, under a deliberately conservative rule. An
MVP is not a prototype or a shortcut: it ships behind the full set of checks, including its own guard,
and is correct within its scope; only its reach is small.
Hop. Moving the converted standard library to a new release of one of its two inputs: a Go hop moves the Go release the standard library is converted from, and a .NET hop moves the .NET runtime and target framework. A hop changes one of the two at a time, never both.
Names in converted code
Marker glyphs / Symbols.cs / symbols.json.
The emitted C# uses reserved glyphs (ж pointer box, Ꮡ address-of, Δ shadow/collision rename,
ꓸ type-alias dot, ᴛ temp, ᒐ the per-function GoFrame defer local, …).
Converter and generator source reference named constants, never literal glyphs: the C# side (golib,
go2cs-gen) uses Symbols.cs (PointerPrefix, AddressPrefix, ShadowVarMarker, …, via
using static go2cs.Symbols); the Go converter uses the same-named constants in src/go2cs/symbols.go. Both
files are generated projections of the canonical symbol table src/core/go2cs/symbols.json (kept pure ASCII,
every glyph a \uXXXX escape, so no tool can mangle it): edit the JSON and regenerate with go generate . from
src/go2cs, or run src/check-symbol-sync.ps1, which regenerates and exits 1 on drift. Never hand-edit the two
generated files. Glyphs inside goldens, docs examples, and comments are fine: they are the output.
Adapter class names (XжI / XᴠI).
A generated interface-implementation adapter composes its name from the concrete type, a form
glyph, and the interface’s simple name: errorStringжerror reads “*errorString as error”;
net_ConnᴠWriter, “a foreign net.Conn value as io.Writer”. Three shapes exist. A same-package
value implementation needs no adapter at all — the generator adds the interface to the struct’s
own base list (partial struct T : I) and the conversion is implicit. A pointer implementation
(GoImplement<T, I>(Pointer = true), i.e. Go’s method set puts the method on *T) emits a TжI
class wrapping the receiver box ж<T>, so the interface value aliases the pointer exactly as Go’s
does — pointer identity, write-through, and type-assert-back all survive — and every conversion site
must therefore construct it (new errorStringжerror(Ꮡ(…))). A value implementation whose
concrete cannot carry a base list — a foreign struct, or a named func type (a C# delegate cannot be
a partial struct) — emits a TᴠI class. Converter (adapterTypeRef / valueAdapterTypeRef in
src/go2cs/interfaceConversion.go) and generator (ImplementGenerator) compose the name independently and must
stay in sync; a foreign concrete is package-qualified first (os_FileжReader).
Three properties of the scheme are load-bearing, and answer the recurring “why not жT_I?”: the
separator must be a character illegal in a Go identifier, or the name stops decomposing back
into its parts — _ is legal in Go names and already means package-qualifier here, so a
hypothetical жos_File_error is ambiguous where os_FileжReader is not. Concrete-first keeps
each adapter sorted beside its own type in IntelliSense, object browsers, and CS-error output,
where a leading glyph would instead bucket every adapter in the package together. And the form is
part of the name (ж vs ᴠ), so only same-form pairs can collide — which the CS0102 prune
relies on. One naming trap: the constant is PointerPrefix because it prefixes the type in
ж<T>; in a composed adapter name the same glyph sits between the two names.
See Interfaces.
.NET and tooling terms
General .NET / C# / toolchain terms the conversion docs assume. (For the emitted-code glyphs
ж/Ꮡ/Δ see Marker glyphs above and the conversion docs:
summary · reference.)
BCL — Base Class Library.
The core class library that ships with .NET: the fundamental System.* types — System.Object,
System.String, the integer primitives (System.Int32 and the native nint/nuint),
System.Collections, System.Numerics, and so on. When a Go construct maps “to the BCL” it maps onto a
built-in .NET type rather than onto golib or a source generator — e.g. Go any/interface{} → object,
the fixed-width integer aliases (int32 = System.Int32), and the System.Numerics operator interfaces
that generic constraints lift to.
ILC — the IL Compiler (Native AOT). .NET’s ahead-of-time compiler: it consumes IL at publish time and emits native machine code, so the program starts without a JIT. go2cs measures it as the third column of the performance suite (Go binary / C# JIT / C# Native AOT), and it is expensive here — each AOT publish compiles the full converted-stdlib closure, ~25 min per benchmark on an older six-core desktop, which is why a full perf run takes hours. ILC arrives as a runtime pack selected by the TFM, not by the SDK performing the publish — so a new SDK publishing an old TFM silently resolves the OLD ILC and produces the previous generation’s codegen, which looks like “the new runtime changed nothing” rather than like an error. So no AOT measurement of a new runtime exists before the TFM moves.
TFM — Target Framework Moniker.
The identifier a .csproj uses to declare which framework a project builds against: <TargetFramework>net10.0</TargetFramework>
— the go2cs corpus’s own value today. It selects the reference assemblies compiled against, the runtime the
output binds to, and the Native-AOT compiler, since ILC arrives as a runtime pack chosen by the TFM rather
than by the SDK performing the publish. That is why moving go2cs to a new .NET release happens in two steps:
the SDK alone moves first (the compiler and build tooling change while the TFM stays put, so the C# language
level and diagnostics move but the runtime does not), then the TFM moves (one line per project, after which the
new runtime and its ILC are what is being measured).
Roslyn.
The .NET compiler platform — the C#/VB compiler plus its analyzer/codegen APIs. Its source generator
feature lets code participate in compilation, and go2cs relies on it heavily: the generators in
src/gen/go2cs-gen/ (referenced as an analyzer by every converted project) synthesize the Go semantics C#
cannot spell directly — interface satisfaction, pointer-receiver overloads, struct-embedding promotion,
named-type operators. See Source Generators.
CRTP — Curiously Recurring Template Pattern.
The idiom where a type is parameterized by itself — T : IFoo<T>. .NET’s generic-math and comparison
interfaces use it (IComparisonOperators<TSelf, TOther, TResult>), and golib’s comparable<T> is a CRTP
constraint so a type can state “I am comparable with my own kind” — the shape Go’s comparable built-in
maps to.
NRE — NullReferenceException.
The .NET exception thrown when a member is accessed through a null reference (the CLR analogue of a nil
dereference). In the conversion docs it appears mostly as a hazard being designed out: a zero-value
@string or an unallocated promoted-embed box would otherwise NRE on first use, so golib and the
converter make those zero values safe to touch.
MSBuild.
.NET’s build engine — it drives dotnet build, project references, and analyzers. It matters to the
converter in two places: cross-package references arrive at the source generators as compiled metadata
references (not source syntax), which changes how embeds/interfaces must be resolved; and the converter
emits MSBuild .csproj project files (carrying the $(go2csPath) property) plus .slnx solutions for the
converted output.