You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Implement #806: migrate Gsharp.Extensions stdlib from C# to G# (#837)
* fix(compiler): emitter and binder fixes to enable G# stdlib dogfooding (#806)
ADR-0084 §L5. While porting Gsharp.Extensions.Optional and
Gsharp.Extensions.Sequences from C# to G#, the bootstrap-SDK path
exposed a cluster of compiler bugs that only surface when gsc is
driven from BuildTask under a MetadataLoadContext. Each fix is
independent and verified by the existing 107+ Extensions.Tests plus
the full GSharp.sln suite.
Binder / lowering:
* ExpressionBinder.Access: route member access on a Nullable[openT]
receiver through the same TypeSpec MemberRef branch the closed
Nullable[int32] path uses, so .HasValue / .Value bind on a generic
T? parameter.
* Binder, StatementBinder, IteratorRewriter, Lowerer: switch five
open-generic IEnumerable[T] / for-in / iterator-return-type
comparisons from reference-identity clrType == typeof(IEnumerable<>)
to FullName matching. The reference-identity form silently regresses
whenever a Type is materialized via MetadataLoadContext (the
BuildTask host); FullName matching works under both hosts.
* KnownAttributes: detect MethodImpl by FullName as well as by
reference identity, and add IsMethodImpl / FindMethodImpl /
GetMethodImplOptions so EmitFunction can OR
MethodImplAttributes.AggressiveInlining into the method header
instead of dropping the attribute as a no-op custom-attribute row.
Same FullName fallback added for ExtensionAttribute lookup.
Emitter:
* ReflectionMetadataEmitter.EmitFunction now ORs MethodImpl bits onto
the method's MethodImplAttributes header so @MethodImpl(...)
reaches the JIT instead of being dropped on the floor.
* The synthesized <Program> static-host TypeDef is now emitted as
Sealed | Abstract. MemberLookup.IsStaticClass requires
IsClass && IsAbstract && IsSealed for C#-side extension-method
discovery; without it, cross-assembly consumers (including the
C# Extensions.Tests project) never see the G#-emitted extension
surface and silently fall back to the missing-overload error.
* IsCoreLibBaseType / TryNormalizeToSymbolicContainer /
ChooseErasedValueTypeType / IsValueTypeSymbol learn about
Nullable[T], ValueTuple[T..T7], IDisposable, IEnumerable,
IEnumerator, IEnumerable[T], IEnumerator[T], IAsyncEnumerable[T],
IAsyncEnumerator[T] so generic instances of these collection
interfaces route through System.Runtime / System.Collections
instead of the host assembly.
* StateMachineEmitter (sync + async): pick the host package from
plan.Function.Package?.Name first, falling back to hostPackage,
so iterator/async state machines attach to the <Program> typedef
of the package the user wrote the iterator in (not the package
the binder happened to be processing).
Test baseline:
* RefactoringBaselineTests baseline regenerated for the
Sealed | Abstract change to every <Program> typedef.
(Workflow: temporarily unskip RegenerateBaseline in
test/Core.Tests/.../RefactoringBaselineTests.cs, rerun, restore.)
Refs #806, #792, #793. Parent #706.
* feat(extensions): port Gsharp.Extensions to idiomatic G# (#806)
ADR-0084 §L5. Migrate Gsharp.Extensions.Optional,
Gsharp.Extensions.Sequences (Sequences + SequenceExtensions), and the
Gsharp.Extensions.Go marker from C# to idiomatic G#, exercising the
bootstrap SDK landed by #792 / #793 end-to-end. The 107+
test/Extensions.Tests/ tests (now 104 active + 3 conditional skips)
remain the source of truth and all pass against the G# port.
Surface:
* Optional/Optional.gs: 14 helpers (class + struct overloads of
Map, FlatMap, OrElse, OrCompute, IfPresent, Filter, OptionOf).
Constraint-aware overload filter (ADR-0088 / ADR-0097) routes call
sites correctly via the *OrNil split closed by #814. Struct path
uses HasValue/Value; class path uses identity-aware null checks.
Every hot helper carries @MethodImpl(AggressiveInlining) and the
Extensions.Tests/AggressiveInliningTests check enforces parity with
the C# baseline.
* Sequences/Sequences.gs: static builders Empty, Of (variadic +
IEnumerable overloads), Range, RangeStep, Iterate, Repeat. Each
multi-yield builder is a private iterator (RangeIterator,
IterateIterator, RepeatIterator) so the lazy / yield-per-element
semantics match the C# baseline.
* Sequences/SequenceExtensions.gs: transformers (Map, Filter,
FlatMap, Take, Skip, TakeWhile, SkipWhile, Concat, Zip, Indexed,
Windowed, Chunked, Distinct, DistinctBy), terminals (First,
FirstOrNil, Last, LastOrNil, Single, SingleOrNil, Count, Any,
All, Aggregate, Sum, Min, Max), and collectors (ToList, ToArray,
ToDictionary). All extension receivers are non-nullable to match
the C# baseline (a nullable receiver shape causes the C# binder
to drop the extension from MemberLookup). WindowedIterator uses
List[T].RemoveAt(0) instead of Queue[T].Dequeue() to dodge #832.
* Go/Go.gs: 1-class marker so the namespace round-trips through
System.Type-based reference resolution and the assembly carries
at least one public type. The Go-flavored concurrency surface
itself is compiler-built-in (ADR-0082 / #722); helper namespaces
follow in #723 / #724.
Build wiring:
* Gsharp.Extensions.csproj reshapes to the bootstrap-SDK form: a
pre-import _GsharpExtensionsResetCompileItems target strips the
default @(Compile) glob and re-includes *.gs files; the SDK from
src/Sdk/Gsharp.NET.Sdk/Sdk drives gsc via the BuildTask DLL. The
AssemblyName / TargetFileName / TargetPath override block runs
AFTER the SDK imports so the SDK's default 'Library1' name is
overridden. ImplicitUsings is disabled and GenerateAssemblyInfo /
GenerateAssemblyVersionInfo are off so gsc owns the metadata
layout end-to-end.
* Gsharp.NET.Sdk.csproj removes the prior
Gsharp.Extensions <-> Gsharp.NET.Sdk arrow (which would form a
cycle now that Extensions depends on the SDK) and passes
BuildProjectReferences=false to the inline pack-time build so
the standalone pack path doesn't try to re-traverse the cycle.
Test updates:
* AggressiveInliningTests resolves the <Program> static-host
typedefs reflectively (no longer pins the lookup to C#-emitted
type tokens) and walks the same 7 + 12 + 5 helper set.
* OptionalExtensionsTests adds a localized #pragma warning disable
CS8602 around the four call sites that exercise unannotated
parameters whose annotations land in #834.
* Interpreter.Tests/Issue750ConstraintOverloadInterpreterTests
drops a now-unresolvable typeof(...) into the deleted C# host
classes and resolves Gsharp.Extensions.dll via Assembly.LoadFrom
with an explanatory comment.
* samples/GsharpExtensionsMixed.gs updates the sumOf parameter to
IEnumerable[int32] and the window-collector lambdas to
IList[int32] to match the new Windowed return shape (the G# port
yields List[T] typed as IList[T], not []T).
Known residual emitter gaps (acceptable per #806 'file Oats and
continue' policy):
* #831 open-T 'self == nil' for class T emits ldnull/ceq pair the
ECMA verifier rejects (JIT is permissive; runtime is green).
* #832 discarded T-typed call results emit a spurious unbox.any !T.
* #833 Enumerable.Empty[T] erases to object[].
* #834 missing NullableAttribute on T? ref parameters.
* #835 audit of clrType == typeof(X) reference-identity comparisons
that silently regress under BuildTask's MetadataLoadContext.
* #836 iterator lowering rejects try/finally around yield.
ilverify reports 8 dirty methods (Optional class overloads from
#831, Sequences.Empty from #833, WindowedIterator from #832).
Runtime is green across the full GSharp.sln suite. Tests pass.
Refs #806, #792, #793. Parent #706.
* chore(extensions): remove C# stdlib implementations replaced by G# port (#806)
ADR-0084 §L5 closure. The previous commit ported the Optional,
Sequences, and Go marker surfaces to G#; the .cs files are no longer
compiled (the new bootstrap-SDK csproj strips @(Compile) and re-adds
only *.gs). Delete them now to keep the repo single-source-of-truth
and document the closure in ADR-0084.
* Optional/OptionalExtensions.cs deleted (replaced by Optional.gs).
* Sequences/Sequences.cs, SequenceExtensions.cs,
SequenceValueExtensions.cs deleted (replaced by Sequences.gs +
SequenceExtensions.gs).
* Go/GoExtensions.cs deleted (replaced by Go.gs).
* docs/adr/0084-gsharp-extensions-optional-sequences.md: §L5
marked closed with a paragraph noting the dogfooded port landed
in #806 and the Consequences paragraph updated from
'L5 broken' to 'L5 broken and exercised end-to-end by #806'.
Refs #806, #792, #793. Parent #706. Closes#806.
* fix(sdk): build Compiler before packing Extensions in PackGsharpExtensions
After #806 ported Gsharp.Extensions to G#, the bootstrap SDK build
requires out/bin/$(Configuration)/Compiler/gsc.dll. When the SDK
csproj is built standalone (as the e2e tests do via
`dotnet build src/Sdk/Gsharp.NET.Sdk/Gsharp.NET.Sdk.csproj`),
MSBuild's project-reference walker isn't traversed by the nested
MSBuild call below (BuildProjectReferences=false). Add an explicit
MSBuild call to build Compiler before the Extensions pack step so
gsc.dll exists when the bootstrap fires.
---------
Co-authored-by: Copilot CLI <noreply@github.com>
Co-authored-by: David Obando <>
0 commit comments