Skip to content

Commit cfb0589

Browse files
committed
Respect the return qualifier for attributes on class methods
1 parent 37d78e9 commit cfb0589

File tree

7 files changed

+149
-39
lines changed

7 files changed

+149
-39
lines changed

FSharp.sln

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,10 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".FSharp.Compiler.Service",
146146
docs\release-notes\.FSharp.Compiler.Service\8.0.400.md = docs\release-notes\.FSharp.Compiler.Service\8.0.400.md
147147
docs\release-notes\.FSharp.Compiler.Service\9.0.100.md = docs\release-notes\.FSharp.Compiler.Service\9.0.100.md
148148
docs\release-notes\.FSharp.Compiler.Service\9.0.200.md = docs\release-notes\.FSharp.Compiler.Service\9.0.200.md
149+
docs\release-notes\.FSharp.Compiler.Service\9.0.202.md = docs\release-notes\.FSharp.Compiler.Service\9.0.202.md
150+
docs\release-notes\.FSharp.Compiler.Service\9.0.300.md = docs\release-notes\.FSharp.Compiler.Service\9.0.300.md
151+
docs\release-notes\.FSharp.Compiler.Service\10.0.100.md = docs\release-notes\.FSharp.Compiler.Service\10.0.100.md
152+
docs\release-notes\.FSharp.Compiler.Service\11.0.0.md = docs\release-notes\.FSharp.Compiler.Service\11.0.0.md
149153
EndProjectSection
150154
EndProject
151155
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".FSharp.Core", ".FSharp.Core", "{23798638-A1E9-4DAE-9C9C-F5D87499ADD6}"

docs/release-notes/.FSharp.Compiler.Service/11.0.0.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,12 @@
66
* Fix name is bound multiple times is not reported in 'as' pattern ([PR #18984](https://github.com/dotnet/fsharp/pull/18984))
77
* Fix: warn FS0049 on upper union case label. ([PR #19003](https://github.com/dotnet/fsharp/pull/19003))
88
* Type relations cache: handle potentially "infinite" types ([PR #19010](https://github.com/dotnet/fsharp/pull/19010))
9+
* Respect the return qualifier for attributes on class methods ([PR #19025](https://github.com/dotnet/fsharp/pull/19025))
910

1011
### Added
1112

1213
### Changed
1314

1415
* Parallel compilation stabilised and enabled by default ([PR #18998](https://github.com/dotnet/fsharp/pull/18998))
1516

16-
### Breaking Changes
17+
### Breaking Changes

src/Compiler/Checking/Expressions/CheckExpressions.fs

Lines changed: 44 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -10982,47 +10982,15 @@ and TcNormalizedBinding declKind (cenv: cenv) env tpenv overallTy safeThisValOpt
1098210982
false, SynExpr.ObjExpr(targetType, Some(expr, None), None, [], [], [], m, rhsExpr.Range), overallTy, overallTy
1098310983
| e -> false, e, overallTy, overallTy
1098410984

10985-
// Check the attributes of the binding, parameters or return value
10986-
let TcAttrs tgt isRet attrs =
10987-
// For all but attributes positioned at the return value, disallow implicitly
10988-
// targeting the return value.
10989-
let tgtEx = if isRet then enum 0 else AttributeTargets.ReturnValue
10990-
let attrs, _ = TcAttributesMaybeFailEx TcCanFail.ReportAllErrors cenv envinner tgt tgtEx attrs
10991-
let attrs: Attrib list = attrs
10992-
if attrTgt = enum 0 && not (isNil attrs) then
10993-
for attr in attrs do
10994-
errorR(Error(FSComp.SR.tcAttributesAreNotPermittedOnLetBindings(), attr.Range))
10995-
attrs
10996-
1099710985
// Rotate [<return:...>] from binding to return value
1099810986
// Also patch the syntactic representation
10999-
let retAttribs, valAttribs, valSynData =
11000-
let attribs = TcAttrs attrTgt false attrs
11001-
let rotRetSynAttrs, rotRetAttribs, valAttribs =
11002-
// Do not rotate if some attrs fail to typecheck...
11003-
if attribs.Length <> attrs.Length then [], [], attribs
11004-
else attribs
11005-
|> List.zip attrs
11006-
|> List.partition(function | _, Attrib(_, _, _, _, _, Some ts, _) -> ts &&& AttributeTargets.ReturnValue <> enum 0 | _ -> false)
11007-
|> fun (r, v) -> (List.map fst r, List.map snd r, List.map snd v)
11008-
let retAttribs =
11009-
match rtyOpt with
11010-
| Some (SynBindingReturnInfo(attributes = Attributes retAttrs)) ->
11011-
rotRetAttribs @ TcAttrs AttributeTargets.ReturnValue true retAttrs
11012-
| None -> rotRetAttribs
11013-
let valSynData =
11014-
match rotRetSynAttrs with
11015-
| [] -> valSynData
11016-
| {Range=mHead} :: _ ->
11017-
let (SynValData(valMf, SynValInfo(args, SynArgInfo(attrs, opt, retId)), valId)) = valSynData
11018-
SynValData(valMf, SynValInfo(args, SynArgInfo({Attributes=rotRetSynAttrs; Range=mHead} :: attrs, opt, retId)), valId)
11019-
retAttribs, valAttribs, valSynData
10987+
let retAttribs, valAttribs, valSynData = TcNormalizeReturnAttribs cenv envinner attrTgt attrs valSynData rtyOpt
1102010988

1102110989
let isVolatile = HasFSharpAttribute g g.attrib_VolatileFieldAttribute valAttribs
1102210990
let inlineFlag = ComputeInlineFlag memberFlagsOpt isInline isMutable g valAttribs mBinding
1102310991

1102410992
let argAttribs =
11025-
spatsL |> List.map (SynInfo.InferSynArgInfoFromSimplePats >> List.map (SynInfo.AttribsOfArgData >> TcAttrs AttributeTargets.Parameter false))
10993+
spatsL |> List.map (SynInfo.InferSynArgInfoFromSimplePats >> List.map (SynInfo.AttribsOfArgData >> TcAttrs cenv envinner attrTgt AttributeTargets.Parameter false))
1102610994

1102710995
// Assert the return type of an active pattern. A [<return:Struct>] attribute may be used on a partial active pattern.
1102810996
let isStructRetTy = HasFSharpAttribute g g.attrib_StructAttribute retAttribs
@@ -11220,6 +11188,30 @@ and TcNormalizedBinding declKind (cenv: cenv) env tpenv overallTy safeThisValOpt
1122011188

1122111189
CheckedBindingInfo(inlineFlag, valAttribs, xmlDoc, tcPatPhase2, explicitTyparInfo, nameToPrelimValSchemeMap, rhsExprChecked, argAndRetAttribs, overallPatTy, mBinding, debugPoint, isCompGen, literalValue, isFixed), tpenv
1122211190

11191+
// Rotate [<return:...>] from binding to return value
11192+
// Also patch the syntactic representation
11193+
and TcNormalizeReturnAttribs cenv env attrTgt attrs valSynData rtyOpt =
11194+
let attribs = TcAttrs cenv env attrTgt attrTgt false attrs
11195+
let rotRetSynAttrs, rotRetAttribs, valAttribs =
11196+
// Do not rotate if some attrs fail to typecheck...
11197+
if List.length attribs <> attrs.Length then [], [], attribs
11198+
else attribs
11199+
|> List.zip attrs
11200+
|> List.partition(function | _, Attrib(_, _, _, _, _, Some ts, _) -> ts &&& AttributeTargets.ReturnValue <> enum 0 | _ -> false)
11201+
|> fun (r, v) -> (List.map fst r, List.map snd r, List.map snd v)
11202+
let retAttribs =
11203+
match rtyOpt with
11204+
| Some (SynBindingReturnInfo(attributes = Attributes retAttrs)) ->
11205+
rotRetAttribs @ TcAttrs cenv env attrTgt AttributeTargets.ReturnValue true retAttrs
11206+
| None -> rotRetAttribs
11207+
let valSynData =
11208+
match rotRetSynAttrs with
11209+
| [] -> valSynData
11210+
| {Range=mHead} :: _ ->
11211+
let (SynValData(valMf, SynValInfo(args, SynArgInfo(attrs, opt, retId)), valId)) = valSynData
11212+
SynValData(valMf, SynValInfo(args, SynArgInfo({Attributes=rotRetSynAttrs; Range=mHead} :: attrs, opt, retId)), valId)
11213+
retAttribs, valAttribs, valSynData
11214+
1122311215
// Note:
1122411216
// - Let bound values can only have attributes that uses AttributeTargets.Field ||| AttributeTargets.Property ||| AttributeTargets.ReturnValue
1122511217
// - Let function bindings can only have attributes that uses AttributeTargets.Method ||| AttributeTargets.ReturnValue
@@ -11547,6 +11539,17 @@ and TcAttributesCanFail cenv env attrTgt synAttribs =
1154711539
and TcAttributes cenv env attrTgt synAttribs =
1154811540
TcAttributesMaybeFail TcCanFail.ReportAllErrors cenv env attrTgt synAttribs |> fst
1154911541

11542+
// Check the attributes of the binding, parameters or return value
11543+
and TcAttrs cenv env attrTgt tgt isRet attrs =
11544+
// For all but attributes positioned at the return value, disallow implicitly
11545+
// targeting the return value.
11546+
let tgtEx = if isRet then enum 0 else AttributeTargets.ReturnValue
11547+
let attrs, _ = TcAttributesMaybeFailEx TcCanFail.ReportAllErrors cenv env tgt tgtEx attrs
11548+
if attrTgt = enum 0 && not (isNil attrs) then
11549+
for attr in attrs do
11550+
errorR(Error(FSComp.SR.tcAttributesAreNotPermittedOnLetBindings(), attr.Range))
11551+
attrs
11552+
1155011553
//-------------------------------------------------------------------------
1155111554
// TcLetBinding
1155211555
//------------------------------------------------------------------------
@@ -12246,14 +12249,18 @@ and AnalyzeAndMakeAndPublishRecursiveValue
1224612249

1224712250
// Pull apart the inputs
1224812251
let (NormalizedBinding(vis1, bindingKind, isInline, isMutable, bindingSynAttribs, bindingXmlDoc, synTyparDecls, valSynData, declPattern, bindingRhs, mBinding, debugPoint)) = binding
12249-
let (NormalizedBindingRhs(_, _, bindingExpr)) = bindingRhs
12250-
let (SynValData(memberFlagsOpt, valSynInfo, thisIdOpt)) = valSynData
12252+
let (NormalizedBindingRhs(_, rtyOpt, bindingExpr)) = bindingRhs
12253+
let (SynValData(memberFlagsOpt, _, thisIdOpt)) = valSynData
1225112254
let (ContainerInfo(altActualParent, tcrefContainerInfo)) = containerInfo
1225212255

1225312256
let attrTgt = declKind.AllowedAttribTargets memberFlagsOpt
1225412257

1225512258
// Check the attributes on the declaration
12256-
let bindingAttribs = TcAttributes cenv env attrTgt bindingSynAttribs
12259+
let retAttribs, bindingAttribs, (SynValData(_, valSynInfo, _) as valSynData) = TcNormalizeReturnAttribs cenv env attrTgt bindingSynAttribs valSynData rtyOpt
12260+
12261+
// Add the return attributes back onto the binding attributes so that ActivePatternElemsOfValRef will see
12262+
// `[<return: Struct>]` as ValRef does not contain return attributes.
12263+
let bindingAttribs = retAttribs @ bindingAttribs
1225712264

1225812265
// Allocate the type inference variable for the inferred type
1225912266
let ty = NewInferenceType g

src/Compiler/CodeGen/IlxGen.fs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9360,6 +9360,12 @@ and GenMethodForBinding
93609360
g.DebuggerNonUserCodeAttribute
93619361
]
93629362

9363+
// Remove attributes that are applied to the return. These attributes need to be passed through ValRef.Attribs so that
9364+
// ActivePatternElemsOfValRef will correctly propagate `[<return: Struct>]` on active patterns.
9365+
let attrs =
9366+
attrs
9367+
|> List.filter (function Attrib(targetsOpt = Some flags) -> not (flags.HasFlag(AttributeTargets.ReturnValue)) | _ -> true)
9368+
93639369
let ilAttrsThatGoOnPrimaryItem =
93649370
[
93659371
yield! GenAttrs cenv eenv attrs

tests/FSharp.Compiler.ComponentTests/EmittedIL/Misc/Misc.fs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
namespace EmittedIL.RealInternalSignature
22

33
open Xunit
4-
open System.IO
54
open FSharp.Test
65
open FSharp.Test.Compiler
76

@@ -218,3 +217,10 @@ module Misc =
218217
|> getCompilation
219218
|> asExe
220219
|> verifyCompilation
220+
221+
[<Theory; FileInlineData("ReturnAttributeOnClassMethod.fs")>]
222+
let ``ReturnAttributeOnClassMethod_fs`` compilation =
223+
compilation
224+
|> getCompilation
225+
|> asExe
226+
|> verifyCompilation
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
open System.Diagnostics.CodeAnalysis
2+
3+
type Class() =
4+
[<return: NotNull>]
5+
static member ClassMethod () = obj()
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
2+
3+
4+
5+
6+
.assembly extern runtime { }
7+
.assembly extern FSharp.Core { }
8+
.assembly assembly
9+
{
10+
.custom instance void [FSharp.Core]Microsoft.FSharp.Core.FSharpInterfaceDataVersionAttribute::.ctor(int32,
11+
int32,
12+
int32) = ( 01 00 02 00 00 00 00 00 00 00 00 00 00 00 00 00 )
13+
14+
15+
16+
17+
.hash algorithm 0x00008004
18+
.ver 0:0:0:0
19+
}
20+
.module assembly.exe
21+
22+
.imagebase {value}
23+
.file alignment 0x00000200
24+
.stackreserve 0x00100000
25+
.subsystem 0x0003
26+
.corflags 0x00000001
27+
28+
29+
30+
31+
32+
.class public abstract auto ansi sealed assembly
33+
extends [runtime]System.Object
34+
{
35+
.custom instance void [FSharp.Core]Microsoft.FSharp.Core.CompilationMappingAttribute::.ctor(valuetype [FSharp.Core]Microsoft.FSharp.Core.SourceConstructFlags) = ( 01 00 07 00 00 00 00 00 )
36+
.class auto ansi serializable nested public Class
37+
extends [runtime]System.Object
38+
{
39+
.custom instance void [FSharp.Core]Microsoft.FSharp.Core.CompilationMappingAttribute::.ctor(valuetype [FSharp.Core]Microsoft.FSharp.Core.SourceConstructFlags) = ( 01 00 03 00 00 00 00 00 )
40+
.method public specialname rtspecialname instance void .ctor() cil managed
41+
{
42+
43+
.maxstack 8
44+
IL_0000: ldarg.0
45+
IL_0001: callvirt instance void [runtime]System.Object::.ctor()
46+
IL_0006: ldarg.0
47+
IL_0007: pop
48+
IL_0008: ret
49+
}
50+
51+
.method public static object ClassMethod() cil managed
52+
{
53+
.param [0]
54+
.custom instance void [runtime]System.Diagnostics.CodeAnalysis.NotNullAttribute::.ctor() = ( 01 00 00 00 )
55+
56+
.maxstack 8
57+
IL_0000: newobj instance void [runtime]System.Object::.ctor()
58+
IL_0005: ret
59+
}
60+
61+
}
62+
63+
}
64+
65+
.class private abstract auto ansi sealed '<StartupCode$assembly>'.$assembly
66+
extends [runtime]System.Object
67+
{
68+
.method public static void main@() cil managed
69+
{
70+
.entrypoint
71+
72+
.maxstack 8
73+
IL_0000: ret
74+
}
75+
76+
}
77+
78+
79+
80+
81+

0 commit comments

Comments
 (0)