diff --git a/src/fsharp/ConstraintSolver.fs b/src/fsharp/ConstraintSolver.fs index 586b91c41b0..4f00fd73ed2 100644 --- a/src/fsharp/ConstraintSolver.fs +++ b/src/fsharp/ConstraintSolver.fs @@ -1931,7 +1931,7 @@ and SolveTypeIsNonNullableValueType (csenv: ConstraintSolverEnv) ndeep m2 trace | _ -> let underlyingTy = stripTyEqnsAndMeasureEqns g ty if isStructTy g underlyingTy then - if isAppTy g underlyingTy && tyconRefEq g g.system_Nullable_tcref (tcrefOfAppTy g underlyingTy) then + if isNullableTy g underlyingTy then return! ErrorD (ConstraintSolverError(FSComp.SR.csTypeParameterCannotBeNullable(), m, m)) else return! ErrorD (ConstraintSolverError(FSComp.SR.csGenericConstructRequiresStructType(NicePrint.minimalStringOfType denv ty), m, m2)) diff --git a/src/fsharp/MethodCalls.fs b/src/fsharp/MethodCalls.fs index 5a8d66c875b..895f2d1f729 100644 --- a/src/fsharp/MethodCalls.fs +++ b/src/fsharp/MethodCalls.fs @@ -20,6 +20,7 @@ open FSharp.Compiler.Tastops.DebugPrint open FSharp.Compiler.TcGlobals open FSharp.Compiler.TypeRelations open FSharp.Compiler.AttributeChecking +open Internal.Utilities #if !NO_EXTENSIONTYPING open FSharp.Compiler.ExtensionTyping @@ -151,7 +152,9 @@ let AdjustCalledArgType (infoReader: InfoReader) isConstraint (calledArg: Called mkRefCellTy g (destByrefTy g calledArgTy) else - // If the called method argument is a delegate type, then the caller may provide a function + // If the called method argument is a delegate type, and the caller is known to be a function type, then the caller may provide a function + // If the called method argument is an Expression type, and the caller is known to be a function type, then the caller may provide a T + // If the called method argument is an [] Quotations.Expr, and the caller is not known to be a quoted expression type, then the caller may provide a T let calledArgTy = let adjustDelegateTy calledTy = let (SigOfFunctionForDelegate(_, delArgTys, _, fty)) = GetSigOfFunctionForDelegate infoReader calledTy m AccessibleFromSomewhere @@ -178,14 +181,18 @@ let AdjustCalledArgType (infoReader: InfoReader) isConstraint (calledArg: Called else calledArgTy // Adjust the called argument type to take into account whether the caller's argument is M(?arg=Some(3)) or M(arg=1) - // If the called method argument is optional with type Option, then the caller may provide a T, unless their argument is propagating-optional (i.e. isOptCallerArg) + // If the called method argument is Callee-side optional with type Option, and the caller argument is not explicitly optional (callerArg.IsOptional), then the caller may provide a T + // If the called method argument is Caller-side optional with type Nullable, and the caller argument is not explicitly optional (callerArg.IsOptional), then the caller may provide a T let calledArgTy = match calledArg.OptArgInfo with - | NotOptional -> calledArgTy + | NotOptional -> calledArgTy | CalleeSide when not callerArg.IsOptional && isOptionTy g calledArgTy -> destOptionTy g calledArgTy - | CalleeSide | CallerSide _ -> calledArgTy - calledArgTy - + // This will be added in https://github.com/dotnet/fsharp/pull/7276 + //| CallerSide _ when not callerArg.IsOptional && isNullableTy g calledArgTy -> destNullableTy g calledArgTy + | CalleeSide + | CallerSide _ -> calledArgTy + + calledArgTy //------------------------------------------------------------------------- // CalledMeth @@ -540,6 +547,351 @@ let ExamineMethodForLambdaPropagation (x: CalledMeth) = else None +//------------------------------------------------------------------------- +// Adjust caller arguments as part of building a method call +//------------------------------------------------------------------------- + +/// Build a call to the System.Object constructor taking no arguments, +let BuildObjCtorCall (g: TcGlobals) m = + let ilMethRef = (mkILCtorMethSpecForTy(g.ilg.typ_Object, [])).MethodRef + Expr.Op (TOp.ILCall (false, false, false, false, CtorValUsedAsSuperInit, false, true, ilMethRef, [], [], [g.obj_ty]), [], [], m) + +/// Implements the elaborated form of adhoc conversions from functions to delegates at member callsites +let BuildNewDelegateExpr (eventInfoOpt: EventInfo option, g, amap, delegateTy, invokeMethInfo: MethInfo, delArgTys, f, fty, m) = + let slotsig = invokeMethInfo.GetSlotSig(amap, m) + let delArgVals, expr = + let topValInfo = ValReprInfo([], List.replicate (max 1 (List.length delArgTys)) ValReprInfo.unnamedTopArg, ValReprInfo.unnamedRetVal) + + // Try to pull apart an explicit lambda and use it directly + // Don't do this in the case where we're adjusting the arguments of a function used to build a .NET-compatible event handler + let lambdaContents = + if Option.isSome eventInfoOpt then + None + else + tryDestTopLambda g amap topValInfo (f, fty) + + match lambdaContents with + | None -> + + if List.exists (isByrefTy g) delArgTys then + error(Error(FSComp.SR.tcFunctionRequiresExplicitLambda(List.length delArgTys), m)) + + let delArgVals = delArgTys |> List.mapi (fun i argty -> fst (mkCompGenLocal m ("delegateArg" + string i) argty)) + let expr = + let args = + match eventInfoOpt with + | Some einfo -> + match delArgVals with + | [] -> error(nonStandardEventError einfo.EventName m) + | h :: _ when not (isObjTy g h.Type) -> error(nonStandardEventError einfo.EventName m) + | h :: t -> [exprForVal m h; mkRefTupledVars g m t] + | None -> + if isNil delArgTys then [mkUnit g m] else List.map (exprForVal m) delArgVals + mkApps g ((f, fty), [], args, m) + delArgVals, expr + + | Some _ -> + let _, _, _, vsl, body, _ = IteratedAdjustArityOfLambda g amap topValInfo f + List.concat vsl, body + + let meth = TObjExprMethod(slotsig, [], [], [delArgVals], expr, m) + mkObjExpr(delegateTy, None, BuildObjCtorCall g m, [meth], [], m) + +let CoerceFromFSharpFuncToDelegate g amap infoReader ad callerArgTy m callerArgExpr delegateTy = + let (SigOfFunctionForDelegate(invokeMethInfo, delArgTys, _, _)) = GetSigOfFunctionForDelegate infoReader delegateTy m ad + BuildNewDelegateExpr (None, g, amap, delegateTy, invokeMethInfo, delArgTys, callerArgExpr, callerArgTy, m) + +// Handle adhoc argument conversions +let AdjustCallerArgExprForCoercions (g: TcGlobals) amap infoReader ad isOutArg calledArgTy (reflArgInfo: ReflectedArgInfo) callerArgTy m callerArgExpr = + + if isByrefTy g calledArgTy && isRefCellTy g callerArgTy then + None, Expr.Op (TOp.RefAddrGet false, [destRefCellTy g callerArgTy], [callerArgExpr], m) + +#if IMPLICIT_ADDRESS_OF + elif isInByrefTy g calledArgTy && not (isByrefTy g callerArgTy) then + let wrap, callerArgExprAddress, _readonly, _writeonly = mkExprAddrOfExpr g true false NeverMutates callerArgExpr None m + Some wrap, callerArgExprAddress +#endif + + elif isDelegateTy g calledArgTy && isFunTy g callerArgTy then + None, CoerceFromFSharpFuncToDelegate g amap infoReader ad callerArgTy m callerArgExpr calledArgTy + + elif isLinqExpressionTy g calledArgTy && isDelegateTy g (destLinqExpressionTy g calledArgTy) && isFunTy g callerArgTy then + let delegateTy = destLinqExpressionTy g calledArgTy + let expr = CoerceFromFSharpFuncToDelegate g amap infoReader ad callerArgTy m callerArgExpr delegateTy + None, mkCallQuoteToLinqLambdaExpression g m delegateTy (Expr.Quote (expr, ref None, false, m, mkQuotedExprTy g delegateTy)) + + // auto conversions to quotations (to match auto conversions to LINQ expressions) + elif reflArgInfo.AutoQuote && isQuotedExprTy g calledArgTy && not (isQuotedExprTy g callerArgTy) then + match reflArgInfo with + | ReflectedArgInfo.Quote true -> + None, mkCallLiftValueWithDefn g m calledArgTy callerArgExpr + | ReflectedArgInfo.Quote false -> + None, Expr.Quote (callerArgExpr, ref None, false, m, calledArgTy) + | ReflectedArgInfo.None -> failwith "unreachable" // unreachable due to reflArgInfo.AutoQuote condition + + // Note: out args do not need to be coerced + elif isOutArg then + None, callerArgExpr + + // Note: not all these casts are reported in quotations + else + None, mkCoerceIfNeeded g calledArgTy callerArgTy callerArgExpr + +// Handle CallerSide optional arguments. +// +// CallerSide optional arguments are largely for COM interop, e.g. to PIA assemblies for Word etc. +// As a result we follow the VB and C# behavior here. +// +// "1. If the parameter is statically typed as System.Object and does not have a value, then there are four cases: +// a. The parameter is marked with MarshalAs(IUnknown), MarshalAs(Interface), or MarshalAs(IDispatch). In this case we pass null. +// b. Else if the parameter is marked with IUnknownConstantAttribute. In this case we pass new System.Runtime.InteropServices.UnknownWrapper(null) +// c. Else if the parameter is marked with IDispatchConstantAttribute. In this case we pass new System.Runtime.InteropServices.DispatchWrapper(null) +// d. Else, we will pass Missing.Value. +// 2. Otherwise, if there is a value attribute, then emit the default value. +// 3. Otherwise, we emit default(T). +// 4. Finally, we apply conversions from the value to the parameter type. This is where the nullable conversions take place for VB. +// - VB allows you to mark ref parameters as optional. The semantics of this is that we create a temporary +// with type = type of parameter, load the optional value to it, and call the method. +// - VB also allows you to mark arrays with Nothing as the optional value. +// - VB also allows you to pass intrinsic values as optional values to parameters +// typed as Object. What we do in this case is we box the intrinsic value." +// +let AdjustOptionalCallerArgExprs tcFieldInit eCallerMemberName g (calledMeth: CalledMeth<_>) mItem mMethExpr = + + let assignedNamedArgs = calledMeth.ArgSets |> List.collect (fun argSet -> argSet.AssignedNamedArgs) + let unnamedCalledArgs = calledMeth.ArgSets |> List.collect (fun argSet -> argSet.UnnamedCalledArgs) + let unnamedCallerArgs = calledMeth.ArgSets |> List.collect (fun argSet -> argSet.UnnamedCallerArgs) + let unnamedArgs = + (unnamedCalledArgs, unnamedCallerArgs) ||> List.map2 (fun called caller -> + { NamedArgIdOpt = None; CalledArg=called; CallerArg=caller }) + + let emptyPreBinder (e: Expr) = e + + // Adjust all the optional arguments that require a default value to be inserted into the call + let optArgs, optArgPreBinder = + (emptyPreBinder, calledMeth.UnnamedCalledOptArgs) ||> List.mapFold (fun wrapper calledArg -> + let calledArgTy = calledArg.CalledArgumentType + let wrapper2, expr = + match calledArg.OptArgInfo with + | NotOptional -> + error(InternalError("Unexpected NotOptional", mItem)) + + | CallerSide dfltVal -> + + let rec build currCalledArgTy currDfltVal = + match currDfltVal with + | MissingValue -> + // Add an I_nop if this is an initonly field to make sure we never recognize it as an lvalue. See mkExprAddrOfExpr. + emptyPreBinder, mkAsmExpr ([ mkNormalLdsfld (fspec_Missing_Value g); AI_nop ], [], [], [currCalledArgTy], mMethExpr) + + | DefaultValue -> + emptyPreBinder, mkDefault(mMethExpr, currCalledArgTy) + + | Constant fieldInit -> + match currCalledArgTy with + | NullableTy g inst when fieldInit <> ILFieldInit.Null -> + let nullableTy = mkILNonGenericBoxedTy(g.FindSysILTypeRef "System.Nullable`1") + let ctor = mkILCtorMethSpecForTy(nullableTy, [ILType.TypeVar 0us]).MethodRef + let ctorArgs = [Expr.Const (tcFieldInit mMethExpr fieldInit, mMethExpr, inst)] + emptyPreBinder, Expr.Op (TOp.ILCall (false, false, true, true, NormalValUse, false, false, ctor, [inst], [], [currCalledArgTy]), [], ctorArgs, mMethExpr) + | ByrefTy g inst -> + build inst (PassByRef(inst, currDfltVal)) + | _ -> + match calledArg.CallerInfo, eCallerMemberName with + | CallerLineNumber, _ when typeEquiv g currCalledArgTy g.int_ty -> + emptyPreBinder, Expr.Const (Const.Int32(mMethExpr.StartLine), mMethExpr, currCalledArgTy) + | CallerFilePath, _ when typeEquiv g currCalledArgTy g.string_ty -> + let fileName = mMethExpr.FileName |> FileSystem.GetFullPathShim |> PathMap.apply g.pathMap + emptyPreBinder, Expr.Const (Const.String fileName, mMethExpr, currCalledArgTy) + | CallerMemberName, Some callerName when (typeEquiv g currCalledArgTy g.string_ty) -> + emptyPreBinder, Expr.Const (Const.String callerName, mMethExpr, currCalledArgTy) + | _ -> + emptyPreBinder, Expr.Const (tcFieldInit mMethExpr fieldInit, mMethExpr, currCalledArgTy) + + | WrapperForIDispatch -> + match g.TryFindSysILTypeRef "System.Runtime.InteropServices.DispatchWrapper" with + | None -> error(Error(FSComp.SR.fscSystemRuntimeInteropServicesIsRequired(), mMethExpr)) + | Some tref -> + let ty = mkILNonGenericBoxedTy tref + let mref = mkILCtorMethSpecForTy(ty, [g.ilg.typ_Object]).MethodRef + let expr = Expr.Op (TOp.ILCall (false, false, false, true, NormalValUse, false, false, mref, [], [], [g.obj_ty]), [], [mkDefault(mMethExpr, currCalledArgTy)], mMethExpr) + emptyPreBinder, expr + + | WrapperForIUnknown -> + match g.TryFindSysILTypeRef "System.Runtime.InteropServices.UnknownWrapper" with + | None -> error(Error(FSComp.SR.fscSystemRuntimeInteropServicesIsRequired(), mMethExpr)) + | Some tref -> + let ty = mkILNonGenericBoxedTy tref + let mref = mkILCtorMethSpecForTy(ty, [g.ilg.typ_Object]).MethodRef + let expr = Expr.Op (TOp.ILCall (false, false, false, true, NormalValUse, false, false, mref, [], [], [g.obj_ty]), [], [mkDefault(mMethExpr, currCalledArgTy)], mMethExpr) + emptyPreBinder, expr + + | PassByRef (ty, dfltVal2) -> + let v, _ = mkCompGenLocal mMethExpr "defaultByrefArg" ty + let wrapper2, rhs = build currCalledArgTy dfltVal2 + (wrapper2 >> mkCompGenLet mMethExpr v rhs), mkValAddr mMethExpr false (mkLocalValRef v) + build calledArgTy dfltVal + + | CalleeSide -> + let calledNonOptTy = + if isOptionTy g calledArgTy then + destOptionTy g calledArgTy + else + calledArgTy // should be unreachable + + match calledArg.CallerInfo, eCallerMemberName with + | CallerLineNumber, _ when typeEquiv g calledNonOptTy g.int_ty -> + let lineExpr = Expr.Const(Const.Int32 mMethExpr.StartLine, mMethExpr, calledNonOptTy) + emptyPreBinder, mkSome g calledNonOptTy lineExpr mMethExpr + | CallerFilePath, _ when typeEquiv g calledNonOptTy g.string_ty -> + let fileName = mMethExpr.FileName |> FileSystem.GetFullPathShim |> PathMap.apply g.pathMap + let filePathExpr = Expr.Const (Const.String(fileName), mMethExpr, calledNonOptTy) + emptyPreBinder, mkSome g calledNonOptTy filePathExpr mMethExpr + | CallerMemberName, Some(callerName) when typeEquiv g calledNonOptTy g.string_ty -> + let memberNameExpr = Expr.Const (Const.String callerName, mMethExpr, calledNonOptTy) + emptyPreBinder, mkSome g calledNonOptTy memberNameExpr mMethExpr + | _ -> + emptyPreBinder, mkNone g calledNonOptTy mMethExpr + + // Combine the variable allocators (if any) + let wrapper = (wrapper >> wrapper2) + let callerArg = CallerArg(calledArgTy, mMethExpr, false, expr) + { NamedArgIdOpt = None; CalledArg = calledArg; CallerArg = callerArg }, wrapper) + + // Adjust all the optional arguments + let wrapOptionalArg (assignedArg: AssignedCalledArg<_>) = + let (CallerArg(callerArgTy, m, isOptCallerArg, callerArgExpr)) = assignedArg.CallerArg + match assignedArg.CalledArg.OptArgInfo with + | NotOptional -> + if isOptCallerArg then errorR(Error(FSComp.SR.tcFormalArgumentIsNotOptional(), m)) + assignedArg + | _ -> + let callerArgExpr2 = + match assignedArg.CalledArg.OptArgInfo with + | CallerSide _ -> + if isOptCallerArg then + // M(?x=bopt) when M(A) --> M(?x=bopt.Value) for caller-side + // STRUCT OPTIONS: if we allow struct options as optional arguments then we should take + // the address correctly. + mkUnionCaseFieldGetUnprovenViaExprAddr (callerArgExpr, mkSomeCase g, [destOptionTy g callerArgTy], 0, m) + else + // M(x=b) when M(A) --> M(?x=b) for caller-side + callerArgExpr + + | CalleeSide -> + if isOptCallerArg then + // M(?x=bopt) when M(A) --> M(?x=Some(bopt.Value)) + callerArgExpr + else + // M(x=b) when M(A) --> M(?x=Some(b :> A)) + let calledArgTy = assignedArg.CalledArg.CalledArgumentType + if isOptionTy g calledArgTy then + let calledNonOptTy = destOptionTy g calledArgTy + mkSome g calledNonOptTy (mkCoerceIfNeeded g calledNonOptTy callerArgTy callerArgExpr) m + else + callerArgExpr // should be unreachable + + | _ -> failwith "Unreachable" + { assignedArg with CallerArg=CallerArg(tyOfExpr g callerArgExpr2, m, isOptCallerArg, callerArgExpr2) } + + let adjustedNormalUnnamedArgs = List.map wrapOptionalArg unnamedArgs + let adjustedAssignedNamedArgs = List.map wrapOptionalArg assignedNamedArgs + + optArgs, optArgPreBinder, adjustedNormalUnnamedArgs, adjustedAssignedNamedArgs + +/// Adjust any 'out' arguments, passing in the address of a mutable local +let AdjustOutCallerArgExprs g (calledMeth: CalledMeth<_>) mMethExpr = + calledMeth.UnnamedCalledOutArgs |> List.map (fun calledArg -> + let calledArgTy = calledArg.CalledArgumentType + let outArgTy = destByrefTy g calledArgTy + let outv, outArgExpr = mkMutableCompGenLocal mMethExpr PrettyNaming.outArgCompilerGeneratedName outArgTy // mutable! + let expr = mkDefault (mMethExpr, outArgTy) + let callerArg = CallerArg (calledArgTy, mMethExpr, false, mkValAddr mMethExpr false (mkLocalValRef outv)) + let outArg = { NamedArgIdOpt=None;CalledArg=calledArg;CallerArg=callerArg } + outArg, outArgExpr, mkCompGenBind outv expr) + |> List.unzip3 + +let AdjustParamArrayCallerArgExprs g amap infoReader ad (calledMeth: CalledMeth<_>) mMethExpr = + let argSets = calledMeth.ArgSets + + let paramArrayCallerArgs = argSets |> List.collect (fun argSet -> argSet.ParamArrayCallerArgs) + match calledMeth.ParamArrayCalledArgOpt with + | None -> + [], [] + | Some paramArrayCalledArg -> + let paramArrayCalledArgElementType = destArrayTy g paramArrayCalledArg.CalledArgumentType + + let paramArrayPreBinders, es = + paramArrayCallerArgs + |> List.map (fun callerArg -> + let (CallerArg(callerArgTy, m, isOutArg, callerArgExpr)) = callerArg + AdjustCallerArgExprForCoercions g amap infoReader ad isOutArg paramArrayCalledArgElementType paramArrayCalledArg.ReflArgInfo callerArgTy m callerArgExpr) + |> List.unzip + + let arg = + [ { NamedArgIdOpt = None + CalledArg=paramArrayCalledArg + CallerArg=CallerArg(paramArrayCalledArg.CalledArgumentType, mMethExpr, false, Expr.Op (TOp.Array, [paramArrayCalledArgElementType], es, mMethExpr)) } ] + paramArrayPreBinders, arg + +/// Build the argument list for a method call. Adjust for param array, optional arguments, byref arguments and coercions. +/// For example, if you pass an F# reference cell to a byref then we must get the address of the +/// contents of the ref. Likewise lots of adjustments are made for optional arguments etc. +let AdjustCallerArgExprs tcFieldInit eCallerMemberName g amap infoReader ad (calledMeth: CalledMeth<_>) objArgs lambdaVars mItem mMethExpr = + let calledMethInfo = calledMeth.Method + + // Some of the code below must allocate temporary variables or bind other variables to particular values. + // As usual we represent variable allocators by expr -> expr functions + // which we then use to wrap the whole expression. These will either do nothing or pre-bind a variable. It doesn't + // matter what order they are applied in as long as they are all composed together. + let emptyPreBinder (e: Expr) = e + + // For unapplied 'e.M' we first evaluate 'e' outside the lambda, i.e. 'let v = e in (fun arg -> v.M(arg))' + let objArgPreBinder, objArgs = + match objArgs, lambdaVars with + | [objArg], Some _ -> + if calledMethInfo.IsExtensionMember && calledMethInfo.ObjArgNeedsAddress(amap, mMethExpr) then + error(Error(FSComp.SR.tcCannotPartiallyApplyExtensionMethodForByref(calledMethInfo.DisplayName), mMethExpr)) + let objArgTy = tyOfExpr g objArg + let v, ve = mkCompGenLocal mMethExpr "objectArg" objArgTy + (fun body -> mkCompGenLet mMethExpr v objArg body), [ve] + | _ -> + emptyPreBinder, objArgs + + // Handle param array and optional arguments + let paramArrayPreBinders, paramArrayArgs = + AdjustParamArrayCallerArgExprs g amap infoReader ad calledMeth mMethExpr + + let optArgs, optArgPreBinder, adjustedNormalUnnamedArgs, adjustedFinalAssignedNamedArgs = + AdjustOptionalCallerArgExprs tcFieldInit eCallerMemberName g calledMeth mItem mMethExpr + + let outArgs, outArgExprs, outArgTmpBinds = + AdjustOutCallerArgExprs g calledMeth mMethExpr + + let allArgs = + adjustedNormalUnnamedArgs @ + adjustedFinalAssignedNamedArgs @ + paramArrayArgs @ + optArgs @ + outArgs + + let allArgs = + allArgs |> List.sortBy (fun x -> x.Position) + + let allArgsPreBinders, allArgsCoerced = + allArgs + |> List.map (fun assignedArg -> + let isOutArg = assignedArg.CalledArg.IsOutArg + let reflArgInfo = assignedArg.CalledArg.ReflArgInfo + let calledArgTy = assignedArg.CalledArg.CalledArgumentType + let (CallerArg(callerArgTy, m, _, e)) = assignedArg.CallerArg + + AdjustCallerArgExprForCoercions g amap infoReader ad isOutArg calledArgTy reflArgInfo callerArgTy m e) + |> List.unzip + + objArgPreBinder, objArgs, allArgsPreBinders, allArgs, allArgsCoerced, optArgPreBinder, paramArrayPreBinders, outArgExprs, outArgTmpBinds + //------------------------------------------------------------------------- // Additional helpers for building method calls and doing TAST generation //------------------------------------------------------------------------- @@ -573,27 +925,30 @@ let ComputeConstrainedCallInfo g amap m (objArgs, minfo: MethInfo) = | _ -> None - /// Adjust the 'this' pointer before making a call /// Take the address of a struct, and coerce to an interface/base/constraint type if necessary let TakeObjAddrForMethodCall g amap (minfo: MethInfo) isMutable m objArgs f = let ccallInfo = ComputeConstrainedCallInfo g amap m (objArgs, minfo) let wrap, objArgs = + match objArgs with | [objArgExpr] -> + let hasCallInfo = ccallInfo.IsSome let mustTakeAddress = hasCallInfo || minfo.ObjArgNeedsAddress(amap, m) let objArgTy = tyOfExpr g objArgExpr - let wrap, objArgExpr', isReadOnly, _isWriteOnly = mkExprAddrOfExpr g mustTakeAddress hasCallInfo isMutable objArgExpr None m + + let wrap, objArgExprAddr, isReadOnly, _isWriteOnly = + mkExprAddrOfExpr g mustTakeAddress hasCallInfo isMutable objArgExpr None m // Extension members and calls to class constraints may need a coercion for their object argument - let objArgExpr' = + let objArgExprCoerced = if not hasCallInfo && not (TypeDefinitelySubsumesTypeNoCoercion 0 g amap m minfo.ApparentEnclosingType objArgTy) then - mkCoerceExpr(objArgExpr', minfo.ApparentEnclosingType, m, objArgTy) + mkCoerceExpr(objArgExprAddr, minfo.ApparentEnclosingType, m, objArgTy) else - objArgExpr' + objArgExprAddr // Check to see if the extension member uses the extending type as a byref. // If so, make sure we don't allow readonly/immutable values to be passed byref from an extension member. @@ -605,7 +960,7 @@ let TakeObjAddrForMethodCall g amap (minfo: MethInfo) isMutable m objArgs f = errorR(Error(FSComp.SR.tcCannotCallExtensionMethodInrefToByref(minfo.DisplayName), m))) - wrap, [objArgExpr'] + wrap, [objArgExprCoerced] | _ -> id, objArgs @@ -616,11 +971,6 @@ let TakeObjAddrForMethodCall g amap (minfo: MethInfo) isMutable m objArgs f = // Build method calls. //------------------------------------------------------------------------- -//------------------------------------------------------------------------- -// Build calls -//------------------------------------------------------------------------- - - /// Build an expression node that is a call to a .NET method. let BuildILMethInfoCall g amap m isProp (minfo: ILMethInfo) valUseFlags minst direct args = let valu = isStructTy g minfo.ApparentEnclosingType @@ -638,11 +988,6 @@ let BuildILMethInfoCall g amap m isProp (minfo: ILMethInfo) valUseFlags minst di Expr.Op (TOp.ILCall (useCallvirt, isProtected, valu, newobj, valUseFlags, isProp, isDllImport, ilMethRef, minfo.DeclaringTypeInst, minst, retTy), [], args, m), exprTy -/// Build a call to the System.Object constructor taking no arguments, -let BuildObjCtorCall (g: TcGlobals) m = - let ilMethRef = (mkILCtorMethSpecForTy(g.ilg.typ_Object, [])).MethodRef - Expr.Op (TOp.ILCall (false, false, false, false, CtorValUsedAsSuperInit, false, true, ilMethRef, [], [], [g.obj_ty]), [], [], m) - /// Build a call to an F# method. /// @@ -825,55 +1170,6 @@ let BuildMethodCall tcVal g amap isMutable m isProp minfo valUseFlags minst objA errorR(Error(FSComp.SR.tcDefaultStructConstructorCall(), m)) mkDefault (m, ty), ty) -//------------------------------------------------------------------------- -// Build delegate constructions (lambdas/functions to delegates) -//------------------------------------------------------------------------- - -/// Implements the elaborated form of adhoc conversions from functions to delegates at member callsites -let BuildNewDelegateExpr (eventInfoOpt: EventInfo option, g, amap, delegateTy, invokeMethInfo: MethInfo, delArgTys, f, fty, m) = - let slotsig = invokeMethInfo.GetSlotSig(amap, m) - let delArgVals, expr = - let topValInfo = ValReprInfo([], List.replicate (max 1 (List.length delArgTys)) ValReprInfo.unnamedTopArg, ValReprInfo.unnamedRetVal) - - // Try to pull apart an explicit lambda and use it directly - // Don't do this in the case where we're adjusting the arguments of a function used to build a .NET-compatible event handler - let lambdaContents = - if Option.isSome eventInfoOpt then - None - else - tryDestTopLambda g amap topValInfo (f, fty) - - match lambdaContents with - | None -> - - if List.exists (isByrefTy g) delArgTys then - error(Error(FSComp.SR.tcFunctionRequiresExplicitLambda(List.length delArgTys), m)) - - let delArgVals = delArgTys |> List.mapi (fun i argty -> fst (mkCompGenLocal m ("delegateArg" + string i) argty)) - let expr = - let args = - match eventInfoOpt with - | Some einfo -> - match delArgVals with - | [] -> error(nonStandardEventError einfo.EventName m) - | h :: _ when not (isObjTy g h.Type) -> error(nonStandardEventError einfo.EventName m) - | h :: t -> [exprForVal m h; mkRefTupledVars g m t] - | None -> - if isNil delArgTys then [mkUnit g m] else List.map (exprForVal m) delArgVals - mkApps g ((f, fty), [], args, m) - delArgVals, expr - - | Some _ -> - let _, _, _, vsl, body, _ = IteratedAdjustArityOfLambda g amap topValInfo f - List.concat vsl, body - - let meth = TObjExprMethod(slotsig, [], [], [delArgVals], expr, m) - mkObjExpr(delegateTy, None, BuildObjCtorCall g m, [meth], [], m) - -let CoerceFromFSharpFuncToDelegate g amap infoReader ad callerArgTy m callerArgExpr delegateTy = - let (SigOfFunctionForDelegate(invokeMethInfo, delArgTys, _, _)) = GetSigOfFunctionForDelegate infoReader delegateTy m ad - BuildNewDelegateExpr (None, g, amap, delegateTy, invokeMethInfo, delArgTys, callerArgExpr, callerArgTy, m) - //------------------------------------------------------------------------- // Import provided expressions @@ -972,11 +1268,14 @@ module ProvidedMethodCalls = st loop inputType - let convertProvidedExpressionToExprAndWitness tcVal (thisArg: Expr option, - allArgs: Exprs, - paramVars: Tainted[], - g, amap, mut, isProp, isSuperInit, m, - expr: Tainted) = + let convertProvidedExpressionToExprAndWitness + tcVal + (thisArg: Expr option, + allArgs: Exprs, + paramVars: Tainted[], + g, amap, mut, isProp, isSuperInit, m, + expr: Tainted) = + let varConv = // note: using paramVars.Length as assumed initial size, but this might not // be the optimal value; this wasn't checked before obsoleting Dictionary.ofList diff --git a/src/fsharp/TastOps.fs b/src/fsharp/TastOps.fs index 00fb9a08278..c638d9a864e 100644 --- a/src/fsharp/TastOps.fs +++ b/src/fsharp/TastOps.fs @@ -839,16 +839,6 @@ let tryNiceEntityRefOfTyOption ty = | TType_app (tcref, _) -> Some tcref | TType_measure (Measure.Con tcref) -> Some tcref | _ -> None - -let (|NullableTy|_|) g ty = - match tryAppTy g ty with - | ValueSome (tcref, [tyarg]) when tyconRefEq g tcref g.system_Nullable_tcref -> Some tyarg - | _ -> None - -let (|StripNullableTy|) g ty = - match tryAppTy g ty with - | ValueSome (tcref, [tyarg]) when tyconRefEq g tcref g.system_Nullable_tcref -> tyarg - | _ -> ty let mkInstForAppTy g ty = match tryAppTy g ty with @@ -3125,6 +3115,31 @@ let destOptionTy g ty = | ValueSome ty -> ty | ValueNone -> failwith "destOptionTy: not an option type" +let isNullableTy (g: TcGlobals) ty = + match tryDestAppTy g ty with + | ValueNone -> false + | ValueSome tcref -> tyconRefEq g g.system_Nullable_tcref tcref + +let tryDestNullableTy g ty = + match argsOfAppTy g ty with + | [ty1] when isNullableTy g ty -> ValueSome ty1 + | _ -> ValueNone + +let destNullableTy g ty = + match tryDestNullableTy g ty with + | ValueSome ty -> ty + | ValueNone -> failwith "destNullableTy: not a Nullable type" + +let (|NullableTy|_|) g ty = + match tryAppTy g ty with + | ValueSome (tcref, [tyarg]) when tyconRefEq g tcref g.system_Nullable_tcref -> Some tyarg + | _ -> None + +let (|StripNullableTy|) g ty = + match tryDestNullableTy g ty with + | ValueSome tyarg -> tyarg + | _ -> ty + let isLinqExpressionTy g ty = match tryDestAppTy g ty with | ValueNone -> false diff --git a/src/fsharp/TastOps.fsi b/src/fsharp/TastOps.fsi index 8c71736a844..b0743de8536 100755 --- a/src/fsharp/TastOps.fsi +++ b/src/fsharp/TastOps.fsi @@ -1388,7 +1388,7 @@ val mkVoidPtrTy : TcGlobals -> TType /// Build a single-dimensional array type val mkArrayType : TcGlobals -> TType -> TType -/// Determine is a type is an option type +/// Determine if a type is an option type val isOptionTy : TcGlobals -> TType -> bool /// Take apart an option type @@ -1397,6 +1397,15 @@ val destOptionTy : TcGlobals -> TType -> TType /// Try to take apart an option type val tryDestOptionTy : TcGlobals -> TType -> ValueOption +/// Determine is a type is a System.Nullable type +val isNullableTy : TcGlobals -> TType -> bool + +/// Try to take apart a System.Nullable type +val tryDestNullableTy: TcGlobals -> TType -> ValueOption + +/// Take apart a System.Nullable type +val destNullableTy: TcGlobals -> TType -> TType + /// Determine if a type is a System.Linq.Expression type val isLinqExpressionTy : TcGlobals -> TType -> bool diff --git a/src/fsharp/TypeChecker.fs b/src/fsharp/TypeChecker.fs index ae464f15758..70bb0f6b212 100644 --- a/src/fsharp/TypeChecker.fs +++ b/src/fsharp/TypeChecker.fs @@ -9671,75 +9671,74 @@ and TcMethodApplication // In one case (the second "single named item" rule) we delay the application of a // argument until we've produced a lambda that detuples an input tuple let curriedCallerArgsOpt, unnamedDelayedCallerArgExprOpt, exprTy = - match curriedCallerArgs with - | [] -> - None, None, exprTy - | _ -> - let unnamedCurriedCallerArgs, namedCurriedCallerArgs = curriedCallerArgs |> List.map GetMethodArgs |> List.unzip + match curriedCallerArgs with + | [] -> + None, None, exprTy + | _ -> + let unnamedCurriedCallerArgs, namedCurriedCallerArgs = curriedCallerArgs |> List.map GetMethodArgs |> List.unzip - // There is an mismatch when _uses_ of indexed property setters in the tc.fs code that calls this function. - // The arguments are passed as if they are curried with arity [numberOfIndexParameters;1], however in the TAST, indexed property setters - // are uncurried and have arity [numberOfIndexParameters+1]. - // - // Here we work around this mismatch by crunching all property argument lists to uncirred form. - // Ideally the problem needs to be solved at its root cause at the callsites to this function - let unnamedCurriedCallerArgs, namedCurriedCallerArgs = - if isProp then - [List.concat unnamedCurriedCallerArgs], [List.concat namedCurriedCallerArgs] - else - unnamedCurriedCallerArgs, namedCurriedCallerArgs + // There is an mismatch when _uses_ of indexed property setters in the tc.fs code that calls this function. + // The arguments are passed as if they are curried with arity [numberOfIndexParameters;1], however in the TAST, indexed property setters + // are uncurried and have arity [numberOfIndexParameters+1]. + // + // Here we work around this mismatch by crunching all property argument lists to uncirred form. + // Ideally the problem needs to be solved at its root cause at the callsites to this function + let unnamedCurriedCallerArgs, namedCurriedCallerArgs = + if isProp then + [List.concat unnamedCurriedCallerArgs], [List.concat namedCurriedCallerArgs] + else + unnamedCurriedCallerArgs, namedCurriedCallerArgs - let MakeUnnamedCallerArgInfo x = (x, GetNewInferenceTypeForMethodArg cenv env tpenv x, x.Range) - - // "single named item" rule. This is where we have a single accessible method - // member x.M(arg1) - // being used with - // x.M (x, y) - // Without this rule this requires - // x.M ((x, y)) - match candidates with - | [calledMeth] - when (namedCurriedCallerArgs |> List.forall isNil && - let curriedCalledArgs = calledMeth.GetParamAttribs(cenv.amap, mItem) - curriedCalledArgs.Length = 1 && - curriedCalledArgs.Head.Length = 1 && - curriedCalledArgs.Head.Head |> isSimpleFormalArg) -> - let unnamedCurriedCallerArgs = curriedCallerArgs |> List.map (MakeUnnamedCallerArgInfo >> List.singleton) - let namedCurriedCallerArgs = namedCurriedCallerArgs |> List.map (fun _ -> []) - (Some (unnamedCurriedCallerArgs, namedCurriedCallerArgs), None, exprTy) - - // "single named item" rule. This is where we have a single accessible method - // member x.M(arg1, arg2) - // being used with - // x.M p - // We typecheck this as if it has been written "(fun (v1, v2) -> x.M(v1, v2)) p" - // Without this rule this requires - // x.M (fst p, snd p) - | [calledMeth] - when (namedCurriedCallerArgs |> List.forall isNil && - unnamedCurriedCallerArgs.Length = 1 && - unnamedCurriedCallerArgs.Head.Length = 1 && - let curriedCalledArgs = calledMeth.GetParamAttribs(cenv.amap, mItem) - curriedCalledArgs.Length = 1 && - curriedCalledArgs.Head.Length > 1 && - curriedCalledArgs.Head |> List.forall isSimpleFormalArg) -> - - // The call lambda has function type - let exprTy = mkFunTy (NewInferenceType ()) exprTy + let MakeUnnamedCallerArgInfo x = (x, GetNewInferenceTypeForMethodArg cenv env tpenv x, x.Range) + + // "single named item" rule. This is where we have a single accessible method + // member x.M(arg1) + // being used with + // x.M (x, y) + // Without this rule this requires + // x.M ((x, y)) + match candidates with + | [calledMeth] + when (namedCurriedCallerArgs |> List.forall isNil && + let curriedCalledArgs = calledMeth.GetParamAttribs(cenv.amap, mItem) + curriedCalledArgs.Length = 1 && + curriedCalledArgs.Head.Length = 1 && + curriedCalledArgs.Head.Head |> isSimpleFormalArg) -> + let unnamedCurriedCallerArgs = curriedCallerArgs |> List.map (MakeUnnamedCallerArgInfo >> List.singleton) + let namedCurriedCallerArgs = namedCurriedCallerArgs |> List.map (fun _ -> []) + (Some (unnamedCurriedCallerArgs, namedCurriedCallerArgs), None, exprTy) + + // "single named item" rule. This is where we have a single accessible method + // member x.M(arg1, arg2) + // being used with + // x.M p + // We typecheck this as if it has been written "(fun (v1, v2) -> x.M(v1, v2)) p" + // Without this rule this requires + // x.M (fst p, snd p) + | [calledMeth] + when (namedCurriedCallerArgs |> List.forall isNil && + unnamedCurriedCallerArgs.Length = 1 && + unnamedCurriedCallerArgs.Head.Length = 1 && + let curriedCalledArgs = calledMeth.GetParamAttribs(cenv.amap, mItem) + curriedCalledArgs.Length = 1 && + curriedCalledArgs.Head.Length > 1 && + curriedCalledArgs.Head |> List.forall isSimpleFormalArg) -> + + // The call lambda has function type + let exprTy = mkFunTy (NewInferenceType ()) exprTy - (None, Some unnamedCurriedCallerArgs.Head.Head, exprTy) + (None, Some unnamedCurriedCallerArgs.Head.Head, exprTy) - | _ -> - let unnamedCurriedCallerArgs = unnamedCurriedCallerArgs |> List.mapSquared MakeUnnamedCallerArgInfo - let namedCurriedCallerArgs = namedCurriedCallerArgs |> List.mapSquared (fun (isOpt, nm, x) -> - let ty = GetNewInferenceTypeForMethodArg cenv env tpenv x - // #435263: compiler crash with .net optional parameters and F# optional syntax - // named optional arguments should always have option type - let ty = if isOpt then mkOptionTy denv.g ty else ty - nm, isOpt, x, ty, x.Range - ) - - (Some (unnamedCurriedCallerArgs, namedCurriedCallerArgs), None, exprTy) + | _ -> + let unnamedCurriedCallerArgs = unnamedCurriedCallerArgs |> List.mapSquared MakeUnnamedCallerArgInfo + let namedCurriedCallerArgs = namedCurriedCallerArgs |> List.mapSquared (fun (isOpt, nm, x) -> + let ty = GetNewInferenceTypeForMethodArg cenv env tpenv x + // #435263: compiler crash with .net optional parameters and F# optional syntax + // named optional arguments should always have option type + let ty = if isOpt then mkOptionTy denv.g ty else ty + nm, isOpt, x, ty, x.Range) + + (Some (unnamedCurriedCallerArgs, namedCurriedCallerArgs), None, exprTy) let CalledMethHasSingleArgumentGroupOfThisLength n (calledMeth: MethInfo) = @@ -9983,21 +9982,12 @@ and TcMethodApplication let finalCalledMethInfo = finalCalledMeth.Method let finalCalledMethInst = finalCalledMeth.CalledTyArgs - let finalArgSets = finalCalledMeth.ArgSets let finalAssignedItemSetters = finalCalledMeth.AssignedItemSetters - let finalCalledPropInfoOpt = finalCalledMeth.AssociatedPropertyInfo let finalAttributeAssignedNamedItems = finalCalledMeth.AttributeAssignedNamedArgs - let finalUnnamedCalledOptArgs = finalCalledMeth.UnnamedCalledOptArgs - let finalUnnamedCalledOutArgs = finalCalledMeth.UnnamedCalledOutArgs - let finalAssignedNamedArgs = finalArgSets |> List.collect (fun argSet -> argSet.AssignedNamedArgs) - let finalParamArrayCallerArgs = finalArgSets |> List.collect (fun argSet -> argSet.ParamArrayCallerArgs) - let finalUnnamedCalledArgs = finalArgSets |> List.collect (fun argSet -> argSet.UnnamedCalledArgs) - let finalUnnamedCallerArgs = finalArgSets |> List.collect (fun argSet -> argSet.UnnamedCallerArgs) - // STEP 4. Check the attributes on the method and the corresponding event/property, if any - finalCalledPropInfoOpt |> Option.iter (fun pinfo -> CheckPropInfoAttributes pinfo mItem |> CommitOperationResult) + finalCalledMeth.AssociatedPropertyInfo |> Option.iter (fun pinfo -> CheckPropInfoAttributes pinfo mItem |> CommitOperationResult) let isInstance = not (isNil objArgs) MethInfoChecks cenv.g cenv.amap isInstance tyargsOpt objArgs ad mItem finalCalledMethInfo @@ -10026,266 +10016,13 @@ and TcMethodApplication | _ -> () end - if (finalArgSets |> List.existsi (fun i argSet -> argSet.UnnamedCalledArgs |> List.existsi (fun j ca -> ca.Position <> (i, j)))) then + if (finalCalledMeth.ArgSets |> List.existsi (fun i argSet -> argSet.UnnamedCalledArgs |> List.existsi (fun j ca -> ca.Position <> (i, j)))) then errorR(Deprecated(FSComp.SR.tcUnnamedArgumentsDoNotFormPrefix(), mMethExpr)) + /// STEP 5. Build the argument list. Adjust for optional arguments, byref arguments and coercions. - // STEP 5. Build the argument list. Adjust for optional arguments, byref arguments and coercions. - // For example, if you pass an F# reference cell to a byref then we must get the address of the - // contents of the ref. Likewise lots of adjustments are made for optional arguments etc. - - // Some of the code below must allocate temporary variables or bind other variables to particular values. - // As usual we represent variable allocators by expr -> expr functions - // which we then use to wrap the whole expression. These will either do nothing or pre-bind a variable. It doesn't - // matter what order they are applied in as long as they are all composed together. - let emptyPreBinder (e: Expr) = e - - // For unapplied 'e.M' we first evaluate 'e' outside the lambda, i.e. 'let v = e in (fun arg -> v.M(arg))' - let objArgPreBinder, objArgs = - match objArgs, lambdaVars with - | [objArg], Some _ -> - if finalCalledMethInfo.IsExtensionMember && finalCalledMethInfo.ObjArgNeedsAddress(cenv.amap, mMethExpr) then - error(Error(FSComp.SR.tcCannotPartiallyApplyExtensionMethodForByref(finalCalledMethInfo.DisplayName), mMethExpr)) - let objArgTy = tyOfExpr cenv.g objArg - let v, ve = mkCompGenLocal mMethExpr "objectArg" objArgTy - (fun body -> mkCompGenLet mMethExpr v objArg body), [ve] - | _ -> - emptyPreBinder, objArgs - - // Handle adhoc argument conversions - let coerceExpr isOutArg calledArgTy (reflArgInfo: ReflectedArgInfo) callerArgTy m callerArgExpr = - let g = cenv.g - - if isByrefTy g calledArgTy && isRefCellTy g callerArgTy then - None, Expr.Op (TOp.RefAddrGet false, [destRefCellTy g callerArgTy], [callerArgExpr], m) - -#if IMPLICIT_ADDRESS_OF - elif isInByrefTy g calledArgTy && not (isByrefTy cenv.g callerArgTy) then - let wrap, callerArgExprAddress, _readonly, _writeonly = mkExprAddrOfExpr g true false NeverMutates callerArgExpr None m - Some wrap, callerArgExprAddress -#endif - - elif isDelegateTy cenv.g calledArgTy && isFunTy cenv.g callerArgTy then - None, CoerceFromFSharpFuncToDelegate cenv.g cenv.amap cenv.infoReader ad callerArgTy m callerArgExpr calledArgTy - - elif isLinqExpressionTy cenv.g calledArgTy && isDelegateTy cenv.g (destLinqExpressionTy cenv.g calledArgTy) && isFunTy cenv.g callerArgTy then - let delegateTy = destLinqExpressionTy cenv.g calledArgTy - let expr = CoerceFromFSharpFuncToDelegate cenv.g cenv.amap cenv.infoReader ad callerArgTy m callerArgExpr delegateTy - None, mkCallQuoteToLinqLambdaExpression cenv.g m delegateTy (Expr.Quote (expr, ref None, false, m, mkQuotedExprTy cenv.g delegateTy)) - - // auto conversions to quotations (to match auto conversions to LINQ expressions) - elif reflArgInfo.AutoQuote && isQuotedExprTy cenv.g calledArgTy && not (isQuotedExprTy cenv.g callerArgTy) then - match reflArgInfo with - | ReflectedArgInfo.Quote true -> - None, mkCallLiftValueWithDefn cenv.g m calledArgTy callerArgExpr - | ReflectedArgInfo.Quote false -> - None, Expr.Quote (callerArgExpr, ref None, false, m, calledArgTy) - | ReflectedArgInfo.None -> failwith "unreachable" // unreachable due to reflArgInfo.AutoQuote condition - - // Note: out args do not need to be coerced - elif isOutArg then - None, callerArgExpr - - // Note: not all these casts are reported in quotations - else - None, mkCoerceIfNeeded cenv.g calledArgTy callerArgTy callerArgExpr - - // Handle param array and optional arguments - let optArgPreBinder, paramArrayPreBinders, allArgs, outArgExprs, outArgTmpBinds = - - let normalUnnamedArgs = - (finalUnnamedCalledArgs, finalUnnamedCallerArgs) ||> List.map2 (fun called caller -> { NamedArgIdOpt = None; CalledArg=called; CallerArg=caller }) - - let paramArrayPreBinders, paramArrayArgs = - match finalCalledMeth.ParamArrayCalledArgOpt with - | None -> - [], [] - | Some paramArrayCalledArg -> - let paramArrayCalledArgElementType = destArrayTy cenv.g paramArrayCalledArg.CalledArgumentType - - let paramArrayPreBinders, es = - finalParamArrayCallerArgs - |> List.map (fun callerArg -> - let (CallerArg(callerArgTy, m, isOutArg, callerArgExpr)) = callerArg - coerceExpr isOutArg paramArrayCalledArgElementType paramArrayCalledArg.ReflArgInfo callerArgTy m callerArgExpr) - |> List.unzip - - let arg = - [ { NamedArgIdOpt = None - CalledArg=paramArrayCalledArg - CallerArg=CallerArg(paramArrayCalledArg.CalledArgumentType, mMethExpr, false, Expr.Op (TOp.Array, [paramArrayCalledArgElementType], es, mMethExpr)) } ] - paramArrayPreBinders, arg - - // CLEANUP: Move all this code into some isolated file, e.g. "optional.fs" - // - // Handle CallerSide optional arguments. - // - // CallerSide optional arguments are largely for COM interop, e.g. to PIA assemblies for Word etc. - // As a result we follow the VB and C# behavior here. - // - // "1. If the parameter is statically typed as System.Object and does not have a value, then there are four cases: - // a. The parameter is marked with MarshalAs(IUnknown), MarshalAs(Interface), or MarshalAs(IDispatch). In this case we pass null. - // b. Else if the parameter is marked with IUnknownConstantAttribute. In this case we pass new System.Runtime.InteropServices.UnknownWrapper(null) - // c. Else if the parameter is marked with IDispatchConstantAttribute. In this case we pass new System.Runtime.InteropServices.DispatchWrapper(null) - // d. Else, we will pass Missing.Value. - // 2. Otherwise, if there is a value attribute, then emit the default value. - // 3. Otherwise, we emit default(T). - // 4. Finally, we apply conversions from the value to the parameter type. This is where the nullable conversions take place for VB. - // - VB allows you to mark ref parameters as optional. The semantics of this is that we create a temporary - // with type = type of parameter, load the optional value to it, and call the method. - // - VB also allows you to mark arrays with Nothing as the optional value. - // - VB also allows you to pass intrinsic values as optional values to parameters - // typed as Object. What we do in this case is we box the intrinsic value." - // - let optArgs, optArgPreBinder = - (emptyPreBinder, finalUnnamedCalledOptArgs) ||> List.mapFold (fun wrapper calledArg -> - let calledArgTy = calledArg.CalledArgumentType - let wrapper2, expr = - match calledArg.OptArgInfo with - | NotOptional -> - error(InternalError("Unexpected NotOptional", mItem)) - | CallerSide dfltVal -> - let rec build currCalledArgTy currDfltVal = - match currDfltVal with - | MissingValue -> - // Add an I_nop if this is an initonly field to make sure we never recognize it as an lvalue. See mkExprAddrOfExpr. - emptyPreBinder, mkAsmExpr ([ mkNormalLdsfld (fspec_Missing_Value cenv.g); AI_nop ], [], [], [currCalledArgTy], mMethExpr) - | DefaultValue -> - emptyPreBinder, mkDefault(mMethExpr, currCalledArgTy) - | Constant fieldInit -> - match currCalledArgTy with - | NullableTy cenv.g inst when fieldInit <> ILFieldInit.Null -> - let nullableTy = mkILNonGenericBoxedTy(cenv.g.FindSysILTypeRef "System.Nullable`1") - let ctor = mkILCtorMethSpecForTy(nullableTy, [ILType.TypeVar 0us]).MethodRef - let ctorArgs = [Expr.Const (TcFieldInit mMethExpr fieldInit, mMethExpr, inst)] - emptyPreBinder, Expr.Op (TOp.ILCall (false, false, true, true, NormalValUse, false, false, ctor, [inst], [], [currCalledArgTy]), [], ctorArgs, mMethExpr) - | ByrefTy cenv.g inst -> - build inst (PassByRef(inst, currDfltVal)) - | _ -> - match calledArg.CallerInfo, env.eCallerMemberName with - | CallerLineNumber, _ when typeEquiv cenv.g currCalledArgTy cenv.g.int_ty -> - emptyPreBinder, Expr.Const (Const.Int32(mMethExpr.StartLine), mMethExpr, currCalledArgTy) - | CallerFilePath, _ when typeEquiv cenv.g currCalledArgTy cenv.g.string_ty -> - let fileName = mMethExpr.FileName |> FileSystem.GetFullPathShim |> PathMap.apply cenv.g.pathMap - emptyPreBinder, Expr.Const (Const.String fileName, mMethExpr, currCalledArgTy) - | CallerMemberName, Some callerName when (typeEquiv cenv.g currCalledArgTy cenv.g.string_ty) -> - emptyPreBinder, Expr.Const (Const.String callerName, mMethExpr, currCalledArgTy) - | _ -> - emptyPreBinder, Expr.Const (TcFieldInit mMethExpr fieldInit, mMethExpr, currCalledArgTy) - - | WrapperForIDispatch -> - match cenv.g.TryFindSysILTypeRef "System.Runtime.InteropServices.DispatchWrapper" with - | None -> error(Error(FSComp.SR.fscSystemRuntimeInteropServicesIsRequired(), mMethExpr)) - | Some tref -> - let ty = mkILNonGenericBoxedTy tref - let mref = mkILCtorMethSpecForTy(ty, [cenv.g.ilg.typ_Object]).MethodRef - let expr = Expr.Op (TOp.ILCall (false, false, false, true, NormalValUse, false, false, mref, [], [], [cenv.g.obj_ty]), [], [mkDefault(mMethExpr, currCalledArgTy)], mMethExpr) - emptyPreBinder, expr - | WrapperForIUnknown -> - match cenv.g.TryFindSysILTypeRef "System.Runtime.InteropServices.UnknownWrapper" with - | None -> error(Error(FSComp.SR.fscSystemRuntimeInteropServicesIsRequired(), mMethExpr)) - | Some tref -> - let ty = mkILNonGenericBoxedTy tref - let mref = mkILCtorMethSpecForTy(ty, [cenv.g.ilg.typ_Object]).MethodRef - let expr = Expr.Op (TOp.ILCall (false, false, false, true, NormalValUse, false, false, mref, [], [], [cenv.g.obj_ty]), [], [mkDefault(mMethExpr, currCalledArgTy)], mMethExpr) - emptyPreBinder, expr - | PassByRef (ty, dfltVal2) -> - let v, _ = mkCompGenLocal mMethExpr "defaultByrefArg" ty - let wrapper2, rhs = build currCalledArgTy dfltVal2 - (wrapper2 >> mkCompGenLet mMethExpr v rhs), mkValAddr mMethExpr false (mkLocalValRef v) - build calledArgTy dfltVal - | CalleeSide -> - let calledNonOptTy = - if isOptionTy cenv.g calledArgTy then - destOptionTy cenv.g calledArgTy - else - calledArgTy // should be unreachable - - match calledArg.CallerInfo, env.eCallerMemberName with - | CallerLineNumber, _ when typeEquiv cenv.g calledNonOptTy cenv.g.int_ty -> - let lineExpr = Expr.Const(Const.Int32 mMethExpr.StartLine, mMethExpr, calledNonOptTy) - emptyPreBinder, mkSome cenv.g calledNonOptTy lineExpr mMethExpr - | CallerFilePath, _ when typeEquiv cenv.g calledNonOptTy cenv.g.string_ty -> - let fileName = mMethExpr.FileName |> FileSystem.GetFullPathShim |> PathMap.apply cenv.g.pathMap - let filePathExpr = Expr.Const (Const.String(fileName), mMethExpr, calledNonOptTy) - emptyPreBinder, mkSome cenv.g calledNonOptTy filePathExpr mMethExpr - | CallerMemberName, Some(callerName) when typeEquiv cenv.g calledNonOptTy cenv.g.string_ty -> - let memberNameExpr = Expr.Const (Const.String callerName, mMethExpr, calledNonOptTy) - emptyPreBinder, mkSome cenv.g calledNonOptTy memberNameExpr mMethExpr - | _ -> - emptyPreBinder, mkNone cenv.g calledNonOptTy mMethExpr - - // Combine the variable allocators (if any) - let wrapper = (wrapper >> wrapper2) - let callerArg = CallerArg(calledArgTy, mMethExpr, false, expr) - { NamedArgIdOpt = None; CalledArg = calledArg; CallerArg = callerArg }, wrapper) - - - // Handle optional arguments - let wrapOptionalArg (assignedArg: AssignedCalledArg<_>) = - let (CallerArg(callerArgTy, m, isOptCallerArg, expr)) = assignedArg.CallerArg - match assignedArg.CalledArg.OptArgInfo with - | NotOptional -> - if isOptCallerArg then errorR(Error(FSComp.SR.tcFormalArgumentIsNotOptional(), m)) - assignedArg - | _ -> - let expr = - match assignedArg.CalledArg.OptArgInfo with - | CallerSide _ -> - if isOptCallerArg then - // STRUCT OPTIONS: if we allow struct options as optional arguments then we should take - // the address correctly. - mkUnionCaseFieldGetUnprovenViaExprAddr (expr, mkSomeCase cenv.g, [destOptionTy cenv.g callerArgTy], 0, m) - else - expr - | CalleeSide -> - if isOptCallerArg then - // M(?x=bopt) when M(A) --> M(?x=Some(b.Value)) - expr - else - // M(x=b) when M(A) --> M(?x=Some(b :> A)) - let calledArgTy = assignedArg.CalledArg.CalledArgumentType - if isOptionTy cenv.g calledArgTy then - let calledNonOptTy = destOptionTy cenv.g calledArgTy - mkSome cenv.g calledNonOptTy (mkCoerceIfNeeded cenv.g calledNonOptTy callerArgTy expr) m - else - expr // should be unreachable - - | _ -> failwith "Unreachable" - { assignedArg with CallerArg=CallerArg((tyOfExpr cenv.g expr), m, isOptCallerArg, expr) } - - let outArgsAndExprs, outArgTmpBinds = - finalUnnamedCalledOutArgs |> List.map (fun calledArg -> - let calledArgTy = calledArg.CalledArgumentType - let outArgTy = destByrefTy cenv.g calledArgTy - let outv, outArgExpr = mkMutableCompGenLocal mMethExpr PrettyNaming.outArgCompilerGeneratedName outArgTy // mutable! - let expr = mkDefault(mMethExpr, outArgTy) - let callerArg = CallerArg(calledArgTy, mMethExpr, false, mkValAddr mMethExpr false (mkLocalValRef outv)) - let outArg = { NamedArgIdOpt=None;CalledArg=calledArg;CallerArg=callerArg } - (outArg, outArgExpr), mkCompGenBind outv expr) - |> List.unzip - - let outArgs, outArgExprs = List.unzip outArgsAndExprs - - let allArgs = - List.map wrapOptionalArg normalUnnamedArgs @ - List.map wrapOptionalArg finalAssignedNamedArgs @ - paramArrayArgs @ - optArgs @ - outArgs - - let allArgs = - allArgs |> List.sortBy (fun x -> x.Position) - - optArgPreBinder, paramArrayPreBinders, allArgs, outArgExprs, outArgTmpBinds - - let coerce (assignedArg: AssignedCalledArg<_>) = - let isOutArg = assignedArg.CalledArg.IsOutArg - let reflArgInfo = assignedArg.CalledArg.ReflArgInfo - let calledArgTy = assignedArg.CalledArg.CalledArgumentType - let (CallerArg(callerArgTy, m, _, e)) = assignedArg.CallerArg - - coerceExpr isOutArg calledArgTy reflArgInfo callerArgTy m e + let objArgPreBinder, objArgs, allArgsPreBinders, allArgs, allArgsCoerced, optArgPreBinder, paramArrayPreBinders, outArgExprs, outArgTmpBinds = + AdjustCallerArgExprs TcFieldInit env.eCallerMemberName cenv.g cenv.amap cenv.infoReader ad finalCalledMeth objArgs lambdaVars mItem mMethExpr // Record the resolution of the named argument for the Language Service allArgs |> List.iter (fun assignedArg -> @@ -10295,84 +10032,61 @@ and TcMethodApplication let item = Item.ArgName (defaultArg assignedArg.CalledArg.NameOpt id, assignedArg.CalledArg.CalledArgumentType, Some(ArgumentContainer.Method finalCalledMethInfo)) CallNameResolutionSink cenv.tcSink (id.idRange, env.NameEnv, item, item, emptyTyparInst, ItemOccurence.Use, env.DisplayEnv, ad)) - let allArgsPreBinders, allArgsCoerced = List.map coerce allArgs |> List.unzip - // Make the call expression - let expr, exprty = + /// STEP 6. Build the call expression, then adjust for byref-returns, out-parameters-as-tuples, post-hoc property assignments, methods-as-first-class-value, + /// + + let callExpr0, exprty = BuildPossiblyConditionalMethodCall cenv env mut mMethExpr isProp finalCalledMethInfo isSuperInit finalCalledMethInst objArgs allArgsCoerced // Handle byref returns - let expr = + let callExpr1 = // byref-typed returns get implicitly dereferenced - let vty = tyOfExpr cenv.g expr + let vty = tyOfExpr cenv.g callExpr0 if isByrefTy cenv.g vty then let v, _ = mkCompGenLocal mMethExpr "byrefReturn" vty - mkCompGenLet mMethExpr v expr (mkAddrGet mMethExpr (mkLocalValRef v)) + mkCompGenLet mMethExpr v callExpr0 (mkAddrGet mMethExpr (mkLocalValRef v)) else - expr + callExpr0 // Bind "out" parameters as part of the result tuple - let expr, exprty = + let callExpr2, exprty = + let expr = callExpr1 if isNil outArgTmpBinds then expr, exprty else let outArgTys = outArgExprs |> List.map (tyOfExpr cenv.g) - let expr = if isUnitTy cenv.g exprty then mkCompGenSequential mMethExpr expr (mkRefTupled cenv.g mMethExpr outArgExprs outArgTys) - else mkRefTupled cenv.g mMethExpr (expr :: outArgExprs) (exprty :: outArgTys) + let expr = + if isUnitTy cenv.g exprty then + mkCompGenSequential mMethExpr expr (mkRefTupled cenv.g mMethExpr outArgExprs outArgTys) + else + mkRefTupled cenv.g mMethExpr (expr :: outArgExprs) (exprty :: outArgTys) let expr = mkLetsBind mMethExpr outArgTmpBinds expr expr, tyOfExpr cenv.g expr // Handle post-hoc property assignments - let setterExprPrebinders, expr = - if isCheckingAttributeCall then - [], expr - elif isNil finalAssignedItemSetters then - [], expr - else - // This holds the result of the call - let objv, objExpr = mkMutableCompGenLocal mMethExpr "returnVal" exprty // mutable in case it's a struct - // This expression mutates the properties on the result of the call - let setterExprPrebinders, propSetExpr = - (mkUnit cenv.g mMethExpr, finalAssignedItemSetters) ||> List.mapFold (fun acc (AssignedItemSetter(id, setter, CallerArg(callerArgTy, m, isOptCallerArg, argExpr))) -> - if isOptCallerArg then error(Error(FSComp.SR.tcInvalidOptionalAssignmentToPropertyOrField(), m)) - - let argExprPrebinder, action, defnItem = - match setter with - | AssignedPropSetter (pinfo, pminfo, pminst) -> - MethInfoChecks cenv.g cenv.amap true None [objExpr] ad m pminfo - let calledArgTy = List.head (List.head (pminfo.GetParamTypes(cenv.amap, m, pminst))) - let argExprPrebinder, argExpr = coerceExpr false calledArgTy ReflectedArgInfo.None callerArgTy m argExpr - let mut = (if isStructTy cenv.g (tyOfExpr cenv.g objExpr) then DefinitelyMutates else PossiblyMutates) - let action = BuildPossiblyConditionalMethodCall cenv env mut m true pminfo NormalValUse pminst [objExpr] [argExpr] |> fst - argExprPrebinder, action, Item.Property (pinfo.PropertyName, [pinfo]) - - | AssignedILFieldSetter finfo -> - // Get or set instance IL field - ILFieldInstanceChecks cenv.g cenv.amap ad m finfo - let calledArgTy = finfo.FieldType (cenv.amap, m) - let argExprPrebinder, argExpr = coerceExpr false calledArgTy ReflectedArgInfo.None callerArgTy m argExpr - let action = BuildILFieldSet cenv.g m objExpr finfo argExpr - argExprPrebinder, action, Item.ILField finfo - - | AssignedRecdFieldSetter rfinfo -> - RecdFieldInstanceChecks cenv.g cenv.amap ad m rfinfo - let calledArgTy = rfinfo.FieldType - CheckRecdFieldMutation m denv rfinfo - let argExprPrebinder, argExpr = coerceExpr false calledArgTy ReflectedArgInfo.None callerArgTy m argExpr - let action = BuildRecdFieldSet cenv.g m objExpr rfinfo argExpr - argExprPrebinder, action, Item.RecdField rfinfo - - // Record the resolution for the Language Service - let item = Item.SetterArg (id, defnItem) - CallNameResolutionSink cenv.tcSink (id.idRange, env.NameEnv, item, item, emptyTyparInst, ItemOccurence.Use, env.DisplayEnv, ad) - - argExprPrebinder, mkCompGenSequential m acc action) - - // now put them together - let expr = mkCompGenLet mMethExpr objv expr (mkCompGenSequential mMethExpr propSetExpr objExpr) - setterExprPrebinders, expr - - // Build the lambda expression if any - let expr = + let setterExprPrebinders, callExpr3 = + let expr = callExpr2 + if isCheckingAttributeCall then + [], expr + elif isNil finalAssignedItemSetters then + [], expr + else + // This holds the result of the call + let objv, objExpr = mkMutableCompGenLocal mMethExpr "returnVal" exprty // mutable in case it's a struct + + // Build the expression that mutates the properties on the result of the call + let setterExprPrebinders, propSetExpr = + (mkUnit cenv.g mMethExpr, finalAssignedItemSetters) ||> List.mapFold (fun acc assignedItemSetter -> + let argExprPrebinder, action, m = TcSetterArgExpr cenv env denv objExpr ad assignedItemSetter + argExprPrebinder, mkCompGenSequential m acc action) + + // now put them together + let expr = mkCompGenLet mMethExpr objv expr (mkCompGenSequential mMethExpr propSetExpr objExpr) + setterExprPrebinders, expr + + // Build the lambda expression if any, if the method is used as a first-class value + let callExpr4 = + let expr = callExpr3 match lambdaVars with | None -> expr | Some curriedLambdaVars -> @@ -10382,7 +10096,8 @@ and TcMethodApplication | _ -> mkMultiLambda mMethExpr vs (expr, tyOfExpr cenv.g expr) List.foldBack mkLambda curriedLambdaVars expr - let expr, tpenv = + let callExpr5, tpenv = + let expr = callExpr4 match unnamedDelayedCallerArgExprOpt with | Some synArgExpr -> match lambdaVars with @@ -10395,15 +10110,53 @@ and TcMethodApplication expr, tpenv // Apply the PreBinders, if any - let expr = (expr, setterExprPrebinders) ||> List.fold (fun expr argPreBinder -> match argPreBinder with None -> expr | Some f -> f expr) - let expr = (expr, paramArrayPreBinders) ||> List.fold (fun expr argPreBinder -> match argPreBinder with None -> expr | Some f -> f expr) - let expr = (expr, allArgsPreBinders) ||> List.fold (fun expr argPreBinder -> match argPreBinder with None -> expr | Some f -> f expr) + let callExpr6 = + let expr = callExpr5 + let expr = (expr, setterExprPrebinders) ||> List.fold (fun expr argPreBinder -> match argPreBinder with None -> expr | Some f -> f expr) + let expr = (expr, paramArrayPreBinders) ||> List.fold (fun expr argPreBinder -> match argPreBinder with None -> expr | Some f -> f expr) + let expr = (expr, allArgsPreBinders) ||> List.fold (fun expr argPreBinder -> match argPreBinder with None -> expr | Some f -> f expr) - let expr = optArgPreBinder expr - let expr = objArgPreBinder expr + let expr = optArgPreBinder expr + let expr = objArgPreBinder expr + expr - (expr, finalAttributeAssignedNamedItems, delayed), tpenv + (callExpr6, finalAttributeAssignedNamedItems, delayed), tpenv +and TcSetterArgExpr cenv env denv objExpr ad (AssignedItemSetter(id, setter, CallerArg(callerArgTy, m, isOptCallerArg, argExpr))) = + if isOptCallerArg then error(Error(FSComp.SR.tcInvalidOptionalAssignmentToPropertyOrField(), m)) + + let argExprPrebinder, action, defnItem = + match setter with + | AssignedPropSetter (pinfo, pminfo, pminst) -> + MethInfoChecks cenv.g cenv.amap true None [objExpr] ad m pminfo + let calledArgTy = List.head (List.head (pminfo.GetParamTypes(cenv.amap, m, pminst))) + let argExprPrebinder, argExpr = MethodCalls.AdjustCallerArgExprForCoercions cenv.g cenv.amap cenv.infoReader ad false calledArgTy ReflectedArgInfo.None callerArgTy m argExpr + let mut = (if isStructTy cenv.g (tyOfExpr cenv.g objExpr) then DefinitelyMutates else PossiblyMutates) + let action = BuildPossiblyConditionalMethodCall cenv env mut m true pminfo NormalValUse pminst [objExpr] [argExpr] |> fst + argExprPrebinder, action, Item.Property (pinfo.PropertyName, [pinfo]) + + | AssignedILFieldSetter finfo -> + // Get or set instance IL field + ILFieldInstanceChecks cenv.g cenv.amap ad m finfo + let calledArgTy = finfo.FieldType (cenv.amap, m) + let argExprPrebinder, argExpr = MethodCalls.AdjustCallerArgExprForCoercions cenv.g cenv.amap cenv.infoReader ad false calledArgTy ReflectedArgInfo.None callerArgTy m argExpr + let action = BuildILFieldSet cenv.g m objExpr finfo argExpr + argExprPrebinder, action, Item.ILField finfo + + | AssignedRecdFieldSetter rfinfo -> + RecdFieldInstanceChecks cenv.g cenv.amap ad m rfinfo + let calledArgTy = rfinfo.FieldType + CheckRecdFieldMutation m denv rfinfo + let argExprPrebinder, argExpr = MethodCalls.AdjustCallerArgExprForCoercions cenv.g cenv.amap cenv.infoReader ad false calledArgTy ReflectedArgInfo.None callerArgTy m argExpr + let action = BuildRecdFieldSet cenv.g m objExpr rfinfo argExpr + argExprPrebinder, action, Item.RecdField rfinfo + + // Record the resolution for the Language Service + let item = Item.SetterArg (id, defnItem) + CallNameResolutionSink cenv.tcSink (id.idRange, env.NameEnv, item, item, emptyTyparInst, ItemOccurence.Use, env.DisplayEnv, ad) + + argExprPrebinder, action, m + and TcUnnamedMethodArgs cenv env lambdaPropagationInfo tpenv args = List.mapiFoldSquared (TcUnnamedMethodArg cenv env) (lambdaPropagationInfo, tpenv) args diff --git a/src/fsharp/xlf/FSComp.txt.es.xlf b/src/fsharp/xlf/FSComp.txt.es.xlf index 56f087fb38d..279d16a7c90 100644 --- a/src/fsharp/xlf/FSComp.txt.es.xlf +++ b/src/fsharp/xlf/FSComp.txt.es.xlf @@ -124,7 +124,7 @@ All branches of a pattern match expression must return values of the same type as the first branch, which here is '{0}'. This branch returns a value of type '{1}'. - Todas las ramas de una expresión de coincidencia de patrón deben devolver valores del mismo tipo. La primera rama devolvió un valor de tipo "{0}", pero esta rama devolvió un valor de tipo "\{1 \}". + All branches of a pattern match expression must return values of the same type as the first branch, which here is '{0}'. This branch returns a value of type '{1}'. diff --git a/tests/fsharp/core/fsfromfsviacs/lib3.cs b/tests/fsharp/core/fsfromfsviacs/lib3.cs index b688b710bc2..91a3dffe4bb 100644 --- a/tests/fsharp/core/fsfromfsviacs/lib3.cs +++ b/tests/fsharp/core/fsfromfsviacs/lib3.cs @@ -24,3 +24,29 @@ public static void SomeMethod() { } } } +namespace CSharpOptionalParameters +{ + // This should be preferred over the same type in lib2.cs + public class SomeClass + { + public SomeClass() { } + public static int MethodTakingOptionals(int x = 3, string y = "abc", double d = 5.0) + { + return x + y.Length + (int) d; + } + public static int MethodTakingNullableOptionalsWithDefaults(int? x = 3, string y = "abc", double? d = 5.0) + { + return (x.HasValue ? x.Value : -100) + y.Length + (int) (d.HasValue ? d.Value : 0.0); + } + public static int MethodTakingNullableOptionals(int? x = null, string y = null, double? d = null) + { + int length; + if (y == null) + length = -1; + else + length = y.Length; + return (x.HasValue ? x.Value : -1) + length + (int) (d.HasValue ? d.Value : -1.0); + } + } + +} diff --git a/tests/fsharp/core/fsfromfsviacs/test.fsx b/tests/fsharp/core/fsfromfsviacs/test.fsx index 76ec203112f..072a896c1eb 100644 --- a/tests/fsharp/core/fsfromfsviacs/test.fsx +++ b/tests/fsharp/core/fsfromfsviacs/test.fsx @@ -71,22 +71,53 @@ let _ = test "structunion394b36" (Lib.NestedStructUnionsTests.testPattern3mut(u2 // F# option implicit converter tests -let testFsOpt() = - let testOpt (t : 'T option) = - test (sprintf "fsimplicitconv (%A)" t) (ApiWrapper.ConsumeOptionalParam<'T>(t) = t) - - testOpt(Option.None) - testOpt(Some 42) - - // check that implicit conversion of optionals does - // differentiate between 'null' and 'Some null' - testOpt(Option.None) - testOpt(Option.Some null) - testOpt(Some "") - testOpt(Some "test") - -testFsOpt() - +module TestConsumeOptionalParameter = + let testFsOpt() = + let testOpt (t : 'T option) = + test (sprintf "fsimplicitconv (%A)" t) (ApiWrapper.ConsumeOptionalParam<'T>(t) = t) + + testOpt(Option.None) + testOpt(Some 42) + + // check that implicit conversion of optionals does + // differentiate between 'null' and 'Some null' + testOpt(Option.None) + testOpt(Option.Some null) + testOpt(Some "") + testOpt(Some "test") + + testFsOpt() + +module TestConsumeCSharpOptionalParameter = + open System + open CSharpOptionalParameters + check "csoptional23982f31" (SomeClass.MethodTakingOptionals()) 11 + check "csoptional23982f32" (SomeClass.MethodTakingOptionals(x = 6)) 14 + check "csoptional23982f33" (SomeClass.MethodTakingOptionals(y = "aaaaaa")) 14 + check "csoptional23982f34" (SomeClass.MethodTakingOptionals(d = 8.0)) 14 + + check "csoptional23982f41" (SomeClass.MethodTakingNullableOptionalsWithDefaults()) 11 + check "csoptional23982f42" (SomeClass.MethodTakingNullableOptionalsWithDefaults(x = Nullable 6)) 14 + check "csoptional23982f43" (SomeClass.MethodTakingNullableOptionalsWithDefaults(y = "aaaaaa")) 14 + check "csoptional23982f44" (SomeClass.MethodTakingNullableOptionalsWithDefaults(d = Nullable 8.0)) 14 + + check "csoptional23982f51" (SomeClass.MethodTakingNullableOptionals()) -3 + check "csoptional23982f52" (SomeClass.MethodTakingNullableOptionals(x = Nullable 6)) 4 + check "csoptional23982f53" (SomeClass.MethodTakingNullableOptionals(y = "aaaaaa")) 4 + check "csoptional23982f54" (SomeClass.MethodTakingNullableOptionals(d = Nullable 8.0)) 6 + + // These require https://github.com/fsharp/fslang-suggestions/issues/774 to be implemented + //check "csoptional23982f3no" (SomeClass.SomeMethod(?x = Some 6)) 14 + //check "csoptional23982f3no" (SomeClass.SomeMethod(?y = Some "aaaaaa")) 14 + //check "csoptional23982f3no" (SomeClass.SomeMethod(?d = Some 8.0)) 14 + //check "csoptional23982f3no" (SomeClass.SomeMethod(?x = None)) 11 + //check "csoptional23982f3no" (SomeClass.SomeMethod(?y = None)) 11 + //check "csoptional23982f3no" (SomeClass.SomeMethod(?d = None)) 11 + + //check "csoptional23982f42" (SomeClass.MethodTakingNullableOptionalsWithDefaults(x = 6)) 14 + //check "csoptional23982f44" (SomeClass.MethodTakingNullableOptionalsWithDefaults(d = 8.0)) 14 + //check "csoptional23982f52" (SomeClass.MethodTakingNullableOptionals(x = 6)) 4 + //check "csoptional23982f54" (SomeClass.MethodTakingNullableOptionals(d = 8.0)) 6 module NestedStructPatternMatchingAcrossAssemblyBoundaries = open Lib.NestedStructUnionsTests diff --git a/vsintegration/src/FSharp.ProjectSystem.PropertyPages/Resources/xlf/Microsoft.VisualStudio.Editors.Designer.cs.xlf b/vsintegration/src/FSharp.ProjectSystem.PropertyPages/Resources/xlf/Microsoft.VisualStudio.Editors.Designer.cs.xlf index a678a4bcfeb..860555f3440 100644 --- a/vsintegration/src/FSharp.ProjectSystem.PropertyPages/Resources/xlf/Microsoft.VisualStudio.Editors.Designer.cs.xlf +++ b/vsintegration/src/FSharp.ProjectSystem.PropertyPages/Resources/xlf/Microsoft.VisualStudio.Editors.Designer.cs.xlf @@ -1828,8 +1828,9 @@ CONSIDER: get this from CodeDom {0} x {1} - {0} x {1} + {0} x {1} Format string for showing a graphic's size + # {0} = width (as an integer) # {1} = height (as an integer) #Example, for a bitmap of width=123, height = 456, the English version of this string would be "123x456" diff --git a/vsintegration/src/FSharp.ProjectSystem.PropertyPages/Resources/xlf/Microsoft.VisualStudio.Editors.Designer.de.xlf b/vsintegration/src/FSharp.ProjectSystem.PropertyPages/Resources/xlf/Microsoft.VisualStudio.Editors.Designer.de.xlf index 662133483e3..65b4328fdc2 100644 --- a/vsintegration/src/FSharp.ProjectSystem.PropertyPages/Resources/xlf/Microsoft.VisualStudio.Editors.Designer.de.xlf +++ b/vsintegration/src/FSharp.ProjectSystem.PropertyPages/Resources/xlf/Microsoft.VisualStudio.Editors.Designer.de.xlf @@ -1828,8 +1828,9 @@ CONSIDER: get this from CodeDom {0} x {1} - {0} x {1} + {0} x {1} Format string for showing a graphic's size + # {0} = width (as an integer) # {1} = height (as an integer) #Example, for a bitmap of width=123, height = 456, the English version of this string would be "123x456" diff --git a/vsintegration/src/FSharp.ProjectSystem.PropertyPages/Resources/xlf/Microsoft.VisualStudio.Editors.Designer.es.xlf b/vsintegration/src/FSharp.ProjectSystem.PropertyPages/Resources/xlf/Microsoft.VisualStudio.Editors.Designer.es.xlf index 41dbc88f0bd..1153755c3dd 100644 --- a/vsintegration/src/FSharp.ProjectSystem.PropertyPages/Resources/xlf/Microsoft.VisualStudio.Editors.Designer.es.xlf +++ b/vsintegration/src/FSharp.ProjectSystem.PropertyPages/Resources/xlf/Microsoft.VisualStudio.Editors.Designer.es.xlf @@ -1828,8 +1828,9 @@ CONSIDER: get this from CodeDom {0} x {1} - {0} x {1} + {0} x {1} Format string for showing a graphic's size + # {0} = width (as an integer) # {1} = height (as an integer) #Example, for a bitmap of width=123, height = 456, the English version of this string would be "123x456" diff --git a/vsintegration/src/FSharp.ProjectSystem.PropertyPages/Resources/xlf/Microsoft.VisualStudio.Editors.Designer.fr.xlf b/vsintegration/src/FSharp.ProjectSystem.PropertyPages/Resources/xlf/Microsoft.VisualStudio.Editors.Designer.fr.xlf index 0f1e16e95f2..cd60e9f07e9 100644 --- a/vsintegration/src/FSharp.ProjectSystem.PropertyPages/Resources/xlf/Microsoft.VisualStudio.Editors.Designer.fr.xlf +++ b/vsintegration/src/FSharp.ProjectSystem.PropertyPages/Resources/xlf/Microsoft.VisualStudio.Editors.Designer.fr.xlf @@ -1828,8 +1828,9 @@ CONSIDER: get this from CodeDom {0} x {1} - {0} x {1} + {0} x {1} Format string for showing a graphic's size + # {0} = width (as an integer) # {1} = height (as an integer) #Example, for a bitmap of width=123, height = 456, the English version of this string would be "123x456" diff --git a/vsintegration/src/FSharp.ProjectSystem.PropertyPages/Resources/xlf/Microsoft.VisualStudio.Editors.Designer.it.xlf b/vsintegration/src/FSharp.ProjectSystem.PropertyPages/Resources/xlf/Microsoft.VisualStudio.Editors.Designer.it.xlf index 8902030751c..dd58cb9a119 100644 --- a/vsintegration/src/FSharp.ProjectSystem.PropertyPages/Resources/xlf/Microsoft.VisualStudio.Editors.Designer.it.xlf +++ b/vsintegration/src/FSharp.ProjectSystem.PropertyPages/Resources/xlf/Microsoft.VisualStudio.Editors.Designer.it.xlf @@ -1828,8 +1828,9 @@ CONSIDER: get this from CodeDom {0} x {1} - {0} x {1} + {0} x {1} Format string for showing a graphic's size + # {0} = width (as an integer) # {1} = height (as an integer) #Example, for a bitmap of width=123, height = 456, the English version of this string would be "123x456" diff --git a/vsintegration/src/FSharp.ProjectSystem.PropertyPages/Resources/xlf/Microsoft.VisualStudio.Editors.Designer.ja.xlf b/vsintegration/src/FSharp.ProjectSystem.PropertyPages/Resources/xlf/Microsoft.VisualStudio.Editors.Designer.ja.xlf index a39e49dfa0d..bdb896a5c83 100644 --- a/vsintegration/src/FSharp.ProjectSystem.PropertyPages/Resources/xlf/Microsoft.VisualStudio.Editors.Designer.ja.xlf +++ b/vsintegration/src/FSharp.ProjectSystem.PropertyPages/Resources/xlf/Microsoft.VisualStudio.Editors.Designer.ja.xlf @@ -1828,8 +1828,9 @@ CONSIDER: get this from CodeDom {0} x {1} - {0} x {1} + {0} x {1} Format string for showing a graphic's size + # {0} = width (as an integer) # {1} = height (as an integer) #Example, for a bitmap of width=123, height = 456, the English version of this string would be "123x456" diff --git a/vsintegration/src/FSharp.ProjectSystem.PropertyPages/Resources/xlf/Microsoft.VisualStudio.Editors.Designer.ko.xlf b/vsintegration/src/FSharp.ProjectSystem.PropertyPages/Resources/xlf/Microsoft.VisualStudio.Editors.Designer.ko.xlf index e7191ac3c4a..6f540b54250 100644 --- a/vsintegration/src/FSharp.ProjectSystem.PropertyPages/Resources/xlf/Microsoft.VisualStudio.Editors.Designer.ko.xlf +++ b/vsintegration/src/FSharp.ProjectSystem.PropertyPages/Resources/xlf/Microsoft.VisualStudio.Editors.Designer.ko.xlf @@ -1828,8 +1828,9 @@ CONSIDER: get this from CodeDom {0} x {1} - {0} x {1} + {0} x {1} Format string for showing a graphic's size + # {0} = width (as an integer) # {1} = height (as an integer) #Example, for a bitmap of width=123, height = 456, the English version of this string would be "123x456" diff --git a/vsintegration/src/FSharp.ProjectSystem.PropertyPages/Resources/xlf/Microsoft.VisualStudio.Editors.Designer.pl.xlf b/vsintegration/src/FSharp.ProjectSystem.PropertyPages/Resources/xlf/Microsoft.VisualStudio.Editors.Designer.pl.xlf index d0675bd2fcb..73bef853b9f 100644 --- a/vsintegration/src/FSharp.ProjectSystem.PropertyPages/Resources/xlf/Microsoft.VisualStudio.Editors.Designer.pl.xlf +++ b/vsintegration/src/FSharp.ProjectSystem.PropertyPages/Resources/xlf/Microsoft.VisualStudio.Editors.Designer.pl.xlf @@ -1828,8 +1828,9 @@ CONSIDER: get this from CodeDom {0} x {1} - {0} x {1} + {0} x {1} Format string for showing a graphic's size + # {0} = width (as an integer) # {1} = height (as an integer) #Example, for a bitmap of width=123, height = 456, the English version of this string would be "123x456" diff --git a/vsintegration/src/FSharp.ProjectSystem.PropertyPages/Resources/xlf/Microsoft.VisualStudio.Editors.Designer.pt-BR.xlf b/vsintegration/src/FSharp.ProjectSystem.PropertyPages/Resources/xlf/Microsoft.VisualStudio.Editors.Designer.pt-BR.xlf index acfade88845..3e8a1693485 100644 --- a/vsintegration/src/FSharp.ProjectSystem.PropertyPages/Resources/xlf/Microsoft.VisualStudio.Editors.Designer.pt-BR.xlf +++ b/vsintegration/src/FSharp.ProjectSystem.PropertyPages/Resources/xlf/Microsoft.VisualStudio.Editors.Designer.pt-BR.xlf @@ -1828,8 +1828,9 @@ CONSIDER: get this from CodeDom {0} x {1} - {0} x {1} + {0} x {1} Format string for showing a graphic's size + # {0} = width (as an integer) # {1} = height (as an integer) #Example, for a bitmap of width=123, height = 456, the English version of this string would be "123x456" diff --git a/vsintegration/src/FSharp.ProjectSystem.PropertyPages/Resources/xlf/Microsoft.VisualStudio.Editors.Designer.ru.xlf b/vsintegration/src/FSharp.ProjectSystem.PropertyPages/Resources/xlf/Microsoft.VisualStudio.Editors.Designer.ru.xlf index 355d363084e..2b5303d91de 100644 --- a/vsintegration/src/FSharp.ProjectSystem.PropertyPages/Resources/xlf/Microsoft.VisualStudio.Editors.Designer.ru.xlf +++ b/vsintegration/src/FSharp.ProjectSystem.PropertyPages/Resources/xlf/Microsoft.VisualStudio.Editors.Designer.ru.xlf @@ -1828,8 +1828,9 @@ CONSIDER: get this from CodeDom {0} x {1} - {0} x {1} + {0} x {1} Format string for showing a graphic's size + # {0} = width (as an integer) # {1} = height (as an integer) #Example, for a bitmap of width=123, height = 456, the English version of this string would be "123x456" diff --git a/vsintegration/src/FSharp.ProjectSystem.PropertyPages/Resources/xlf/Microsoft.VisualStudio.Editors.Designer.tr.xlf b/vsintegration/src/FSharp.ProjectSystem.PropertyPages/Resources/xlf/Microsoft.VisualStudio.Editors.Designer.tr.xlf index 86ac32f4b37..73a72b806a2 100644 --- a/vsintegration/src/FSharp.ProjectSystem.PropertyPages/Resources/xlf/Microsoft.VisualStudio.Editors.Designer.tr.xlf +++ b/vsintegration/src/FSharp.ProjectSystem.PropertyPages/Resources/xlf/Microsoft.VisualStudio.Editors.Designer.tr.xlf @@ -1828,8 +1828,9 @@ CONSIDER: get this from CodeDom {0} x {1} - {0} x {1} + {0} x {1} Format string for showing a graphic's size + # {0} = width (as an integer) # {1} = height (as an integer) #Example, for a bitmap of width=123, height = 456, the English version of this string would be "123x456" diff --git a/vsintegration/src/FSharp.ProjectSystem.PropertyPages/Resources/xlf/Microsoft.VisualStudio.Editors.Designer.zh-Hans.xlf b/vsintegration/src/FSharp.ProjectSystem.PropertyPages/Resources/xlf/Microsoft.VisualStudio.Editors.Designer.zh-Hans.xlf index 0537632d5fc..474f99a3dcd 100644 --- a/vsintegration/src/FSharp.ProjectSystem.PropertyPages/Resources/xlf/Microsoft.VisualStudio.Editors.Designer.zh-Hans.xlf +++ b/vsintegration/src/FSharp.ProjectSystem.PropertyPages/Resources/xlf/Microsoft.VisualStudio.Editors.Designer.zh-Hans.xlf @@ -1828,8 +1828,9 @@ CONSIDER: get this from CodeDom {0} x {1} - {0} x {1} + {0} x {1} Format string for showing a graphic's size + # {0} = width (as an integer) # {1} = height (as an integer) #Example, for a bitmap of width=123, height = 456, the English version of this string would be "123x456" diff --git a/vsintegration/src/FSharp.ProjectSystem.PropertyPages/Resources/xlf/Microsoft.VisualStudio.Editors.Designer.zh-Hant.xlf b/vsintegration/src/FSharp.ProjectSystem.PropertyPages/Resources/xlf/Microsoft.VisualStudio.Editors.Designer.zh-Hant.xlf index 17c64ffefb9..16b7339b2a4 100644 --- a/vsintegration/src/FSharp.ProjectSystem.PropertyPages/Resources/xlf/Microsoft.VisualStudio.Editors.Designer.zh-Hant.xlf +++ b/vsintegration/src/FSharp.ProjectSystem.PropertyPages/Resources/xlf/Microsoft.VisualStudio.Editors.Designer.zh-Hant.xlf @@ -1828,8 +1828,9 @@ CONSIDER: get this from CodeDom {0} x {1} - {0} x {1} + {0} x {1} Format string for showing a graphic's size + # {0} = width (as an integer) # {1} = height (as an integer) #Example, for a bitmap of width=123, height = 456, the English version of this string would be "123x456"