Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Generic Attributes #17258

Open
wants to merge 26 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 22 commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
c6a11c4
basic parsing implementation of generic attribute type parameters
mflibby May 19, 2024
80f8ed8
Merge remote-tracking branch 'upstream' into main
mflibby May 19, 2024
cd39370
generalized basic parsing for type parameters on attributes
mflibby May 24, 2024
8093188
Merge branch 'main' into hcrd_main
mflibby May 24, 2024
0064634
threading type parameters into type tree
mflibby May 25, 2024
10c7d7e
Merge branch 'main' into hcrd_main
mflibby May 25, 2024
cdb0974
fully threaded base implementation of generic attributes
mflibby May 28, 2024
c611fb6
Merge branch 'main' into hcrd_main
mflibby May 28, 2024
799ad54
updating originally named TypeParam to TypeArg (for attr types) to al…
mflibby May 28, 2024
d9ac04e
updated with language feature and reverted modified test cases
mflibby May 28, 2024
e545b49
updated release notes
mflibby May 28, 2024
64607ef
formatting fixes
mflibby May 28, 2024
45281ef
more tests for ensuring appropriate type arg behavior in attributes
mflibby May 30, 2024
b24ffe5
Merge remote-tracking branch 'upstream' into hcrd_main
mflibby May 30, 2024
796a4ba
fixed type level generic attributes not being digested properly in Is…
mflibby May 30, 2024
97c326e
fixed lexing issue where type args adjacent to >] clashed
mflibby May 31, 2024
6566460
cleanup
mflibby Jun 1, 2024
d81d3d3
Merge branch 'main' into hcrd_main
mflibby Jun 1, 2024
d6c846e
added missing baseline for new ILEmission test
mflibby Jun 1, 2024
cdb2b4e
more cleanup
mflibby Jun 2, 2024
e40ed04
Merge branch 'main' into hcrd_main
mflibby Jun 7, 2024
24d679a
fully implemented type args into other production cases and added tes…
mflibby Jun 7, 2024
1e886be
revert irrelevant formatting change
mflibby Jun 13, 2024
47c5dc7
formatting adjustment to avoid unnecessary diff
mflibby Jun 14, 2024
993d1fe
added alias generic test
mflibby Jun 20, 2024
4255a3f
added self reference generic attribute usage test
mflibby Jun 23, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/release-notes/.FSharp.Compiler.Service/8.0.400.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
* Generate new `Equals` overload to avoid boxing for structural comparison ([PR #16857](https://github.com/dotnet/fsharp/pull/16857))
* Parser: better recovery for unfinished patterns ([PR #17231](https://github.com/dotnet/fsharp/pull/17231))
* Parser: recover on empty match clause ([PR #17233](https://github.com/dotnet/fsharp/pull/17233))
* Support Generic Attributes ([PR #17258](https://github.com/dotnet/fsharp/pull/17258))

### Changed
* Enforce `AttributeTargets.Interface` ([PR #17173](https://github.com/dotnet/fsharp/pull/17173))
Expand Down
1 change: 1 addition & 0 deletions docs/release-notes/.Language/preview.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
* Bidirectional F#/C# interop for 'unmanaged' constraint. ([PR #12154](https://github.com/dotnet/fsharp/pull/12154))
* Make `.Is*` discriminated union properties visible. ([Language suggestion #222](https://github.com/fsharp/fslang-suggestions/issues/222), [PR #16341](https://github.com/dotnet/fsharp/pull/16341))
* Allow returning bool instead of unit option for partial active patterns. ([Language suggestion #1041](https://github.com/fsharp/fslang-suggestions/issues/1041), [PR #16473](https://github.com/dotnet/fsharp/pull/16473))
* Support for Generic Attributes ([PR #17258](https://github.com/dotnet/fsharp/pull/17258))

### Fixed

Expand Down
10 changes: 10 additions & 0 deletions src/Compiler/AbstractIL/il.fs
Original file line number Diff line number Diff line change
Expand Up @@ -4851,6 +4851,16 @@ let encodeCustomAttrNamedArg (nm, ty, prop, elem) =
yield! encodeCustomAttrValue ty elem
|]

/// <summary>
/// Generate the syntax of a custom attribute per ECMA-335 II.23.3
/// <para>Prolog (always 01 00)</para>
/// <para>FixedArgs - the implicit arguments, i.e. the ctor arguments</para>
/// <para>NumNamed - the number of named arguments used in this instance of an attribute</para>
/// <para>NamedArgs - the actual named arguments used in this instance of an attribute</para>
/// </summary>
/// <param name="mspec"></param>
/// <param name="fixedArgs"></param>
/// <param name="namedArgs"></param>
let encodeCustomAttrArgs (mspec: ILMethodSpec) (fixedArgs: _ list) (namedArgs: _ list) =
let argTys = mspec.MethodRef.ArgTypes

Expand Down
99 changes: 59 additions & 40 deletions src/Compiler/AbstractIL/ilwrite.fs
Original file line number Diff line number Diff line change
Expand Up @@ -353,7 +353,7 @@ type UnsharedRow(elems: RowElement[]) =
type ILTypeWriterEnv = { EnclosingTyparCount: int }
let envForTypeDef (tdef: ILTypeDef) = { EnclosingTyparCount=tdef.GenericParams.Length }
let envForMethodRef env (ty: ILType) = { EnclosingTyparCount=(match ty with ILType.Array _ -> env.EnclosingTyparCount | _ -> ty.GenericArgs.Length) }
let envForNonGenericMethodRef _mref = { EnclosingTyparCount=Int32.MaxValue }
//let envForNonGenericMethodRef _mref = { EnclosingTyparCount=Int32.MaxValue } do we need this for attributes? It doesn't appear so
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I realize it's generally a "no-no" to do this to obscure functionality (i.e. I dont yet understand what this was for), but it seems that with the introduction of generic attributes, we will need to fully qualify the environment for attribute usage. Since this function was only used in the generation of attributes, my current position is that this function can be deleted - though I am still curious why the count was being maxed for the non-generic case, if anyone has that wisdom on hand.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

let envForFieldSpec (fspec: ILFieldSpec) = { EnclosingTyparCount=fspec.DeclaringType.GenericArgs.Length }
let envForOverrideSpec (ospec: ILOverridesSpec) = { EnclosingTyparCount=ospec.DeclaringType.GenericArgs.Length }

Expand Down Expand Up @@ -1454,42 +1454,61 @@ let rec GetMethodRefAsMemberRefIdx cenv env fenv (mref: ILMethodRef) =
let row = MethodRefInfoAsMemberRefRow cenv env fenv (mref.Name, mkILNonGenericBoxedTy mref.DeclaringTypeRef, mref.CallingConv, mref.ArgTypes, mref.ReturnType, None, mref.GenericArity)
FindOrAddSharedRow cenv TableNames.MemberRef row

and GetMethodRefAsCustomAttribType cenv (mref: ILMethodRef) =
let fenv = envForNonGenericMethodRef mref
let tref = mref.DeclaringTypeRef
if isTypeRefLocal tref then
try (cat_MethodDef, GetMethodRefAsMethodDefIdx cenv mref)
with MethodDefNotFound -> (cat_MemberRef, GetMethodRefAsMemberRefIdx cenv fenv fenv mref)
/// <summary>MethodRef Index for an Attribute *with* type arguments</summary>
and GetMemberRefAsCustomGenericAttribType cenv env fenv (mspec: ILMethodSpec) =
let typeSpec = (tdor_TypeSpec, GetTypeAsTypeSpecIdx cenv env mspec.DeclaringType)
let memberRef = MemberRefParent (mrp_TypeSpec, snd typeSpec)
let memberRow = MemberRefRow( memberRef,
GetStringHeapIdx cenv mspec.Name,
GetMethodRefInfoAsBlobIdx cenv fenv (mspec.CallingConv, mspec.MethodRef.ArgTypes, mspec.MethodRef.ReturnType, None, mspec.MethodRef.GenericArity)
)
(cat_MemberRef, FindOrAddSharedRow cenv TableNames.MemberRef memberRow)

/// <summary>MethodRef Index for an Attribute without type arguments</summary>
and GetMethodRefAsCustomAttribType cenv _env fenv (mspec: ILMethodSpec) =

let tref = mspec.MethodRef.DeclaringTypeRef

if isTypeRefLocal tref && mspec.DeclaringType.GenericArgs.Length = 0 then
try (cat_MethodDef, GetMethodRefAsMethodDefIdx cenv mspec.MethodRef)
with MethodDefNotFound ->
(cat_MemberRef, GetMethodRefAsMemberRefIdx cenv fenv fenv mspec.MethodRef)
else
(cat_MemberRef, GetMethodRefAsMemberRefIdx cenv fenv fenv mref)

(cat_MemberRef, GetMethodRefAsMemberRefIdx cenv fenv fenv mspec.MethodRef)
// --------------------------------------------------------------------
// ILAttributes --> CustomAttribute rows
// --------------------------------------------------------------------

let rec GetCustomAttrDataAsBlobIdx cenv (data: byte[]) =
if data.Length = 0 then 0 else GetBytesAsBlobIdx cenv data

and GetCustomAttrRow cenv hca (attr: ILAttribute) =
let cat = GetMethodRefAsCustomAttribType cenv attr.Method.MethodRef
and GetCustomAttrRow cenv env hca (attr: ILAttribute) =
let cat =
let fenv = envForMethodRef env attr.Method.DeclaringType
if attr.Method.DeclaringType.GenericArgs.Length = 0
then GetMethodRefAsCustomAttribType cenv env fenv attr.Method
else GetMemberRefAsCustomGenericAttribType cenv env fenv attr.Method
let data = getCustomAttrData attr

for element in attr.Elements do
match element with
| ILAttribElem.Type (Some ty) when ty.IsNominal -> GetTypeRefAsTypeRefIdx cenv ty.TypeRef |> ignore
| ILAttribElem.TypeRef (Some tref) -> GetTypeRefAsTypeRefIdx cenv tref |> ignore
| _ -> ()

UnsharedRow
[| HasCustomAttribute (fst hca, snd hca)
CustomAttributeType (fst cat, snd cat)
Blob (GetCustomAttrDataAsBlobIdx cenv data)
[|
HasCustomAttribute (fst hca, snd hca)
CustomAttributeType (fst cat, snd cat)
Blob (GetCustomAttrDataAsBlobIdx cenv data)
|]

and GenCustomAttrPass3Or4 cenv hca attr =
AddUnsharedRow cenv TableNames.CustomAttribute (GetCustomAttrRow cenv hca attr) |> ignore
and GenCustomAttrPass3Or4 cenv env hca attr =
AddUnsharedRow cenv TableNames.CustomAttribute (GetCustomAttrRow cenv env hca attr) |> ignore

and GenCustomAttrsPass3Or4 cenv hca (attrs: ILAttributes) =
attrs.AsArray() |> Array.iter (GenCustomAttrPass3Or4 cenv hca)
and GenCustomAttrsPass3Or4 cenv env hca (attrs: ILAttributes) =
attrs.AsArray() |> Array.iter (GenCustomAttrPass3Or4 cenv env hca)

// --------------------------------------------------------------------
// ILSecurityDecl --> DeclSecurity rows
Expand Down Expand Up @@ -2347,14 +2366,14 @@ let rec GenPdbImports (cenv: cenv) (input: ILDebugImports option) =

let GenILMethodBody mname cenv env (il: ILMethodBody) =
let localSigs =
if cenv.generatePdb then
il.Locals |> List.toArray |> Array.map (fun l ->
// Write a fake entry for the local signature headed by e_IMAGE_CEE_CS_CALLCONV_FIELD. This is referenced by the PDB file
ignore (FindOrAddSharedRow cenv TableNames.StandAloneSig (SharedRow [| Blob (GetFieldDefTypeAsBlobIdx cenv env l.Type) |]))
// Now write the type
GetTypeOfLocalAsBytes cenv env l)
else
[| |]
if cenv.generatePdb then
il.Locals |> List.toArray |> Array.map (fun l ->
// Write a fake entry for the local signature headed by e_IMAGE_CEE_CS_CALLCONV_FIELD. This is referenced by the PDB file
ignore (FindOrAddSharedRow cenv TableNames.StandAloneSig (SharedRow [| Blob (GetFieldDefTypeAsBlobIdx cenv env l.Type) |]))
// Now write the type
GetTypeOfLocalAsBytes cenv env l)
else
[| |]

let imports = GenPdbImports cenv il.DebugImports
let requiredStringFixups, seh, code, seqpoints, scopes = Codebuf.EmitMethodCode cenv imports localSigs env mname il.Code
Expand Down Expand Up @@ -2461,7 +2480,7 @@ and GetFieldDefSigAsBlobIdx cenv env fd = GetFieldDefTypeAsBlobIdx cenv env fd.F
and GenFieldDefPass3 tdef cenv env fd =
if canGenFieldDef tdef cenv fd then
let fidx = AddUnsharedRow cenv TableNames.Field (GetFieldDefAsFieldDefRow cenv env fd)
GenCustomAttrsPass3Or4 cenv (hca_FieldDef, fidx) fd.CustomAttrs
GenCustomAttrsPass3Or4 cenv env (hca_FieldDef, fidx) fd.CustomAttrs
// Write FieldRVA table - fixups into data section done later
match fd.Data with
| None -> ()
Expand Down Expand Up @@ -2542,7 +2561,7 @@ and GenGenericParamPass3 cenv env idx owner gp =

and GenGenericParamPass4 cenv env idx owner gp =
let gpidx = FindOrAddSharedRow cenv TableNames.GenericParam (GetGenericParamAsGenericParamRow cenv env idx owner gp)
GenCustomAttrsPass3Or4 cenv (hca_GenericParam, gpidx) gp.CustomAttrs
GenCustomAttrsPass3Or4 cenv env (hca_GenericParam, gpidx) gp.CustomAttrs
gp.Constraints |> List.iter (GenGenericParamConstraintPass4 cenv env gpidx)

// --------------------------------------------------------------------
Expand All @@ -2567,7 +2586,7 @@ and GenParamPass3 cenv env seq (param: ILParameter) =
then ()
else
let pidx = AddUnsharedRow cenv TableNames.Param (GetParamAsParamRow cenv env seq param)
GenCustomAttrsPass3Or4 cenv (hca_ParamDef, pidx) param.CustomAttrs
GenCustomAttrsPass3Or4 cenv env (hca_ParamDef, pidx) param.CustomAttrs
// Write FieldRVA table - fixups into data section done later
match param.Marshal with
| None -> ()
Expand All @@ -2594,7 +2613,7 @@ let GenReturnAsParamRow (returnv : ILReturn) =
let GenReturnPass3 cenv (returnv: ILReturn) =
if Option.isSome returnv.Marshal || not (Array.isEmpty (returnv.CustomAttrs.AsArray())) then
let pidx = AddUnsharedRow cenv TableNames.Param (GenReturnAsParamRow returnv)
GenCustomAttrsPass3Or4 cenv (hca_ParamDef, pidx) returnv.CustomAttrs
GenCustomAttrsPass3Or4 cenv {EnclosingTyparCount = 0} (hca_ParamDef, pidx) returnv.CustomAttrs
match returnv.Marshal with
| None -> ()
| Some ntyp ->
Expand Down Expand Up @@ -2706,7 +2725,7 @@ let GenMethodDefPass3 tdef cenv env (mdef: ILMethodDef) =
if midx <> idx2 then failwith "index of method def on pass 3 does not match index on pass 2"
GenReturnPass3 cenv mdef.Return
mdef.Parameters |> List.iteri (fun n param -> GenParamPass3 cenv env (n+1) param)
mdef.CustomAttrs |> GenCustomAttrsPass3Or4 cenv (hca_MethodDef, midx)
mdef.CustomAttrs |> GenCustomAttrsPass3Or4 cenv env (hca_MethodDef, midx)
mdef.SecurityDecls.AsList() |> GenSecurityDeclsPass3 cenv (hds_MethodDef, midx)
mdef.GenericParams |> List.iteri (fun n gp -> GenGenericParamPass3 cenv env n (tomd_MethodDef, midx) gp)
match mdef.Body with
Expand Down Expand Up @@ -2796,7 +2815,7 @@ and GenPropertyPass3 cenv env (prop: ILPropertyDef) =
[| GetFieldInitFlags i
HasConstant (hc_Property, pidx)
Blob (GetFieldInitAsBlobIdx cenv i) |]) |> ignore
GenCustomAttrsPass3Or4 cenv (hca_Property, pidx) prop.CustomAttrs
GenCustomAttrsPass3Or4 cenv env (hca_Property, pidx) prop.CustomAttrs

let rec GenEventMethodSemanticsPass3 cenv eidx kind mref =
let addIdx = try GetMethodRefAsMethodDefIdx cenv mref with MethodDefNotFound -> 1
Expand All @@ -2822,7 +2841,7 @@ and GenEventPass3 cenv env (edef: ILEventDef) =
edef.RemoveMethod |> GenEventMethodSemanticsPass3 cenv eidx 0x0010
Option.iter (GenEventMethodSemanticsPass3 cenv eidx 0x0020) edef.FireMethod
List.iter (GenEventMethodSemanticsPass3 cenv eidx 0x0004) edef.OtherMethods
GenCustomAttrsPass3Or4 cenv (hca_Event, eidx) edef.CustomAttrs
GenCustomAttrsPass3Or4 cenv env (hca_Event, eidx) edef.CustomAttrs


// --------------------------------------------------------------------
Expand Down Expand Up @@ -2856,7 +2875,7 @@ let rec GetResourceAsManifestResourceRow cenv rdef =

and GenResourcePass3 cenv rdef =
let idx = AddUnsharedRow cenv TableNames.ManifestResource (GetResourceAsManifestResourceRow cenv rdef)
GenCustomAttrsPass3Or4 cenv (hca_ManifestResource, idx) rdef.CustomAttrs
GenCustomAttrsPass3Or4 cenv {EnclosingTyparCount = 0} (hca_ManifestResource, idx) rdef.CustomAttrs

// --------------------------------------------------------------------
// ILTypeDef --> generate ILFieldDef, ILMethodDef, ILPropertyDef etc. rows
Expand All @@ -2883,7 +2902,7 @@ let rec GenTypeDefPass3 enc cenv (tdef: ILTypeDef) =
SimpleIndex (TableNames.TypeDef, tidx) |]) |> ignore

tdef.SecurityDecls.AsList() |> GenSecurityDeclsPass3 cenv (hds_TypeDef, tidx)
tdef.CustomAttrs |> GenCustomAttrsPass3Or4 cenv (hca_TypeDef, tidx)
tdef.CustomAttrs |> GenCustomAttrsPass3Or4 cenv env (hca_TypeDef, tidx)
tdef.GenericParams |> List.iteri (fun n gp -> GenGenericParamPass3 cenv env n (tomd_TypeDef, tidx) gp)
tdef.NestedTypes.AsList() |> GenTypeDefsPass3 (enc@[tdef.Name]) cenv
with exn ->
Expand Down Expand Up @@ -2928,7 +2947,7 @@ let rec GenNestedExportedTypePass3 cenv cidx (ce: ILNestedExportedType) =
StringE (GetStringHeapIdx cenv ce.Name)
StringE 0
Implementation (i_ExportedType, cidx) |])
GenCustomAttrsPass3Or4 cenv (hca_ExportedType, nidx) ce.CustomAttrs
GenCustomAttrsPass3Or4 cenv {EnclosingTyparCount = 0} (hca_ExportedType, nidx) ce.CustomAttrs
GenNestedExportedTypesPass3 cenv nidx ce.Nested

and GenNestedExportedTypesPass3 cenv nidx (nce: ILNestedExportedTypes) =
Expand All @@ -2946,7 +2965,7 @@ and GenExportedTypePass3 cenv (ce: ILExportedTypeOrForwarder) =
nelem
nselem
Implementation (fst impl, snd impl) |])
GenCustomAttrsPass3Or4 cenv (hca_ExportedType, cidx) ce.CustomAttrs
GenCustomAttrsPass3Or4 cenv {EnclosingTyparCount = 0} (hca_ExportedType, cidx) ce.CustomAttrs
GenNestedExportedTypesPass3 cenv cidx ce.Nested

and GenExportedTypesPass3 cenv (ce: ILExportedTypesAndForwarders) =
Expand Down Expand Up @@ -2983,7 +3002,7 @@ and GetManifestAsAssemblyRow cenv m =
and GenManifestPass3 cenv m =
let aidx = AddUnsharedRow cenv TableNames.Assembly (GetManifestAsAssemblyRow cenv m)
GenSecurityDeclsPass3 cenv (hds_Assembly, aidx) (m.SecurityDecls.AsList())
GenCustomAttrsPass3Or4 cenv (hca_Assembly, aidx) m.CustomAttrs
GenCustomAttrsPass3Or4 cenv {EnclosingTyparCount = 0} (hca_Assembly, aidx) m.CustomAttrs
GenExportedTypesPass3 cenv m.ExportedTypes
// Record the entrypoint decl if needed.
match m.EntrypointElsewhere with
Expand Down Expand Up @@ -3045,7 +3064,7 @@ let GenModule (cenv : cenv) (modul: ILModuleDef) =
(match modul.Manifest with None -> () | Some m -> GenManifestPass3 cenv m)
GenTypeDefsPass3 [] cenv tdefs
reportTime "Module Generation Pass 3"
GenCustomAttrsPass3Or4 cenv (hca_Module, midx) modul.CustomAttrs
GenCustomAttrsPass3Or4 cenv {EnclosingTyparCount = 0} (hca_Module, midx) modul.CustomAttrs
// GenericParam is the only sorted table indexed by Columns in other tables (GenericParamConstraint\CustomAttributes).
// Hence we need to sort it before we emit any entries in GenericParamConstraint\CustomAttributes that are attached to generic params.
// Note this mutates the rows in a table. 'SetRowsOfTable' clears
Expand Down
Loading
Loading