Conversion Strategies — Technical Reference
📖 This is the exhaustive technical reference. For a shorter, example-driven overview of how each Go construct maps to C#, start with
ConversionStrategies.md— every section there links back here for the full detail. Read this document when you need the why: the exact emitted form, the edge cases, the converter fixes, the behavioral-test guards, and the C#-vs-Go semantic reasoning behind a decision. It is the authoritative record; the summary is the front door.
A living document, refined as more code is converted. The converter is written in Go (using the official
go/ast+go/typestoolchain, undersrc/go2cs/) and emits C# that leans on two things the visible code does not show in full: a hand-written runtime library,golib(src/core/golib/), and a set of Roslyn source generators (src/gen/go2cs-gen/) that synthesize the Go semantics which cannot be written directly in C#. See also:Architecture.md,Glossary.mdandRoadmap.md.
The guiding goal: the generated C# should be both behaviorally and visually similar to the original Go, so that a Go developer can read the output and follow it. The runtime library and the generators exist to keep the visible converted code close to the Go original.
How this reference is organized. Each
##topic opens with the high-level rule (the same ground the summary covers) and is then followed by###subsections documenting specific conversion decisions, edge cases, and fixes — most keyed to the behavioral test that guards them. When updating the converter, add the deep detail here and the reader-facing example to the summary.
Contents
Each topic is its own page. The summary, Conversion Strategies, gives the short, example-driven version of each one.
- Package Conversion
- A project name is the package’s FULL import path — a go-file-free container directory does not truncate it
- The repository’s own
go2cs/module marker is elided — on BOTH sides, by one rule - The emitted project PATH is budgeted — the file name compresses, the identity never does
- The recurse output root records its runtime root: the
$(go2csPath)default - Path separators in emitted MSBuild files: forward slashes, on every host
- The validation-proof block follows the OUTPUT LOCATION, not the invocation mode
- A GOROOT-vendored reference is named for the package’s ON-DISK path
- An importer spells the package class from the package NAME — the standard library included
- Generated output path:
$(OutDir)defers to$(BaseOutputPath) - Per-GOOS sources: layout L3 and
$(GoTargetOS) - Build-warning suppression: what the emitted
.csprojsilences, and what it deliberately does not - A doc-comment link resolves to a fully-qualified, version-pinned URL
- The README has TWO emission points, because its Tests badge reads a page the run writes LAST
- Assembly metadata is one derivation chain, and the framework is hoisted to props
- Cross-package imports (importing another package / assembly)
- An UPWARD
//go:linknamevar alias INVERTS its storage instead of giving up - Build constraints are parsed and evaluated by
go/build/constraint - An import forces the imported package’s
initto run - A
-testsproduction-reference project forces the package under test’s owninit - A NuGet-referenced standard library carries its exported metadata IN THE CONVERTER
- A foreign implement record is keyed in ONE spelling, and a VALUE one is trusted only for a partial struct
- A package records the pairs it SATISFIES, not only the ones it witnesses
- The POINTER method set records the same way, for a different contract
- Standard-library solution file (
.slnx) - Recurse per-project solution file (
.slnx)
- Package-Level Variable Initialization Order
- Compiled Library versus Source Code
- Constant Values
- A constant C# cannot declare
constis a get-only PROPERTY, not astatic readonlyfield - A complex constant emits a complex VALUE, rendered from its two exact halves
string()of an untyped constant reference hops through the default type- A
:=from a named untyped constant materializes the default type - A computed untyped float constant materializes at its destination’s float width
complex()over a NAMED untyped constant pins the element width
- A constant C# cannot declare
- Native and Narrow Integer Types
- Named Numeric Types and Constant Contexts
- A function-LOCAL
GoBigConsthoists its parse to astatic readonlyfield - The
&^=(bit-clear) compound assignment on a narrow type - A standalone
^xonuint8/uint16truncates back to the operand’s type - Logical operators on a named boolean type cast through
bool - Casting a negative value to a non-keyword type parenthesizes the operand
- A cast’s operand asks TWO questions: parse ambiguity AND precedence
- A stacked sign keeps its space:
- -anever becomes--a - A named complex type emits only Go’s complex operator set
- A function-LOCAL
- Floating-Point Formatting
- Nil and Zero Values
- A NARROW-UNSIGNED target folds a constant only when nothing else can make it compile
- The THREE deref accessors of
ж<T>— when each is needed, and how the converter picks - Canonical typed-nil pointer boxing
- A HAND-OWN’s pointer parameter sees
NilBox, nevernull— and the doctrine alone did not hold it reflect.Value.Interface()is a boundary into interface space, so it packs the typed nil too- A pointer crossing into an interface carries its static type, however the pointer was produced
- Pointer-to-interface assignment through selector fields
- Empty Interface (
any) - Multi-Assignment and Evaluation Order
- A multi-value RETURN reads its plain operands AFTER its calls
- A SELECTOR left-hand side counts as a reassignment — a field swap must stay simultaneous
- A STAR-DEREF of a CALL result counts as a reassignment — the last shape of the classification gap
- An address-taken reference-typed local heap-boxes too —
Ꮡ(value)copies are only for reads - An address-taken NAMED RESULT heap-boxes too
- An address-taken VALUE PARAMETER heap-boxes too
- A field-addressed value local heap-boxes —
Ꮡ(x).of(…)copy-boxes orphan writes - A capture-mode method called on a FIELD CHAIN of a local is an address-of too
- The same chain one level up:
&recv.f1.f2on a POINTER RECEIVER Ꮡx.of(T.Ꮡf)returns ONE view per (box, field) — the field-view cache- A TYPE-SWITCH BINDING is escape-analyzed like any other local
- A PACKAGE-LEVEL function literal’s own locals are analyzed too
- A pointer-receiver METHOD VALUE heap-boxes its receiver — the implicit
(&x).M
- Short Variable Redeclaration (Shadowing)
- Type-vs-Method Name Collisions
- Test-variant name coherence: production names are pinned, test-side method declarators Δ-rename
- A NESTED package’s production
GoImplementrecord anchors to the production metadata file - Test suites REFERENCE the production project instead of recompiling it
- Under the RECOMPILE model the test half CONTINUES the production emission (the
productionSeed) - A recompile-model test project compiles the production sources — so it owes their references and their per-GOOS half
- An Example/Benchmark-ONLY test file is dropped from the compile set (Phase-4D file exclusion)
- A package-qualifier
usingin a converted TEST SOURCE contributes a project reference - Shadowing the names go2cs itself spells (
nil, golib names, emitter-spelled type names, C# keywords) - A parameter that shadows an imported package is renamed at its declaration too
- A declaration shadowing a BUILT-IN makes the call an ordinary call
- A declaration named
heapqualifies the boxing intrinsic - A local that shadows a PACKAGE name is not a package qualifier
- A collision-renamed member keeps the file’s RENAMED qualifier
- A DOT-imported collision-renamed member has no selector to carry the rename
- Multi-Result Values and Comma-Ok Forms
- Slices and Arrays
- Array ASSIGNMENT copies the whole array (
.Clone()on the RHS) - Array VALUE-COPY at every transfer site (range, composite, return, send, append) — DEEP for nested arrays
- A STRUCT carrying array fields copies through its generated
ΔClone() - Nil-vs-empty slice identity (
s == nilis representation nilness, not emptiness) - A slice of a ZERO-SIZE element type carries no storage —
make([]struct{}, math.MaxInt)allocates nothing - A composite literal omitting a fixed-array field keeps the zeroed backing
- A fixed-array composite literal carries its DECLARED length (
.array(N)) - An ELIDED element carries the same construction as the spelling it elides
- A NAMED array type’s EMPTY literal is its zero value — and its element may need constructing
- An array or slice literal may MIX positional and keyed elements
- A fixed-size array constructs its ELEMENTS when
default(T)is not usable storage - A NAMED array’s ZERO VALUE constructs its elements through the wrapper’s lazy backing
- A NAMED array compared with the unnamed array it is written over casts the unnamed side
make([]E, n)constructs its ELEMENTS by the same ruleclearrebuilds each element through golib’sGoZero— the RUN-TIME half of zero-value construction- A map READ of a shape-carrying element supplies the zero from the CALL SITE
- A named slice wrapper’s non-generic
ISlice.Appendis an EXPLICIT implementation - Composite types render structurally (
[]*Tkeeps the pointer) - Appending to an interface-typed slice casts the element
- A
nilelement appended to a slice casts to the element type
- Array ASSIGNMENT copies the whole array (
- Strings (
@stringandsstring)@stringis a byte string, and slicing it is a window- String literals render as
u8spans - A string literal with raw-byte escapes emits a byte-array
@string - Converting between strings and slices
- A value-materializing string literal is HOISTED to a
static readonlyfield beside its first use - A function-LOCAL string const hoists to a
static readonlyfield under its own name - Named string types
sstring: a string view that allocates nothing- A non-escaping
string([]byte)local emits the stack-stringsstring - An sstring TWIN: a registered function gains an
sstringoverload that calls bind
- Maps and Channels
- A
rangebody may MUTATE the map it is ranging over — the enumerator walks a KEY SNAPSHOT m[string(b)]— the map-READ key does not copy (tmpstring)- The NIL map key
- An INTERFACE map key compares by Go equality, never by adapter identity
- Named map types and constrained map access
- Named channel types
- A function-LOCAL named type declaration hoists to member level (slice/map/channel/array/pointer)
- An embedded field’s NAME is the UNQUALIFIED type name (dot-imported embeds)
- Select statement lowering (terminating and empty clauses)
- A NIL channel is never ready — and asking must not throw
- The default form routes through trySelect — send cases are unguarded in both forms
- Every case’s operands are hoisted — evaluated exactly once, in SOURCE ORDER, at select entry
- Known exposure: marker-shaped USER identifiers can collide with synthetic names
- Real channel runtime — the hchan/selectgo port (rendezvous, cap/len, single-fire, uniform-random)
- An escaping comm-clause binding receives into a temp and heap-boxes at clause entry
- A
- Generic Constraints
- An array-core constraint
~[N]Elifts toIArray<E> - A type set of COMPOSITE terms lifts nothing — the union survives only as a comment
- A single-term pointer constraint
[P *T]erases the parameter toж<T> - An integer named-numeric wrapper implements the integer operator interfaces
- A named-numeric wrapper is
IComparable<T>as well as ordered by operators - An untyped constant argument to
min/maxtakes the call’s type - Lifted shift constraint uses the BCL shape
IShiftOperators<T, int, T> - Builtins over constrained slice type parameters
- Integer type-parameter conversions route through golib (the
E(100)family) - A named-wide-integer or type-parameter slice index casts to
nint - Method-set interface constraints bind the interface directly; pointer instantiations project through the adapter
- A SELF-REFERENTIAL generic method-set constraint uses a box-wrapping proxy as the type argument
- A struct embedding the constrained generic promotes its members — three residual crypto-curve fixes
- Constraint-only type parameters need explicit type arguments
- The
comparableconstraint - Generic struct equality is decided per FIELD, not per type parameter
- A generic struct implementing an interface BY VALUE partials at its OPEN definition
- The
string | []byteunion - Explicit type arguments come from the callee’s instantiation
- A CONSTANT argument that fixes a type parameter is retyped to the instantiation
- Increment/decrement on a type parameter
- Unary negation on a type parameter
uintptras a generic numeric type argument- Union-constrained sub-slices ARE the type parameter
- Spreading a union-constrained value
- An array-core constraint
- Type Aliasing
- Delegates to Value Receiver Instances
- A method value reassigned via
=hoists its receiver capture - A POINTER-receiver method value binds the ADDRESS in assignment context too
- A VALUE-receiver method value snapshots its receiver in EVERY position
- The receiver EXPRESSION is evaluated exactly once, for every kind and every shape
- A VALUE receiver reached through a POINTER expression hoists the POINTEE’s copy
- A bare function value in
:=takes its named delegate type, notvar - A DISCARDED function value is cast, never declared
- Two
unsafe.Pointers compare as BOXES, not as addresses
- A method value reassigned via
- Defer / Panic / Recover
- A function that defers or recovers emits its body INLINE, inside a frame
- Why the body is not a lambda
- The named-result form: results outside the try, exits through a label
- The catch arm returns Go’s zero results
recover()is a static call, not a parameterdeferregistration: the arity ladder, and no bang- A deferred RECEIVER-FIELD call with no arguments is lowered INTO the frame’s
finally - A VARIADIC deferred/spawned func literal is cast to its golib family delegate
- A ZERO-ARG deferred/spawned call of a NAMED variadic callee keeps the lambda — the group carries
params defer f(g())spreads a MULTI-VALUE call: the tuple is the eager argument, the thunk expands itdefer panic(v)captures its value at the defer, and the sequence survives it- The re-raise of an unrecovered panic belongs to the frame that CAUGHT it, not to the thread
- A nested defer scope gets a frame of its own
- An unrecovered panic crashes the process Go-style: report on stderr, exit code 2
- A panic VALUE renders through Go’s
preprintpanicsrule — anerrorprints its message, not its address - An IMPLICIT divide-by-zero panics with the runtime’s OWN value, so it satisfies
runtime.Error make([]T, len[, cap])out-of-range panics are RECOVERABLE, with Go’s messages- A panicked C#
stringboxes as Gostringat golib’s boxing boundary - A NIL-POINTER dereference is a RECOVERABLE panic, with Go’s message
- Named-delegate and builtin callees keep the lambda form
- A value-returning goroutine callee is wrapped in a discarding lambda
- A func-literal ARGUMENT of a deferred call hoists its captures before the call
- Defer/go EAGER arguments follow the enclosing closure’s capture renames
- A func-literal ARGUMENT inside an
if/forcondition hoists its captures before the statement - A func-literal ARGUMENT inside a return expression hoists its captures before the
return - A func literal inside a RANGE expression hoists its captures before the loop
- The enclosing statement’s hoist buffer does NOT extend into a literal’s BODY
- A BLANK result mixed with a named one still needs the named-return-defer handling
- Function-literal named results
- Deferred calls whose callee returns a value take the lambda form
- Deferred pointer-receiver nullary calls bind the box method group
- A deferred pointer-receiver method on an escaping value local captures by-box, not by-copy
- Expression Switch Statements
- A switch on a
static readonlyconstant tag lowers to if-else - A leading constant-true case stays opaque to the compiler
- No constant pattern against a named-numeric wrapper
- An index-expression case label falls back to equality
- Literal case labels under a named-type tag compare through a cast
- A trailing
defaultin a switch WITHfallthroughis guarded on!match - A NON-TRAILING
defaultis guarded on the PRECOMPUTED any-case-match, never the running flag - A clause’s
elsemay only be dropped when EVERY preceding clause terminates - A
breakin a case that alsofallthroughs must skip the fallthrough - A
continuein a break-wrapped case targets the LOOP, not the wrapper
- A switch on a
- Type Switch Statements
- Struct Types
- Lifted anonymous structs embedding an interface
- An anonymous struct lifts from ANY depth of its declared type
- A global addressed only by the package’s own
_test.gois still heap-boxed - Astral rune literals
- Type-switch default arm binds the interface value
- The type-switch tag evaluates exactly once
- Generated code global::-qualifies root-namespace references
- A GoImplement record’s adapter key is canonical, not textual
- A test project’s references cover UNROOTED alias targets (single- AND multi-segment)
- Generic embedded fields
- A func literal in an
anyslot states its Go result type explicitly - A
:=-bound func literal states its declared result type when its body cannot supply it - Lifted function-local types: anonymous structs dedupe, named types carry [GoLocalName]
- A lift inside a PACKAGE-LEVEL func literal flushes at package scope, seeded by the declaration
- A methodless named func type renders as its base delegate
- Named delegate types wrap mismatched initializers
- A named delegate value passed to a structural func parameter re-wraps
- Func-typed fields with a cross-package (slash-path) type render structurally
- A variadic func type lowers to the golib
Actionꓸꓸꓸ/Funcꓸꓸꓸdelegates reflect.Value.Callover a variadic func value is TYPED dispatch — no reflective invoke can carry the tailreflect.MakeFuncisValue.Call’s exact inverse — a compiled delegate over the descriptor’s carried System.Type (2026-08-29)- Major-version import directories
- A C# keyword inside a dotted import-path element
- The import-path rewrite rewrites only the PATH, not the constructor in front of it
- A non-canonically-aliased import renders foreign types via the file’s alias
- A ONE-FIELD struct’s positional
nilliteral names its field constructor
- Struct Type Embedding
- An embedded struct is an INLINE field, so a value copy copies it
- The address of a FIELD of a slice or array element aliases the element
- The array-backing publish is atomic per box
- The element address of a VIRGIN named array must materialize through the receiver
- The named-array wrapper publishes its lazy backing atomically
- A promoted field whose name equals the enclosing type is Δ-renamed
- An embedded field is named by GO, not by the C# rendering of its type
- An embedded PREDECLARED type is a plain field stamped
[GoEmbedded](2026-09-05) - An EMBEDDED field whose derived name equals the enclosing type is Δ-renamed
- Promoted pointer methods descend multi-hop value-embed chains
- A nil embedded pointer is holdable and assignable — only its dereference panics
- A field promoted through an embedded POINTER is rooted at the POINTED-TO allocation
- Interfaces
- Every eligible interface carries runtime duck-typing shells — the sole resolver of a structural assert
- Cross-package pointer-to-interface conversions use the foreign adapter
%T(and type-name rendering generally) unwraps generated adapters and pointer boxes- A cross-package interface’s unexported sealing marker is stubbed
- A dynamic interface’s runtime conversion class re-escapes a keyword method name
- A keyword-named type’s interface adapters escape declarations and compose class names unescaped
- An interface member’s keyword-named PARAMETERS escape in every generated implementation
- A keyword-named addressed global’s heap-box field strips the escape after the Ꮡ prefix
- A foreign struct’s promoted method forwards through its value embed
- A promoted box-receiver method through an UNEXPORTED value embed is called cross-package via a public forwarder
- A value embed promotes its pointer-receiver methods into the outer POINTER method set
- A named field whose name equals its interface type is NOT an embedded interface
- Cross-package value-to-interface conversions use the local VALUE adapter
- Under
-tests, a white-box PRODUCTION type is FOREIGN to the generator — so the name carries the prefix - An exported func type publicizes the unexported types in its signature
- A test-file exported helper over an unexported PRODUCTION type is emitted
internal(the MIRROR) - A FUNCTION-LOCAL type is emitted
internal— its Go name’s case carries no export meaning - A publicized unexported interface is emitted
public - Publicized unexported types make their exported methods public
- Structural interface satisfaction emits C# interface inheritance
- Embedded-pointer hop receivers split per method
- With SEVERAL embedded pointers the hop is chosen per member, not per struct
- A forwarded multi-value call deconstructs when tuple elements need interface conversion
- A multi-value call spread into a call’s parameters in an assignment hoists into temps
- A range over a pointer-typed type conversion parenthesizes before the deref
- Adapter accessibility: symbol-OR-name on both sides
- GoImplement records de-duplicate at attribute emission
- Anonymous interfaces used as an adapter target are lifted package-wide
- An INITIALIZED var lifts its explicit anonymous declared type too — and a blank name lifts from the GO identifier
- A collision-renamed type’s pointer adapter composes on the package qualifier, never the whole-type alias
- Every type-name render resolves a lifted anonymous struct cross-file
- A lifted type name is unique across the PACKAGE, and the
-testsvariant inherits production’s - Function-literal parameters share the body scope
- System-colliding local type names are root-qualified in assembly attributes
- A name both
-testsvariant classes declare is qualified with the FILE’s anchor class - A struct-literal interface field takes a pointer element’s adapter
- The struct-field interface routing also fires on an ELIDED element composite
- A keyed element’s interface target is the composite’s own SLOT, never the LHS variable’s type
- A GoImplicitConv record needs at least one LOCAL operand
- Pointers
- A colliding pointer-adapter name qualifies its FOREIGN interface side
- Pointer equality canonicalizes the STORAGE, not the referent — slice/array element identity
- A pointer’s nilness and identity are STRUCTURAL — the
IsNull/IsNilPointersplit - Reading a pointer and taking a field pointer allocate NOTHING — the two costs hidden inside
ж<T> - A pointer parameter whose every use is a dereference is a
refparameter — the ж-box ref-lowering - The lowered emission, row by row — the seven argument shapes in emitted code
- Pointer-typed globals and double-pointer walks (
&head,*pp,ValueSlot) - Capturing the address of a heap-boxed local in a closure
- A capture that is WRITTEN after the capture point routes to shared storage, not a snapshot
- A write that ENCLOSES the literal counts as written-after-capture — the self-recursive closure
- A nested closure’s capture snapshot reads the enclosing closure’s snapshot
- A func literal that is only ever CALLED emits as a C# LOCAL FUNCTION
- A variable DECLARED INSIDE a closure is not captured BY it
- Capture-mode methods called through a value field of the receiver
unsafe.Alignof/unsafe.Offsetofname a TYPE, resolved throughgo/typesunsafe.Sizeof/Alignof/OffsetofFOLD to a constant at expression sites- The run-time
unsafe.Sizeofanswers through Go’s layout rule, not the CLR’s marshaller - Converting a Go pointer to
unsafe.Pointer - Pointer-derived funnel arguments and bridged
unsafe.Pointerarguments keep their box alive across the call — the statement-scopedGC.KeepAlivedrain - A numeric value pun READ is a bitcast —
*(*uint64)(unsafe.Pointer(&f))boxes nothing - The pointer-word read —
*(*unsafe.Pointer)(unsafe.Pointer(&x))emits a CARRYING pointer, never a byte pun - A pointer
reflecthanded out as anunsafe.Pointermust convert BACK — the order token is remembered, not redefined - An OPAQUE
*struct{}conversion mints a recoverable token —syscall.Pointer(unsafe.Pointer(p))keeps its referent - Pointer DISPLAY never dereferences out-of-range;
unsafe.StringData("")is nil - Reinterpreting a pointer to a defined type with identical underlying —
(*Base)(p) - The club-41 mop-up batch (flag/flate/binary/syntax roots)
- Slice-to-array: the VALUE form copies, the POINTER form ALIASES
- An element pointer reinterpreted as an array pointer ALIASES the element’s storage
- A direct-ж method on a value field-chain boxes through the &-machinery
- Field address of a collision-renamed heap-boxed local uses the raw box name
- A capture-mode method on a shadow-renamed heap-boxed local uses the rendered box name
- Nested dereferences parenthesize before the outer
.Value - Function literals returning
unsafe.Pointerstate their return type - Interface-returning literals with distinct arm types state their return type too
- Multi-value literals with no fully-typed arm state their return type — named results included
- String-returning literals in assignment position state their return type
- Numeric-returning literals with untyped-constant arms state their return type
- A literal in GENERIC-RESULT inference position states its return type
- A returned FUNC LITERAL is typeless in C
- Publicization decides WHAT a type’s modifier is; the test-bridge arm only decides WHERE
- A white-box PRODUCTION↔PRODUCTION pointer pair is already implemented — do not record it again
- A GoImplement record is gated on the method set actually satisfying the interface
- Promoted methods through a FOREIGN pointer embed forward via the embedded box
- Promoted methods through embedded INTERFACE fields route per-member to the declaring field
- A pointer parameter used only through its box gets no deref VALUE alias
- A pointer RECEIVER compared to
nilcompares its box, not its deref’d value - A pointer RECEIVER’s deref alias is nil-DEFERRING — the panic moves to the body, it does not vanish
- A pointer PARAMETER is nil-deferring for exactly the reason a receiver is
- A receiver or parameter RE-POINTED before first use — the normalization idiom
- A reinterpreted raw address ALIASES native memory instead of boxing a copy
- An address of MANAGED storage that outlives its statement must carry a PIN
unsafe.Sliceover MANAGED element storage ALIASES it- A reinterpret of a MANAGED pointer aliases the box — it never round-trips through the address
- A pointer-cast slice with a LOW bound offsets the span
unsafe.Pointer(p)on a pointer PARAMETER renders the box, never a deref- A reference-type-pointee pointer parameter uses the nil-check-free
.ValueSlotderef alias - A pointer-element composite literal takes the box for a deref-aliased ident
- A pointer value passed to an
anyargument takes the box
- Implicit Pointer Dereferencing
- Labeled Control Flow and Loop Variables
- Break Label
- Continue Label
- A user label on an empty statement emits an explicit empty statement
- Reassigned or ref-bound range variable
- For-clause variables are per-iteration (Go 1.22 loop-variable semantics)
- A range-over-int index is
nint(golib’srangehelper yields Go’sint) - Range-over-integer covers EVERY integer type, and the iteration variable keeps the operand’s width
- A blank scalar range variable never emits as
_ for rangeover a slice allocates NOTHING —slice<T>.GetEnumerator()returns a structrangeover an ARRAY VALUE iterates a COPY — the snapshot is the range EXPRESSION’s.Clone()
- The
go.golibsupport namespace- Foreign renamed types reference the recorded imported-type alias
- A foreign package’s collision rename is derived from that package, not from the conversion run
- A foreign package’s re-exported type ALIAS is derived from that package too
- A DOT-IMPORTED renamed type is spelled through the same alias as the qualified reference
- The reflection bridge answers a read where the answer EXISTS — four members that did not (2026-08-19)
- A typed nil keeps its type across BOTH interface-space boundaries
- Converted programs write UTF-8 stdout — the ambient console code page never reaches the bytes
- Source Generators
- The standard-library conversion applies
-tags purego - Manually-Converted Declarations
- The managed netpoller — the ten
runtime_poll*contracts on .NET’s completion machinery - The overlapped SUBMIT seam — a per-operation record owning native lifetime, and a golib rendezvous
- The Linux flavor’s poller — the fallback first, then the readiness poller: epoll, one drain thread, and the Windows descriptor state machine
- The Linux
struct statmirror and the NoError raw bottom — the first Linux members of the struct-passing class Uname— the struct-passing class taken to its limit, and the deferral rule it exposed- The sockaddr family on Linux — L10’s mirror, arm for arm, as the socket poller’s prerequisite; and
Mmap’s slice is a snapshot - The CryptoAPI chain seam — when a native record must be READ as a struct and HANDED BACK as a pointer
- A hand-owned file can declare that it needs
/unsafe - Hand-owns have a platform, and it is not the same question as a folder
- Realizing an asm-backed arch layer with managed hardware intrinsics
- A cross-package
//go:linknamePULL emits a forwarder, not a throwing stub - A cross-package
//go:linknamePUSH resolves per recorded disposition — forwarder or announced panic - A
//go:cgo_import_dynamictrampoline gets a RECORD, so its address can be resolved rather than invented internal/concurrent.HashTrieMap— a managed map where Go seeds itself fromMapType().Hasher- The Linux syscall bottom — ONE libc P/Invoke, and why
r2is exact rather than approximate - Every P/Invoke is source-generated
- The scheduler brackets are a faithful no-op, not an omission
runtime.argslice— forwarding and populating are ONE changeruntime.sysDirectory— the same pairing, one consumer shape further out- Long-path awareness is process SETUP, and golib does what Go’s
osinitdoes - Linux standard-descriptor hygiene is process SETUP too — Go’s close-of-stdout must release the pipe
internal/weak.Pointer— the CLR already has weak references, so the runtime handle becomes oneunique.clone— a raw-offset string walk the managed model cannot express, hand-owned after the@stringwindow made it GC-fatalinternal/cpu.getGOAMD64level— a BUILD constant, so the honest answer is the baselineStructField.Tagis a REAL read — the converter has always emitted the tag, nothing had ever read itreflect.Copyis bridged element-wise — the auto form is a flat two-header memory movereflect.Type.Name()— a DEFINED type HAS a name even when its underlying type is a composite- Go’s ASSIGNABILITY rule, and the identity walk underneath it
- The reflectlite MINI-BRIDGE mirrors reflect one layer down — and the closure that validated it landed five rules in SHARED machinery
abi.Type’s SPECIALIZATIONS are synthesized, not downcast —StructType()/ArrayType()- A managed REFERENCE is a Go pointer, not a Go struct — the reflection bridge’s descent rule
- An EMBEDDED field is an embed to
reflect, tag included — and Go’s whole embedding contract reads it - An UNNAMED func type renders STRUCTURALLY, exactly as an unnamed struct does
reflect.Value.Bytes/SetBytesare defined over the element KIND, and they ALIASnew(T)is Go’s ZERO value — and for a container kind that means the NIL one- An
unsafe.Pointeris compared BY ADDRESS, and a container’s pointer names its STORAGE - A map ENTRY is a SLOT — its Value is typed by the map’s ELEMENT type, never by what it holds
- A NaN map key is never equal to anything, itself included
- A COMPLEX constant expression must be FOLDED — .NET’s mixed operators are not Go’s arithmetic
- Realizing the runtime TIMER contract (
Sleep/newTimer/stopTimer/resetTimer) - The runtime’s PROCESS-CONTROL surface: implement the CONTRACT, never the mechanism
- Stop-the-world: the CONTRACT model, and the regions that run without Ps
- The GC measurement surface — one recorder, one ring, one snapshot
iter.Pull’s coro — a symmetric handoff between two threads, and the goroutine count that had never been wiredsync.Pool— a managed-reference ring slot, and a thread-affine stand-in for the P pin- A pointer’s REFERENT, not its box, answers every lifetime and identity question
sync.Cond’s copy detector, on reference identity rather than an addressruntime.Stackrenders a GO-shaped traceback, and recovers the panic siteruntime.Stack(all: true)enumerates every goroutine, and each one names the wait it is parked on- EVERY reader of a panic’s trace gets the origin — not just
runtime.Stack runtime.Callers/Frames.Nextwalk the managed stack projected to GO-LOGICAL framescodegen-liveness— a frame holds what Go has already droppedhost-limit— the third disclosed-divergence class: what the test HOST cannot BEdeferredandstructural— the two labels every allocation-count disclosure resolves into- The process ROOTS a converted program never gets from a Go bootstrap:
runtime.envs,os.runtime_rand reflect.ArrayOfcomposes a descriptor; it does not reconstruct a linker recordreflect.StructOfMINTS a CLR value type, and then changes nothing elsereflect.SliceOfis the same one-liner asPointerToruntime.Pinner— a pin BIT keyed by the referent allocation, and Go’s two-level cgo walk over managed values
- The managed netpoller — the ten
- Comments
- Deterministic Output
- Packages That Do Not Type-Check