Skip to content

Commit 78029a5

Browse files
T-GroCopilot
andcommitted
Fix internal error when using custom attribute with optional value type argument (#8353)
GenAttribArg in IlxGen.fs handles Const.Zero (the default for [<Optional>] params without [<DefaultParameterValue>]) for System.Object, System.String, and System.Type, but not for primitive value types. This causes an internal error (FS0073) when applying an attribute with an [<Optional>] value type parameter and no default. Add Const.Zero -> default value mappings for all 12 primitive types valid in custom attributes: bool, sbyte, int16, int32, int64, byte, uint16, uint32, uint64, single, double, char. Fixes #8353 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 872352f commit 78029a5

4 files changed

Lines changed: 148 additions & 0 deletions

File tree

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
* Fix box instruction for literal upcasts. (Issue [#18319](https://github.com/dotnet/fsharp/issues/18319), [PR #19338](https://github.com/dotnet/fsharp/pull/19338))
2727
* Fix Decimal Literal causes InvalidProgramException in Debug builds. (Issue [#18956](https://github.com/dotnet/fsharp/issues/18956), [PR #19338](https://github.com/dotnet/fsharp/pull/19338))
2828
* Fix `AttributeUsage.AllowMultiple` not being inherited for attributes subclassed in C#. ([Issue #17107](https://github.com/dotnet/fsharp/issues/17107), [PR #19315](https://github.com/dotnet/fsharp/pull/19315))
29+
* Fix internal error when using custom attribute with `[<Optional>]` value type parameter and no `[<DefaultParameterValue>]`. ([Issue #8353](https://github.com/dotnet/fsharp/issues/8353), [PR #19484](https://github.com/dotnet/fsharp/pull/19484))
2930

3031
### Added
3132

src/Compiler/CodeGen/IlxGen.fs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10328,6 +10328,18 @@ and GenAttribArg amap g eenv x (ilArgTy: ILType) =
1032810328
| Const.Zero when isobj -> ILAttribElem.Null
1032910329
| Const.Zero when tynm = "System.String" -> ILAttribElem.String None
1033010330
| Const.Zero when tynm = "System.Type" -> ILAttribElem.Type None
10331+
| Const.Zero when tynm = "System.Boolean" -> ILAttribElem.Bool false
10332+
| Const.Zero when tynm = "System.SByte" -> ILAttribElem.SByte 0y
10333+
| Const.Zero when tynm = "System.Int16" -> ILAttribElem.Int16 0s
10334+
| Const.Zero when tynm = "System.Int32" -> ILAttribElem.Int32 0
10335+
| Const.Zero when tynm = "System.Int64" -> ILAttribElem.Int64 0L
10336+
| Const.Zero when tynm = "System.Byte" -> ILAttribElem.Byte 0uy
10337+
| Const.Zero when tynm = "System.UInt16" -> ILAttribElem.UInt16 0us
10338+
| Const.Zero when tynm = "System.UInt32" -> ILAttribElem.UInt32 0u
10339+
| Const.Zero when tynm = "System.UInt64" -> ILAttribElem.UInt64 0UL
10340+
| Const.Zero when tynm = "System.Single" -> ILAttribElem.Single 0.0f
10341+
| Const.Zero when tynm = "System.Double" -> ILAttribElem.Double 0.0
10342+
| Const.Zero when tynm = "System.Char" -> ILAttribElem.Char '\000'
1033110343
| Const.String i when isobj || tynm = "System.String" -> ILAttribElem.String(Some i)
1033210344
| _ -> error (InternalError("The type '" + tynm + "' may not be used as a custom attribute value", m))
1033310345

tests/FSharp.Compiler.ComponentTests/Conformance/BasicGrammarElements/CustomAttributes/Basic/Basic.fs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -303,6 +303,14 @@ module CustomAttributes_Basic =
303303
|> verifyCompileAndRun
304304
|> shouldSucceed
305305

306+
// SOURCE=OptionalAttributeArgs.fs # OptionalAttributeArgs.fs
307+
// Regression test for https://github.com/dotnet/fsharp/issues/8353
308+
[<Theory; Directory(__SOURCE_DIRECTORY__, Includes=[|"OptionalAttributeArgs.fs"|])>]
309+
let ``OptionalAttributeArgs_fs`` compilation =
310+
compilation
311+
|> verifyCompileAndRun
312+
|> shouldSucceed
313+
306314
// SOURCE=W_ReturnType03b.fs SCFLAGS="--test:ErrorRanges" # W_ReturnType03b.fs
307315
[<Theory; Directory(__SOURCE_DIRECTORY__, Includes=[|"W_ReturnType03b.fs"|])>]
308316
let ``W_ReturnType03b_fs`` compilation =
Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
// #Regression #Conformance #DeclarationElements #Attributes
2+
// Regression test for https://github.com/dotnet/fsharp/issues/8353
3+
// Verify that custom attributes with [<Optional>] parameters (no DefaultParameterValue) compile for all value types
4+
// and that the emitted default values are correct at runtime.
5+
6+
open System
7+
open System.Runtime.InteropServices
8+
9+
type BoolAttribute(name: string, flag: bool) =
10+
inherit Attribute()
11+
member _.Flag = flag
12+
new([<Optional>] flag: bool) = BoolAttribute("", flag)
13+
14+
type IntAttribute(name: string, value: int) =
15+
inherit Attribute()
16+
member _.Value = value
17+
new([<Optional>] value: int) = IntAttribute("", value)
18+
19+
type ByteAttribute(name: string, value: byte) =
20+
inherit Attribute()
21+
member _.Value = value
22+
new([<Optional>] value: byte) = ByteAttribute("", value)
23+
24+
type SByteAttribute(name: string, value: sbyte) =
25+
inherit Attribute()
26+
member _.Value = value
27+
new([<Optional>] value: sbyte) = SByteAttribute("", value)
28+
29+
type Int16Attribute(name: string, value: int16) =
30+
inherit Attribute()
31+
member _.Value = value
32+
new([<Optional>] value: int16) = Int16Attribute("", value)
33+
34+
type Int64Attribute(name: string, value: int64) =
35+
inherit Attribute()
36+
member _.Value = value
37+
new([<Optional>] value: int64) = Int64Attribute("", value)
38+
39+
type UInt16Attribute(name: string, value: uint16) =
40+
inherit Attribute()
41+
member _.Value = value
42+
new([<Optional>] value: uint16) = UInt16Attribute("", value)
43+
44+
type UInt32Attribute(name: string, value: uint32) =
45+
inherit Attribute()
46+
member _.Value = value
47+
new([<Optional>] value: uint32) = UInt32Attribute("", value)
48+
49+
type UInt64Attribute(name: string, value: uint64) =
50+
inherit Attribute()
51+
member _.Value = value
52+
new([<Optional>] value: uint64) = UInt64Attribute("", value)
53+
54+
type FloatAttribute(name: string, value: float) =
55+
inherit Attribute()
56+
member _.Value = value
57+
new([<Optional>] value: float) = FloatAttribute("", value)
58+
59+
type SingleAttribute(name: string, value: float32) =
60+
inherit Attribute()
61+
member _.Value = value
62+
new([<Optional>] value: float32) = SingleAttribute("", value)
63+
64+
type CharAttribute(name: string, value: char) =
65+
inherit Attribute()
66+
member _.Value = value
67+
new([<Optional>] value: char) = CharAttribute("", value)
68+
69+
[<Bool>]
70+
type T1() = class end
71+
72+
[<Int>]
73+
type T2() = class end
74+
75+
[<Byte>]
76+
type T3() = class end
77+
78+
[<Float>]
79+
type T4() = class end
80+
81+
[<Single>]
82+
type T5() = class end
83+
84+
[<Char>]
85+
type T6() = class end
86+
87+
[<SByte>]
88+
type T7() = class end
89+
90+
[<Int16>]
91+
type T8() = class end
92+
93+
[<Int64>]
94+
type T9() = class end
95+
96+
[<UInt16>]
97+
type T10() = class end
98+
99+
[<UInt32>]
100+
type T11() = class end
101+
102+
[<UInt64>]
103+
type T12() = class end
104+
105+
// Verify default values at runtime via reflection
106+
let inline getAttr<'a when 'a :> Attribute> (t: Type) = t.GetCustomAttributes(typeof<'a>, false).[0] :?> 'a
107+
108+
let check (name: string) (actual: 'a) (expected: 'a) =
109+
if actual <> expected then
110+
failwithf "%s: expected %A but got %A" name expected actual
111+
112+
[<EntryPoint>]
113+
let main _ =
114+
check "bool" (getAttr<BoolAttribute>(typeof<T1>)).Flag false
115+
check "int" (getAttr<IntAttribute>(typeof<T2>)).Value 0
116+
check "byte" (getAttr<ByteAttribute>(typeof<T3>)).Value 0uy
117+
check "float" (getAttr<FloatAttribute>(typeof<T4>)).Value 0.0
118+
check "single" (getAttr<SingleAttribute>(typeof<T5>)).Value 0.0f
119+
check "char" (getAttr<CharAttribute>(typeof<T6>)).Value '\000'
120+
check "sbyte" (getAttr<SByteAttribute>(typeof<T7>)).Value 0y
121+
check "int16" (getAttr<Int16Attribute>(typeof<T8>)).Value 0s
122+
check "int64" (getAttr<Int64Attribute>(typeof<T9>)).Value 0L
123+
check "uint16" (getAttr<UInt16Attribute>(typeof<T10>)).Value 0us
124+
check "uint32" (getAttr<UInt32Attribute>(typeof<T11>)).Value 0u
125+
check "uint64" (getAttr<UInt64Attribute>(typeof<T12>)).Value 0UL
126+
0
127+

0 commit comments

Comments
 (0)