Skip to content

[automated] Merge branch 'main' => 'feature/net11-scouting'#19582

Open
github-actions[bot] wants to merge 5 commits intofeature/net11-scoutingfrom
merge/main-to-feature/net11-scouting
Open

[automated] Merge branch 'main' => 'feature/net11-scouting'#19582
github-actions[bot] wants to merge 5 commits intofeature/net11-scoutingfrom
merge/main-to-feature/net11-scouting

Conversation

@github-actions
Copy link
Copy Markdown
Contributor

I detected changes in the main branch which have not been merged yet to feature/net11-scouting. I'm a robot and am configured to help you automatically keep feature/net11-scouting up to date, so I've opened this PR.

This PR merges commits made on main by the following committers:

  • github-actions[bot]
  • T-Gro
  • omajid

Instructions for merging from UI

This PR will not be auto-merged. When pull request checks pass, complete this PR by creating a merge commit, not a squash or rebase commit.

merge button instructions

If this repo does not allow creating merge commits from the GitHub UI, use command line instructions.

Instructions for merging via command line

Run these commands to merge this pull request from the command line.

git fetch
git checkout main
git pull --ff-only
git checkout feature/net11-scouting
git pull --ff-only
git merge --no-ff main

# If there are merge conflicts, resolve them and then run git merge --continue to complete the merge
# Pushing the changes to the PR branch will re-trigger PR validation.
git push https://github.com/dotnet/fsharp HEAD:merge/main-to-feature/net11-scouting
or if you are using SSH
git push git@github.com:dotnet/fsharp HEAD:merge/main-to-feature/net11-scouting

After PR checks are complete push the branch

git push

Instructions for resolving conflicts

⚠️ If there are merge conflicts, you will need to resolve them manually before merging. You can do this using GitHub or using the command line.

Instructions for updating this pull request

Contributors to this repo have permission update this pull request by pushing to the branch 'merge/main-to-feature/net11-scouting'. This can be done to resolve conflicts or make other changes to this pull request before it is merged.
The provided examples assume that the remote is named 'origin'. If you have a different remote name, please replace 'origin' with the name of your remote.

git fetch
git checkout -b merge/main-to-feature/net11-scouting origin/feature/net11-scouting
git pull https://github.com/dotnet/fsharp merge/main-to-feature/net11-scouting
(make changes)
git commit -m "Updated PR with my changes"
git push https://github.com/dotnet/fsharp HEAD:merge/main-to-feature/net11-scouting
or if you are using SSH
git fetch
git checkout -b merge/main-to-feature/net11-scouting origin/feature/net11-scouting
git pull git@github.com:dotnet/fsharp merge/main-to-feature/net11-scouting
(make changes)
git commit -m "Updated PR with my changes"
git push git@github.com:dotnet/fsharp HEAD:merge/main-to-feature/net11-scouting

Contact .NET Core Engineering (dotnet/dnceng) if you have questions or issues.
Also, if this PR was generated incorrectly, help us fix it. See https://github.com/dotnet/arcade/blob/main/.github/workflows/scripts/inter-branch-merge.ps1.

omajid and others added 5 commits April 13, 2026 12:56
This allows it to accept arguments that contain spaces, such as
propreties like "/p:OfficialBuilder=Foo Bar".

Fixes the regression introduced in #19211
* Create TypedTreeOps.Remap.fs and .fsi (Sprint 1 of 7)

Extract lines 1-1230 from TypedTreeOps.fs into a new file pair
using namespace FSharp.Compiler.TypedTreeOps with three AutoOpen modules:

- TypeRemapping: TyparMap, TyconRefMap, ValMap, Remap types and the
  remapTypeAux/remapValRef let-rec chain plus instantiation wrappers
- TypeConstruction: type construction/destruction/query functions,
  Erasure DU, strip/dest/is functions
- TypeEquivalence: TypeEquivEnv, the traitsAEquivAux/typeEquivAux
  let-rec chain, equivalence wrappers, getErasedTypes

Files are not yet added to the fsproj - that happens in a later sprint.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Extract TypedTreeOps.ExprConstruction.fs/.fsi (File 2 of 7)

Extract expression construction primitives, collection types, and
arity/metadata analysis from TypedTreeOps.fs lines ~1231-2260 into
TypedTreeOps.ExprConstruction.fs with matching .fsi file.

Organized into 3 [<AutoOpen>] modules:
- ExprConstruction: orderings, type builders, expr constructors,
  MatchBuilder, lambda/let/bind builders
- CollectionTypes: ValHash, ValMultiMap, TyconRefMultiMap
- ArityAndMetadata: rescoping, field accessors, type testers,
  TypeDefMetadata, free tyvar accumulators

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Extract TypedTreeOps.FreeVars.fs/.fsi (File 3 of 7)

Create File 3 of the TypedTreeOps split: free type variable analysis,
pretty-printing, and display environment.

Structure:
- FreeTypeVars module: FreeVarOptions, collection options, two recursive
  chains (accFreeTycon..accFreeInVal, boundTyparsLeftToRight..accFreeInTypesLeftToRight),
  addFreeInModuleTy, public wrappers, checkMemberVal/checkMemberValRef
- Display module: GetFSharpViewOfReturnType, TraitConstraintInfo extension,
  member type functions, nested PrettyTypes and SimplifyTypes modules,
  GenericParameterStyle, DisplayEnv, display text helpers, superOfTycon

Files are not yet added to the project file (will happen in final integration sprint).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Extract TypedTreeOps.Attributes.fs/.fsi (File 4 of 7)

Create File 4 of the TypedTreeOps split: IL extensions, attribute helpers,
and debug printing.

Structure:
- ILExtensions module: IL attribute detection functions (isILAttribByName,
  classifyILAttrib, computeILWellKnownFlags, tryFindILAttribByFlag,
  (|ILAttribDecoded|_|)) and type extensions on ILAttributesStored,
  ILTypeDef, ILMethodDef, ILFieldDef, ILAttributes
- AttributeHelpers module: F# attribute discovery (resolveAttribPath,
  classifyEntityAttrib, classifyValAttrib, computeEntityWellKnownFlags,
  EntityHasWellKnownAttribute, etc.), type construction helpers, and
  type ValRef extension members
- DebugPrinting module: nested DebugPrint module with showType, showExpr,
  layout functions (exprL, bindingL, atomL, etc.), and
  wrapModuleOrNamespace* helpers

The DebugPrint module is nested inside DebugPrinting to preserve the
FSharp.Compiler.TypedTreeOps.DebugPrint reference path used by callers.

All let rec ... and chains inside DebugPrint are kept intact.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Fix TypedTreeOps.Attributes.fsi: remove misplaced declarations

Remove 12 val declarations from .fsi that have no implementations in the
.fs file. These were from original TypedTreeOps.fs lines outside the
~3523-5462 extraction range:

- isInByrefTy, isOutByrefTy, isByrefTy, isNativePtrTy (original ~L1907-1923)
- EvalLiteralExprOrAttribArg, EvaledAttribExprEquality (original ~L10943-10969)
- IsSimpleSyntacticConstantExpr, ConstToILFieldInit (original ~L10646-10951)
- Int32Expr, SpecialComparableHeadType, SpecialEquatableHeadType,
  SpecialNotEquatableHeadType (original ~L9986-11049)
- TyparTy|NullableTypar|... (incomplete signature causing FS0010 parse error)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Extract TypedTreeOps.Remapping.fs/.fsi (File 5 of 7)

Create the 5th split file from TypedTreeOps.fs containing:
- SignatureOps: signature repackage/hiding types and operations
- ExprFreeVars: expression-level free variable analysis (24-function chain)
- ExprRemapping: expression remapping and copying (57-function chain)
- ExprShapeQueries: type inference, remark, decision tree simplification

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Extract TypedTreeOps.ExprOps.fs/.fsi (File 6 of 7)

Create File 6 of the TypedTreeOps split: address-of operations, expression
folding, intrinsic call wrappers, and higher-level expression helpers.

Extracts original TypedTreeOps.fs lines ~7651-9599 into 4 modules:
- AddressOps: Mutates DU, address-of helpers, mkExprAddrOfExpr, field gets
- ExprFolding: IterateRecursiveFixups, ExprFolder/ExprFolders, FoldExpr
- IntrinsicCalls: 105 mkCall* wrappers, literal constructors, compilation attrs
- ExprHelpers: MakeApplicationAndBetaReduce, lambda tupling, subsumption, etc.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Extract TypedTreeOps.Transforms.fs/.fsi (File 7 of 7)

Create the final split file containing type encoding, expression rewriting,
tuple compilation, integral constants, and attribute checking.

Structure:
- TypeEncoding: typeEnc, XML doc encoding, nullness helpers, compiled-as
- Rewriting: ExprRewritingEnv, RewriteExpr, export remapping
- TupleCompilation: mkCompiledTuple, IntegralConst (nested), mkFastForLoop
- AttribChecking: CombineCcuContentFragments, Seq patterns, resumable code

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Fix TypedTreeOps.Transforms.fsi: remove duplicates, misplaced declarations, fix module placement

- Remove duplicate declarations: HasConstraint, IsTyparTyWithConstraint, doesActivePatternHaveFreeTypars
- Remove declarations belonging to other files (FreeVars: GetMemberTypeInMemberForm,
  PartitionValTypars*, CountEnclosingTyparsOfActualParentOfVal, ReturnTypeOfPropertyVal,
  ArgInfosOfPropertyVal, ArgInfosOfMember; ExprOps: mkCallDispose, mkCallSeq, mkCallTypeTest)
- Move GetTypeOfIntrinsicMemberInCompiledForm from TypeEncoding to TupleCompilation
- Move TraitWitnessInfoHashMap/EmptyTraitWitnessInfoHashMap from TupleCompilation to AttribChecking
- Move mkDebugPoint and IfThenElseExpr from AttribChecking to TupleCompilation
- Remove TraitConstraintInfo type extension (belongs to FreeVars, not this file)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Split TypedTreeOps into 7 files: wire up project, delete originals, fix cross-file deps

Replace the monolithic TypedTreeOps.fs (12,569 lines) and TypedTreeOps.fsi
(3,094 lines) with 7 focused files:

1. TypedTreeOps.Remap - Type remapping, instantiation, equivalence
2. TypedTreeOps.ExprConstruction - Expression/type construction helpers
3. TypedTreeOps.FreeVars - Free variable collection, display helpers
4. TypedTreeOps.Attributes - IL extensions, attribute classification
5. TypedTreeOps.Remapping - Signature ops, expr free vars, expr remapping
6. TypedTreeOps.ExprOps - Address ops, folding, intrinsic calls
7. TypedTreeOps.Transforms - Debug printing, pattern matching, transforms

All files use namespace FSharp.Compiler.TypedTreeOps with [<AutoOpen>]
internal modules, so the 69 callers doing 'open FSharp.Compiler.TypedTreeOps'
required zero modifications.

Integration fixes:
- Added missing opens (FSharp.Compiler, Internal.Utilities.Collections,
  FSharp.Compiler.Syntax, FSharp.Compiler.CompilerGlobalState)
- Fixed .fsi signature mismatches (generic vs concrete type params)
- Exposed cross-file functions in .fsi files
- Fixed type name references (Mutability -> ValMutability, etc.)
- Applied dotnet fantomas formatting to all 14 files

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Create CommonContainers module in file 2, move container type helpers from file 4

Move container type helpers (Option, RefCell, Nullable, Choice, Byref,
LinqExpression, etc.) from AttributeHelpers in TypedTreeOps.Attributes.fs
to a new CommonContainers module in TypedTreeOps.ExprConstruction.fs.

These functions only depend on tyconRefEq, stripTyEqns, tryTcrefOfAppTy,
argsOfAppTy from file 1, not attribute infrastructure.

Create ByrefAndSpanHelpers module in file 4 for span/byref-like functions
that depend on TyconRefHasAttributeByName (must stay in file 4).

All modules use [<AutoOpen>] so callers need no changes.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Rename modules for coherence, move type constructors and encoding helpers

- Rename ArityAndMetadata → TypeTesters (core content is 46 is*Ty predicates)
- Rename IntrinsicCalls → Makers (174 mk* expression constructors)
- Move mkForallTy/mkForallTyIfNeeded/+-> from ExprConstruction to TypeConstruction
- Move commaEncs/angleEnc/typarEnc/ticksAndArgCountTextOfTyconRef from ExprHelpers to TypeEncoding

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Round 2: Move misplaced functions to correct modules

- Free var accumulators TypeTesters→FreeTypeVars
- Attribute helpers Makers→AttributeHelpers
- mkFunTy/mkIteratedFunTy ExprConstruction→TypeConstruction
- mk*Ty ExprShapeQueries→TypeConstruction
- mk*Type Makers→TypeConstruction
- mk*Test TypeEncoding→Makers (except mkIsInstConditional which
  depends on canUseTypeTestFast in Transforms.fs, compiled after
  ExprOps.fs)
- Linear* APs: kept in SignatureOps (moving to ExprShapeQueries
  impossible - ExprShapeQueries is at end of Remapping.fs but
  Linear* are used in ExprFreeVars and ExprRemapping modules
  which appear earlier in the same file)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Round 3: Move quotation type helpers to TypeConstruction

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Round 4: Move type queries to TypeTesters, member helpers to Display

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Round 5: Move CombineCcuContentFragments to SignatureOps

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Rename TupleCompilation → LoopAndConstantOptimization

The module contains tuple compilation, fast for loops, integral range
detection, constant evaluation, and loop optimization — not just tuples.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Move unblocked functions to correct modules

- underlyingTypeOfEnumTy/normalizeEnumTy ExprRemapping→TypeTesters
- ClearValReprInfo ExprRemapping→ExprHelpers
- mkArray AddressOps→Makers

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Move mkLabelled from AttribChecking to Makers

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Break rec chain assumptions: move standalone functions to correct modules

- isResumableCodeTy/isReturnsResumableCodeTy/isFSharpExceptionTy → TypeTesters
- serializeEntity + helpers → DebugPrint
- Linear* APs SignatureOps → ExprFreeVars (same file, earlier position)
- mkApps group ExprShapeQueries → Makers

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Final placement fixes: isResumableCodeTy, mkArray, isSealedTy, export remapping

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Move updateSeqTypeIsPrefix→SignatureOps, isTyparOrderMismatch→Display

Last 2 cross-model agreed misplacements resolved.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Incorporate reviewer findings: move attribute/expr helpers to correct modules

- CompileAsEvent/ValCompileAsEvent/ModuleNameIsMangled/MemberIsCompiledAsInstance → AttributeHelpers
- valOfBind/valsOfBinds → ExprConstruction
- WhileExpr/TryWithExpr/TryFinallyExpr/IntegerForLoopExpr APs → ExprShapeQueries
- mkDebugPoint/(|InnerExprPat|)/(|Int32Expr|_|) → ExprConstruction or earlier

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Split TypeEncoding into XmlDocSignatures, NullnessAnalysis, TypeTestsAndPatterns

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Split AttribChecking into ResumableCodePatterns, SeqExprPatterns, ExtensionAndMiscHelpers

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Split TypeConstruction into MeasureOps, TypeBuilders, TypeAbbreviations, TypeDecomposition

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Rename Display→MemberRepresentation, dissolve ExtensionAndMiscHelpers, split LoopAndConstantOptimization

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Rename TypeTesters→TypeQueries, ExprHelpers→ExprTransforms, tighten ExprShapeQueries→ExprAnalysis

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Fix B-grade outliers: rename CollectionTypes, move dest/is from Makers, move patterns from TupleCompilation

- Rename CollectionTypes → TypedTreeCollections
- Move destInt32/destThrow/isThrow/isIDelegateEventType/destIDelegateEventType Makers→ExprTransforms
- Move (|Int32Expr|_|)/(|IntegralRange|_|) TupleCompilation→ConstantEvaluation

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Rename TypeQueries back to TypeTesters

70% of vals operate on TType, 12% on Tycon, 5% on Val — 'Testers' better
describes the is*/dest*/strip* nature than 'Queries'.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Remove accidentally committed scratch files

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Fix FS0667: disambiguate cenv record update in IlxGen.fs

The [<AutoOpen>] split brought multiple record types with 'stackGuard'
field into scope (FreeVarOptions, RemapContext, cenv). Add type annotation
to resolve ambiguity.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Apply fantomas formatting to all TypedTreeOps files

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Add UnionLayout taxonomy + assertions (no behavior change)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Extract shared concerns (nullability, debug proxy)

Extract three helper functions from megafunctions in EraseUnions.fs:

- rewriteFieldsForStructFlattening: Nullable attribute rewriting for struct DU
  field flattening, extracted from mkClassUnionDef
- emitDebugProxyType: Debug proxy type generation for union alternatives,
  extracted from convAlternativeDef
- rootTypeNullableAttrs: Root type [Nullable(2)] attribute application,
  extracted from mkClassUnionDef

Zero behavior change - code is moved verbatim into named functions.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Migrate instruction functions to exhaustive layout matching

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Decompose convAlternativeDef into focused sub-functions

- Wire TypeDefContext into mkClassUnionDef for parameter bundling
- Change return type from 6-element tuple to AlternativeDefResult record
- Extract emitMakerMethod for non-nullary case maker methods
- Extract emitTesterMethodAndProperty for Is* test methods/properties
- Extract emitNullaryCaseAccessor for nullary case getter properties
- Extract emitConstantAccessor for unique singleton object accessors
- Extract emitNullaryConstField for nullary case static field definitions
- Extract emitNestedAlternativeType for nested case type definitions
- Rename convAlternativeDef to processAlternative (~46 lines)
- Format with fantomas

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Decompose mkClassUnionDef into focused sub-functions

Extract emitRootClassFields, emitRootConstructors, emitConstFieldInitializers,
emitTagInfrastructure, assembleUnionTypeDef, computeSelfAndTagFields, and
computeEnumTypeDef from the monolithic mkClassUnionDef function. The main
function is now a 54-line orchestrator that delegates to these helpers.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Remove old UnionReprDecisions infrastructure

Delete DiscriminationTechnique DU, UnionReprDecisions generic class,
cuspecRepr/cudefRepr instances, NoTypesGeneratedViaThisReprDecider,
and layoutToTechnique bridge function.

All code paths now use UnionLayout type with exhaustive active patterns
and focused helper functions (altFoldsAsRootInstance, altOptimizesToRoot,
maintainConstantField, hasNullCase).

Simplify AlternativeDefResult.NullaryConstFields tuple by removing the
unused (ILTypeDef * IlxUnionInfo) element.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Refactor mkNewData: replace boolean deconstruction with pattern match

The NoHelpers branch extracted isStruct/isNull into booleans then used
if/elif chains. Replace with direct pattern matching on (layout, cidx)
and layout active patterns, making the logic readable at a glance.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Deduplicate tyForAlt by reusing altOptimizesToRoot

tyForAlt duplicated the entire altOptimizesToRoot classification logic
inline. Extract tyForAltIdx taking cidx for direct calls, and have
tyForAlt look up cidx. All internal callers that already have cidx
now call tyForAltIdx directly.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Simplify processAlternative NoHelpers branch with pattern match

Replace 'not alt.IsNullary && (match ctx.layout with ValueTypeLayout
-> true | ReferenceTypeLayout -> false)' with a direct match on
ctx.layout with a when guard, collapsing two match arms into one.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Simplify emitTesterMethodAndProperty nullness guard

Replace the boolean chain 'g.checkNullness && g.langFeatureNullness &&
(match layout ... -> true | ... -> false) && not alt.IsNullary' with a
direct pattern match on ctx.layout with when guard. Also merge the two
early-return conditions into one.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Simplify emitRootClassFields loop guard with layout match

Replace 'altFoldsAsRootInstance || (match layout ValueTypeLayout -> true
| ReferenceTypeLayout -> false)' with a clear match: value types always
put fields on root, reference types only when they fold as root instance.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Decompose emitRootConstructors complex guard into named conditions

Break the 5-line boolean expression into named conditions with comments
explaining when the root ctor is needed vs skipped.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Replace isStruct bool with layout match in rewriteFieldsForStructFlattening

The function took an isStruct bool and checked 'isStruct && cases > 1'
which is exactly TaggedStructUnion. Match on layout directly and remove
the now-unnecessary cud parameter.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Eliminate isSingleNonNullaryFoldedToRoot in favor of altFoldsAsRootInstance

isSingleNonNullaryFoldedToRoot duplicated logic already in
altFoldsAsRootInstance for SmallRefUnion. Replace all 4 call sites with
altFoldsAsRootInstance which encodes the same semantics. Also simplify
caseFoldsToRootClass which was a thin wrapper around the eliminated fn.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Eliminate caseFoldsToRootClass, inline via altFoldsAsRootInstance

caseFoldsToRootClass was a thin wrapper around altFoldsAsRootInstance
for SmallRefUnion. All 3 callers are already inside SmallRefUnion match
arms, so they can call altFoldsAsRootInstance directly.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Refactor classifyUnion: use match expression for readable classification

Replace nested if/elif chain with a match on (isList, alts.Length,
isStruct) tuple. Hoist allNullary computation. Each match arm maps
cleanly to one UnionLayout case.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Simplify maintainConstantField: replace chained AP-to-bool with match

Replace 'alt.IsNullary && (match ValueTypeLayout -> false | ...) &&
(match CaseIsNull -> false | ...)' with a nested match that reads
naturally: null-represented cases don't need a constant field,
value types don't need one, ref types do.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Simplify emitNullaryCaseAccessor: match on CaseIsNull directly

Replace 'g.checkNullness && g.langFeatureNullness && (match CaseIsNull
-> true | ...)' with a direct match on (layout, num) with a when guard,
eliminating the AP-to-bool intermediate.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Apply fantomas formatting

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Wave 1: Replace avoidHelpers bool with DataAccess DU

Introduce DataAccess = RawFields | ViaHelpers | ViaListHelpers that
collapses the per-call-site avoidHelpers:bool × per-union HasHelpers
enum into a single DU computed once at entry points.

- avoidHelpers parameter eliminated from 16 internal + 7 public functions
- doesRuntimeTypeDiscriminateUseHelper simplified from 3-way && to DU match
- emitLdDataTagPrim: 'match HasHelpers with AllHelpers when not avoidHelpers'
  becomes clean 'match access with ViaHelpers | ViaListHelpers'
- adjustFieldName split: DataAccess version for access path,
  adjustFieldNameForTypeDef for type-def path

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Wave 2: Rename nullCaseIdx→nullAsTrueValueIdx, ListTailOrNull→FSharpList

nullCaseIdx was confusable with 'nullary' (no data). It actually means
UseNullAsTrueValue — a case represented as null at runtime. Renamed to
nullAsTrueValueIdx throughout.

ListTailOrNull renamed to FSharpList for clarity — this layout is
exclusively for F# list<'T>.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Wave 3: Split allNullary bool into explicit TaggedRef/TaggedRefAllNullary DU cases

TaggedRefUnion(baseTy, allNullary:bool) → TaggedRef baseTy + TaggedRefAllNullary baseTy
TaggedStructUnion(baseTy, allNullary:bool) → TaggedStruct baseTy + TaggedStructAllNullary baseTy

Eliminates the hidden 3-way logic where TaggedRefUnion(_, true) was
enum-like (all on root), TaggedRefUnion(_, false) split nullary→root
vs non-nullary→nested. Now explicit DU cases, no boolean field.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Wave 3+4: Split allNullary and SmallRefUnion into explicit DU cases

Fix SpecialFSharpOptionHelpers mapping (ViaHelpers, not RawFields).

TaggedRefUnion(_, allNullary) → TaggedRef + TaggedRefAllNullary
TaggedStructUnion(_, allNullary) → TaggedStruct + TaggedStructAllNullary
SmallRefUnion(_, opt) → SmallRef + SmallRefWithNullAsTrueValue(_, idx)

UnionLayout now has 8 cases, zero boolean fields, zero option fields.
Every match arm is explicit — no hidden 3-way logic via bool destructuring.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Wave 5: Remove dead code, eliminate hasNullCase, explicit match arms

- Remove 3 dead SmallRef when-guards (altFoldsAsRootInstance always
  returns false for SmallRef — dead code since the DU split)
- Replace wildcard in altFoldsAsRootInstance with explicit cases
  for compiler-enforced exhaustiveness
- Eliminate hasNullCase function — callers match SmallRefWithNullAsTrueValue directly

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Wave 6: Extract nullnessCheckingEnabled helper, restructure classifyUnion

- Add nullnessCheckingEnabled helper replacing 5 scattered
  'g.checkNullness && g.langFeatureNullness' conjunctions
- Restructure classifyUnion: replace 4 when-guarded arms with
  nested match on (isStruct, allNullary) — exhaustive, no guards

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Wave 7: Inline doesRuntimeTypeDiscriminateUseHelper, match-based tester guard

- Inline doesRuntimeTypeDiscriminateUseHelper (1-line function, 2 call
  sites) directly into mkRuntimeTypeDiscriminate and mkRuntimeTypeDiscriminateThen
- Replace 'cud.UnionCases.Length <= 1 || (match SmallRefWithNull -> true)'
  with clean match on layout cases (SingleCaseRef, SingleCaseStruct,
  SmallRefWithNullAsTrueValue → skip tester)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Wave 8: Split TaggedStructAllNullary from when guard, remove dead fallthrough

TaggedStructAllNullary + when alt.IsNullary was redundant — all cases
are nullary by definition. Split into explicit arm without guard.
Remove TaggedStructAllNullary from default fallthrough (already handled).
Remove dead 'when cud.UnionCases.Length <= 1' guard (covered by layout).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* F10+F11: Remove duplicate comment, clean unreachable match arm

Remove duplicate XML doc comment at line 1785.
Remove dead FSharpList arm from emitRawConstruction default (already
handled by when alt.IsNullary and non-nullary Cons arms above).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Round 2: Domain model improvements (F1+F5+F7+F8+F4)

F1: Replace NullaryConstFields 5-tuple with NullaryConstFieldInfo record
    — named fields instead of opaque (IlxUnionCase*ILType*int*ILFieldDef*bool)

F5: Introduce CaseStorage DU (Null|Singleton|OnRoot|InNestedType|StructTag)
    — classifyCaseStorage computes once per case, used in emitCastToCase
      and processAlternative to eliminate duplicated decision trees

F7: Rename helpers HOW→WHAT for domain clarity:
    altFoldsAsRootInstance → caseFieldsOnRoot
    altOptimizesToRoot → caseRepresentedOnRoot
    maintainConstantField → needsSingletonField
    convNewDataInstrInternal → emitRawNewData

F8: emitDebugProxyType: 11 positional params → TypeDefContext + 2 params

F4: Extract ILStamping record from TypeDefContext, separating domain
    data (layout, cuspec, cud) from infrastructure callbacks

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Round 2b: CaseIdentity, CaseStorage broadened, renames, dedup

G1: Introduce CaseIdentity record + resolveCase — eliminates repeated
    (alt, altTy, altName) computation in 4 emit functions

G3: Use CaseStorage in emitRawConstruction — replaces nested layout
    match + 4 when guards with flat match on precomputed CaseStorage.
    Fix: needsSingletonField fallback for nullary SmallRef cases.

G4: Merge duplicate Singleton/InNestedType branches (OR-pattern)

G5: Remove mkTagFieldFormalType (identical to mkTagFieldType)

G9: Rename self* → rootCase* for domain clarity

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Perf: struct CaseIdentity, inline CaseIsNull AP, eliminate allocations

- [<Struct>] on CaseIdentity record — allocated per-instruction-site,
  immediately destructured, no need to heap-allocate
- inline on (|CaseIsNull|CaseIsAllocated|) AP — avoids tuple allocation
  at 8 call sites (tuple was (layout, cidx))
- Array.filter |> Array.length = 1 → Array.existsOne in caseFieldsOnRoot
  — eliminates intermediate array allocation per call
- cuspec.Alternatives → cuspec.AlternativesArray in emitLdDataTagPrim
  — avoids Array.toList allocation

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Eliminate redundant classifyFromSpec/baseTyOfUnionSpec in EraseUnions

Add tyForAltIdxWith and resolveCaseWith that accept precomputed layout
and baseTy. Update emit functions (emitRawConstruction, emitIsCase,
emitBranchOnCase, emitCastToCase, emitCaseSwitch, emitLdDataTagPrim)
and type-def functions (emitNullaryConstField, emitNestedAlternativeType)
to use the precomputed values instead of recomputing per call.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Readability: extract nullable rewrite helper, improve comments

- Extract rewriteNullableAttrForFlattenedField from
  rewriteFieldsForStructFlattening — reduces nesting from 3→1 level,
  documents the byte semantics clearly
- Add comments: reverse iteration rationale in emitLdDataTagPrim,
  minNullaryIdx ctor sharing logic, isAbstract derivation

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Phase 4: emitIsCase+emitBranchOnCase → CaseStorage × DiscriminationMethod

Replace inline re-derivation from raw UnionLayout with two-axis
classification: CaseStorage (WHERE) × DiscriminationMethod AP (HOW).

emitIsCase: match storage with Null→null-ceq, then match (storage,
layout) with OnRoot+RuntimeType→non-null-test, NoDiscrimination→
always-true, RuntimeType→isinst, TagField→tag-ceq, TailNull→tail-check.

emitBranchOnCase: same two-axis structure with branch instructions.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Phase 4: Architecture docs, InteropCapability, reduce mkMethodsAndProps params

- Add two-axis architecture comment documenting UnionLayout×CaseStorage model
- Add InteropCapability type + classifier for fslang-suggestions/1463 readiness
  (ClosedHierarchy|UnionProtocol|UnionProtocolWithTupleBoxing|NoInterop)
- Reduce mkMethodsAndPropertiesForFields from 8 params to 3 (ctx, ilTy, fields)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Remove speculative InteropCapability code

Dead code for a future feature that hasn't been approved.
Only code that is actually used belongs in the codebase.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Remove dead code: _validateActivePatterns, NonNullaryFoldsToRoot, FieldsOnRootType APs

All three were never called by any live code path:
- _validateActivePatterns: 'compile-time check' trick — real match sites enforce exhaustiveness
- NonNullaryFoldsToRoot AP: only consumed by the removed validation function
- FieldsOnRootType AP: same

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Enrich DiscriminationMethod AP to carry baseTy and nullAsTrueValueIdx

Active patterns can carry data. The DiscriminationMethod AP now returns:
- DiscriminateByTagField baseTy
- DiscriminateByRuntimeType(baseTy, nullAsTrueValueIdx: int option)
- DiscriminateByTailNull baseTy
- NoDiscrimination baseTy

This eliminates 3 separate baseTyOfUnionSpec calls and 2 re-matches
on SmallRefWithNullAsTrueValue to extract nullIdx in emit functions.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Split EraseUnions.fs into 3 files by semantic boundary

EraseUnions.Types.fs (421 lines): Domain model, classification, APs
  UnionLayout, CaseStorage, DataAccess, CaseIdentity, all active
  patterns, classification functions, layout-based helpers.

EraseUnions.Emit.fs (442 lines): IL instruction emission
  Per-instruction-site IL: construct, discriminate, branch, cast,
  switch, load field, load tag. Public API consumed by IlxGen.fs.

EraseUnions.fs (1039 lines): Type definition generation
  Per-union-definition: generate ILTypeDef with nested types, methods,
  properties, fields, debug proxies. The orchestrator.

Dependency flow: Types ← Emit ← TypeDef (one-directional).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Fix bug + review findings: bounds check, stale comments, magic numbers

BUG FIX: emitRawConstruction for SingleCaseStruct nullary — was emitting
tag-ctor for CaseStorage.OnRoot+IsNullary but SingleCaseStruct has no
tag field (NoTagField). Now checks HasTagField|NoTagField before
choosing ctor shape.

Review fixes (3-model voted, 8/10 unanimous):
- H1: altOfUnionSpec blanket 'with _' catch → bounds check
- H2: tyForAlt Array.findIndex → tryFindIndex + meaningful error
- H3+H4: Fix stale case counts in architecture comment (8→9, 5→4)
- H5: Remove unused _cuspec param from mkTagFieldType (10 call sites)
- H7: List.ofArray|>List.mapi → Array.mapi|>Array.toList
- H8: Rename quadruple negation hasFieldsOrTagButNoMethods → onlyMethodsOnRoot
- H9: Named constants for DynamicDependency flags 0x660/0x7E0

Also revert unrelated FSharp.Core doc/test changes (per expert review).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* DDD review: pure classification in Types.fs, architecture docs, section headers

P1: Move TypeDefContext/ILStamping/AlternativeDefResult/NullaryConstFieldInfo
    from Types.fs to EraseUnions.fs — they're type-def scaffolding, not
    classification algebra. Types.fs is now purely: DU types, classifiers, APs.

P2+P6: Add architecture overview at top of EraseUnions.fs with pipeline
    description and concrete DU→UnionLayout→CaseStorage example mappings
    (Option, Color, Result, Shape, Token).

P3: Add section header before nullable-attr rewriting functions.

P7: Fix duplicate comment in rewriteNullableAttrForFlattenedField.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Council fixes: private mkMethodsAndPropertiesForFields, shared adjustFieldNameForList

- mkMethodsAndPropertiesForFields → private (internal-only, flagged by Opus)
- Extract adjustFieldNameForList to Types.fs as shared naming core
  (adjustFieldNameForTypeDef and adjustFieldName both delegate to it,
  eliminating duplicated Head→HeadOrDefault / Tail→TailOrNull mapping)
- Flagged by 4/7 council models across 2 rounds

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Fix: preserve exact IL for Option — add DataAccess.ViaOptionHelpers

SpecialFSharpOptionHelpers needs helpers for FIELD access (private
fields) but raw discrimination for TAG access (inline isinst, not
GetTag call). The old code made these decisions independently.

DataAccess.ViaOptionHelpers: field access uses get_X helpers (like
ViaHelpers), tag access uses raw isinst chain (like RawFields).
This preserves exact IL output — zero .bsl changes.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Apply patch from /run fantomas

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: GH Actions <actions@github.com>
#19579)

Verifies that identifiers containing '@' in double-backtick notation
produce correct IL code generation, specifically that static let bindings
with '@' in their names don't get confused with other identifiers at
the IL level.

Fixes #1255

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Reset patterns:
- global.json
- eng/Version.Details.xml
- eng/Version.Details.props
- eng/Versions.props
- eng/common/**
- eng/TargetFrameworks.props
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: New

Development

Successfully merging this pull request may close these issues.

2 participants