Skip to content

Commit 2c108ed

Browse files
charlesroddieclaude
andcommitted
Add EmittedIL tests for reflection-free record and union ToString
Locks in the IL emitted under --reflectionfree: each field is boxed and rendered through Operators.ToString with a null guard, and the parts are joined with String.Concat (array form for the record, 3-arg form for the single-field union case). Nullary union cases return the bare case name. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1 parent 1a5065e commit 2c108ed

2 files changed

Lines changed: 133 additions & 0 deletions

File tree

Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
// Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information.
2+
3+
namespace EmittedIL
4+
5+
open Xunit
6+
open FSharp.Test.Compiler
7+
8+
module ``ReflectionFreeToString`` =
9+
10+
// Under --reflectionfree the reflective sprintf "%+A" ToString is replaced by a structurally
11+
// generated one. These tests lock in the emitted IL: a match/field-read that boxes each field,
12+
// renders it through Operators.ToString (the `string` operator) with a null guard, and joins the
13+
// parts with String.Concat. No PrintfFormat is constructed.
14+
15+
[<Fact>]
16+
let ``Record ToString is generated structurally without printf`` () =
17+
FSharp """
18+
module ReflectionFreeToString
19+
type Point = { X: int; Y: int }
20+
"""
21+
|> withOptions [ "--reflectionfree" ]
22+
|> compile
23+
|> shouldSucceed
24+
|> verifyIL ["""
25+
.method public strict virtual instance string ToString() cil managed
26+
{
27+
.custom instance void [runtime]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 )
28+
29+
.maxstack 6
30+
.locals init (int32 V_0)
31+
IL_0000: ldc.i4.7
32+
IL_0001: newarr [runtime]System.String
33+
IL_0006: dup
34+
IL_0007: ldc.i4.0
35+
IL_0008: ldstr "{ "
36+
IL_000d: stelem [runtime]System.String
37+
IL_0012: dup
38+
IL_0013: ldc.i4.1
39+
IL_0014: ldstr "X = "
40+
IL_0019: stelem [runtime]System.String
41+
IL_001e: dup
42+
IL_001f: ldc.i4.2
43+
IL_0020: ldarg.0
44+
IL_0021: ldfld int32 ReflectionFreeToString/Point::X@
45+
IL_0026: stloc.0
46+
IL_0027: ldloc.0
47+
IL_0028: call object [FSharp.Core]Microsoft.FSharp.Core.Operators::Box<int32>(!!0)
48+
IL_002d: brfalse.s IL_0037
49+
50+
IL_002f: ldloc.0
51+
IL_0030: call string [FSharp.Core]Microsoft.FSharp.Core.Operators::ToString<int32>(!!0)
52+
IL_0035: br.s IL_003c
53+
54+
IL_0037: ldstr "null"
55+
IL_003c: stelem [runtime]System.String
56+
IL_0041: dup
57+
IL_0042: ldc.i4.3
58+
IL_0043: ldstr "; "
59+
IL_0048: stelem [runtime]System.String
60+
IL_004d: dup
61+
IL_004e: ldc.i4.4
62+
IL_004f: ldstr "Y = "
63+
IL_0054: stelem [runtime]System.String
64+
IL_0059: dup
65+
IL_005a: ldc.i4.5
66+
IL_005b: ldarg.0
67+
IL_005c: ldfld int32 ReflectionFreeToString/Point::Y@
68+
IL_0061: stloc.0
69+
IL_0062: ldloc.0
70+
IL_0063: call object [FSharp.Core]Microsoft.FSharp.Core.Operators::Box<int32>(!!0)
71+
IL_0068: brfalse.s IL_0072
72+
73+
IL_006a: ldloc.0
74+
IL_006b: call string [FSharp.Core]Microsoft.FSharp.Core.Operators::ToString<int32>(!!0)
75+
IL_0070: br.s IL_0077
76+
77+
IL_0072: ldstr "null"
78+
IL_0077: stelem [runtime]System.String
79+
IL_007c: dup
80+
IL_007d: ldc.i4.6
81+
IL_007e: ldstr " }"
82+
IL_0083: stelem [runtime]System.String
83+
IL_0088: call string [runtime]System.String::Concat(string[])
84+
IL_008d: ret
85+
}"""]
86+
87+
[<Fact>]
88+
let ``Union ToString is generated structurally without printf`` () =
89+
FSharp """
90+
module ReflectionFreeToString
91+
type Color = | Red | Custom of int
92+
"""
93+
|> withOptions [ "--reflectionfree" ]
94+
|> compile
95+
|> shouldSucceed
96+
|> verifyIL ["""
97+
.method public strict virtual instance string ToString() cil managed
98+
{
99+
.custom instance void [runtime]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 )
100+
101+
.maxstack 5
102+
.locals init (class ReflectionFreeToString/Color/Custom V_0,
103+
int32 V_1)
104+
IL_0000: ldarg.0
105+
IL_0001: isinst ReflectionFreeToString/Color/_Red
106+
IL_0006: brfalse.s IL_000e
107+
108+
IL_0008: ldstr "Red"
109+
IL_000d: ret
110+
111+
IL_000e: ldarg.0
112+
IL_000f: castclass ReflectionFreeToString/Color/Custom
113+
IL_0014: stloc.0
114+
IL_0015: ldstr "Custom("
115+
IL_001a: ldloc.0
116+
IL_001b: ldfld int32 ReflectionFreeToString/Color/Custom::item
117+
IL_0020: stloc.1
118+
IL_0021: ldloc.1
119+
IL_0022: call object [FSharp.Core]Microsoft.FSharp.Core.Operators::Box<int32>(!!0)
120+
IL_0027: brfalse.s IL_0031
121+
122+
IL_0029: ldloc.1
123+
IL_002a: call string [FSharp.Core]Microsoft.FSharp.Core.Operators::ToString<int32>(!!0)
124+
IL_002f: br.s IL_0036
125+
126+
IL_0031: ldstr "null"
127+
IL_0036: ldstr ")"
128+
IL_003b: call string [runtime]System.String::Concat(string,
129+
string,
130+
string)
131+
IL_0040: ret
132+
}"""]

tests/FSharp.Compiler.ComponentTests/FSharp.Compiler.ComponentTests.fsproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -247,6 +247,7 @@
247247
<Compile Include="EmittedIL\RealInternalSignature\RealInternalSignature.fs" />
248248
<Compile Include="EmittedIL\SkipLocalsInit.fs" />
249249
<Compile Include="EmittedIL\StringFormatAndInterpolation.fs" />
250+
<Compile Include="EmittedIL\ReflectionFreeToString.fs" />
250251
<!--<Compile Include="EmittedIL\StructGettersReadOnly.fs" />-->
251252
<Compile Include="EmittedIL\TailCalls.fs" />
252253
<Compile Include="EmittedIL\TupleElimination.fs" />

0 commit comments

Comments
 (0)