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

Feature nullness metadata export #15981

Merged
merged 33 commits into from
Oct 9, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
ac4d4ea
attribute embedding
T-Gro Sep 14, 2023
d1f7ec6
Emitting nullness attribute for typer constraints
T-Gro Sep 14, 2023
5fd0baf
IlxGen reshuffle to generate parameter and return type attributes
T-Gro Sep 14, 2023
1527647
Prepare for nullness attr inclusion
T-Gro Sep 14, 2023
5a3f879
Initial emission of Nullable attribute, without NullableContext optim…
T-Gro Sep 15, 2023
8ce201e
nullable attribute IL emit test
T-Gro Sep 15, 2023
329241d
Fix special case for System.Nullable
T-Gro Sep 15, 2023
1a8e288
formatting
T-Gro Sep 18, 2023
9c80269
mkLocalPrivateAttributeWithByteAndByteArrayConstructors
T-Gro Sep 18, 2023
4853886
Compacted metadata if byte[] has .Length of 1
T-Gro Sep 18, 2023
490347e
nullablecontext attribute generation
T-Gro Sep 19, 2023
b2ca9ac
Updating tests to reflect space reduction heuristics
T-Gro Sep 19, 2023
a0a7d73
Emit field level nullable attributes
T-Gro Sep 19, 2023
33ca5c0
tests coverage it emitted IL
T-Gro Sep 20, 2023
df1e7e8
Separate baselines into net472/netcore in case it needed DynamicAcces…
T-Gro Sep 20, 2023
a1e08f9
tyarg can be omitted when it is `not null`
T-Gro Sep 20, 2023
82d0dcc
properties annotated on IL level
T-Gro Sep 20, 2023
9cb137c
let bindings which created fields annotated
T-Gro Sep 20, 2023
7cbff2c
Ref unions nullness
T-Gro Sep 20, 2023
c635234
propagate DU nullness into functions working with it
T-Gro Sep 20, 2023
ed57342
nullable context for inner classes of DUs, ambivalent nullness for fi…
T-Gro Sep 20, 2023
77948c1
use Map instead of dict so that .bsl can be the same across net472/ne…
T-Gro Sep 20, 2023
bec7b87
various Option<> representations
T-Gro Sep 21, 2023
83989ec
Defensive field access to unions
T-Gro Sep 21, 2023
93f0f2a
MemberNotNullWhenAttribute applied when codegening struct DUs with fi…
T-Gro Sep 21, 2023
1bd5813
il baseline fixed
T-Gro Sep 21, 2023
23ee4ab
fantomas
T-Gro Sep 21, 2023
1fbc35f
fantomas
T-Gro Sep 21, 2023
8a2edf9
add test-signature file to VCS so that we can spot differences in it
T-Gro Sep 29, 2023
ba4786e
Nullness C# (consumer) interop test -> TODOs are inside the .cs test …
T-Gro Sep 29, 2023
4de0a7e
Changing NullableAttribute codegen (System.Array<byte> is not the sam…
T-Gro Oct 3, 2023
c21c416
Get stricter for DUs - WithNull instead of Ambivalent if fields are n…
T-Gro Oct 3, 2023
27eeeb0
do not emit NullableContext for DU alttypes if not asked for via the …
T-Gro Oct 3, 2023
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
13 changes: 11 additions & 2 deletions src/Compiler/AbstractIL/il.fs
Original file line number Diff line number Diff line change
Expand Up @@ -3412,6 +3412,11 @@ type ILGlobals(primaryScopeRef: ILScopeRef, equivPrimaryAssemblyRefs: ILAssembly

let mkSysILTypeRef nm = mkILTyRef (primaryScopeRef, nm)

let byteIlType = ILType.Value(mkILNonGenericTySpec (mkSysILTypeRef tname_Byte))

let stringIlType =
mkILBoxedType (mkILNonGenericTySpec (mkSysILTypeRef tname_String))

member _.primaryAssemblyScopeRef = primaryScopeRef

member x.primaryAssemblyRef =
Expand All @@ -3429,7 +3434,7 @@ type ILGlobals(primaryScopeRef: ILScopeRef, equivPrimaryAssemblyRefs: ILAssembly

member val typ_Object = mkILBoxedType (mkILNonGenericTySpec (mkSysILTypeRef tname_Object))

member val typ_String = mkILBoxedType (mkILNonGenericTySpec (mkSysILTypeRef tname_String))
member val typ_String = stringIlType

member val typ_Array = mkILBoxedType (mkILNonGenericTySpec (mkSysILTypeRef tname_Array))

Expand All @@ -3443,7 +3448,11 @@ type ILGlobals(primaryScopeRef: ILScopeRef, equivPrimaryAssemblyRefs: ILAssembly

member val typ_Int64 = ILType.Value(mkILNonGenericTySpec (mkSysILTypeRef tname_Int64))

member val typ_Byte = ILType.Value(mkILNonGenericTySpec (mkSysILTypeRef tname_Byte))
member val typ_Byte = byteIlType

member val typ_ByteArray = ILType.Array(ILArrayShape.SingleDimensional, byteIlType)

member val typ_StringArray = ILType.Array(ILArrayShape.SingleDimensional, stringIlType)

member val typ_UInt16 = ILType.Value(mkILNonGenericTySpec (mkSysILTypeRef tname_UInt16))

Expand Down
2 changes: 2 additions & 0 deletions src/Compiler/AbstractIL/il.fsi
Original file line number Diff line number Diff line change
Expand Up @@ -1876,11 +1876,13 @@ type internal ILGlobals =
member typ_Enum: ILType
member typ_Object: ILType
member typ_String: ILType
member typ_StringArray: ILType
member typ_Type: ILType
member typ_Array: ILType
member typ_IntPtr: ILType
member typ_UIntPtr: ILType
member typ_Byte: ILType
member typ_ByteArray: ILType
member typ_Int16: ILType
member typ_Int32: ILType
member typ_Int64: ILType
Expand Down
151 changes: 141 additions & 10 deletions src/Compiler/CodeGen/EraseUnions.fs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ open FSharp.Compiler.IlxGenSupport
open System.Collections.Generic
open System.Reflection
open Internal.Utilities.Library
open FSharp.Compiler.TypedTreeOps
open FSharp.Compiler.TcGlobals
open FSharp.Compiler.AbstractIL.IL
open FSharp.Compiler.AbstractIL.ILX.Types
Expand Down Expand Up @@ -665,6 +666,7 @@ let emitDataSwitch ilg (cg: ICodeGen<'Mark>) (avoidHelpers, cuspec, cases) =

let mkMethodsAndPropertiesForFields
(addMethodGeneratedAttrs, addPropertyGeneratedAttrs)
(g: TcGlobals)
access
attr
imports
Expand Down Expand Up @@ -704,15 +706,28 @@ let mkMethodsAndPropertiesForFields
for field in fields do
let fspec = mkILFieldSpecInTy (ilTy, field.LowerName, field.Type)

let ilReturn = mkILReturn field.Type

let ilReturn =
if TryFindILAttribute g.attrib_NullableAttribute field.ILField.CustomAttrs then
let attrs =
field.ILField.CustomAttrs.AsArray()
|> Array.filter (IsILAttrib g.attrib_NullableAttribute)

ilReturn.WithCustomAttrs(mkILCustomAttrsFromArray attrs)
else
ilReturn

yield
mkILNonGenericInstanceMethod (
"get_" + adjustFieldName hasHelpers field.Name,
access,
[],
mkILReturn field.Type,
ilReturn,
mkMethodBody (true, [], 2, nonBranchingInstrsToCode [ mkLdarg 0us; mkNormalLdfld fspec ], attr, imports)
)
|> addMethodGeneratedAttrs

]

basicProps, basicMethods
Expand Down Expand Up @@ -801,8 +816,34 @@ let convAlternativeDef
elif repr.RepresentOneAlternativeAsNull info then
[], []
else
let additionalAttributes =
if
g.checkNullness
&& g.langFeatureNullness
&& repr.RepresentAlternativeAsStructValue info
&& not alt.IsNullary
then
let notnullfields =
alt.FieldDefs
// Fields that are nullable even from F# perspective has an [Nullable] attribute on them
// Non-nullable fields are implicit in F#, therefore not annotated separately
|> Array.filter (fun f -> TryFindILAttribute g.attrib_NullableAttribute f.ILField.CustomAttrs |> not)

let fieldNames =
notnullfields
|> Array.map (fun f -> f.LowerName)
|> Array.append (notnullfields |> Array.map (fun f -> f.Name))

if fieldNames |> Array.isEmpty then
emptyILCustomAttrs
else
mkILCustomAttrsFromArray [| GetNotNullWhenTrueAttribute g fieldNames |]

else
emptyILCustomAttrs

[
mkILNonGenericInstanceMethod (
(mkILNonGenericInstanceMethod (
"get_" + mkTesterName altName,
cud.HelpersAccessibility,
[],
Expand All @@ -815,7 +856,8 @@ let convAlternativeDef
attr,
imports
)
)
))
.With(customAttrs = additionalAttributes)
|> addMethodGeneratedAttrs
],
[
Expand All @@ -838,7 +880,7 @@ let convAlternativeDef
propertyType = g.ilg.typ_Bool,
init = None,
args = [],
customAttrs = emptyILCustomAttrs
customAttrs = additionalAttributes
)
|> addPropertyGeneratedAttrs
|> addPropertyNeverAttrs
Expand All @@ -847,13 +889,24 @@ let convAlternativeDef
let baseMakerMeths, baseMakerProps =

if alt.IsNullary then
let attributes =
if
g.checkNullness
&& g.langFeatureNullness
&& repr.RepresentAlternativeAsNull(info, alt)
then
GetNullableAttribute g [ FSharp.Compiler.TypedTree.NullnessInfo.WithNull ]
|> Array.singleton
|> mkILCustomAttrsFromArray
else
emptyILCustomAttrs

let nullaryMeth =
mkILNonGenericStaticMethod (
"get_" + altName,
cud.HelpersAccessibility,
[],
mkILReturn baseTy,
(mkILReturn baseTy).WithCustomAttrs attributes,
mkMethodBody (
true,
[],
Expand All @@ -877,7 +930,7 @@ let convAlternativeDef
propertyType = baseTy,
init = None,
args = [],
customAttrs = emptyILCustomAttrs
customAttrs = attributes
)
|> addPropertyGeneratedAttrs
|> addPropertyNeverAttrs
Expand Down Expand Up @@ -921,7 +974,19 @@ let convAlternativeDef
mkMakerName cuspec altName,
cud.HelpersAccessibility,
fields
|> Array.map (fun fd -> mkILParamNamed (fd.LowerName, fd.Type))
|> Array.map (fun fd ->
let plainParam = mkILParamNamed (fd.LowerName, fd.Type)

if TryFindILAttribute g.attrib_NullableAttribute fd.ILField.CustomAttrs then
let attrs =
fd.ILField.CustomAttrs.AsArray()
|> Array.filter (IsILAttrib g.attrib_NullableAttribute)

{ plainParam with
CustomAttrsStored = storeILCustomAttrs (mkILCustomAttrsFromArray attrs)
}
else
plainParam)
|> Array.toList,
mkILReturn baseTy,
mkMethodBody (true, locals, fields.Length + locals.Length, nonBranchingInstrsToCode ilInstrs, attr, imports)
Expand Down Expand Up @@ -1090,6 +1155,7 @@ let convAlternativeDef
let basicProps, basicMethods =
mkMethodsAndPropertiesForFields
(addMethodGeneratedAttrs, addPropertyGeneratedAttrs)
g
cud.UnionCasesAccessibility
attr
imports
Expand Down Expand Up @@ -1123,6 +1189,12 @@ let convAlternativeDef
.With(customAttrs = mkILCustomAttrs [ GetDynamicDependencyAttribute g 0x660 baseTy ])
|> addMethodGeneratedAttrs

let attrs =
if g.checkNullness && g.langFeatureNullness then
GetNullableContextAttribute g :: debugAttrs
else
debugAttrs

let altTypeDef =
mkILGenericClass (
altTy.TypeSpec.Name,
Expand All @@ -1141,7 +1213,7 @@ let convAlternativeDef
emptyILTypeDefs,
mkILProperties basicProps,
emptyILEvents,
mkILCustomAttrs debugAttrs,
mkILCustomAttrs attrs,
ILTypeInit.BeforeField
)

Expand Down Expand Up @@ -1261,15 +1333,67 @@ let mkClassUnionDef
|> addMethodGeneratedAttrs
]

let fieldDefs =
// Since structs are flattened out for all cases together, all boxed fields are potentially nullable
if
isStruct
&& cud.UnionCases.Length > 1
&& g.checkNullness
&& g.langFeatureNullness
then
alt.FieldDefs
|> Array.map (fun field ->
if field.Type.IsNominal && field.Type.Boxity = AsValue then
field
else
let attrs =
let existingAttrs = field.ILField.CustomAttrs.AsArray()

let nullableIdx =
existingAttrs |> Array.tryFindIndex (IsILAttrib g.attrib_NullableAttribute)

match nullableIdx with
| None ->
existingAttrs
|> Array.append [| GetNullableAttribute g [ FSharp.Compiler.TypedTree.NullnessInfo.WithNull ] |]
| Some idx ->
let replacementAttr =
match existingAttrs[idx] with
(*
The attribute carries either a single byte, or a list of bytes for the fields itself and all its generic type arguments
The way we lay out DUs does not affect nullability of the typars of a field, therefore we just change the very first byte
If the field was already declared as nullable (value = 2uy) or ambivalent(value = 0uy), we can keep it that way
If it was marked as non-nullable within that UnionCase, we have to convert it to WithNull (2uy) due to other cases being possible
*)
| Encoded (method, _data, [ ILAttribElem.Byte 1uy ]) ->
mkILCustomAttribMethRef (method, [ ILAttribElem.Byte 2uy ], [])
| Encoded (method,
_data,
[ ILAttribElem.Array (elemType, (ILAttribElem.Byte 1uy) :: otherElems) ]) ->
mkILCustomAttribMethRef (
method,
[ ILAttribElem.Array(elemType, (ILAttribElem.Byte 2uy) :: otherElems) ],
[]
)
| attrAsBefore -> attrAsBefore

existingAttrs |> Array.replace idx replacementAttr

field.ILField.With(customAttrs = mkILCustomAttrsFromArray attrs)
|> IlxUnionCaseField)
else
alt.FieldDefs

let props, meths =
mkMethodsAndPropertiesForFields
(addMethodGeneratedAttrs, addPropertyGeneratedAttrs)
g
cud.UnionCasesAccessibility
cud.DebugPoint
cud.DebugImports
cud.HasHelpers
baseTy
alt.FieldDefs
fieldDefs

yield (fields, (ctor @ meths), props)
]
Expand Down Expand Up @@ -1467,7 +1591,14 @@ let mkClassUnionDef
@ List.map (fun (_, _, _, _, fdef, _) -> fdef) altNullaryFields
@ td.Fields.AsList()
),
properties = mkILProperties (tagProps @ basePropsFromAlt @ selfProps @ existingProps)
properties = mkILProperties (tagProps @ basePropsFromAlt @ selfProps @ existingProps),
customAttrs =
if cud.IsNullPermitted && g.checkNullness && g.langFeatureNullness then
td.CustomAttrs.AsArray()
|> Array.append [| GetNullableAttribute g [ FSharp.Compiler.TypedTree.NullnessInfo.WithNull ] |]
|> mkILCustomAttrsFromArray
else
td.CustomAttrs
)
// The .cctor goes on the Cases type since that's where the constant fields for nullary constructors live
|> addConstFieldInit
Expand Down
Loading