Skip to content

Commit 2feeea6

Browse files
authored
Fix #12796: handle arrays of user-defined types in custom attributes (#19472)
* 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 FS3887 instead of the internal error FS0192.
1 parent 87c5685 commit 2feeea6

18 files changed

Lines changed: 479 additions & 2 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
@@ -39,6 +39,7 @@
3939
* Fix methods being tagged as `Member` instead of `Method` in tooltips. ([Issue #10540](https://github.com/dotnet/fsharp/issues/10540), [PR #19507](https://github.com/dotnet/fsharp/pull/19507))
4040
* Fix Debug-mode compilation when mixing resumable and standard computation expressions. ([Issue #19625](https://github.com/dotnet/fsharp/issues/19625), [PR #19630](https://github.com/dotnet/fsharp/pull/19630))
4141
* IlxGen: fix missing CompilationMapping attribute for generic values ([PR #19643](https://github.com/dotnet/fsharp/pull/19643))
42+
* 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 (FS3887) instead of an internal error. ([Issue #12796](https://github.com/dotnet/fsharp/issues/12796), [PR #19472](https://github.com/dotnet/fsharp/pull/19472))
4243
* Fix internal compiler error in `use` bindings when a C#-style `Dispose` extension method is in scope alongside `IDisposable.Dispose`. ([Issue #19552](https://github.com/dotnet/fsharp/issues/19552), [PR #19568](https://github.com/dotnet/fsharp/pull/19568))
4344
* Fix signature generation: single-case struct DU gets spurious bar causing FS0300. ([Issue #19597](https://github.com/dotnet/fsharp/issues/19597), [PR #19609](https://github.com/dotnet/fsharp/pull/19609))
4445
* Fix signature generation: backticked active pattern case names lose escaping. ([Issue #19592](https://github.com/dotnet/fsharp/issues/19592), [PR #19609](https://github.com/dotnet/fsharp/pull/19609))

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: 41 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10300,7 +10300,7 @@ and EmitRestoreStack cgbuf (savedStack, savedStackLocals) =
1030010300
//GenAttr: custom attribute generation
1030110301
//-------------------------------------------------------------------------
1030210302

10303-
and GenAttribArg amap g eenv x (ilArgTy: ILType) =
10303+
and GenAttribArg amap (g: TcGlobals) eenv x (ilArgTy: ILType) =
1030410304

1030510305
match stripDebugPoints x, ilArgTy with
1030610306
// Detect 'null' used for an array argument
@@ -10356,7 +10356,46 @@ and GenAttribArg amap g eenv x (ilArgTy: ILType) =
1035610356
// Detect '[| ... |]' nodes
1035710357
| Expr.Op(TOp.Array, [ elemTy ], args, m), _ ->
1035810358
let ilElemTy = GenType amap m eenv.tyenv elemTy
10359-
ILAttribElem.Array(ilElemTy, List.map (fun arg -> GenAttribArg amap g eenv arg ilElemTy) args)
10359+
10360+
// Check if element type can be encoded in custom attribute metadata (ECMA-335 II.23.3).
10361+
// Valid element types: primitives, enums, string, System.Type, System.Object.
10362+
let isEncodableElemType =
10363+
match ilElemTy with
10364+
| ILType.Value tspec ->
10365+
match tspec.Name with
10366+
| "System.SByte"
10367+
| "System.Byte"
10368+
| "System.Int16"
10369+
| "System.UInt16"
10370+
| "System.Int32"
10371+
| "System.UInt32"
10372+
| "System.Int64"
10373+
| "System.UInt64"
10374+
| "System.Double"
10375+
| "System.Single"
10376+
| "System.Char"
10377+
| "System.Boolean" -> true
10378+
| _ -> isEnumTy g elemTy
10379+
| ILType.Boxed tspec ->
10380+
tspec.Name = "System.String"
10381+
|| tspec.Name = "System.Object"
10382+
|| tspec.Name = "System.Type"
10383+
| _ -> false
10384+
10385+
if not isEncodableElemType then
10386+
if args.IsEmpty then
10387+
// Empty arrays: substitute System.Object as element type since no elements need encoding.
10388+
ILAttribElem.Array(g.ilg.typ_Object, [])
10389+
else
10390+
let elemTypeName =
10391+
if ilElemTy.IsNominal then
10392+
ilElemTy.TypeRef.FullName
10393+
else
10394+
string ilElemTy
10395+
10396+
error (Error(FSComp.SR.ilCustomAttrInvalidArrayElemType elemTypeName, m))
10397+
else
10398+
ILAttribElem.Array(ilElemTy, List.map (fun arg -> GenAttribArg amap g eenv arg ilElemTy) args)
1036010399

1036110400
// Detect 'typeof<ty>' calls
1036210401
| 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
@@ -1817,3 +1817,4 @@ featurePreprocessorElif,"#elif preprocessor directive"
18171817
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."
18181818
3885,parsLetBangCannotBeLastInCE,"'%s' cannot be the final expression in a computation expression. Finish with 'return', 'return!', or a simple expression."
18191819
3886,tcListLiteralWithSingleTupleElement,"This list expression contains a single tuple element. Did you mean to use ';' instead of ',' to separate list elements?"
1820+
3887,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: 5 additions & 0 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: 5 additions & 0 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: 5 additions & 0 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: 5 additions & 0 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: 5 additions & 0 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: 5 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)