55namespace FSharp.Core.UnitTests.Quotations
66
77open System
8+ open FSharp.Core .UnitTests
9+ open FSharp.Core .UnitTests .Collections
810open FSharp.Core .UnitTests .LibraryTestFx
911open Xunit
1012open FSharp.Quotations
@@ -139,4 +141,122 @@ type FSharpQuotationsTests() =
139141 let expr = Expr.NewTuple [ <@@ 1 @@>; <@@ " " @@> ]
140142 match expr with
141143 | NewStructTuple _ -> Assert.Fail()
142- | _ -> ()
144+ | _ -> ()
145+
146+ /// This fixture is here to test handling of EqualityConditionalOn and ComparisonConditionalOn.
147+ /// We don't generate witnesses for equality and comparison if they're conditional; the tests
148+ /// assert that code gen doesn't fail in those cases.
149+ [<RequireQualifiedAccess>]
150+ module TestConditionalConstraints =
151+ open FSharp.Linq .RuntimeHelpers
152+
153+ let eval q = LeafExpressionConverter.EvaluateQuotation q
154+
155+ type DiscriminatedUnionWithGeneric < 'a > =
156+ | Case of 'a
157+
158+ [<NoComparison>]
159+ type ThingWithNoComparison =
160+ | NoComparison
161+
162+ [<NoEquality ; NoComparison>]
163+ type ThingWithNoEquality =
164+ | NoEquality
165+
166+ override this.ToString () =
167+ " NoEquality"
168+
169+ let inline compare < ^T when ^T : comparison > ( x : ^T ) ( y : ^T ) : bool =
170+ x < y
171+
172+ let inline equate < ^T when ^T : equality > ( x : ^T ) ( y : ^T ) : bool =
173+ x = y
174+
175+ [<Fact>]
176+ let ``SRTP quotations can consume conditionally constrained types `` () =
177+ // Just normal calls, no quotation
178+ Assert.False ( equate ( DiscriminatedUnionWithGeneric.Case 3 ) ( DiscriminatedUnionWithGeneric.Case 4 ))
179+ Assert.True ( equate ( DiscriminatedUnionWithGeneric.Case 3 ) ( DiscriminatedUnionWithGeneric.Case 3 ))
180+ Assert.True ( compare ( DiscriminatedUnionWithGeneric.Case 3 ) ( DiscriminatedUnionWithGeneric.Case 4 ))
181+
182+ // Typed quotation, int
183+ <@ equate ( DiscriminatedUnionWithGeneric.Case 3 ) ( DiscriminatedUnionWithGeneric.Case 4 ) @>
184+ |> eval
185+ |> unbox< bool>
186+ |> Assert.False
187+
188+ <@ equate ( DiscriminatedUnionWithGeneric.Case 3 ) ( DiscriminatedUnionWithGeneric.Case 3 ) @>
189+ |> eval
190+ |> unbox< bool>
191+ |> Assert.True
192+
193+ <@ compare ( DiscriminatedUnionWithGeneric.Case 3 ) ( DiscriminatedUnionWithGeneric.Case 4 ) @>
194+ |> eval
195+ |> unbox< bool>
196+ |> Assert.True
197+
198+ // Untyped quotation, int
199+ <@@ equate ( DiscriminatedUnionWithGeneric.Case 3 ) ( DiscriminatedUnionWithGeneric.Case 4 ) @@>
200+ |> eval
201+ |> unbox< bool>
202+ |> Assert.False
203+
204+ <@@ equate ( DiscriminatedUnionWithGeneric.Case 3 ) ( DiscriminatedUnionWithGeneric.Case 3 ) @@>
205+ |> eval
206+ |> unbox< bool>
207+ |> Assert.True
208+
209+ <@@ compare ( DiscriminatedUnionWithGeneric.Case 3 ) ( DiscriminatedUnionWithGeneric.Case 4 ) @@>
210+ |> eval
211+ |> unbox< bool>
212+ |> Assert.True
213+
214+ // Typed and untyped quotation, ThingWithNoComparison
215+ <@ equate ThingWithNoComparison.NoComparison ThingWithNoComparison.NoComparison @>
216+ |> eval
217+ |> unbox< bool>
218+ |> Assert.True
219+
220+ <@@ equate ThingWithNoComparison.NoComparison ThingWithNoComparison.NoComparison @@>
221+ |> eval
222+ |> unbox< bool>
223+ |> Assert.True
224+
225+ // Typed and untyped quotation, ThingWithNoEquality
226+ <@ ( fun x -> x.ToString ()) ThingWithNoEquality.NoEquality @>
227+ |> eval
228+ |> unbox< string>
229+ |> fun s -> Assert.AreEqual ( s, " NoEquality" )
230+
231+ <@@ ( fun x -> x.ToString ()) ThingWithNoEquality.NoEquality @@>
232+ |> eval
233+ |> unbox< string>
234+ |> fun s -> Assert.AreEqual ( s, " NoEquality" )
235+
236+ // This test isn't quotation-related, but it *is* closely related to the quotation test: both are checking
237+ // we can cope without witnesses.
238+ [<Fact>]
239+ let ``Reflective invocations of conditionally constrained types throw with a reasonable error`` () =
240+ let compare = typeof< ThingWithNoComparison>. DeclaringType.GetMethod " compare"
241+ let compare = compare.MakeGenericMethod([| typeof< ThingWithNoComparison> |])
242+ let exc =
243+ try
244+ compare.Invoke ( null , [| ThingWithNoComparison.NoComparison ; ThingWithNoComparison.NoComparison|])
245+ |> ignore< obj>
246+ None
247+ with
248+ | exc ->
249+ Some exc
250+
251+ Assert.Contains ( " does not implement the System.IComparable interface" , exc.Value.InnerException.Message, StringComparison.Ordinal)
252+
253+ // This test isn't quotation-related, but it *is* closely related to the quotation test: both are checking
254+ // we can cope without witnesses.
255+ [<Fact>]
256+ let ``We still use Object.ReferenceEquals for non - equatable methods when reflectively invoked`` () =
257+ let equate = typeof< ThingWithNoComparison>. DeclaringType.GetMethod " equate"
258+ let equate = equate.MakeGenericMethod([| typeof< ThingWithNoEquality> |])
259+ let anotherOne = Activator.CreateInstance ( typeof< ThingWithNoEquality>, nonPublic= true )
260+ equate.Invoke ( null , [| ThingWithNoEquality.NoEquality ; anotherOne |])
261+ |> unbox< bool>
262+ |> Assert.False
0 commit comments