Skip to content

Commit cc89f66

Browse files
authored
Merge branch 'main' into allow-and-bang-typed-bindings
2 parents 63ba102 + 4afdc9f commit cc89f66

File tree

4 files changed

+79
-19
lines changed

4 files changed

+79
-19
lines changed

.github/workflows/commands.yml

Lines changed: 22 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,11 @@ on:
55
types: [created]
66

77
jobs:
8-
# This first job by definiton runs user-supplied code - you must NOT elevate its permissions to `write`
9-
# Malicious code could change nuget source URL, build targets or even compiler itself to pass a GH token
10-
# And use it to create branches, spam issues etc. Any write-actions happen in the second job, which does not allow
11-
# user extension points (i.e. plain scripts, must NOT run scripts from within checked-out code)
12-
detect-and-run:
8+
parsing_job:
139
runs-on: ubuntu-latest
10+
permissions:
11+
issues: write # Allow adding a reaction via the comment-pipeline
12+
pull-requests: write
1413
outputs:
1514
command: ${{ steps.parse.outputs.command }}
1615
arg: ${{ steps.parse.outputs.arguments }}
@@ -28,11 +27,20 @@ jobs:
2827
/run test-baseline
2928
github-token: ${{ secrets.GITHUB_TOKEN }}
3029

30+
# This second job by definiton runs user-supplied code - you must NOT elevate its permissions to `write`
31+
# Malicious code could change nuget source URL, build targets or even compiler itself to pass a GH token
32+
# And use it to create branches, spam issues etc. Any write-actions happen in the second job, which does not allow
33+
# user extension points (i.e. plain scripts, must NOT run scripts from within checked-out code)
34+
run-parsed-command:
35+
needs: parsing_job
36+
runs-on: ubuntu-latest
37+
if: needs.parsing_job.outputs.command != ''
38+
steps:
39+
3140
- name: Checkout the repository
3241
uses: actions/checkout@v4
3342

3443
- name: Checkout PR branch
35-
if: ${{ steps.parse.outputs.command }}
3644
run: gh auth setup-git && gh pr checkout ${{ github.event.issue.number }}
3745
env:
3846
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
@@ -46,7 +54,7 @@ jobs:
4654
run: dotnet tool restore
4755

4856
- name: Setup .NET 9.0.0 Runtime for test execution
49-
if: ${{ steps.parse.outputs.command == '/run test-baseline' }}
57+
if: ${{ needs.parsing_job.outputs.command == '/run test-baseline' }}
5058
uses: actions/setup-dotnet@v4
5159
with:
5260
dotnet-version: '9.0.x'
@@ -57,17 +65,17 @@ jobs:
5765
TEST_UPDATE_BSL: 1
5866
continue-on-error: true
5967
run: |
60-
case "${{ steps.parse.outputs.command }}" in
68+
case "${{ needs.parsing_job.outputs.command }}" in
6169
"/run fantomas") dotnet fantomas . ;;
6270
"/run xlf") dotnet build src/Compiler /t:UpdateXlf ;;
6371
"/run ilverify") pwsh tests/ILVerify/ilverify.ps1 ;;
64-
"/run test-baseline") dotnet test ./FSharp.Compiler.Service.sln --filter "${{ steps.parse.outputs.arguments }}" -c Release || true ;;
72+
"/run test-baseline") dotnet test ./FSharp.Compiler.Service.sln --filter "${{ needs.parsing_job.outputs.arg }}" -c Release || true ;;
6573
*) echo "Unknown command" && exit 1 ;;
6674
esac
6775
6876
- name: Create patch & metadata
6977
id: meta
70-
if: steps.parse.outputs.command
78+
if: needs.parsing_job.outputs.command
7179
run: |
7280
echo "run_step_outcome=${{ steps.run-cmd.outcome }}" > result
7381
if [[ "${{ steps.run-cmd.outcome }}" == "success" ]]; then
@@ -87,12 +95,12 @@ jobs:
8795
result
8896
8997
apply-and-report:
90-
needs: detect-and-run
98+
needs: [parsing_job, run-parsed-command]
9199
runs-on: ubuntu-latest
92100
permissions:
93101
contents: write
94102
pull-requests: write
95-
if: needs.detect-and-run.outputs.command != ''
103+
if: needs.parsing_job.outputs.command != '' && needs.run-parsed-command.result == 'success'
96104
steps:
97105
- name: Checkout the repository
98106
uses: actions/checkout@v4
@@ -121,7 +129,7 @@ jobs:
121129
git config user.name "GH Actions"
122130
git config user.email "actions@github.com"
123131
git add -u
124-
git commit -m "Apply patch from ${{ needs.detect-and-run.outputs.command }}"
132+
git commit -m "Apply patch from ${{ needs.parsing_job.outputs.command }}"
125133
upstream=$(git rev-parse --abbrev-ref --symbolic-full-name @{u})
126134
remote=${upstream%%/*}
127135
branch=${upstream#*/}
@@ -140,7 +148,7 @@ jobs:
140148
- name: Generate and publish report
141149
if: always()
142150
env:
143-
COMMAND: ${{ needs.detect-and-run.outputs.command }}
151+
COMMAND: ${{ needs.parsing_job.outputs.command }}
144152
OUTCOME: ${{ steps.read-meta.outputs.run_step_outcome }}
145153
PATCH: ${{ steps.read-meta.outputs.hasPatch }}
146154
run: |

src/Compiler/Facilities/LanguageFeatures.fs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -236,10 +236,10 @@ type LanguageVersion(versionText) =
236236
LanguageFeature.BetterAnonymousRecordParsing, languageVersion100
237237
LanguageFeature.ScopedNowarn, languageVersion100
238238
LanguageFeature.AllowTypedLetUseAndBang, languageVersion100
239+
LanguageFeature.UnmanagedConstraintCsharpInterop, languageVersion100
239240
LanguageFeature.AllowAccessModifiersToAutoPropertiesGettersAndSetters, languageVersion100
240241

241242
// F# preview (still preview in 10.0)
242-
LanguageFeature.UnmanagedConstraintCsharpInterop, previewVersion // not enabled because: https://github.com/dotnet/fsharp/issues/17509
243243
LanguageFeature.FromEndSlicing, previewVersion // Unfinished features --- needs work
244244
]
245245

src/Compiler/TypedTree/TypedTreeOps.fs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9970,6 +9970,7 @@ let isCompiledOrWitnessPassingConstraint (g: TcGlobals) cx =
99709970
| TyparConstraint.IsNonNullableStruct _
99719971
| TyparConstraint.IsReferenceType _
99729972
| TyparConstraint.RequiresDefaultConstructor _
9973+
| TyparConstraint.IsUnmanaged _ // implies "struct" and also causes a modreq
99739974
| TyparConstraint.CoercesTo _ -> true
99749975
| TyparConstraint.MayResolveMember _ when g.langVersion.SupportsFeature LanguageFeature.WitnessPassing -> true
99759976
| _ -> false

tests/FSharp.Compiler.ComponentTests/Conformance/Constraints/Unmanaged.fs

Lines changed: 55 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -471,21 +471,72 @@ printf "%s" (CsharpStruct<int>.Hi<MultiCaseUnion>())
471471
IL_0000: ret
472472
} """]
473473

474-
[<Fact>]
475-
let ``FSharp does not generate modreq for VBNET to consume in v7`` () =
474+
[<FactForNETCOREAPP>]
475+
let ``FSharp generates modreq for CSharp to consume in v9`` () =
476476
Fsx "let testMyFunction (x: 'TUnmanaged when 'TUnmanaged : unmanaged) = ()"
477-
|> withLangVersion70
477+
|> withLangVersion10
478478
|> compile
479479
|> shouldSucceed
480480
|> verifyIL ["""
481-
.method public static void testMyFunction<TUnmanaged>(!!TUnmanaged x) cil managed
481+
.method public static void testMyFunction<valuetype (class [runtime]System.ValueType modreq([runtime]System.Runtime.InteropServices.UnmanagedType)) TUnmanaged>(!!TUnmanaged x) cil managed
482482
{
483+
.param type TUnmanaged
484+
.custom instance void [runtime]System.Runtime.CompilerServices.IsUnmanagedAttribute::.ctor() = ( 01 00 00 00 )
483485
484486
.maxstack 8
485487
IL_0000: ret
486488
} """]
487489

488490

491+
[<Fact>]
492+
let ``Unmanaged constraint in lambda reproduces issue 17509`` () =
493+
// This test reproduces the issue https://github.com/dotnet/fsharp/issues/17509
494+
// When UnmanagedConstraintCsharpInterop is enabled, it generates invalid IL
495+
// causing a TypeLoadException at runtime
496+
Fsx """
497+
open System
498+
499+
let printTypeConstraintsNative<'T when 'T : unmanaged> () = printf $"Hello: {typeof<'T>.FullName} is unmanaged"
500+
501+
let Main() =
502+
let func (x:int) : 'T when 'T : unmanaged = Unchecked.defaultof<'T>
503+
let initFinite = Seq.init<nativeint> 3 func
504+
printf "%A" initFinite
505+
506+
printTypeConstraintsNative<nativeint>()
507+
Main()
508+
"""
509+
|> withLangVersionPreview
510+
|> asExe
511+
|> compileAndRun
512+
|> shouldSucceed
513+
|> verifyOutput "Hello: System.IntPtr is unmanagedseq [0n; 0n; 0n]"
514+
515+
[<FactForNETCOREAPP>]
516+
let ``Unmanaged constraint in lambda generates invalid IL for Specialize method with preview version`` () =
517+
Fsx """
518+
let Main() =
519+
let func (x:int) : 'T when 'T : unmanaged = Unchecked.defaultof<'T>
520+
let initFinite = Seq.init<nativeint> 3 func
521+
printfn "%A" initFinite
522+
Main()
523+
"""
524+
|> withLangVersionPreview
525+
|> asExe
526+
|> compile
527+
|> shouldSucceed
528+
|> verifyIL ["""
529+
.method assembly strict virtual instance class [FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2<int32,!!T> DirectInvoke<valuetype (class [runtime]System.ValueType modreq([runtime]System.Runtime.InteropServices.UnmanagedType)) T>() cil managed
530+
{
531+
.param type T
532+
.custom instance void [runtime]System.Runtime.CompilerServices.IsUnmanagedAttribute::.ctor() = ( 01 00 00 00 )
533+
534+
.maxstack 8
535+
IL_0000: ldsfld class Test/'func@3-1'<!0> class Test/'func@3-1'<!!T>::@_instance
536+
IL_0005: ret
537+
} """]
538+
539+
489540
[<Fact>]
490541
let ``C# can consume F#-defined struct with unmanaged constraint - valid`` () =
491542
let fsharpLib =

0 commit comments

Comments
 (0)