Skip to content

Commit

Permalink
Feature nullness :: apply nullness annotations to usages of 'obj' in …
Browse files Browse the repository at this point in the history
…Fsharp.Core (#17284)
  • Loading branch information
T-Gro authored Jun 11, 2024
1 parent a72457e commit d4ad891
Show file tree
Hide file tree
Showing 46 changed files with 373 additions and 399 deletions.
2 changes: 1 addition & 1 deletion src/Compiler/Checking/CheckExpressions.fs
Original file line number Diff line number Diff line change
Expand Up @@ -2997,7 +2997,7 @@ let TcRuntimeTypeTest isCast isOperator (cenv: cenv) denv m tgtTy srcTy =
else
error(Error(FSComp.SR.tcTypeTestErased(NicePrint.minimalStringOfType denv tgtTy, NicePrint.minimalStringOfType denv (stripTyEqnsWrtErasure EraseAll g tgtTy)), m))
else
for ety in getErasedTypes g tgtTy do
for ety in getErasedTypes g tgtTy true do
if isMeasureTy g ety then
warning(Error(FSComp.SR.tcTypeTestLosesMeasures(NicePrint.minimalStringOfType denv ety), m))
else
Expand Down
22 changes: 11 additions & 11 deletions src/Compiler/TypedTree/TypedTreeOps.fs
Original file line number Diff line number Diff line change
Expand Up @@ -1156,30 +1156,30 @@ let isErasedType g ty =
| _ -> false

// Return all components of this type expression that cannot be tested at runtime
let rec getErasedTypes g ty =
let rec getErasedTypes g ty checkForNullness =
let ty = stripTyEqns g ty
if isErasedType g ty then [ty] else
match ty with
| TType_forall(_, bodyTy) ->
getErasedTypes g bodyTy
getErasedTypes g bodyTy checkForNullness

| TType_var (tp, nullness) ->
match nullness.Evaluate() with
| NullnessInfo.WithNull -> [ty] // with-null annotations can't be tested at runtime (TODO NULLNESS: for value types Nullable<_> they can be)
match checkForNullness, nullness.Evaluate() with
| true, NullnessInfo.WithNull -> [ty] // with-null annotations can't be tested at runtime (TODO NULLNESS: for value types Nullable<_> they can be)
| _ -> if tp.IsErased then [ty] else []

| TType_app (_, b, nullness) ->
match nullness.Evaluate() with
| NullnessInfo.WithNull -> [ty]
| _ -> List.foldBack (fun ty tys -> getErasedTypes g ty @ tys) b []
match checkForNullness, nullness.Evaluate() with
| true, NullnessInfo.WithNull -> [ty]
| _ -> List.foldBack (fun ty tys -> getErasedTypes g ty false @ tys) b []

| TType_ucase(_, b) | TType_anon (_, b) | TType_tuple (_, b) ->
List.foldBack (fun ty tys -> getErasedTypes g ty @ tys) b []
List.foldBack (fun ty tys -> getErasedTypes g ty false @ tys) b []

| TType_fun (domainTy, rangeTy, nullness) ->
match nullness.Evaluate() with
| NullnessInfo.WithNull -> [ty]
| _ -> getErasedTypes g domainTy @ getErasedTypes g rangeTy
match checkForNullness, nullness.Evaluate() with
| true, NullnessInfo.WithNull -> [ty]
| _ -> getErasedTypes g domainTy false @ getErasedTypes g rangeTy false
| TType_measure _ ->
[ty]

Expand Down
2 changes: 1 addition & 1 deletion src/Compiler/TypedTree/TypedTreeOps.fsi
Original file line number Diff line number Diff line change
Expand Up @@ -927,7 +927,7 @@ val anonInfoEquiv: AnonRecdTypeInfo -> AnonRecdTypeInfo -> bool
val isErasedType: TcGlobals -> TType -> bool

// Return all components (units-of-measure, and types) of this type that would be erased
val getErasedTypes: TcGlobals -> TType -> TType list
val getErasedTypes: TcGlobals -> TType -> checkForNullness: bool -> TType list

//-------------------------------------------------------------------------
// Unit operations
Expand Down
12 changes: 6 additions & 6 deletions src/FSharp.Core/Linq.fs
Original file line number Diff line number Diff line change
Expand Up @@ -55,15 +55,15 @@ module LeafExpressionConverter =
tyargs.[0], tyargs.[1]

let StringConcat =
methodhandleof (fun (x:obj, y:obj) -> String.Concat (x, y))
methodhandleof (fun (x:objnull, y:objnull) -> String.Concat (x, y))
|> System.Reflection.MethodInfo.GetMethodFromHandle
:?> MethodInfo

let SubstHelperRaw (q:Expr, x:Var array, y:obj array) : Expr =
let SubstHelperRaw (q:Expr, x:Var array, y:objnull array) : Expr =
let d = Map.ofArray (Array.zip x y)
q.Substitute(fun v -> v |> d.TryFind |> Option.map (fun x -> Expr.Value (x, v.Type)))

let SubstHelper<'T> (q:Expr, x:Var array, y:obj array) : Expr<'T> =
let SubstHelper<'T> (q:Expr, x:Var array, y:objnull array) : Expr<'T> =
SubstHelperRaw(q, x, y) |> Expr.Cast

let showAll =
Expand Down Expand Up @@ -393,12 +393,12 @@ module LeafExpressionConverter =
//let (|ArrayAssignQ|_|) = (|SpecificCallToMethod|_|) (methodhandleof (fun -> LanguagePrimitives.IntrinsicFunctions.SetArray : int array -> int -> int -> unit))
//let (|ArrayTypeQ|_|) (ty:System.Type) = if ty.IsArray && ty.GetArrayRank() = 1 then Some (ty.GetElementType()) else None
let substHelperMeth =
methodhandleof (fun (x:Expr, y:Var array, z:obj array) -> SubstHelper<obj> (x, y, z))
methodhandleof (fun (x:Expr, y:Var array, z:objnull array) -> SubstHelper<obj> (x, y, z))
|> System.Reflection.MethodInfo.GetMethodFromHandle
:?> MethodInfo

let substHelperRawMeth =
methodhandleof (fun (x:Expr, y:Var array, z:obj array) -> SubstHelperRaw (x, y, z))
methodhandleof (fun (x:Expr, y:Var array, z:objnull array) -> SubstHelperRaw (x, y, z))
|> System.Reflection.MethodInfo.GetMethodFromHandle
:?> MethodInfo

Expand Down Expand Up @@ -895,7 +895,7 @@ module LeafExpressionConverter =
// provides no other way to evaluate the expression.
//
// REVIEW: It is possible it is just better to interpret the expression in many common cases, e.g. property-gets, values etc.
let EvaluateQuotation (e: Microsoft.FSharp.Quotations.Expr) : obj =
let EvaluateQuotation (e: Microsoft.FSharp.Quotations.Expr) : objnull =
#if FX_NO_QUOTATIONS_COMPILE
raise (new NotSupportedException())
#else
Expand Down
6 changes: 3 additions & 3 deletions src/FSharp.Core/Linq.fsi
Original file line number Diff line number Diff line change
Expand Up @@ -71,21 +71,21 @@ module LeafExpressionConverter =
/// </summary>
///
/// <example-tbd></example-tbd>
val EvaluateQuotation: Expr -> obj
val EvaluateQuotation: Expr -> objnull

/// <summary>
/// A runtime helper used to evaluate nested quotation literals.
/// </summary>
///
/// <example-tbd></example-tbd>
val SubstHelper: Expr * Var array * obj array -> Expr<'T>
val SubstHelper: Expr * Var array * objnull array -> Expr<'T>

/// <summary>
/// A runtime helper used to evaluate nested quotation literals.
/// </summary>
///
/// <example-tbd></example-tbd>
val SubstHelperRaw: Expr * Var array * obj array -> Expr
val SubstHelperRaw: Expr * Var array * objnull array -> Expr

val internal (|SpecificCallToMethod|_|):
System.RuntimeMethodHandle -> (Expr -> (Expr option * Reflection.MethodInfo * Expr list) option)
14 changes: 7 additions & 7 deletions src/FSharp.Core/Query.fs
Original file line number Diff line number Diff line change
Expand Up @@ -372,7 +372,7 @@ module Query =

let CallGenericStaticMethod (methHandle:System.RuntimeMethodHandle) =
let methInfo = methHandle |> System.Reflection.MethodInfo.GetMethodFromHandle :?> MethodInfo
fun (tyargs: Type list, args: obj list) ->
fun (tyargs: Type list, args: objnull list) ->
let methInfo = if methInfo.IsGenericMethod then methInfo.MakeGenericMethod(Array.ofList tyargs) else methInfo
try
methInfo.Invoke(null, Array.ofList args)
Expand All @@ -381,7 +381,7 @@ module Query =

let CallGenericInstanceMethod (methHandle:System.RuntimeMethodHandle) =
let methInfo = methHandle |> System.Reflection.MethodInfo.GetMethodFromHandle :?> MethodInfo
fun (objExpr:obj, tyargs: Type list, args: obj list) ->
fun (objExpr:obj, tyargs: Type list, args: objnull list) ->
let methInfo = if methInfo.IsGenericMethod then methInfo.MakeGenericMethod(Array.ofList tyargs) else methInfo
try
methInfo.Invoke(objExpr, Array.ofList args)
Expand Down Expand Up @@ -467,7 +467,7 @@ module Query =
else
ME ([srcItemTy], [src; key])

let Call (isIQ, srcItemTy, src:obj, key: Expr) =
let Call (isIQ, srcItemTy, src:objnull, key: Expr) =
let key = key |> LeafExpressionConverter.EvaluateQuotation
let C = if isIQ then CQ else CE
C ([srcItemTy], [src; box key])
Expand Down Expand Up @@ -496,7 +496,7 @@ module Query =
else
ME ([srcItemTy; keyElemTy], [src; valSelector])

let Call (isIQ, srcItemTy: Type, _keyItemTy: Type, src:obj, keyElemTy: Type, v: Var, res: Expr) =
let Call (isIQ, srcItemTy: Type, _keyItemTy: Type, src:objnull, keyElemTy: Type, v: Var, res: Expr) =
if isIQ then
let selector = FuncExprToLinqFunc2Expression (srcItemTy, keyElemTy, v, res)
CQ ([srcItemTy; keyElemTy], [src; box selector])
Expand All @@ -505,7 +505,7 @@ module Query =
CE ([srcItemTy; keyElemTy], [src; selector])
Make, Call

let (MakeMinBy: bool * Expr * Var * Expr -> Expr), (CallMinBy : bool * Type * Type * obj * Type * Var * Expr -> obj) =
let (MakeMinBy: bool * Expr * Var * Expr -> Expr), (CallMinBy : bool * Type * Type * objnull * Type * Var * Expr -> obj) =
let FQ = methodhandleof (fun (x, y: Expression<Func<_, _>>) -> System.Linq.Queryable.Min(x, y))
let FE = methodhandleof (fun (x, y: Func<_, 'Result>) -> Enumerable.Min(x, y))
MakeOrCallMinByOrMaxBy FQ FE
Expand Down Expand Up @@ -539,7 +539,7 @@ module Query =
else
ME ([srcItemTy], [src; predicate])

let Call (isIQ, srcItemTy: Type, src:obj, v: Var, res: Expr) =
let Call (isIQ, srcItemTy: Type, src:objnull, v: Var, res: Expr) =
if isIQ then
let selector = FuncExprToLinqFunc2Expression (srcItemTy, boolTy, v, res)
CQ ([srcItemTy], [src; box selector])
Expand Down Expand Up @@ -612,7 +612,7 @@ module Query =
let selector = Expr.Lambda (v, res)
ME (qb, [srcItemTy; qTy; resTyNoNullable], [src; selector])

let Call (qb:obj, isIQ, srcItemTy: Type, resTyNoNullable: Type, src:obj, resTy: Type, v: Var, res: Expr) =
let Call (qb:obj, isIQ, srcItemTy: Type, resTyNoNullable: Type, src:objnull, resTy: Type, v: Var, res: Expr) =
if isIQ then
let selector = FuncExprToLinqFunc2Expression (srcItemTy, resTy, v, res)
let caller =
Expand Down
10 changes: 5 additions & 5 deletions src/FSharp.Core/async.fs
Original file line number Diff line number Diff line change
Expand Up @@ -1076,7 +1076,7 @@ module AsyncPrimitives =

/// Create an instance of an arbitrary delegate type delegating to the given F# function
type FuncDelegate<'T>(f) =
member _.Invoke(sender: obj, a: 'T) : unit =
member _.Invoke(sender: objnull, a: 'T) : unit =
ignore sender
f a

Expand Down Expand Up @@ -1309,7 +1309,7 @@ module AsyncPrimitives =
| None -> ()

[<Sealed; AutoSerializable(false)>]
type AsyncIAsyncResult<'T>(callback: System.AsyncCallback, state: obj) =
type AsyncIAsyncResult<'T>(callback: System.AsyncCallback, state: objnull) =
// This gets set to false if the result is not available by the
// time the IAsyncResult is returned to the caller of Begin
let mutable completedSynchronously = true
Expand Down Expand Up @@ -2035,7 +2035,7 @@ type Async =
// Register the result.
resultCell.RegisterResult(res, reuseThread = true) |> unfake)

let (iar: IAsyncResult) = beginAction (callback, (null: obj))
let (iar: IAsyncResult) = beginAction (callback, (null: objnull))

if iar.CompletedSynchronously then
// Ensure cancellation is not possible beyond this point
Expand Down Expand Up @@ -2067,7 +2067,7 @@ type Async =
static member AsBeginEnd<'Arg, 'T>
(computation: ('Arg -> Async<'T>))
// The 'Begin' member
: ('Arg * System.AsyncCallback * obj -> System.IAsyncResult) *
: ('Arg * System.AsyncCallback * objnull -> System.IAsyncResult) *
(System.IAsyncResult -> 'T) *
(System.IAsyncResult -> unit)
=
Expand Down Expand Up @@ -2334,7 +2334,7 @@ module WebExtensions =
Async.FromContinuations(fun (cont, econt, ccont) ->
let userToken = obj ()

let rec delegate' (_: obj) (args: #ComponentModel.AsyncCompletedEventArgs) =
let rec delegate' (_: objnull) (args: #ComponentModel.AsyncCompletedEventArgs) =
// ensure we handle the completed event from correct download call
if userToken = args.UserState then
event.RemoveHandler handle
Expand Down
10 changes: 5 additions & 5 deletions src/FSharp.Core/async.fsi
Original file line number Diff line number Diff line change
Expand Up @@ -860,7 +860,7 @@ namespace Microsoft.FSharp.Control
/// <category index="5">Legacy .NET Async Interoperability</category>
///
/// <example-tbd></example-tbd>
static member FromBeginEnd : beginAction:(System.AsyncCallback * obj -> System.IAsyncResult) * endAction:(System.IAsyncResult -> 'T) * ?cancelAction : (unit -> unit) -> Async<'T>
static member FromBeginEnd : beginAction:(System.AsyncCallback * objnull -> System.IAsyncResult) * endAction:(System.IAsyncResult -> 'T) * ?cancelAction : (unit -> unit) -> Async<'T>

/// <summary>
/// Creates an asynchronous computation in terms of a Begin/End pair of actions in
Expand All @@ -885,7 +885,7 @@ namespace Microsoft.FSharp.Control
/// <category index="5">Legacy .NET Async Interoperability</category>
///
/// <example-tbd></example-tbd>
static member FromBeginEnd : arg:'Arg1 * beginAction:('Arg1 * System.AsyncCallback * obj -> System.IAsyncResult) * endAction:(System.IAsyncResult -> 'T) * ?cancelAction : (unit -> unit) -> Async<'T>
static member FromBeginEnd : arg:'Arg1 * beginAction:('Arg1 * System.AsyncCallback * objnull -> System.IAsyncResult) * endAction:(System.IAsyncResult -> 'T) * ?cancelAction : (unit -> unit) -> Async<'T>

/// <summary>
/// Creates an asynchronous computation in terms of a Begin/End pair of actions in
Expand All @@ -909,7 +909,7 @@ namespace Microsoft.FSharp.Control
/// <category index="5">Legacy .NET Async Interoperability</category>
///
/// <example-tbd></example-tbd>
static member FromBeginEnd : arg1:'Arg1 * arg2:'Arg2 * beginAction:('Arg1 * 'Arg2 * System.AsyncCallback * obj -> System.IAsyncResult) * endAction:(System.IAsyncResult -> 'T) * ?cancelAction : (unit -> unit) -> Async<'T>
static member FromBeginEnd : arg1:'Arg1 * arg2:'Arg2 * beginAction:('Arg1 * 'Arg2 * System.AsyncCallback * objnull -> System.IAsyncResult) * endAction:(System.IAsyncResult -> 'T) * ?cancelAction : (unit -> unit) -> Async<'T>

/// <summary>Creates an asynchronous computation in terms of a Begin/End pair of actions in
/// the style used in .NET 2.0 APIs.</summary>
Expand All @@ -933,7 +933,7 @@ namespace Microsoft.FSharp.Control
/// <category index="5">Legacy .NET Async Interoperability</category>
///
/// <example-tbd></example-tbd>
static member FromBeginEnd : arg1:'Arg1 * arg2:'Arg2 * arg3:'Arg3 * beginAction:('Arg1 * 'Arg2 * 'Arg3 * System.AsyncCallback * obj -> System.IAsyncResult) * endAction:(System.IAsyncResult -> 'T) * ?cancelAction : (unit -> unit) -> Async<'T>
static member FromBeginEnd : arg1:'Arg1 * arg2:'Arg2 * arg3:'Arg3 * beginAction:('Arg1 * 'Arg2 * 'Arg3 * System.AsyncCallback * objnull -> System.IAsyncResult) * endAction:(System.IAsyncResult -> 'T) * ?cancelAction : (unit -> unit) -> Async<'T>

/// <summary>Creates three functions that can be used to implement the .NET 1.0 Asynchronous
/// Programming Model (APM) for a given asynchronous computation.</summary>
Expand All @@ -948,7 +948,7 @@ namespace Microsoft.FSharp.Control
/// <example-tbd></example-tbd>
static member AsBeginEnd : computation:('Arg -> Async<'T>) ->
// The 'Begin' member
('Arg * System.AsyncCallback * obj -> System.IAsyncResult) *
('Arg * System.AsyncCallback * objnull -> System.IAsyncResult) *
// The 'End' member
(System.IAsyncResult -> 'T) *
// The 'Cancel' member
Expand Down
18 changes: 9 additions & 9 deletions src/FSharp.Core/event.fs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ module private Atomic =
type DelegateEvent<'Delegate when 'Delegate :> System.Delegate>() =
let mutable multicast: System.Delegate = null

member x.Trigger(args: obj array) =
member x.Trigger(args: objnull array) =
match multicast with
| null -> ()
| d -> d.DynamicInvoke(args) |> ignore
Expand All @@ -54,30 +54,30 @@ type EventDelegee<'Args>(observer: System.IObserver<'Args>) =
assert false
null // should not be called, one-argument case don't use makeTuple function

member x.Invoke(_sender: obj, args: 'Args) =
member x.Invoke(_sender: objnull, args: 'Args) =
observer.OnNext args

member x.Invoke(_sender: obj, a, b) =
member x.Invoke(_sender: objnull, a, b) =
let args = makeTuple ([| a; b |]) :?> 'Args
observer.OnNext args

member x.Invoke(_sender: obj, a, b, c) =
member x.Invoke(_sender: objnull, a, b, c) =
let args = makeTuple ([| a; b; c |]) :?> 'Args
observer.OnNext args

member x.Invoke(_sender: obj, a, b, c, d) =
member x.Invoke(_sender: objnull, a, b, c, d) =
let args = makeTuple ([| a; b; c; d |]) :?> 'Args
observer.OnNext args

member x.Invoke(_sender: obj, a, b, c, d, e) =
member x.Invoke(_sender: objnull, a, b, c, d, e) =
let args = makeTuple ([| a; b; c; d; e |]) :?> 'Args
observer.OnNext args

member x.Invoke(_sender: obj, a, b, c, d, e, f) =
member x.Invoke(_sender: objnull, a, b, c, d, e, f) =
let args = makeTuple ([| a; b; c; d; e; f |]) :?> 'Args
observer.OnNext args

type EventWrapper<'Delegate, 'Args> = delegate of 'Delegate * obj * 'Args -> unit
type EventWrapper<'Delegate, 'Args> = delegate of 'Delegate * objnull * 'Args -> unit

[<CompiledName("FSharpEvent`2")>]
type Event<'Delegate, 'Args
Expand Down Expand Up @@ -122,7 +122,7 @@ type Event<'Delegate, 'Args
else
mi

member x.Trigger(sender: obj, args: 'Args) =
member x.Trigger(sender: objnull, args: 'Args) =
// Copy multicast value into local variable to avoid changing during member call.
let multicast = multicast

Expand Down
4 changes: 2 additions & 2 deletions src/FSharp.Core/event.fsi
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ type DelegateEvent<'Delegate when 'Delegate :> System.Delegate> =
/// <param name="args">The parameters for the event.</param>
///
/// <example-tbd></example-tbd>
member Trigger: args: obj array -> unit
member Trigger: args: objnull array -> unit

/// <summary>Publishes the event as a first class event value.</summary>
///
Expand All @@ -49,7 +49,7 @@ type Event<'Delegate, 'Args
/// <param name="args">The parameters for the event.</param>
///
/// <example-tbd></example-tbd>
member Trigger: sender: obj * args: 'Args -> unit
member Trigger: sender: objnull * args: 'Args -> unit

/// <summary>Publishes the event as a first class event value.</summary>
///
Expand Down
5 changes: 3 additions & 2 deletions src/FSharp.Core/fslib-extra-pervasives.fs
Original file line number Diff line number Diff line change
Expand Up @@ -483,7 +483,7 @@ type ITypeProvider =
abstract GetStaticParameters: typeWithoutArguments: Type -> ParameterInfo array

abstract ApplyStaticArguments:
typeWithoutArguments: Type * typePathWithArguments: string array * staticArguments: obj array -> Type
typeWithoutArguments: Type * typePathWithArguments: string array * staticArguments: objnull array -> Type

abstract GetInvokerExpression: syntheticMethodBase: MethodBase * parameters: Expr array -> Expr

Expand All @@ -496,4 +496,5 @@ type ITypeProvider2 =
abstract GetStaticParametersForMethod: methodWithoutArguments: MethodBase -> ParameterInfo array

abstract ApplyStaticArgumentsForMethod:
methodWithoutArguments: MethodBase * methodNameWithArguments: string * staticArguments: obj array -> MethodBase
methodWithoutArguments: MethodBase * methodNameWithArguments: string * staticArguments: objnull array ->
MethodBase
Loading

0 comments on commit d4ad891

Please sign in to comment.