Skip to content

Commit 9cd394e

Browse files
authored
Add FsharpType.MakeStructTupleType overload that does not accept an assembly (#14504)
FsharpType.MakeStructTupleType without assembly argument and without locking
1 parent 376466c commit 9cd394e

File tree

11 files changed

+216
-92
lines changed

11 files changed

+216
-92
lines changed

src/FSharp.Core/quotations.fs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ open Microsoft.FSharp.Text.StructuredPrintfImpl.Layout
1919
open Microsoft.FSharp.Text.StructuredPrintfImpl.TaggedText
2020

2121
#nowarn "52" // The value has been copied to ensure the original is not mutated by this operation
22-
2322
//--------------------------------------------------------------------------
2423
// RAW quotations - basic data types
2524
//--------------------------------------------------------------------------
@@ -1101,6 +1100,10 @@ module Patterns =
11011100
let ty = FSharpType.MakeStructTupleType(asm, Array.map typeOf (Array.ofList args))
11021101
mkFEN (NewTupleOp ty) args
11031102

1103+
let mkNewStructTupleNetStandard args =
1104+
let ty = FSharpType.MakeStructTupleType(Array.map typeOf (Array.ofList args))
1105+
mkFEN (NewTupleOp ty) args
1106+
11041107
let mkTupleGet (ty, n, x) =
11051108
checkTypesSR ty (typeOf x) "tupleGet" (SR.GetString(SR.QtmmExprNotMatchTuple))
11061109
let mems = FSharpType.GetTupleElements ty
@@ -2586,6 +2589,9 @@ type Expr with
25862589
static member NewStructTuple(asm: Assembly, elements) =
25872590
mkNewStructTuple (asm, elements)
25882591

2592+
static member NewStructTuple elements =
2593+
mkNewStructTupleNetStandard elements
2594+
25892595
static member NewRecord(recordType: Type, elements) =
25902596
checkNonNull "recordType" recordType
25912597
mkNewRecord (recordType, elements)

src/FSharp.Core/quotations.fsi

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -673,6 +673,22 @@ type Expr =
673673
/// </example>
674674
static member NewStructTuple: asm: Assembly * elements: Expr list -> Expr
675675

676+
/// <summary>Builds an expression that represents the creation of an F# tuple value</summary>
677+
///
678+
/// <param name="elements">The list of elements of the tuple.</param>
679+
///
680+
/// <returns>The resulting expression.</returns>
681+
///
682+
/// <example id="newstructtuple-1">
683+
/// <code lang="fsharp">
684+
/// open FSharp.Quotations
685+
///
686+
/// Expr.NewStructTuple( [ &lt;@ 1 @&gt;; &lt;@ "a" @&gt; ])
687+
/// </code>
688+
/// Evaluates to a quotation with the same structure as <c>&lt;@ struct (1, "a") @&gt;</c>.
689+
/// </example>
690+
static member NewStructTuple: elements: Expr list -> Expr
691+
676692
/// <summary>Builds record-construction expressions </summary>
677693
///
678694
/// <param name="recordType">The type of record.</param>

src/FSharp.Core/reflect.fs

Lines changed: 143 additions & 90 deletions
Original file line numberDiff line numberDiff line change
@@ -628,27 +628,6 @@ module internal Impl =
628628

629629
//-----------------------------------------------------------------
630630
// TUPLE DECOMPILATION
631-
let tupleNames =
632-
[|
633-
"System.Tuple`1"
634-
"System.Tuple`2"
635-
"System.Tuple`3"
636-
"System.Tuple`4"
637-
"System.Tuple`5"
638-
"System.Tuple`6"
639-
"System.Tuple`7"
640-
"System.Tuple`8"
641-
"System.Tuple"
642-
"System.ValueTuple`1"
643-
"System.ValueTuple`2"
644-
"System.ValueTuple`3"
645-
"System.ValueTuple`4"
646-
"System.ValueTuple`5"
647-
"System.ValueTuple`6"
648-
"System.ValueTuple`7"
649-
"System.ValueTuple`8"
650-
"System.ValueTuple"
651-
|]
652631

653632
let simpleTupleNames =
654633
[|
@@ -679,69 +658,137 @@ module internal Impl =
679658
// used in F# type providers.
680659
typ.IsGenericType
681660
&& typ.Namespace = "System"
682-
&& simpleTupleNames |> Seq.exists typ.Name.StartsWith
661+
&& simpleTupleNames |> Array.exists typ.Name.StartsWith
683662

684663
let maxTuple = 8
685664
// Which field holds the nested tuple?
686665
let tupleEncField = maxTuple - 1
687666

688-
let dictionaryLock = obj ()
689-
let refTupleTypes = Dictionary<Assembly, Type[]>()
690-
let valueTupleTypes = Dictionary<Assembly, Type[]>()
691-
692-
let rec mkTupleType isStruct (asm: Assembly) (tys: Type[]) =
693-
let table =
694-
let makeIt n =
695-
let tupleFullName n =
696-
let structOffset = if isStruct then 9 else 0
697-
let index = n - 1 + structOffset
698-
tupleNames.[index]
699-
700-
match n with
701-
| 1 -> asm.GetType(tupleFullName 1)
702-
| 2 -> asm.GetType(tupleFullName 2)
703-
| 3 -> asm.GetType(tupleFullName 3)
704-
| 4 -> asm.GetType(tupleFullName 4)
705-
| 5 -> asm.GetType(tupleFullName 5)
706-
| 6 -> asm.GetType(tupleFullName 6)
707-
| 7 -> asm.GetType(tupleFullName 7)
708-
| 8 -> asm.GetType(tupleFullName 8)
709-
| _ -> invalidArg "tys" (SR.GetString(SR.invalidTupleTypes))
710-
711-
let tables =
712-
if isStruct then
713-
valueTupleTypes
714-
else
715-
refTupleTypes
716-
717-
match lock dictionaryLock (fun () -> tables.TryGetValue asm) with
718-
| false, _ ->
719-
// the Dictionary<>s here could be ConcurrentDictionary<>'s, but then
720-
// that would lock while initializing the Type array (maybe not an issue)
721-
let mutable a = Array.init<Type> 8 (fun i -> makeIt (i + 1))
722-
723-
lock dictionaryLock (fun () ->
724-
match tables.TryGetValue asm with
725-
| true, t -> a <- t
726-
| false, _ -> tables.Add(asm, a))
727-
728-
a
729-
| true, t -> t
730-
731-
match tys.Length with
732-
| 1 -> table.[0].MakeGenericType tys
733-
| 2 -> table.[1].MakeGenericType tys
734-
| 3 -> table.[2].MakeGenericType tys
735-
| 4 -> table.[3].MakeGenericType tys
736-
| 5 -> table.[4].MakeGenericType tys
737-
| 6 -> table.[5].MakeGenericType tys
738-
| 7 -> table.[6].MakeGenericType tys
739-
| n when n >= maxTuple ->
740-
let tysA = tys.[0 .. tupleEncField - 1]
741-
let tysB = tys.[maxTuple - 1 ..]
742-
let tyB = mkTupleType isStruct asm tysB
743-
table.[7].MakeGenericType(Array.append tysA [| tyB |])
744-
| _ -> invalidArg "tys" (SR.GetString(SR.invalidTupleTypes))
667+
module internal TupleFromSpecifiedAssembly =
668+
let private tupleNames =
669+
[|
670+
"System.Tuple`1"
671+
"System.Tuple`2"
672+
"System.Tuple`3"
673+
"System.Tuple`4"
674+
"System.Tuple`5"
675+
"System.Tuple`6"
676+
"System.Tuple`7"
677+
"System.Tuple`8"
678+
"System.Tuple"
679+
"System.ValueTuple`1"
680+
"System.ValueTuple`2"
681+
"System.ValueTuple`3"
682+
"System.ValueTuple`4"
683+
"System.ValueTuple`5"
684+
"System.ValueTuple`6"
685+
"System.ValueTuple`7"
686+
"System.ValueTuple`8"
687+
"System.ValueTuple"
688+
|]
689+
690+
let private dictionaryLock = obj ()
691+
let private refTupleTypes = Dictionary<Assembly, Type[]>()
692+
let private valueTupleTypes = Dictionary<Assembly, Type[]>()
693+
694+
let rec mkTupleType isStruct (asm: Assembly) (tys: Type[]) =
695+
let table =
696+
let makeIt n =
697+
let tupleFullName n =
698+
let structOffset = if isStruct then 9 else 0
699+
let index = n - 1 + structOffset
700+
tupleNames.[index]
701+
702+
match n with
703+
| 1 -> asm.GetType(tupleFullName 1)
704+
| 2 -> asm.GetType(tupleFullName 2)
705+
| 3 -> asm.GetType(tupleFullName 3)
706+
| 4 -> asm.GetType(tupleFullName 4)
707+
| 5 -> asm.GetType(tupleFullName 5)
708+
| 6 -> asm.GetType(tupleFullName 6)
709+
| 7 -> asm.GetType(tupleFullName 7)
710+
| 8 -> asm.GetType(tupleFullName 8)
711+
| _ -> invalidArg "tys" (SR.GetString(SR.invalidTupleTypes))
712+
713+
let tables =
714+
if isStruct then
715+
valueTupleTypes
716+
else
717+
refTupleTypes
718+
719+
match lock dictionaryLock (fun () -> tables.TryGetValue asm) with
720+
| false, _ ->
721+
// the Dictionary<>s here could be ConcurrentDictionary<>'s, but then
722+
// that would lock while initializing the Type array (maybe not an issue)
723+
let mutable a = Array.init<Type> 8 (fun i -> makeIt (i + 1))
724+
725+
lock dictionaryLock (fun () ->
726+
match tables.TryGetValue asm with
727+
| true, t -> a <- t
728+
| false, _ -> tables.Add(asm, a))
729+
730+
a
731+
| true, t -> t
732+
733+
match tys.Length with
734+
| 1 -> table.[0].MakeGenericType tys
735+
| 2 -> table.[1].MakeGenericType tys
736+
| 3 -> table.[2].MakeGenericType tys
737+
| 4 -> table.[3].MakeGenericType tys
738+
| 5 -> table.[4].MakeGenericType tys
739+
| 6 -> table.[5].MakeGenericType tys
740+
| 7 -> table.[6].MakeGenericType tys
741+
| n when n >= maxTuple ->
742+
let tysA = tys.[0 .. tupleEncField - 1]
743+
let tysB = tys.[maxTuple - 1 ..]
744+
let tyB = mkTupleType isStruct asm tysB
745+
table.[7].MakeGenericType(Array.append tysA [| tyB |])
746+
| _ -> invalidArg "tys" (SR.GetString(SR.invalidTupleTypes))
747+
748+
let refTupleTypesNetStandard =
749+
[|
750+
typedefof<System.Tuple<_>>
751+
typedefof<System.Tuple<_, _>>
752+
typedefof<System.Tuple<_, _, _>>
753+
typedefof<System.Tuple<_, _, _, _>>
754+
typedefof<System.Tuple<_, _, _, _, _>>
755+
typedefof<System.Tuple<_, _, _, _, _, _>>
756+
typedefof<System.Tuple<_, _, _, _, _, _, _>>
757+
typedefof<System.Tuple<_, _, _, _, _, _, _, _>>
758+
|]
759+
760+
let structTupleTypesNetStandard =
761+
[|
762+
typedefof<System.ValueTuple<_>>
763+
typedefof<System.ValueTuple<_, _>>
764+
typedefof<System.ValueTuple<_, _, _>>
765+
typedefof<System.ValueTuple<_, _, _, _>>
766+
typedefof<System.ValueTuple<_, _, _, _, _>>
767+
typedefof<System.ValueTuple<_, _, _, _, _, _>>
768+
typedefof<System.ValueTuple<_, _, _, _, _, _, _>>
769+
typedefof<System.ValueTuple<_, _, _, _, _, _, _, _>>
770+
|]
771+
772+
/// Index of the recursively-nested Tuple type within the table of types
773+
[<Literal>]
774+
let nestedTupIndex = 7
775+
776+
/// Index of the last regular (non-nested) tuple type within the table of types
777+
[<Literal>]
778+
let lastRegularTupIndex = 6 //nestedTupIndex - 1 (wait for arithmetic in constants)
779+
780+
let rec mkTupleTypeNetStandard (tupTyTbl: Type[]) (tys: Type[]) =
781+
let tblIdx = tys.Length - 1
782+
assert (tblIdx >= 0)
783+
assert (nestedTupIndex = tupTyTbl.Length - 1)
784+
785+
match tblIdx with
786+
| idx when idx < nestedTupIndex -> tupTyTbl[ idx ].MakeGenericType tys
787+
| _ ->
788+
let tysA = tys.[0..lastRegularTupIndex]
789+
let tysB = tys.[nestedTupIndex..]
790+
let tyB = mkTupleTypeNetStandard tupTyTbl tysB
791+
tupTyTbl.[nestedTupIndex].MakeGenericType([| yield! tysA; yield tyB |])
745792

746793
let rec getTupleTypeInfo (typ: Type) =
747794
if not (isTupleType typ) then
@@ -1111,18 +1158,13 @@ type FSharpType =
11111158
static member MakeTupleType(types: Type[]) =
11121159
checkNonNull "types" types
11131160

1114-
// No assembly passed therefore just get framework local version of Tuple
1115-
let asm = typeof<System.Tuple>.Assembly
1161+
if types.Length = 0 then
1162+
invalidArg "types" (SR.GetString(SR.invalidTupleTypes))
11161163

1117-
if
1118-
types
1119-
|> Array.exists (function
1120-
| null -> true
1121-
| _ -> false)
1122-
then
1164+
if types |> Array.exists isNull then
11231165
invalidArg "types" (SR.GetString(SR.nullsNotAllowedInArray))
11241166

1125-
mkTupleType false asm types
1167+
mkTupleTypeNetStandard refTupleTypesNetStandard types
11261168

11271169
static member MakeTupleType(asm: Assembly, types: Type[]) =
11281170
checkNonNull "types" types
@@ -1135,7 +1177,7 @@ type FSharpType =
11351177
then
11361178
invalidArg "types" (SR.GetString(SR.nullsNotAllowedInArray))
11371179

1138-
mkTupleType false asm types
1180+
TupleFromSpecifiedAssembly.mkTupleType false asm types
11391181

11401182
static member MakeStructTupleType(asm: Assembly, types: Type[]) =
11411183
checkNonNull "types" types
@@ -1148,7 +1190,18 @@ type FSharpType =
11481190
then
11491191
invalidArg "types" (SR.GetString(SR.nullsNotAllowedInArray))
11501192

1151-
mkTupleType true asm types
1193+
TupleFromSpecifiedAssembly.mkTupleType true asm types
1194+
1195+
static member MakeStructTupleType(types: Type[]) =
1196+
checkNonNull "types" types
1197+
1198+
if types.Length = 0 then
1199+
invalidArg "types" (SR.GetString(SR.invalidTupleTypes))
1200+
1201+
if types |> Array.exists isNull then
1202+
invalidArg "types" (SR.GetString(SR.nullsNotAllowedInArray))
1203+
1204+
mkTupleTypeNetStandard structTupleTypesNetStandard types
11521205

11531206
static member GetTupleElements(tupleType: Type) =
11541207
checkTupleType ("tupleType", tupleType)

src/FSharp.Core/reflect.fsi

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -619,6 +619,15 @@ type FSharpType =
619619
/// <example-tbd></example-tbd>
620620
static member MakeStructTupleType: asm: Assembly * types: Type[] -> Type
621621

622+
/// <summary>Returns a <see cref="T:System.Type"/> representing an F# struct tuple type with the given element types</summary>
623+
///
624+
/// <param name="types">An array of types for the tuple elements.</param>
625+
///
626+
/// <returns>The type representing the struct tuple containing the input elements.</returns>
627+
///
628+
/// <example-tbd></example-tbd>
629+
static member MakeStructTupleType: types: Type[] -> Type
630+
622631
/// <summary>Return true if the <c>typ</c> is a representation of an F# tuple type </summary>
623632
///
624633
/// <param name="typ">The type to check.</param>

tests/FSharp.Core.UnitTests/FSharp.Core.SurfaceArea.netstandard20.debug.bsl

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2350,6 +2350,7 @@ Microsoft.FSharp.Quotations.FSharpExpr: Microsoft.FSharp.Quotations.FSharpExpr N
23502350
Microsoft.FSharp.Quotations.FSharpExpr: Microsoft.FSharp.Quotations.FSharpExpr NewDelegate(System.Type, Microsoft.FSharp.Collections.FSharpList`1[Microsoft.FSharp.Quotations.FSharpVar], Microsoft.FSharp.Quotations.FSharpExpr)
23512351
Microsoft.FSharp.Quotations.FSharpExpr: Microsoft.FSharp.Quotations.FSharpExpr NewObject(System.Reflection.ConstructorInfo, Microsoft.FSharp.Collections.FSharpList`1[Microsoft.FSharp.Quotations.FSharpExpr])
23522352
Microsoft.FSharp.Quotations.FSharpExpr: Microsoft.FSharp.Quotations.FSharpExpr NewRecord(System.Type, Microsoft.FSharp.Collections.FSharpList`1[Microsoft.FSharp.Quotations.FSharpExpr])
2353+
Microsoft.FSharp.Quotations.FSharpExpr: Microsoft.FSharp.Quotations.FSharpExpr NewStructTuple(Microsoft.FSharp.Collections.FSharpList`1[Microsoft.FSharp.Quotations.FSharpExpr])
23532354
Microsoft.FSharp.Quotations.FSharpExpr: Microsoft.FSharp.Quotations.FSharpExpr NewStructTuple(System.Reflection.Assembly, Microsoft.FSharp.Collections.FSharpList`1[Microsoft.FSharp.Quotations.FSharpExpr])
23542355
Microsoft.FSharp.Quotations.FSharpExpr: Microsoft.FSharp.Quotations.FSharpExpr NewTuple(Microsoft.FSharp.Collections.FSharpList`1[Microsoft.FSharp.Quotations.FSharpExpr])
23552356
Microsoft.FSharp.Quotations.FSharpExpr: Microsoft.FSharp.Quotations.FSharpExpr NewUnionCase(Microsoft.FSharp.Reflection.UnionCaseInfo, Microsoft.FSharp.Collections.FSharpList`1[Microsoft.FSharp.Quotations.FSharpExpr])
@@ -2467,6 +2468,7 @@ Microsoft.FSharp.Reflection.FSharpType: System.Reflection.PropertyInfo[] GetReco
24672468
Microsoft.FSharp.Reflection.FSharpType: System.Tuple`2[System.Type,System.Type] GetFunctionElements(System.Type)
24682469
Microsoft.FSharp.Reflection.FSharpType: System.Type MakeFunctionType(System.Type, System.Type)
24692470
Microsoft.FSharp.Reflection.FSharpType: System.Type MakeStructTupleType(System.Reflection.Assembly, System.Type[])
2471+
Microsoft.FSharp.Reflection.FSharpType: System.Type MakeStructTupleType(System.Type[])
24702472
Microsoft.FSharp.Reflection.FSharpType: System.Type MakeTupleType(System.Reflection.Assembly, System.Type[])
24712473
Microsoft.FSharp.Reflection.FSharpType: System.Type MakeTupleType(System.Type[])
24722474
Microsoft.FSharp.Reflection.FSharpType: System.Type[] GetTupleElements(System.Type)

tests/FSharp.Core.UnitTests/FSharp.Core.SurfaceArea.netstandard20.release.bsl

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2349,6 +2349,7 @@ Microsoft.FSharp.Quotations.FSharpExpr: Microsoft.FSharp.Quotations.FSharpExpr N
23492349
Microsoft.FSharp.Quotations.FSharpExpr: Microsoft.FSharp.Quotations.FSharpExpr NewDelegate(System.Type, Microsoft.FSharp.Collections.FSharpList`1[Microsoft.FSharp.Quotations.FSharpVar], Microsoft.FSharp.Quotations.FSharpExpr)
23502350
Microsoft.FSharp.Quotations.FSharpExpr: Microsoft.FSharp.Quotations.FSharpExpr NewObject(System.Reflection.ConstructorInfo, Microsoft.FSharp.Collections.FSharpList`1[Microsoft.FSharp.Quotations.FSharpExpr])
23512351
Microsoft.FSharp.Quotations.FSharpExpr: Microsoft.FSharp.Quotations.FSharpExpr NewRecord(System.Type, Microsoft.FSharp.Collections.FSharpList`1[Microsoft.FSharp.Quotations.FSharpExpr])
2352+
Microsoft.FSharp.Quotations.FSharpExpr: Microsoft.FSharp.Quotations.FSharpExpr NewStructTuple(Microsoft.FSharp.Collections.FSharpList`1[Microsoft.FSharp.Quotations.FSharpExpr])
23522353
Microsoft.FSharp.Quotations.FSharpExpr: Microsoft.FSharp.Quotations.FSharpExpr NewStructTuple(System.Reflection.Assembly, Microsoft.FSharp.Collections.FSharpList`1[Microsoft.FSharp.Quotations.FSharpExpr])
23532354
Microsoft.FSharp.Quotations.FSharpExpr: Microsoft.FSharp.Quotations.FSharpExpr NewTuple(Microsoft.FSharp.Collections.FSharpList`1[Microsoft.FSharp.Quotations.FSharpExpr])
23542355
Microsoft.FSharp.Quotations.FSharpExpr: Microsoft.FSharp.Quotations.FSharpExpr NewUnionCase(Microsoft.FSharp.Reflection.UnionCaseInfo, Microsoft.FSharp.Collections.FSharpList`1[Microsoft.FSharp.Quotations.FSharpExpr])
@@ -2466,6 +2467,7 @@ Microsoft.FSharp.Reflection.FSharpType: System.Reflection.PropertyInfo[] GetReco
24662467
Microsoft.FSharp.Reflection.FSharpType: System.Tuple`2[System.Type,System.Type] GetFunctionElements(System.Type)
24672468
Microsoft.FSharp.Reflection.FSharpType: System.Type MakeFunctionType(System.Type, System.Type)
24682469
Microsoft.FSharp.Reflection.FSharpType: System.Type MakeStructTupleType(System.Reflection.Assembly, System.Type[])
2470+
Microsoft.FSharp.Reflection.FSharpType: System.Type MakeStructTupleType(System.Type[])
24692471
Microsoft.FSharp.Reflection.FSharpType: System.Type MakeTupleType(System.Reflection.Assembly, System.Type[])
24702472
Microsoft.FSharp.Reflection.FSharpType: System.Type MakeTupleType(System.Type[])
24712473
Microsoft.FSharp.Reflection.FSharpType: System.Type[] GetTupleElements(System.Type)

0 commit comments

Comments
 (0)