From adb02dc702cf86ec1bfe4e14ef34e5dcfa6ee125 Mon Sep 17 00:00:00 2001 From: Tomas Grosup Date: Tue, 4 Feb 2025 14:03:31 +0100 Subject: [PATCH] Bugfix:: Add missing codegen for mapping of overlapped struct DU fields and read it in fslib reflection (#18274) --- .../.FSharp.Compiler.Service/9.0.300.md | 1 + docs/release-notes/.FSharp.Core/9.0.300.md | 8 + src/Compiler/AbstractIL/ilx.fsi | 2 + src/Compiler/CodeGen/IlxGen.fs | 47 +++++- src/Compiler/TypedTree/TypedTreeOps.fs | 3 - src/Compiler/TypedTree/TypedTreeOps.fsi | 2 - src/Compiler/Utilities/illib.fs | 8 + src/Compiler/Utilities/illib.fsi | 2 + src/FSharp.Core/prim-types.fs | 2 +- src/FSharp.Core/prim-types.fsi | 2 +- src/FSharp.Core/reflect.fs | 148 +++++++++--------- tests/AheadOfTime/Trimming/check.ps1 | 4 +- .../Types/UnionTypes/UnionStructTypes.fs | 71 +++++++++ .../FSharpReflection.fs | 18 ++- ...FSharp.Compiler.Service_Release_net9.0.bsl | 4 +- ...ompiler.Service_Release_netstandard2.0.bsl | 4 +- 16 files changed, 230 insertions(+), 96 deletions(-) create mode 100644 docs/release-notes/.FSharp.Core/9.0.300.md diff --git a/docs/release-notes/.FSharp.Compiler.Service/9.0.300.md b/docs/release-notes/.FSharp.Compiler.Service/9.0.300.md index de19496f010..32ae2560ab0 100644 --- a/docs/release-notes/.FSharp.Compiler.Service/9.0.300.md +++ b/docs/release-notes/.FSharp.Compiler.Service/9.0.300.md @@ -19,3 +19,4 @@ * Remove `Cancellable.UsingToken` from tests ([PR #18276](https://github.com/dotnet/fsharp/pull/18276)) ### Breaking Changes +* Struct unions with overlapping fields now generate mappings needed for reading via reflection ([Issue #18121](https://github.com/dotnet/fsharp/issues/17797), [PR #18274](https://github.com/dotnet/fsharp/pull/17877)) \ No newline at end of file diff --git a/docs/release-notes/.FSharp.Core/9.0.300.md b/docs/release-notes/.FSharp.Core/9.0.300.md new file mode 100644 index 00000000000..87d3502539c --- /dev/null +++ b/docs/release-notes/.FSharp.Core/9.0.300.md @@ -0,0 +1,8 @@ +### Fixed + +### Added + +### Changed + +### Breaking Changes +* Struct unions with overlapping fields now generate mappings needed for reading via reflection ([Issue #18121](https://github.com/dotnet/fsharp/issues/17797), [PR #18274](https://github.com/dotnet/fsharp/pull/17877)). Previous versions of FSharp.Core returned incomplete mapping between fields and cases, these older fslib versions will now report an exception. diff --git a/src/Compiler/AbstractIL/ilx.fsi b/src/Compiler/AbstractIL/ilx.fsi index bb4a7344d84..28122885f8e 100644 --- a/src/Compiler/AbstractIL/ilx.fsi +++ b/src/Compiler/AbstractIL/ilx.fsi @@ -162,6 +162,8 @@ val mkILFormalCloRef: ILGenericParameterDefs -> IlxClosureRef -> useStaticField: // MS-ILX: Unions // -------------------------------------------------------------------- +val mkLowerName: nm: string -> string + val actualTypOfIlxUnionField: IlxUnionSpec -> int -> int -> ILType val mkILFreeVar: string * bool * ILType -> IlxClosureFreeVar diff --git a/src/Compiler/CodeGen/IlxGen.fs b/src/Compiler/CodeGen/IlxGen.fs index 861caeea4ab..ff0859486d7 100644 --- a/src/Compiler/CodeGen/IlxGen.fs +++ b/src/Compiler/CodeGen/IlxGen.fs @@ -601,6 +601,29 @@ let voidCheck m g permits ty = error (InternalError("System.Void unexpectedly detected in IL code generation. This should not occur.", m)) #endif +[] +type DuFieldCoordinates = { CaseIdx: int; FieldIdx: int } + +/// Structure for maintaining field reuse across struct unions +type UnionFieldReuseMap = MultiMap + +let unionFieldReuseMapping thisUnionTy (cases: UnionCase[]) : UnionFieldReuseMap = + + if not (isStructTyconRef thisUnionTy) then + Map.empty + else + let fieldKey (f: RecdField) = mkLowerName f.LogicalName + + [ + for i = 0 to cases.Length - 1 do + let fields = cases[i].RecdFieldsArray + + for j = 0 to fields.Length - 1 do + let f = fields[j] + yield fieldKey f, { CaseIdx = i; FieldIdx = j } + ] + |> MultiMap.ofList + /// When generating parameter and return types generate precise .NET IL pointer types. /// These can't be generated for generic instantiations, since .NET generics doesn't /// permit this. But for 'naked' values (locals, parameters, return values etc.) machine @@ -702,18 +725,24 @@ and GenTypeAux cenv m (tyenv: TypeReprEnv) voidOK ptrsOK ty = //-------------------------------------------------------------------------- // Generate ILX references to closures, classunions etc. given a tyenv //-------------------------------------------------------------------------- - -and GenUnionCaseRef (cenv: cenv) m tyenv i (fspecs: RecdField[]) = +and GenUnionCaseRef (cenv: cenv) m tyenv (reuseMap: UnionFieldReuseMap) i (fspecs: RecdField[]) = let g = cenv.g + let fieldMarker = int SourceConstructFlags.Field + fspecs |> Array.mapi (fun j fspec -> let ilFieldDef = mkILInstanceField (fspec.LogicalName, GenType cenv m tyenv fspec.FormalType, None, ILMemberAccess.Public) // These properties on the "field" of an alternative end up going on a property generated by cu_erase.fs - let attrs = - (mkCompilationMappingAttrWithVariantNumAndSeqNum g (int SourceConstructFlags.Field) i j) - :: GenAdditionalAttributesForTy g fspec.FormalType + let mappingAttrs = + match reuseMap |> MultiMap.find (mkLowerName fspec.LogicalName) with + | [] -> [ mkCompilationMappingAttrWithVariantNumAndSeqNum g fieldMarker i j ] + | mappings -> + mappings + |> List.map (fun m -> mkCompilationMappingAttrWithVariantNumAndSeqNum g fieldMarker m.CaseIdx m.FieldIdx) + + let attrs = mappingAttrs @ GenAdditionalAttributesForTy g fspec.FormalType IlxUnionCaseField(ilFieldDef.With(customAttrs = mkILCustomAttrs attrs))) @@ -731,13 +760,15 @@ and GenUnionRef (cenv: cenv) m (tcref: TyconRef) = match tcref.CompiledRepresentation with | CompiledTypeRepr.ILAsmOpen _ -> failwith "GenUnionRef m: unexpected ASM tyrep" | CompiledTypeRepr.ILAsmNamed(tref, _, _) -> + let fieldReuseMap = unionFieldReuseMapping tcref tycon.UnionCasesArray + let alternatives = tycon.UnionCasesArray |> Array.mapi (fun i cspec -> { altName = cspec.CompiledName altCustomAttrs = emptyILCustomAttrs - altFields = GenUnionCaseRef cenv m tyenvinner i cspec.RecdFieldsArray + altFields = GenUnionCaseRef cenv m tyenvinner fieldReuseMap i cspec.RecdFieldsArray }) let nullPermitted = IsUnionTypeWithNullAsTrueValue g tycon @@ -11658,11 +11689,13 @@ and GenTypeDef cenv mgbuf lazyInitInfo eenv m (tycon: Tycon) : ILTypeRef option | _ -> false) -> let alternatives = + let fieldReuseMap = unionFieldReuseMapping tcref tycon.UnionCasesArray + tycon.UnionCasesArray |> Array.mapi (fun i ucspec -> { altName = ucspec.CompiledName - altFields = GenUnionCaseRef cenv m eenvinner.tyenv i ucspec.RecdFieldsArray + altFields = GenUnionCaseRef cenv m eenvinner.tyenv fieldReuseMap i ucspec.RecdFieldsArray altCustomAttrs = mkILCustomAttrs ( GenAttrs cenv eenv ucspec.Attribs diff --git a/src/Compiler/TypedTree/TypedTreeOps.fs b/src/Compiler/TypedTree/TypedTreeOps.fs index 909d6f437a8..ebe42db34fe 100644 --- a/src/Compiler/TypedTree/TypedTreeOps.fs +++ b/src/Compiler/TypedTree/TypedTreeOps.fs @@ -8337,9 +8337,6 @@ let IsMatchingSignatureDataVersionAttr (version: ILVersionInfo) cattr = warning(Failure(FSComp.SR.tastUnexpectedDecodeOfInterfaceDataVersionAttribute())) false -let mkCompilerGeneratedAttr (g: TcGlobals) n = - mkILCustomAttribute (tref_CompilationMappingAttr g, [mkILNonGenericValueTy (tref_SourceConstructFlags g)], [ILAttribElem.Int32 n], []) - //-------------------------------------------------------------------------- // tupled lambda --> method/function with a given valReprInfo specification. // diff --git a/src/Compiler/TypedTree/TypedTreeOps.fsi b/src/Compiler/TypedTree/TypedTreeOps.fsi index 3bca5cdb21e..3a4a1ca4c0a 100755 --- a/src/Compiler/TypedTree/TypedTreeOps.fsi +++ b/src/Compiler/TypedTree/TypedTreeOps.fsi @@ -2380,8 +2380,6 @@ val mkCompilationSourceNameAttr: TcGlobals -> string -> ILAttribute val mkSignatureDataVersionAttr: TcGlobals -> ILVersionInfo -> ILAttribute -val mkCompilerGeneratedAttr: TcGlobals -> int -> ILAttribute - //------------------------------------------------------------------------- // More common type construction //------------------------------------------------------------------------- diff --git a/src/Compiler/Utilities/illib.fs b/src/Compiler/Utilities/illib.fs index 5e32e3b8699..9ed400f3ab8 100644 --- a/src/Compiler/Utilities/illib.fs +++ b/src/Compiler/Utilities/illib.fs @@ -1307,6 +1307,14 @@ module MultiMap = let initBy f xs : MultiMap<_, _> = xs |> Seq.groupBy f |> Seq.map (fun (k, v) -> (k, List.ofSeq v)) |> Map.ofSeq + let ofList (xs: ('a * 'b) list) : MultiMap<'a,'b> = + (Map.empty, xs) + ||> List.fold (fun m (k, v) -> + m |> Map.change k (function + | None -> Some [v] + | Some vs -> Some (v :: vs))) + |> Map.map (fun _ values -> List.rev values) + type LayeredMap<'Key, 'Value when 'Key: comparison> = Map<'Key, 'Value> [] diff --git a/src/Compiler/Utilities/illib.fsi b/src/Compiler/Utilities/illib.fsi index 03525593188..2b23de7de2e 100644 --- a/src/Compiler/Utilities/illib.fsi +++ b/src/Compiler/Utilities/illib.fsi @@ -570,6 +570,8 @@ module internal MultiMap = val initBy: f: ('a -> 'b) -> xs: seq<'a> -> MultiMap<'b, 'a> when 'b: comparison + val ofList: xs: ('a * 'b) list -> MultiMap<'a,'b> when 'a: comparison + type internal LayeredMap<'Key, 'Value when 'Key: comparison> = Map<'Key, 'Value> [] diff --git a/src/FSharp.Core/prim-types.fs b/src/FSharp.Core/prim-types.fs index 7f1b0a122ad..1fc9e799ab4 100644 --- a/src/FSharp.Core/prim-types.fs +++ b/src/FSharp.Core/prim-types.fs @@ -221,7 +221,7 @@ namespace Microsoft.FSharp.Core member _.Minor = minor member _.Release = release - [] + [] [] type CompilationMappingAttribute(sourceConstructFlags:SourceConstructFlags, variantNumber:int, diff --git a/src/FSharp.Core/prim-types.fsi b/src/FSharp.Core/prim-types.fsi index a992a0204e0..15326559354 100644 --- a/src/FSharp.Core/prim-types.fsi +++ b/src/FSharp.Core/prim-types.fsi @@ -657,7 +657,7 @@ namespace Microsoft.FSharp.Core /// their original forms. It is not intended for use from user code. /// /// Attributes - [] + [] [] type CompilationMappingAttribute = inherit Attribute diff --git a/src/FSharp.Core/reflect.fs b/src/FSharp.Core/reflect.fs index f8c8e57d695..32811ae604c 100644 --- a/src/FSharp.Core/reflect.fs +++ b/src/FSharp.Core/reflect.fs @@ -266,108 +266,103 @@ module internal Impl = //----------------------------------------------------------------- // ATTRIBUTE DECOMPILATION - let tryFindCompilationMappingAttribute (attrs: obj array) = + let findCompilationMappingAttributeAllowMultiple (attrs: obj array) = match attrs with - | null - | [||] -> None - | [| res |] -> - let a = (res :?> CompilationMappingAttribute) - Some(a.SourceConstructFlags, a.SequenceNumber, a.VariantNumber) - | _ -> invalidOp (SR.GetString(SR.multipleCompilationMappings)) - - let findCompilationMappingAttribute (attrs: obj array) = - match tryFindCompilationMappingAttribute attrs with - | None -> failwith "no compilation mapping attribute" - | Some a -> a + | null -> [||] + | attrs -> + attrs + |> Array.map (fun res -> + let a = (res :?> CompilationMappingAttribute) + (a.SourceConstructFlags, a.SequenceNumber, a.VariantNumber)) let cmaName = typeof.FullName let assemblyName = typeof.Assembly.GetName().Name let _ = assert (assemblyName = "FSharp.Core") - let tryFindCompilationMappingAttributeFromData (attrs: IList) = + let findCompilationMappingAttributeFromDataAllowMultiple (attrs: IList) = match attrs with - | null -> None + | null -> [||] | _ -> - let mutable res = None - - for a in attrs do - if a.Constructor.DeclaringType.FullName = cmaName then - let args = a.ConstructorArguments - - let flags = - match args.Count with - | 1 -> - let arg0 = args.[0] - let v0 = arg0.Value :?> SourceConstructFlags - (v0, 0, 0) - | 2 -> - let arg0 = args.[0] - let v0 = arg0.Value :?> SourceConstructFlags - let arg1 = args.[1] - let v1 = arg1.Value :?> int - (v0, v1, 0) - | 3 -> - let arg0 = args.[0] - let v0 = arg0.Value :?> SourceConstructFlags - let arg1 = args.[1] - let v1 = arg1.Value :?> int - let arg2 = args.[2] - let v2 = arg2.Value :?> int - (v0, v1, v2) - | _ -> (enum 0, 0, 0) - - res <- Some flags - - res - - let findCompilationMappingAttributeFromData attrs = - match tryFindCompilationMappingAttributeFromData attrs with - | None -> failwith "no compilation mapping attribute" - | Some a -> a + let filtered = + attrs + |> Array.ofSeq + |> Array.filter (fun a -> a.Constructor.DeclaringType.FullName = cmaName) + + filtered + |> Array.map (fun a -> + let args = a.ConstructorArguments + + match args.Count with + | 1 -> + let arg0 = args.[0] + let v0 = arg0.Value :?> SourceConstructFlags + (v0, 0, 0) + | 2 -> + let arg0 = args.[0] + let v0 = arg0.Value :?> SourceConstructFlags + let arg1 = args.[1] + let v1 = arg1.Value :?> int + (v0, v1, 0) + | 3 -> + let arg0 = args.[0] + let v0 = arg0.Value :?> SourceConstructFlags + let arg1 = args.[1] + let v1 = arg1.Value :?> int + let arg2 = args.[2] + let v2 = arg2.Value :?> int + (v0, v1, v2) + | _ -> (enum 0, 0, 0)) let tryFindCompilationMappingAttributeFromType (typ: Type) = let assem = typ.Assembly if (not (isNull assem)) && assem.ReflectionOnly then - tryFindCompilationMappingAttributeFromData (typ.GetCustomAttributesData()) - else - tryFindCompilationMappingAttribute (typ.GetCustomAttributes(typeof, false)) - - let tryFindCompilationMappingAttributeFromMemberInfo (info: MemberInfo) = - let assem = info.DeclaringType.Assembly - - if (not (isNull assem)) && assem.ReflectionOnly then - tryFindCompilationMappingAttributeFromData (info.GetCustomAttributesData()) + findCompilationMappingAttributeFromDataAllowMultiple (typ.GetCustomAttributesData()) else - tryFindCompilationMappingAttribute (info.GetCustomAttributes(typeof, false)) + findCompilationMappingAttributeAllowMultiple ( + typ.GetCustomAttributes(typeof, false) + ) let findCompilationMappingAttributeFromMemberInfo (info: MemberInfo) = let assem = info.DeclaringType.Assembly if (not (isNull assem)) && assem.ReflectionOnly then - findCompilationMappingAttributeFromData (info.GetCustomAttributesData()) + findCompilationMappingAttributeFromDataAllowMultiple (info.GetCustomAttributesData()) else - findCompilationMappingAttribute (info.GetCustomAttributes(typeof, false)) + findCompilationMappingAttributeAllowMultiple ( + info.GetCustomAttributes(typeof, false) + ) let sequenceNumberOfMember (x: MemberInfo) = - let (_, n, _) = findCompilationMappingAttributeFromMemberInfo x in n + let (_, n, _) = findCompilationMappingAttributeFromMemberInfo x |> Array.head + n + + let sequenceNumberOfUnionCaseField (x: MemberInfo) caseTag = + findCompilationMappingAttributeFromMemberInfo x + |> Array.tryFind (fun (_, _, vn) -> vn = caseTag) + |> Option.map (fun (_, sn, _) -> sn) + |> Option.defaultValue Int32.MaxValue - let variantNumberOfMember (x: MemberInfo) = - let (_, _, vn) = findCompilationMappingAttributeFromMemberInfo x in vn + let belongsToCase (x: MemberInfo) caseTag = + findCompilationMappingAttributeFromMemberInfo x + |> Array.exists (fun (_, _, vn) -> vn = caseTag) let sortFreshArray f arr = Array.sortInPlaceWith f arr arr let isFieldProperty (prop: PropertyInfo) = - match tryFindCompilationMappingAttributeFromMemberInfo prop with - | None -> false - | Some(flags, _n, _vn) -> (flags &&& SourceConstructFlags.KindMask) = SourceConstructFlags.Field + match findCompilationMappingAttributeFromMemberInfo prop with + | [||] -> false + | arr -> + let (flags, _, _) = arr |> Array.head + (flags &&& SourceConstructFlags.KindMask) = SourceConstructFlags.Field let tryFindSourceConstructFlagsOfType (typ: Type) = match tryFindCompilationMappingAttributeFromType typ with - | None -> None - | Some(flags, _n, _vn) -> Some flags + | [||] -> None + | [| flags, _n, _vn |] -> Some flags + | _ -> invalidOp (SR.GetString(SR.multipleCompilationMappings)) //----------------------------------------------------------------- // UNION DECOMPILATION @@ -379,9 +374,11 @@ module internal Impl = | null -> typ.GetMethods(staticMethodFlags ||| bindingFlags) |> Array.choose (fun minfo -> - match tryFindCompilationMappingAttributeFromMemberInfo minfo with - | None -> None - | Some(flags, n, _vn) -> + match findCompilationMappingAttributeFromMemberInfo minfo with + | [||] -> None + | arr -> + let (flags, n, _) = arr |> Array.head + if (flags &&& SourceConstructFlags.KindMask) = SourceConstructFlags.UnionCase then let nm = minfo.Name // chop "get_" or "New" off the front @@ -510,8 +507,9 @@ module internal Impl = caseTyp.GetProperties(instancePropertyFlags ||| bindingFlags) |> Array.filter isFieldProperty - |> Array.filter (fun prop -> variantNumberOfMember prop = tag) - |> sortFreshArray (fun p1 p2 -> compare (sequenceNumberOfMember p1) (sequenceNumberOfMember p2)) + |> Array.filter (fun prop -> belongsToCase prop tag) + |> sortFreshArray (fun p1 p2 -> + compare (sequenceNumberOfUnionCaseField p1 tag) (sequenceNumberOfUnionCaseField p2 tag)) let getUnionCaseRecordReader (typ: Type, tag: int, bindingFlags) = let props = fieldsPropsOfUnionCase (typ, tag, bindingFlags) diff --git a/tests/AheadOfTime/Trimming/check.ps1 b/tests/AheadOfTime/Trimming/check.ps1 index b1380ede186..50a8a9e6422 100644 --- a/tests/AheadOfTime/Trimming/check.ps1 +++ b/tests/AheadOfTime/Trimming/check.ps1 @@ -43,7 +43,7 @@ function CheckTrim($root, $tfm, $outputfile, $expected_len) { # error NETSDK1124: Trimming assemblies requires .NET Core 3.0 or higher. # Check net7.0 trimmed assemblies -CheckTrim -root "SelfContained_Trimming_Test" -tfm "net9.0" -outputfile "FSharp.Core.dll" -expected_len 299008 +CheckTrim -root "SelfContained_Trimming_Test" -tfm "net9.0" -outputfile "FSharp.Core.dll" -expected_len 300032 # Check net8.0 trimmed assemblies -CheckTrim -root "StaticLinkedFSharpCore_Trimming_Test" -tfm "net9.0" -outputfile "StaticLinkedFSharpCore_Trimming_Test.dll" -expected_len 9149952 +CheckTrim -root "StaticLinkedFSharpCore_Trimming_Test" -tfm "net9.0" -outputfile "StaticLinkedFSharpCore_Trimming_Test.dll" -expected_len 9150976 diff --git a/tests/FSharp.Compiler.ComponentTests/Conformance/Types/UnionTypes/UnionStructTypes.fs b/tests/FSharp.Compiler.ComponentTests/Conformance/Types/UnionTypes/UnionStructTypes.fs index b3801e113f5..0ec6dc99632 100644 --- a/tests/FSharp.Compiler.ComponentTests/Conformance/Types/UnionTypes/UnionStructTypes.fs +++ b/tests/FSharp.Compiler.ComponentTests/Conformance/Types/UnionTypes/UnionStructTypes.fs @@ -805,6 +805,77 @@ printf $"{result1};{result2}" |> run |> verifyOutput "333;666" + [] + let ``Struct DU with field overlap can be reflected`` () = + Fsx """module Test +open Microsoft.FSharp.Reflection + +[] +type MySharedStructDu = + | A of a:int64 + | B of a:int64 + | C of a:int64 * s:char + | D of s:char * a:int64 + +printf "Size=%i;" (sizeof) +for value in [A 1L; B 2L;D('x',3L)] do + let caseInfo, inner = FSharpValue.GetUnionFields(value, typeof) + printf $"%s{caseInfo.Name}=%A{inner};" + + """ + |> asExe + |> compile + |> shouldSucceed + |> run + |> verifyOutput """Size=16;A=[|1L|];B=[|2L|];D=[|'x'; 3L|];""" + + [] + let ``Field overlap does carry attributes for all cases`` () = + Fsx """module Test + +[] +type MySharedStructDu = + | A of a:int64 + | B of a:int64 + | C of a:int64 * s:char + | D of s:char * a:int64 + + """ + |> asLibrary + |> compile + |> verifyIL [ // Prop "a" is mapped 4x, for cases 0,1,2,3. For case 3/D, it comes at position one. Prop "s" is mapped to two cases. + """ + .property instance int64 a() + { + .custom instance void [FSharp.Core]Microsoft.FSharp.Core.CompilationMappingAttribute::.ctor(valuetype [FSharp.Core]Microsoft.FSharp.Core.SourceConstructFlags, + int32, + int32) = ( 01 00 04 00 00 00 00 00 00 00 00 00 00 00 00 00 ) + .custom instance void [FSharp.Core]Microsoft.FSharp.Core.CompilationMappingAttribute::.ctor(valuetype [FSharp.Core]Microsoft.FSharp.Core.SourceConstructFlags, + int32, + int32) = ( 01 00 04 00 00 00 01 00 00 00 00 00 00 00 00 00 ) + .custom instance void [FSharp.Core]Microsoft.FSharp.Core.CompilationMappingAttribute::.ctor(valuetype [FSharp.Core]Microsoft.FSharp.Core.SourceConstructFlags, + int32, + int32) = ( 01 00 04 00 00 00 02 00 00 00 00 00 00 00 00 00 ) + .custom instance void [FSharp.Core]Microsoft.FSharp.Core.CompilationMappingAttribute::.ctor(valuetype [FSharp.Core]Microsoft.FSharp.Core.SourceConstructFlags, + int32, + int32) = ( 01 00 04 00 00 00 03 00 00 00 01 00 00 00 00 00 ) + .custom instance void [runtime]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) + .custom instance void [runtime]System.Diagnostics.DebuggerNonUserCodeAttribute::.ctor() = ( 01 00 00 00 ) + .get instance int64 Test/MySharedStructDu::get_a() + } + .property instance char s() + { + .custom instance void [FSharp.Core]Microsoft.FSharp.Core.CompilationMappingAttribute::.ctor(valuetype [FSharp.Core]Microsoft.FSharp.Core.SourceConstructFlags, + int32, + int32) = ( 01 00 04 00 00 00 02 00 00 00 01 00 00 00 00 00 ) + .custom instance void [FSharp.Core]Microsoft.FSharp.Core.CompilationMappingAttribute::.ctor(valuetype [FSharp.Core]Microsoft.FSharp.Core.SourceConstructFlags, + int32, + int32) = ( 01 00 04 00 00 00 03 00 00 00 00 00 00 00 00 00 ) + .custom instance void [runtime]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) + .custom instance void [runtime]System.Diagnostics.DebuggerNonUserCodeAttribute::.ctor() = ( 01 00 00 00 ) + .get instance char Test/MySharedStructDu::get_s() + } """ ] + [] let ``Custom ValueOption keeps working`` () = Fsx """ diff --git a/tests/FSharp.Core.UnitTests/FSharp.Core/Microsoft.FSharp.Reflection/FSharpReflection.fs b/tests/FSharp.Core.UnitTests/FSharp.Core/Microsoft.FSharp.Reflection/FSharpReflection.fs index 95de124e9f5..56fd8a0a1b1 100644 --- a/tests/FSharp.Core.UnitTests/FSharp.Core/Microsoft.FSharp.Reflection/FSharpReflection.fs +++ b/tests/FSharp.Core.UnitTests/FSharp.Core/Microsoft.FSharp.Reflection/FSharpReflection.fs @@ -51,10 +51,12 @@ type DiscUnionType<'T> = | B of 'T * DiscUnionType<'T> option | C of float * string +[] type DiscStructUnionType<'T> = | A // No data associated with tag | B of 'T - | C of float * string + | Evil_C of f:float // This is sharing the field "f" in the struct + | C of f:float * s:string exception ExceptionInt of int @@ -1204,6 +1206,11 @@ type UnionCaseInfoTests() = let ((discUnionInfoC:UnionCaseInfo), discvaluearray) = FSharpValue.GetUnionFields(discUniontypeC, typeof>) let ((recDiscCaseinfo:UnionCaseInfo), recDiscCasevaluearray) = FSharpValue.GetUnionFields(recDiscUniontypeB, typeof>) + + let ((sharedstructUnionInfoA:UnionCaseInfo), _) = FSharpValue.GetUnionFields(DiscStructUnionType.A, typeof>) + let ((sharedstructUnionInfoB:UnionCaseInfo), _) = FSharpValue.GetUnionFields(DiscStructUnionType.B(15), typeof>) + let ((sharedstructUnionInfoC:UnionCaseInfo), _) = FSharpValue.GetUnionFields(DiscStructUnionType.C(15.,"x"), typeof>) + let ((sharedstructUnionInfoEvilC:UnionCaseInfo), _) = FSharpValue.GetUnionFields(DiscStructUnionType.Evil_C(15.), typeof>) [] member _.Equals() = @@ -1266,6 +1273,15 @@ type UnionCaseInfoTests() = // rec disc union let recdiscFieldInfo = (recDiscCaseinfo.GetFields()).[0] Assert.AreEqual(recdiscFieldInfo.PropertyType , typeof) + + + Assert.AreEqual(sharedstructUnionInfoA.GetFields().Length ,0) + Assert.AreEqual(sharedstructUnionInfoB.GetFields().[0].PropertyType ,typeof) + + Assert.AreEqual(sharedstructUnionInfoC.GetFields().[0].PropertyType ,typeof) + Assert.AreEqual(sharedstructUnionInfoC.GetFields().[1].PropertyType ,typeof) + + Assert.AreEqual(sharedstructUnionInfoEvilC.GetFields().[0].PropertyType ,typeof) [] member _.GetHashCode() = diff --git a/tests/ILVerify/ilverify_FSharp.Compiler.Service_Release_net9.0.bsl b/tests/ILVerify/ilverify_FSharp.Compiler.Service_Release_net9.0.bsl index 1a088c5a702..bc7e03d8154 100644 --- a/tests/ILVerify/ilverify_FSharp.Compiler.Service_Release_net9.0.bsl +++ b/tests/ILVerify/ilverify_FSharp.Compiler.Service_Release_net9.0.bsl @@ -43,8 +43,8 @@ [IL]: Error [StackUnexpected]: : FSharp.Compiler.ParseAndCheckInputs+CheckMultipleInputsUsingGraphMode@1865::Invoke(int32)][offset 0x0000003A][found Char] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : FSharp.Compiler.CompilerConfig+TcConfig::.ctor([FSharp.Compiler.Service]FSharp.Compiler.CompilerConfig+TcConfigBuilder, bool)][offset 0x0000059C][found Char] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : FSharp.Compiler.CompilerConfig+TcConfig::.ctor([FSharp.Compiler.Service]FSharp.Compiler.CompilerConfig+TcConfigBuilder, bool)][offset 0x000005A5][found Char] Unexpected type on the stack. -[IL]: Error [StackUnexpected]: : FSharp.Compiler.IlxGen::HashRangeSorted([S.P.CoreLib]System.Collections.Generic.IDictionary`2>)][offset 0x00000011][found ref '[FSharp.Compiler.Service]FSharp.Compiler.IlxGen+HashRangeSorted@1859-1'][expected ref '[FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2,int32>'] Unexpected type on the stack. -[IL]: Error [StackUnexpected]: : FSharp.Compiler.IlxGen::HashRangeSorted([S.P.CoreLib]System.Collections.Generic.IDictionary`2>)][offset 0x00000012][found ref '[FSharp.Compiler.Service]FSharp.Compiler.IlxGen+HashRangeSorted@1859'][expected ref '[FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2,T0>'] Unexpected type on the stack. +[IL]: Error [StackUnexpected]: : FSharp.Compiler.IlxGen::HashRangeSorted([S.P.CoreLib]System.Collections.Generic.IDictionary`2>)][offset 0x00000011][found ref '[FSharp.Compiler.Service]FSharp.Compiler.IlxGen+HashRangeSorted@1890-1'][expected ref '[FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2,int32>'] Unexpected type on the stack. +[IL]: Error [StackUnexpected]: : FSharp.Compiler.IlxGen::HashRangeSorted([S.P.CoreLib]System.Collections.Generic.IDictionary`2>)][offset 0x00000012][found ref '[FSharp.Compiler.Service]FSharp.Compiler.IlxGen+HashRangeSorted@1890'][expected ref '[FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2,T0>'] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : FSharp.Compiler.PatternMatchCompilation::isProblematicClause([FSharp.Compiler.Service]FSharp.Compiler.PatternMatchCompilation+MatchClause)][offset 0x00000040][found Byte] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : .$FSharp.Compiler.PatternMatchCompilation::.cctor()][offset 0x0000000B][found Boolean] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : FSharp.Compiler.TypeProviders::ValidateExpectedName([FSharp.Compiler.Service]FSharp.Compiler.Text.Range, string[], string, [FSharp.Compiler.Service]FSharp.Compiler.Tainted`1)][offset 0x000000A8][found Char] Unexpected type on the stack. diff --git a/tests/ILVerify/ilverify_FSharp.Compiler.Service_Release_netstandard2.0.bsl b/tests/ILVerify/ilverify_FSharp.Compiler.Service_Release_netstandard2.0.bsl index 48af8ec2f2f..88e5b423f24 100644 --- a/tests/ILVerify/ilverify_FSharp.Compiler.Service_Release_netstandard2.0.bsl +++ b/tests/ILVerify/ilverify_FSharp.Compiler.Service_Release_netstandard2.0.bsl @@ -61,8 +61,8 @@ [IL]: Error [StackUnexpected]: : FSharp.Compiler.CompilerImports+TcConfig-TryResolveLibWithDirectories@558-1::Invoke([FSharp.Core]Microsoft.FSharp.Core.Unit)][offset 0x0000003B][found Char] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : FSharp.Compiler.CompilerConfig+TcConfig::.ctor([FSharp.Compiler.Service]FSharp.Compiler.CompilerConfig+TcConfigBuilder, bool)][offset 0x0000059C][found Char] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : FSharp.Compiler.CompilerConfig+TcConfig::.ctor([FSharp.Compiler.Service]FSharp.Compiler.CompilerConfig+TcConfigBuilder, bool)][offset 0x000005A5][found Char] Unexpected type on the stack. -[IL]: Error [StackUnexpected]: : FSharp.Compiler.IlxGen::HashRangeSorted([S.P.CoreLib]System.Collections.Generic.IDictionary`2>)][offset 0x00000011][found ref '[FSharp.Compiler.Service]FSharp.Compiler.IlxGen+HashRangeSorted@1859-1'][expected ref '[FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2,int32>'] Unexpected type on the stack. -[IL]: Error [StackUnexpected]: : FSharp.Compiler.IlxGen::HashRangeSorted([S.P.CoreLib]System.Collections.Generic.IDictionary`2>)][offset 0x00000012][found ref '[FSharp.Compiler.Service]FSharp.Compiler.IlxGen+HashRangeSorted@1859'][expected ref '[FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2,T0>'] Unexpected type on the stack. +[IL]: Error [StackUnexpected]: : FSharp.Compiler.IlxGen::HashRangeSorted([S.P.CoreLib]System.Collections.Generic.IDictionary`2>)][offset 0x00000011][found ref '[FSharp.Compiler.Service]FSharp.Compiler.IlxGen+HashRangeSorted@1890-1'][expected ref '[FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2,int32>'] Unexpected type on the stack. +[IL]: Error [StackUnexpected]: : FSharp.Compiler.IlxGen::HashRangeSorted([S.P.CoreLib]System.Collections.Generic.IDictionary`2>)][offset 0x00000012][found ref '[FSharp.Compiler.Service]FSharp.Compiler.IlxGen+HashRangeSorted@1890'][expected ref '[FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2,T0>'] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : FSharp.Compiler.PatternMatchCompilation::isProblematicClause([FSharp.Compiler.Service]FSharp.Compiler.PatternMatchCompilation+MatchClause)][offset 0x00000040][found Byte] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : .$FSharp.Compiler.PatternMatchCompilation::.cctor()][offset 0x0000000B][found Boolean] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : FSharp.Compiler.NicePrint+TastDefinitionPrinting+meths@2092-3::Invoke([FSharp.Compiler.Service]FSharp.Compiler.Infos+MethInfo)][offset 0x000000B3][found Char] Unexpected type on the stack.