-
Notifications
You must be signed in to change notification settings - Fork 830
Auto-generated getters on structs get readonly attribute #13413
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
14 commits
Select commit
Hold shift + click to select a range
05d7dfe
Getters on struct records get readonly attribute
0101 859d150
Fantomas
0101 41035ab
Explicit argument
0101 a19aa4a
Tests update
0101 6415f14
Getters on struct records get readonly attribute
0101 259c8aa
Fantomas
0101 91d29a6
Explicit argument
0101 738184c
Tests update
0101 8aff1cb
Merge branch 'struct-getters-readonly' of github.com:0101/fsharp into…
0101 bf6063a
Tests update
0101 f6e8644
Small optimization
0101 e8eca37
Reorganized tests
0101 9572852
Revert list construction
0101 b71a62c
Unified way of determining a struct
0101 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
130 changes: 130 additions & 0 deletions
130
tests/FSharp.Compiler.ComponentTests/EmittedIL/StructGettersReadOnly.fs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,130 @@ | ||
| namespace FSharp.Compiler.ComponentTests.EmittedIL | ||
|
|
||
| open Microsoft.FSharp.Core | ||
| open Xunit | ||
| open FSharp.Test.Compiler | ||
| open FSharp.Test.ReflectionHelper | ||
|
|
||
| module ``Struct getters readonly`` = | ||
|
|
||
| let structRecord = | ||
| FSharp | ||
| """ | ||
| module Test | ||
|
|
||
| [<Struct>] type MyRecord = { MyField : int } | ||
| """ | ||
|
|
||
| [<Fact>] | ||
| let ``Struct record has readonly attribute on getter`` () = | ||
| structRecord | ||
| |> compileAssembly | ||
| |> getType "Test+MyRecord" | ||
| |> getMethod "get_MyField" | ||
| |> should haveAttribute "IsReadOnlyAttribute" | ||
|
|
||
| [<Fact>] | ||
| let ``Struct record has readonly attribute on getter in IL`` () = | ||
| structRecord | ||
| |> compile | ||
| |> shouldSucceed | ||
| |> verifyIL [ """ | ||
| .method public hidebysig specialname | ||
| instance int32 get_MyField() cil managed | ||
| { | ||
| .custom instance void [runtime]System.Runtime.CompilerServices.IsReadOnlyAttribute::.ctor() = ( 01 00 00 00 ) | ||
|
|
||
| .maxstack 8 | ||
| IL_0000: ldarg.0 | ||
| IL_0001: ldfld int32 Test/MyRecord::MyField@ | ||
| IL_0006: ret | ||
| }""" ] | ||
|
|
||
| let nonStructRecord = | ||
| FSharp | ||
| """ | ||
| module Test | ||
|
|
||
| type MyRecord = { MyField : int } | ||
| """ | ||
|
|
||
| [<Fact>] | ||
| let ``Non-struct record doesn't have readonly getters`` () = | ||
| nonStructRecord | ||
| |> compileAssembly | ||
| |> getType "Test+MyRecord" | ||
| |> getMethod "get_MyField" | ||
| |> shouldn't haveAttribute "IsReadOnlyAttribute" | ||
|
|
||
| [<Fact>] | ||
| let ``Non-struct record doesn't have readonly getters in IL`` () = | ||
| nonStructRecord | ||
| |> compile | ||
| |> shouldSucceed | ||
| |> verifyIL [ """ | ||
| .method public hidebysig specialname | ||
| instance int32 get_MyField() cil managed | ||
| { | ||
|
|
||
| .maxstack 8 | ||
| IL_0000: ldarg.0 | ||
| IL_0001: ldfld int32 Test/MyRecord::MyField@ | ||
| IL_0006: ret | ||
| } """ ] | ||
|
|
||
| [<Fact>] | ||
| let ``Struct anonymous record has readonly attribute on getter`` () = | ||
| FSharp | ||
| """ | ||
| module Test | ||
|
|
||
| let myRecord = struct {| MyField = 3 |} | ||
| """ | ||
| |> compileAssembly | ||
| |> getFirstAnonymousType | ||
| |> getMethod "get_MyField" | ||
| |> should haveAttribute "IsReadOnlyAttribute" | ||
|
|
||
| [<Fact>] | ||
| let ``Non-struct anonymous record doesn't have readonly attribute on getter`` () = | ||
| FSharp | ||
| """ | ||
| module Test | ||
|
|
||
| let myRecord = {| MyField = 3 |} | ||
| """ | ||
| |> compileAssembly | ||
| |> getFirstAnonymousType | ||
| |> getMethod "get_MyField" | ||
| |> shouldn't haveAttribute "IsReadOnlyAttribute" | ||
|
|
||
| [<Fact>] | ||
| let ``Struct has readonly getters`` () = | ||
| FSharp | ||
| """ | ||
| module Test | ||
|
|
||
| [<Struct>] | ||
| type MyStruct = | ||
| val MyField: int | ||
| """ | ||
| |> compileAssembly | ||
| |> getType "Test+MyStruct" | ||
| |> getMethod "get_MyField" | ||
| |> should haveAttribute "IsReadOnlyAttribute" | ||
|
|
||
| [<Fact>] | ||
| let ``Custom getter on a struct doesn't have readonly attribute`` () = | ||
| FSharp | ||
| """ | ||
| module Test | ||
|
|
||
| [<Struct>] | ||
| type MyStruct = | ||
| val mutable x: int | ||
| member this.MyField with get () = this.x <- 4 | ||
| """ | ||
| |> compileAssembly | ||
| |> getType "Test+MyStruct" | ||
| |> getMethod "get_MyField" | ||
| |> shouldn't haveAttribute "IsReadOnlyAttribute" |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,63 @@ | ||
| module FSharp.Test.ReflectionHelper | ||
|
|
||
| open System | ||
| open System.Reflection | ||
|
|
||
| /// Gets the given type from the assembly (otherwise throws) | ||
| let getType typeName (asm: Assembly) = | ||
| match asm.GetType(typeName, false) with | ||
| | null -> | ||
| let allTypes = | ||
| asm.GetTypes() | ||
| |> Array.map (fun ty -> ty.Name) | ||
| |> Array.reduce (fun x y -> $"%s{x}\r%s{y}") | ||
|
|
||
| failwith $"Error: Assembly did not contain type %s{typeName}.\nAll types in asm:\n%s{allTypes}" | ||
| | ty -> ty | ||
|
|
||
| /// Gets all anonymous types from the assembly | ||
| let getAnonymousTypes (asm: Assembly) = | ||
| [ for ty in asm.GetTypes() do | ||
| if ty.FullName.StartsWith "<>f__AnonymousType" then ty ] | ||
|
|
||
| /// Gets the first anonymous type from the assembly | ||
| let getFirstAnonymousType asm = | ||
| match getAnonymousTypes asm with | ||
| | ty :: _ -> ty | ||
| | [] -> failwith "Error: No anonymous types found in the assembly" | ||
|
|
||
| /// Gets a type's method | ||
| let getMethod methodName (ty: Type) = | ||
| match ty.GetMethod(methodName) with | ||
| | null -> failwith $"Error: Type did not contain member %s{methodName}" | ||
| | methodInfo -> methodInfo | ||
|
|
||
| /// Assert that function f returns Ok for given input | ||
| let should f x y = | ||
| match f x y with | ||
| | Ok _ -> () | ||
| | Error message -> failwith $"%s{message} but it should" | ||
psfinaki marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| /// Assert that function f doesn't return Ok for given input | ||
| let shouldn't f x y = | ||
| match f x y with | ||
| | Ok message -> failwith $"%s{message} but it shouldn't" | ||
| | Error _ -> () | ||
|
|
||
| /// Verify the object contains a custom attribute with the given name. E.g. "ObsoleteAttribute" | ||
| let haveAttribute attrName thingy = | ||
| let attrs = | ||
| match box thingy with | ||
| | :? Type as ty -> ty.GetCustomAttributes(false) | ||
| | :? MethodInfo as mi -> mi.GetCustomAttributes(false) | ||
| | :? PropertyInfo as pi -> pi.GetCustomAttributes(false) | ||
| | :? EventInfo as ei -> ei.GetCustomAttributes(false) | ||
| | _ -> failwith "Error: Unsupported primitive type, unable to get custom attributes." | ||
|
|
||
| let hasAttribute = | ||
| attrs |> Array.exists (fun att -> att.GetType().Name = attrName) | ||
|
|
||
| if hasAttribute then | ||
| Ok $"'{thingy}' has attribute '{attrName}'" | ||
| else | ||
| Error $"'{thingy}' doesn't have attribute '{attrName}'" | ||
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.