Skip to content

Commit 2c9749a

Browse files
T-GroCopilot
andcommitted
Fix #12796: handle arrays of user-defined types in custom attributes
Empty arrays of non-encodable types (classes, records, structs) in custom attributes now compile successfully by encoding them as System.Object[] per ECMA-335. Non-empty arrays of such types produce FS3885 instead of the internal error FS0192. Changes: - IlxGen.fs: detect unencodable element types in array attribute args - il.fs: add 0x51 (SERIALIZATION_TYPE_TAGGED_OBJECT) decoder - FSComp.txt: new diagnostic FS3885 - Tests: 7 new AttributeCheckingTests (compile + ILVerify + run) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent c3c01c9 commit 2c9749a

18 files changed

Lines changed: 337 additions & 28 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
@@ -27,6 +27,7 @@
2727
* Fix box instruction for literal upcasts. (Issue [#18319](https://github.com/dotnet/fsharp/issues/18319), [PR #19338](https://github.com/dotnet/fsharp/pull/19338))
2828
* 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))
2929
* 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))
30+
* Fix internal error `FS0192: encodeCustomAttrElemType` when using arrays of user-defined types as custom attribute arguments. Empty arrays (e.g. `[<DefaultValue([||] : A[])>]`) now compile successfully; non-empty arrays of unencodable types report a proper diagnostic (FS3885) instead of an internal error. ([Issue #12796](https://github.com/dotnet/fsharp/issues/12796), [PR #19472](https://github.com/dotnet/fsharp/pull/19472))
3031

3132
### Added
3233

src/Compiler/AbstractIL/il.fs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4978,6 +4978,7 @@ let rec decodeCustomAttrElemType bytes sigptr x =
49784978
let elemTy, sigptr = decodeCustomAttrElemType bytes sigptr et
49794979
mkILArr1DTy elemTy, sigptr
49804980
| x when x = 0x50uy -> PrimaryAssemblyILGlobals.typ_Type, sigptr
4981+
| x when x = 0x51uy -> PrimaryAssemblyILGlobals.typ_Object, sigptr // SERIALIZATION_TYPE_TAGGED_OBJECT (ECMA-335 II.23.3)
49814982
| _ -> failwithf "decodeCustomAttrElemType ilg: unrecognized custom element type: %A" x
49824983

49834984
/// Given a custom attribute element, encode it to a binary representation according to the rules in Ecma 335 Partition II.

src/Compiler/CodeGen/IlxGen.fs

Lines changed: 35 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10294,7 +10294,7 @@ and EmitRestoreStack cgbuf (savedStack, savedStackLocals) =
1029410294
//GenAttr: custom attribute generation
1029510295
//-------------------------------------------------------------------------
1029610296

10297-
and GenAttribArg amap g eenv x (ilArgTy: ILType) =
10297+
and GenAttribArg amap (g: TcGlobals) eenv x (ilArgTy: ILType) =
1029810298

1029910299
match stripDebugPoints x, ilArgTy with
1030010300
// Detect 'null' used for an array argument
@@ -10334,7 +10334,40 @@ and GenAttribArg amap g eenv x (ilArgTy: ILType) =
1033410334
// Detect '[| ... |]' nodes
1033510335
| Expr.Op(TOp.Array, [ elemTy ], args, m), _ ->
1033610336
let ilElemTy = GenType amap m eenv.tyenv elemTy
10337-
ILAttribElem.Array(ilElemTy, List.map (fun arg -> GenAttribArg amap g eenv arg ilElemTy) args)
10337+
10338+
// Check if element type can be encoded in custom attribute metadata (ECMA-335 II.23.3).
10339+
// Valid element types: primitives, enums, string, System.Type, System.Object.
10340+
let isEncodableElemType =
10341+
match ilElemTy with
10342+
| ILType.Value tspec ->
10343+
match tspec.Name with
10344+
| "System.SByte"
10345+
| "System.Byte"
10346+
| "System.Int16"
10347+
| "System.UInt16"
10348+
| "System.Int32"
10349+
| "System.UInt32"
10350+
| "System.Int64"
10351+
| "System.UInt64"
10352+
| "System.Double"
10353+
| "System.Single"
10354+
| "System.Char"
10355+
| "System.Boolean" -> true
10356+
| _ -> isEnumTy g elemTy
10357+
| ILType.Boxed tspec ->
10358+
tspec.Name = "System.String"
10359+
|| tspec.Name = "System.Object"
10360+
|| tspec.Name = "System.Type"
10361+
| _ -> false
10362+
10363+
if not isEncodableElemType then
10364+
if args.IsEmpty then
10365+
// Empty arrays: substitute System.Object as element type since no elements need encoding.
10366+
ILAttribElem.Array(g.ilg.typ_Object, [])
10367+
else
10368+
error (Error(FSComp.SR.ilCustomAttrInvalidArrayElemType (ilElemTy.TypeRef.Name), m))
10369+
else
10370+
ILAttribElem.Array(ilElemTy, List.map (fun arg -> GenAttribArg amap g eenv arg ilElemTy) args)
1033810371

1033910372
// Detect 'typeof<ty>' calls
1034010373
| TypeOfExpr g ty, _ -> ILAttribElem.Type(Some(GenType amap x.Range eenv.tyenv ty))

src/Compiler/FSComp.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1814,3 +1814,4 @@ featurePreprocessorElif,"#elif preprocessor directive"
18141814
3882,lexHashElifMustBeFirst,"#elif directive must appear as the first non-whitespace character on a line"
18151815
3883,lexHashElifMustHaveIdent,"#elif directive should be immediately followed by an identifier"
18161816
3884,tcFunctionValueUsedAsInterpolatedStringArg,"This expression is a function value. When used in an interpolated string it will be formatted using its 'ToString' method, which is likely not the intended behavior. Consider applying the function to its arguments."
1817+
3885,ilCustomAttrInvalidArrayElemType,"The type '%s' is not a valid custom attribute argument type. Custom attribute arrays must have elements of primitive types, enums, string, System.Type, or System.Object."

src/Compiler/xlf/FSComp.txt.cs.xlf

Lines changed: 7 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/Compiler/xlf/FSComp.txt.de.xlf

Lines changed: 7 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/Compiler/xlf/FSComp.txt.es.xlf

Lines changed: 7 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/Compiler/xlf/FSComp.txt.fr.xlf

Lines changed: 7 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/Compiler/xlf/FSComp.txt.it.xlf

Lines changed: 7 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/Compiler/xlf/FSComp.txt.ja.xlf

Lines changed: 7 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)