@@ -2103,3 +2103,104 @@ type Container<'T>(value: 'T) =
21032103 rude.Kind = RudeEditKind.NotSupportedByRuntime
21042104 && rude.Message.Contains " GenericAddFieldToExistingType"
21052105 )
2106+
2107+ // --- Parity characterization: structural edits to NON-struct-state-machine CEs ---
2108+ // task {} lowers to a struct resumable state machine, so adding/removing a step is a
2109+ // StateMachineShapeChange rude edit (see the task tests above). async/seq/inner functions do
2110+ // NOT use struct resumable code, so the same structural edit should classify differently (and,
2111+ // we expect, more permissively, matching C#). These tests pin that distinction. The positive
2112+ // (Empty rude edits) assertions encode the expected C#-parity outcome; if F# is stricter than
2113+ // predicted, the failure documents exactly where.
2114+
2115+ [<Fact>]
2116+ let ``async let - bang addition is not a struct state machine rude edit`` () =
2117+ use harness = new DiffTestHarness()
2118+ let baseline_source = """
2119+ module Library
2120+ let runAsync () =
2121+ async {
2122+ let! value = async { return 1 }
2123+ return value + 1
2124+ }
2125+ """
2126+ let updated_source = """
2127+ module Library
2128+ let runAsync () =
2129+ async {
2130+ let! value = async { return 1 }
2131+ let! extra = async { return 2 }
2132+ return value + extra
2133+ }
2134+ """
2135+ harness.Rewrite( baseline_ source)
2136+ let baseline = harness.Compile()
2137+ harness.Rewrite( updated_ source)
2138+ let updated = harness.Compile()
2139+
2140+ let result = harness.DiffWith allCapabilities baseline updated
2141+
2142+ // async lowers to continuation closures, not a struct resumable state machine: adding a
2143+ // let! adds a continuation lambda (allowed with NewTypeDefinition/AddMethodToExistingType)
2144+ // and edits the prior continuation body. Unlike task, this should NOT be rude.
2145+ Assert.DoesNotContain( result.RudeEdits, fun rude -> rude.Kind = RudeEditKind.StateMachineShapeChange)
2146+ Assert.Empty( result.RudeEdits)
2147+
2148+ [<Fact>]
2149+ let ``seq yield addition is not a struct state machine rude edit`` () =
2150+ use harness = new DiffTestHarness()
2151+ let baseline_source = """
2152+ module Library
2153+ let numbers () =
2154+ seq {
2155+ yield 1
2156+ yield 2
2157+ }
2158+ """
2159+ let updated_source = """
2160+ module Library
2161+ let numbers () =
2162+ seq {
2163+ yield 1
2164+ yield 2
2165+ yield 3
2166+ }
2167+ """
2168+ harness.Rewrite( baseline_ source)
2169+ let baseline = harness.Compile()
2170+ harness.Rewrite( updated_ source)
2171+ let updated = harness.Compile()
2172+
2173+ let result = harness.DiffWith allCapabilities baseline updated
2174+
2175+ // seq {} lowers via the sequence-expression -> generated IEnumerable object path
2176+ // (ConvertSequenceExprToObject), not resumable struct code. Adding a yield must not be a
2177+ // StateMachineShapeChange; characterizing whether it lands as an allowed body update or a
2178+ // synthesized-declaration change is the point of this test.
2179+ Assert.DoesNotContain( result.RudeEdits, fun rude -> rude.Kind = RudeEditKind.StateMachineShapeChange)
2180+
2181+ [<Fact>]
2182+ let ``adding an inner function is not a struct state machine rude edit`` () =
2183+ use harness = new DiffTestHarness()
2184+ let baseline_source = """
2185+ module Library
2186+ let compute (x: int) =
2187+ x + 1
2188+ """
2189+ let updated_source = """
2190+ module Library
2191+ let compute (x: int) =
2192+ let helper y = y * 2
2193+ helper x + 1
2194+ """
2195+ harness.Rewrite( baseline_ source)
2196+ let baseline = harness.Compile()
2197+ harness.Rewrite( updated_ source)
2198+ let updated = harness.Compile()
2199+
2200+ let result = harness.DiffWith allCapabilities baseline updated
2201+
2202+ // An inner function lowers to a closure/local method, the same family as a lambda: adding
2203+ // one should be an additive (allowed) edit with full capabilities, never a state machine
2204+ // shape change.
2205+ Assert.DoesNotContain( result.RudeEdits, fun rude -> rude.Kind = RudeEditKind.StateMachineShapeChange)
2206+ Assert.Empty( result.RudeEdits)
0 commit comments