@@ -1394,6 +1394,28 @@ let TryStorageForWitness (_g: TcGlobals) eenv (w: TraitWitnessInfo) =
13941394let IsValRefIsDllImport g (vref: ValRef) =
13951395 vref.Attribs |> HasFSharpAttributeOpt g g.attrib_DllImportAttribute
13961396
1397+ /// Check if a type contains nativeptr with a type parameter from the given set.
1398+ /// Used to detect interface implementations that need 'native int' return type.
1399+ let hasNativePtrWithTypar (g: TcGlobals) (typars: Typar list) ty =
1400+ let rec check ty =
1401+ let ty = stripTyEqns g ty
1402+
1403+ match ty with
1404+ | TType_app(tcref, tinst, _) when tyconRefEq g g.nativeptr_tcr tcref ->
1405+ tinst
1406+ |> List.exists (fun t ->
1407+ match stripTyEqns g t with
1408+ | TType_var(tp, _) -> typars |> List.exists (fun tp2 -> tp.Stamp = tp2.Stamp)
1409+ | _ -> false)
1410+ | TType_app(_, tinst, _) -> tinst |> List.exists check
1411+ | TType_fun(d, r, _) -> check d || check r
1412+ | TType_tuple(_, tys) -> tys |> List.exists check
1413+ | TType_anon(_, tys) -> tys |> List.exists check
1414+ | TType_forall(_, t) -> check t
1415+ | _ -> false
1416+
1417+ check ty
1418+
13971419/// Determine how a top level value is represented, when it is being represented
13981420/// as a method.
13991421let GetMethodSpecForMemberVal cenv (memberInfo: ValMemberInfo) (vref: ValRef) =
@@ -1421,7 +1443,34 @@ let GetMethodSpecForMemberVal cenv (memberInfo: ValMemberInfo) (vref: ValRef) =
14211443
14221444 let ilActualRetTy =
14231445 let ilRetTy = GenReturnType cenv m tyenvUnderTypars returnTy
1424- if isCtor || cctor then ILType.Void else ilRetTy
1446+
1447+ if isCtor || cctor then
1448+ ILType.Void
1449+ // When implementing an interface slot with nativeptr<'T> where 'T is an interface
1450+ // type parameter and the interface type args are concrete, the interface method
1451+ // returns 'native int'. The method def must also return 'native int' to match.
1452+ elif memberInfo.MemberFlags.IsOverrideOrExplicitImpl then
1453+ match
1454+ memberInfo.ImplementedSlotSigs
1455+ |> List.tryPick (fun (TSlotSig(_, ty, sctps, _, _, sRetTy)) ->
1456+ let interfaceTypeArgs = argsOfAppTy g ty
1457+
1458+ if
1459+ not sctps.IsEmpty
1460+ && (freeInTypes CollectTypars interfaceTypeArgs).FreeTypars.IsEmpty
1461+ && sRetTy |> Option.exists (hasNativePtrWithTypar g sctps)
1462+ then
1463+ Some(sctps, sRetTy)
1464+ else
1465+ None)
1466+ with
1467+ | Some(sctps, sRetTy) ->
1468+ // Generate return type using the slot's type params so nativeptr<'T> → native int
1469+ let slotEnv = TypeReprEnv.Empty.ForTypars sctps
1470+ GenReturnType cenv m slotEnv sRetTy
1471+ | None -> ilRetTy
1472+ else
1473+ ilRetTy
14251474
14261475 let ilTy =
14271476 GenType cenv m tyenvUnderTypars (mkWoNullAppTy parentTcref (List.map mkTyparTy ctps))
@@ -2508,6 +2557,8 @@ type CodeGenBuffer(m: range, mgbuf: AssemblyBuilder, methodName, alreadyUsedArgs
25082557 let mutable hasDebugPoints = false
25092558 let mutable anyDocument = None // we collect an arbitrary document in order to emit the header FeeFee if needed
25102559
2560+ let mutable hasStackAllocatedLocals = false
2561+
25112562 let codeLabelToPC: Dictionary<ILCodeLabel, int> = Dictionary<_, _>(10)
25122563
25132564 let codeLabelToCodeLabel: Dictionary<ILCodeLabel, ILCodeLabel> =
@@ -2566,11 +2617,19 @@ type CodeGenBuffer(m: range, mgbuf: AssemblyBuilder, methodName, alreadyUsedArgs
25662617 member cgbuf.EmitInstr(pops, pushes, i) =
25672618 cgbuf.DoPops pops
25682619 cgbuf.DoPushes pushes
2620+
2621+ if i = I_localloc then
2622+ hasStackAllocatedLocals <- true
2623+
25692624 codebuf.Add i
25702625
25712626 member cgbuf.EmitInstrs(pops, pushes, is) =
25722627 cgbuf.DoPops pops
25732628 cgbuf.DoPushes pushes
2629+
2630+ if is |> List.exists (fun i -> i = I_localloc) then
2631+ hasStackAllocatedLocals <- true
2632+
25742633 is |> List.iter codebuf.Add
25752634
25762635 member private _.EnsureNopBetweenDebugPoints() =
@@ -2703,6 +2762,8 @@ type CodeGenBuffer(m: range, mgbuf: AssemblyBuilder, methodName, alreadyUsedArgs
27032762 member _.HasPinnedLocals() =
27042763 locals |> Seq.exists (fun (_, _, isFixed, _) -> isFixed)
27052764
2765+ member _.HasStackAllocatedLocals() = hasStackAllocatedLocals
2766+
27062767 member _.Close() =
27072768
27082769 let instrs = codebuf.ToArray()
@@ -3320,32 +3381,50 @@ and GenConstant cenv cgbuf eenv (c, m, ty) sequel =
33203381 match TryEliminateDesugaredConstants g m c with
33213382 | Some e -> GenExpr cenv cgbuf eenv e Continue
33223383 | None ->
3323- let emitInt64Constant i =
3384+ let needsBoxingToTargetTy =
3385+ (match ilTy with
3386+ | ILType.Value _ -> false
3387+ | _ -> true)
3388+
3389+ // Wraps an emitter: calls it, then boxes if target type is not a value type (e.g. literal upcast to obj).
3390+ let inline emitAndBoxIfNeeded emitter uty arg =
3391+ emitter uty arg
3392+
3393+ if needsBoxingToTargetTy then
3394+ CG.EmitInstr cgbuf (pop 1) (Push [ ilTy ]) (I_box uty)
3395+
3396+ let emitInt64Constant uty i =
33243397 // see https://github.com/dotnet/fsharp/pull/3620
33253398 // and https://github.com/dotnet/fsharp/issue/8683
33263399 // and https://github.com/dotnet/roslyn/blob/98f12bb/src/Compilers/Core/Portable/CodeGen/ILBuilderEmit.cs#L679
33273400 if i >= int64 Int32.MinValue && i <= int64 Int32.MaxValue then
3328- CG.EmitInstrs cgbuf (pop 0) (Push [ ilTy ]) [ mkLdcInt32 (int32 i); AI_conv DT_I8 ]
3401+ CG.EmitInstrs cgbuf (pop 0) (Push [ uty ]) [ mkLdcInt32 (int32 i); AI_conv DT_I8 ]
33293402 elif i >= int64 UInt32.MinValue && i <= int64 UInt32.MaxValue then
3330- CG.EmitInstrs cgbuf (pop 0) (Push [ ilTy ]) [ mkLdcInt32 (int32 i); AI_conv DT_U8 ]
3403+ CG.EmitInstrs cgbuf (pop 0) (Push [ uty ]) [ mkLdcInt32 (int32 i); AI_conv DT_U8 ]
33313404 else
3332- CG.EmitInstr cgbuf (pop 0) (Push [ ilTy ]) (iLdcInt64 i)
3405+ CG.EmitInstr cgbuf (pop 0) (Push [ uty ]) (iLdcInt64 i)
3406+
3407+ let emitConst uty instr =
3408+ CG.EmitInstr cgbuf (pop 0) (Push [ uty ]) instr
3409+
3410+ let emitConstI uty instrs =
3411+ CG.EmitInstrs cgbuf (pop 0) (Push [ uty ]) instrs
33333412
33343413 match c with
3335- | Const.Bool b -> CG.EmitInstr cgbuf (pop 0) (Push [ g.ilg.typ_Bool ]) (mkLdcInt32 (if b then 1 else 0))
3336- | Const.SByte i -> CG.EmitInstr cgbuf (pop 0) (Push [ ilTy ]) (mkLdcInt32 (int32 i))
3337- | Const.Int16 i -> CG.EmitInstr cgbuf (pop 0) (Push [ ilTy ]) (mkLdcInt32 (int32 i))
3338- | Const.Int32 i -> CG.EmitInstr cgbuf (pop 0) (Push [ ilTy ]) (mkLdcInt32 i)
3339- | Const.Int64 i -> emitInt64Constant i
3340- | Const.IntPtr i -> CG.EmitInstrs cgbuf (pop 0) (Push [ ilTy ]) [ iLdcInt64 i; AI_conv DT_I ]
3341- | Const.Byte i -> CG.EmitInstr cgbuf (pop 0) (Push [ ilTy ]) (mkLdcInt32 (int32 i))
3342- | Const.UInt16 i -> CG.EmitInstr cgbuf (pop 0) (Push [ ilTy ]) (mkLdcInt32 (int32 i))
3343- | Const.UInt32 i -> CG.EmitInstr cgbuf (pop 0) (Push [ ilTy ]) (mkLdcInt32 (int32 i))
3344- | Const.UInt64 i -> emitInt64Constant (int64 i)
3345- | Const.UIntPtr i -> CG.EmitInstrs cgbuf (pop 0) (Push [ ilTy ]) [ iLdcInt64 (int64 i); AI_conv DT_U ]
3346- | Const.Double f -> CG.EmitInstr cgbuf (pop 0) (Push [ ilTy ]) (AI_ldc(DT_R8, ILConst.R8 f))
3347- | Const.Single f -> CG.EmitInstr cgbuf (pop 0) (Push [ ilTy ]) (AI_ldc(DT_R4, ILConst.R4 f))
3348- | Const.Char c -> CG.EmitInstr cgbuf (pop 0) (Push [ ilTy ]) (mkLdcInt32 (int c))
3414+ | Const.Bool b -> emitAndBoxIfNeeded emitConst g.ilg.typ_Bool (mkLdcInt32 (if b then 1 else 0))
3415+ | Const.SByte i -> emitAndBoxIfNeeded emitConst g.ilg.typ_SByte (mkLdcInt32 (int32 i))
3416+ | Const.Int16 i -> emitAndBoxIfNeeded emitConst g.ilg.typ_Int16 (mkLdcInt32 (int32 i))
3417+ | Const.Int32 i -> emitAndBoxIfNeeded emitConst g.ilg.typ_Int32 (mkLdcInt32 i)
3418+ | Const.Int64 i -> emitAndBoxIfNeeded emitInt64Constant g.ilg.typ_Int64 i
3419+ | Const.IntPtr i -> emitAndBoxIfNeeded emitConstI g.ilg.typ_IntPtr [ iLdcInt64 i; AI_conv DT_I ]
3420+ | Const.Byte i -> emitAndBoxIfNeeded emitConst g.ilg.typ_Byte (mkLdcInt32 (int32 i))
3421+ | Const.UInt16 i -> emitAndBoxIfNeeded emitConst g.ilg.typ_UInt16 (mkLdcInt32 (int32 i))
3422+ | Const.UInt32 i -> emitAndBoxIfNeeded emitConst g.ilg.typ_UInt32 (mkLdcInt32 (int32 i))
3423+ | Const.UInt64 i -> emitAndBoxIfNeeded emitInt64Constant g.ilg.typ_UInt64 (int64 i)
3424+ | Const.UIntPtr i -> emitAndBoxIfNeeded emitConstI g.ilg.typ_UIntPtr [ iLdcInt64 (int64 i); AI_conv DT_U ]
3425+ | Const.Double f -> emitAndBoxIfNeeded emitConst g.ilg.typ_Double (AI_ldc(DT_R8, ILConst.R8 f))
3426+ | Const.Single f -> emitAndBoxIfNeeded emitConst g.ilg.typ_Single (AI_ldc(DT_R4, ILConst.R4 f))
3427+ | Const.Char c -> emitAndBoxIfNeeded emitConst g.ilg.typ_Char (mkLdcInt32 (int c))
33493428 | Const.String s -> GenString cenv cgbuf s
33503429 | Const.Unit -> GenUnit cenv eenv m cgbuf
33513430 | Const.Zero -> GenDefaultValue cenv cgbuf eenv (ty, m)
@@ -4509,6 +4588,7 @@ and CanTailcall
45094588 // Can't tailcall with a .NET 2.0 generic constrained call since it involves a byref
45104589 // Can't tailcall when there are pinned locals since the stack frame must remain alive
45114590 let hasPinnedLocals = cgbuf.HasPinnedLocals()
4591+ let hasStackAllocatedLocals = cgbuf.HasStackAllocatedLocals()
45124592
45134593 if
45144594 not hasStructObjArg
@@ -4519,6 +4599,7 @@ and CanTailcall
45194599 && not isSelfInit
45204600 && not makesNoCriticalTailcalls
45214601 && not hasPinnedLocals
4602+ && not hasStackAllocatedLocals
45224603 &&
45234604
45244605 // We can tailcall even if we need to generate "unit", as long as we're about to throw the value away anyway as par of the return.
@@ -5840,6 +5921,8 @@ and GenFormalReturnType m cenv eenvFormal returnTy : ILReturn =
58405921and instSlotParam inst (TSlotParam(nm, ty, inFlag, fl2, fl3, attrs)) =
58415922 TSlotParam(nm, instType inst ty, inFlag, fl2, fl3, attrs)
58425923
5924+ and containsNativePtrWithTypar (g: TcGlobals) (typars: Typar list) ty = hasNativePtrWithTypar g typars ty
5925+
58435926and GenActualSlotsig
58445927 m
58455928 cenv
@@ -5848,14 +5931,44 @@ and GenActualSlotsig
58485931 methTyparsOfOverridingMethod
58495932 (methodParams: Val list)
58505933 =
5934+ let g = cenv.g
58515935 let ilSlotParams = List.concat ilSlotParams
58525936
5937+ let interfaceTypeArgs = argsOfAppTy g ty
5938+
58535939 let instForSlotSig =
5854- mkTyparInst (ctps @ mtps) (argsOfAppTy cenv.g ty @ generalizeTypars methTyparsOfOverridingMethod)
5940+ mkTyparInst (ctps @ mtps) (interfaceTypeArgs @ generalizeTypars methTyparsOfOverridingMethod)
5941+
5942+ let interfaceTypeArgsAreConcrete =
5943+ not ctps.IsEmpty
5944+ && (freeInTypes CollectTypars interfaceTypeArgs).FreeTypars.IsEmpty
5945+
5946+ let slotHasNativePtrWithCtps =
5947+ interfaceTypeArgsAreConcrete
5948+ && (ilSlotParams
5949+ |> List.exists (fun (TSlotParam(_, ty, _, _, _, _)) -> containsNativePtrWithTypar g ctps ty)
5950+ || ilSlotRetTy |> Option.exists (containsNativePtrWithTypar g ctps))
5951+
5952+ let eenvForSlotGen =
5953+ if slotHasNativePtrWithCtps then
5954+ EnvForTypars ctps eenv
5955+ else
5956+ eenv
5957+
5958+ // When the slot has nativeptr with concrete interface type args, don't substitute
5959+ // the class type params (ctps) - only substitute method type params (mtps).
5960+ // This keeps nativeptr<'T> unsubstituted so it generates 'native int' in IL,
5961+ // matching the interface method's signature. Without this, nativeptr<'T> would be
5962+ // substituted to nativeptr<concrete> which generates 'T*', causing a TypeLoadException.
5963+ let instForSlotSigGen =
5964+ if slotHasNativePtrWithCtps then
5965+ mkTyparInst mtps (generalizeTypars methTyparsOfOverridingMethod)
5966+ else
5967+ instForSlotSig
58555968
58565969 let ilParams =
58575970 ilSlotParams
5858- |> List.map (instSlotParam instForSlotSig >> GenSlotParam m cenv eenv )
5971+ |> List.map (instSlotParam instForSlotSigGen >> GenSlotParam m cenv eenvForSlotGen )
58595972
58605973 // Use the better names if available
58615974 let ilParams =
@@ -5866,15 +5979,15 @@ and GenActualSlotsig
58665979 ilParams
58675980
58685981 let ilRetTy =
5869- GenReturnType cenv m eenv .tyenv (Option.map (instType instForSlotSig ) ilSlotRetTy)
5982+ GenReturnType cenv m eenvForSlotGen .tyenv (Option.map (instType instForSlotSigGen ) ilSlotRetTy)
58705983
58715984 let iLRet = mkILReturn ilRetTy
58725985
58735986 let ilRetWithAttrs =
58745987 match ilSlotRetTy with
58755988 | None -> iLRet
58765989 | Some t ->
5877- match GenAdditionalAttributesForTy cenv. g t with
5990+ match GenAdditionalAttributesForTy g t with
58785991 | [] -> iLRet
58795992 | attrs -> iLRet.WithCustomAttrs(mkILCustomAttrs attrs)
58805993
0 commit comments