Skip to content

Commit bb027e1

Browse files
authored
Support consuming IL(C#)-defined generic T with AllowByRefLike anti-constraint (#17597)
1 parent 11cb682 commit bb027e1

File tree

12 files changed

+310
-54
lines changed

12 files changed

+310
-54
lines changed

docs/release-notes/.FSharp.Compiler.Service/9.0.100.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
* Allow object expression without overrides. ([Language suggestion #632](https://github.com/fsharp/fslang-suggestions/issues/632), [PR #17387](https://github.com/dotnet/fsharp/pull/17387))
3030
* Enable FSharp 9.0 Language Version ([Issue #17497](https://github.com/dotnet/fsharp/issues/17438)), [PR](https://github.com/dotnet/fsharp/pull/17500)))
3131
* Enable LanguageFeature.EnforceAttributeTargets in F# 9.0. ([Issue #17514](https://github.com/dotnet/fsharp/issues/17558), [PR #17516](https://github.com/dotnet/fsharp/pull/17558))
32+
* Enable consuming generic arguments defined as `allows ref struct` in C# ([Issue #17597](https://github.com/dotnet/fsharp/issues/17597)
3233

3334
### Changed
3435

src/Compiler/AbstractIL/il.fs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1862,9 +1862,10 @@ type ILGenericParameterDef =
18621862
Name: string
18631863
Constraints: ILTypes
18641864
Variance: ILGenericVariance
1865-
HasReferenceTypeConstraint: bool
1865+
HasReferenceTypeConstraint: bool
18661866
HasNotNullableValueTypeConstraint: bool
18671867
HasDefaultConstructorConstraint: bool
1868+
HasAllowsRefStruct: bool
18681869
CustomAttrsStored: ILAttributesStored
18691870
MetadataIndex: int32
18701871
}
@@ -3283,6 +3284,7 @@ let mkILSimpleTypar nm =
32833284
HasReferenceTypeConstraint = false
32843285
HasNotNullableValueTypeConstraint = false
32853286
HasDefaultConstructorConstraint = false
3287+
HasAllowsRefStruct = false
32863288
CustomAttrsStored = storeILCustomAttrs emptyILCustomAttrs
32873289
MetadataIndex = NoMetadataIdx
32883290
}

src/Compiler/AbstractIL/il.fsi

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1021,6 +1021,9 @@ type ILGenericParameterDef =
10211021
/// Indicates the type argument must have a public nullary constructor.
10221022
HasDefaultConstructorConstraint: bool
10231023

1024+
/// Indicates the type parameter allows ref struct, i.e. an anti constraint.
1025+
HasAllowsRefStruct: bool
1026+
10241027
/// Do not use this
10251028
CustomAttrsStored: ILAttributesStored
10261029

src/Compiler/AbstractIL/ilread.fs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2292,6 +2292,7 @@ and seekReadGenericParamsUncached ctxtH (GenericParamsIdx(numTypars, a, b)) =
22922292
HasReferenceTypeConstraint = (flags &&& 0x0004) <> 0
22932293
HasNotNullableValueTypeConstraint = (flags &&& 0x0008) <> 0
22942294
HasDefaultConstructorConstraint = (flags &&& 0x0010) <> 0
2295+
HasAllowsRefStruct = (flags &&& 0x0020) <> 0
22952296
})
22962297
)
22972298

src/Compiler/AbstractIL/ilreflect.fs

Lines changed: 23 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1714,31 +1714,34 @@ let buildGenParamsPass1b cenv emEnv (genArgs: Type array) (gps: ILGenericParamet
17141714
gp.CustomAttrs
17151715
|> emitCustomAttrs cenv emEnv (wrapCustomAttr gpB.SetCustomAttribute)
17161716

1717-
let flags = GenericParameterAttributes.None
1718-
17191717
let flags =
17201718
match gp.Variance with
1721-
| NonVariant -> flags
1722-
| CoVariant -> flags ||| GenericParameterAttributes.Covariant
1723-
| ContraVariant -> flags ||| GenericParameterAttributes.Contravariant
1719+
| NonVariant -> GenericParameterAttributes.None
1720+
| CoVariant -> GenericParameterAttributes.Covariant
1721+
| ContraVariant -> GenericParameterAttributes.Contravariant
17241722

1725-
let flags =
1726-
if gp.HasReferenceTypeConstraint then
1727-
flags ||| GenericParameterAttributes.ReferenceTypeConstraint
1728-
else
1729-
flags
1723+
let zero = GenericParameterAttributes.None
17301724

17311725
let flags =
1732-
if gp.HasNotNullableValueTypeConstraint then
1733-
flags ||| GenericParameterAttributes.NotNullableValueTypeConstraint
1734-
else
1735-
flags
1736-
1737-
let flags =
1738-
if gp.HasDefaultConstructorConstraint then
1739-
flags ||| GenericParameterAttributes.DefaultConstructorConstraint
1740-
else
1741-
flags
1726+
flags
1727+
||| (if gp.HasReferenceTypeConstraint then
1728+
GenericParameterAttributes.ReferenceTypeConstraint
1729+
else
1730+
zero)
1731+
||| (if gp.HasNotNullableValueTypeConstraint then
1732+
GenericParameterAttributes.NotNullableValueTypeConstraint
1733+
else
1734+
zero)
1735+
||| (if gp.HasDefaultConstructorConstraint then
1736+
GenericParameterAttributes.DefaultConstructorConstraint
1737+
else
1738+
zero)
1739+
|||
1740+
// GenericParameterAttributes.AllowByRefLike from net9, not present in ns20
1741+
(if gp.HasAllowsRefStruct then
1742+
(enum<GenericParameterAttributes> 0x0020)
1743+
else
1744+
zero)
17421745

17431746
gpB.SetGenericParameterAttributes flags)
17441747
//----------------------------------------------------------------------------

src/Compiler/AbstractIL/ilwrite.fs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2513,7 +2513,8 @@ let rec GetGenericParamAsGenericParamRow cenv _env idx owner gp =
25132513
| ContraVariant -> 0x0002) |||
25142514
(if gp.HasReferenceTypeConstraint then 0x0004 else 0x0000) |||
25152515
(if gp.HasNotNullableValueTypeConstraint then 0x0008 else 0x0000) |||
2516-
(if gp.HasDefaultConstructorConstraint then 0x0010 else 0x0000)
2516+
(if gp.HasDefaultConstructorConstraint then 0x0010 else 0x0000) |||
2517+
(if gp.HasAllowsRefStruct then 0x0020 else 0x0000)
25172518

25182519

25192520
let mdVersionMajor, _ = metadataSchemaVersionSupportedByCLRVersion cenv.desiredMetadataVersion

src/Compiler/Checking/PostInferenceChecks.fs

Lines changed: 85 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ open FSharp.Compiler.TypedTreeBasics
2828
open FSharp.Compiler.TypedTreeOps
2929
open FSharp.Compiler.TypeHierarchy
3030
open FSharp.Compiler.TypeRelations
31+
open Import
3132

3233
//--------------------------------------------------------------------------
3334
// NOTES: reraise safety checks
@@ -334,7 +335,20 @@ let RecordAnonRecdInfo cenv (anonInfo: AnonRecdTypeInfo) =
334335
// approx walk of type
335336
//--------------------------------------------------------------------------
336337

337-
let rec CheckTypeDeep (cenv: cenv) (visitTy, visitTyconRefOpt, visitAppTyOpt, visitTraitSolutionOpt, visitTyparOpt as f) (g: TcGlobals) env isInner ty =
338+
/// Represents the container for nester type instantions, carrying information about the parent (generic type) and data about correspinding generic typar definition.
339+
/// For current use, IlGenericParameterDef was enough. For other future use cases, conversion into F# Typar might be needed.
340+
type TypeInstCtx =
341+
| NoInfo
342+
| IlGenericInst of parent:TyconRef * genericArg:ILGenericParameterDef
343+
| TyparInst of parent:TyconRef
344+
| TopLevelAllowingByRef
345+
346+
with member x.TyparAllowsRefStruct() =
347+
match x with
348+
| IlGenericInst(_,ilTypar) -> ilTypar.HasAllowsRefStruct
349+
| _ -> false
350+
351+
let rec CheckTypeDeep (cenv: cenv) (visitTy, visitTyconRefOpt, visitAppTyOpt, visitTraitSolutionOpt, visitTyparOpt as f) (g: TcGlobals) env (typeInstParentOpt:TypeInstCtx) ty =
338352
// We iterate the _solved_ constraints as well, to pick up any record of trait constraint solutions
339353
// This means we walk _all_ the constraints _everywhere_ in a type, including
340354
// those attached to _solved_ type variables. This is used by PostTypeCheckSemanticChecks to detect uses of
@@ -366,22 +380,30 @@ let rec CheckTypeDeep (cenv: cenv) (visitTy, visitTyconRefOpt, visitAppTyOpt, vi
366380
match ty with
367381
| TType_forall (tps, body) ->
368382
let env = BindTypars g env tps
369-
CheckTypeDeep cenv f g env isInner body
383+
CheckTypeDeep cenv f g env typeInstParentOpt body
370384
tps |> List.iter (fun tp -> tp.Constraints |> List.iter (CheckTypeConstraintDeep cenv f g env))
371385

372386
| TType_measure _ -> ()
373387

374388
| TType_app (tcref, tinst, _) ->
375389
match visitTyconRefOpt with
376-
| Some visitTyconRef -> visitTyconRef isInner tcref
390+
| Some visitTyconRef -> visitTyconRef typeInstParentOpt tcref
377391
| None -> ()
378392

379393
// If it's a 'byref<'T>', don't check 'T as an inner. This allows byref<Span<'T>>.
380394
// 'byref<byref<'T>>' is invalid and gets checked in visitAppTy.
381-
if isByrefTyconRef g tcref then
382-
CheckTypesDeepNoInner cenv f g env tinst
395+
//if isByrefTyconRef g tcref then
396+
// CheckTypesDeepNoInner cenv f g env tinst
397+
398+
if tcref.CanDeref && tcref.IsILTycon && tinst.Length = tcref.ILTyconRawMetadata.GenericParams.Length then
399+
(tinst,tcref.ILTyconRawMetadata.GenericParams)
400+
||> List.iter2 (fun ty ilGenericParam ->
401+
let typeInstParent = IlGenericInst(tcref, ilGenericParam)
402+
CheckTypeDeep cenv f g env typeInstParent ty)
383403
else
384-
CheckTypesDeep cenv f g env tinst
404+
let parentRef = TyparInst(tcref)
405+
for ty in tinst do
406+
CheckTypeDeep cenv f g env parentRef ty
385407

386408
match visitAppTyOpt with
387409
| Some visitAppTy -> visitAppTy (tcref, tinst)
@@ -398,8 +420,8 @@ let rec CheckTypeDeep (cenv: cenv) (visitTy, visitTyconRefOpt, visitAppTyOpt, vi
398420
CheckTypesDeep cenv f g env tys
399421

400422
| TType_fun (s, t, _) ->
401-
CheckTypeDeep cenv f g env true s
402-
CheckTypeDeep cenv f g env true t
423+
CheckTypeDeep cenv f g env NoInfo s
424+
CheckTypeDeep cenv f g env NoInfo t
403425

404426
| TType_var (tp, _) ->
405427
if not tp.IsSolved then
@@ -410,20 +432,16 @@ let rec CheckTypeDeep (cenv: cenv) (visitTy, visitTyconRefOpt, visitAppTyOpt, vi
410432

411433
and CheckTypesDeep cenv f g env tys =
412434
for ty in tys do
413-
CheckTypeDeep cenv f g env true ty
414-
415-
and CheckTypesDeepNoInner cenv f g env tys =
416-
for ty in tys do
417-
CheckTypeDeep cenv f g env false ty
435+
CheckTypeDeep cenv f g env NoInfo ty
418436

419437
and CheckTypeConstraintDeep cenv f g env x =
420438
match x with
421-
| TyparConstraint.CoercesTo(ty, _) -> CheckTypeDeep cenv f g env true ty
439+
| TyparConstraint.CoercesTo(ty, _) -> CheckTypeDeep cenv f g env NoInfo ty
422440
| TyparConstraint.MayResolveMember(traitInfo, _) -> CheckTraitInfoDeep cenv f g env traitInfo
423-
| TyparConstraint.DefaultsTo(_, ty, _) -> CheckTypeDeep cenv f g env true ty
441+
| TyparConstraint.DefaultsTo(_, ty, _) -> CheckTypeDeep cenv f g env NoInfo ty
424442
| TyparConstraint.SimpleChoice(tys, _) -> CheckTypesDeep cenv f g env tys
425-
| TyparConstraint.IsEnum(underlyingTy, _) -> CheckTypeDeep cenv f g env true underlyingTy
426-
| TyparConstraint.IsDelegate(argTys, retTy, _) -> CheckTypeDeep cenv f g env true argTys; CheckTypeDeep cenv f g env true retTy
443+
| TyparConstraint.IsEnum(underlyingTy, _) -> CheckTypeDeep cenv f g env NoInfo underlyingTy
444+
| TyparConstraint.IsDelegate(argTys, retTy, _) -> CheckTypeDeep cenv f g env NoInfo argTys; CheckTypeDeep cenv f g env NoInfo retTy
427445
| TyparConstraint.SupportsComparison _
428446
| TyparConstraint.SupportsEquality _
429447
| TyparConstraint.SupportsNull _
@@ -436,18 +454,18 @@ and CheckTypeConstraintDeep cenv f g env x =
436454
and CheckTraitInfoDeep cenv (_, _, _, visitTraitSolutionOpt, _ as f) g env traitInfo =
437455
CheckTypesDeep cenv f g env traitInfo.SupportTypes
438456
CheckTypesDeep cenv f g env traitInfo.CompiledObjectAndArgumentTypes
439-
Option.iter (CheckTypeDeep cenv f g env true ) traitInfo.CompiledReturnType
457+
Option.iter (CheckTypeDeep cenv f g env NoInfo ) traitInfo.CompiledReturnType
440458
match visitTraitSolutionOpt, traitInfo.Solution with
441459
| Some visitTraitSolution, Some sln -> visitTraitSolution sln
442460
| _ -> ()
443461

444462
/// Check for byref-like types
445463
let CheckForByrefLikeType cenv env m ty check =
446-
CheckTypeDeep cenv (ignore, Some (fun _deep tcref -> if isByrefLikeTyconRef cenv.g m tcref then check()), None, None, None) cenv.g env false ty
464+
CheckTypeDeep cenv (ignore, Some (fun ctx tcref -> if (isByrefLikeTyconRef cenv.g m tcref && not(ctx.TyparAllowsRefStruct())) then check()), None, None, None) cenv.g env NoInfo ty
447465

448466
/// Check for byref types
449467
let CheckForByrefType cenv env ty check =
450-
CheckTypeDeep cenv (ignore, Some (fun _deep tcref -> if isByrefTyconRef cenv.g tcref then check()), None, None, None) cenv.g env false ty
468+
CheckTypeDeep cenv (ignore, Some (fun _ctx tcref -> if isByrefTyconRef cenv.g tcref then check()), None, None, None) cenv.g env NoInfo ty
451469

452470
/// check captures under lambdas
453471
///
@@ -516,7 +534,7 @@ let CheckTypeForAccess (cenv: cenv) env objName valAcc m ty =
516534
if isLessAccessible tyconAcc valAcc then
517535
errorR(Error(FSComp.SR.chkTypeLessAccessibleThanType(tcref.DisplayName, (objName())), m))
518536

519-
CheckTypeDeep cenv (visitType, None, None, None, None) cenv.g env false ty
537+
CheckTypeDeep cenv (visitType, None, None, None, None) cenv.g env NoInfo ty
520538

521539
let WarnOnWrongTypeForAccess (cenv: cenv) env objName valAcc m ty =
522540
if cenv.reportErrors then
@@ -534,7 +552,7 @@ let WarnOnWrongTypeForAccess (cenv: cenv) env objName valAcc m ty =
534552
let warningText = errorText + Environment.NewLine + FSComp.SR.tcTypeAbbreviationsCheckedAtCompileTime()
535553
warning(AttributeChecking.ObsoleteWarning(warningText, m))
536554

537-
CheckTypeDeep cenv (visitType, None, None, None, None) cenv.g env false ty
555+
CheckTypeDeep cenv (visitType, None, None, None, None) cenv.g env NoInfo ty
538556

539557
/// Indicates whether a byref or byref-like type is permitted at a particular location
540558
[<RequireQualifiedAccess>]
@@ -629,16 +647,26 @@ let CheckTypeAux permitByRefLike (cenv: cenv) env m ty onInnerByrefError =
629647
else
630648
errorR (Error(FSComp.SR.checkNotSufficientlyGenericBecauseOfScope(tp.DisplayName), m))
631649

632-
let visitTyconRef isInner tcref =
650+
let visitTyconRef (ctx:TypeInstCtx) tcref =
651+
let checkInner() =
652+
match ctx with
653+
| TopLevelAllowingByRef -> false
654+
| TyparInst(parentTcRef)
655+
| IlGenericInst(parentTcRef,_) when isByrefTyconRef cenv.g parentTcRef -> false
656+
| _ -> true
657+
658+
let isInnerByRefLike() = checkInner() && isByrefLikeTyconRef cenv.g m tcref
659+
660+
let permitByRefLike =
661+
if ctx.TyparAllowsRefStruct() then PermitByRefType.All else permitByRefLike
633662

634-
let isInnerByRefLike = isInner && isByrefLikeTyconRef cenv.g m tcref
635663

636664
match permitByRefLike with
637665
| PermitByRefType.None when isByrefLikeTyconRef cenv.g m tcref ->
638666
errorR(Error(FSComp.SR.chkErrorUseOfByref(), m))
639-
| PermitByRefType.NoInnerByRefLike when isInnerByRefLike ->
667+
| PermitByRefType.NoInnerByRefLike when isInnerByRefLike() ->
640668
onInnerByrefError ()
641-
| PermitByRefType.SpanLike when isByrefTyconRef cenv.g tcref || isInnerByRefLike ->
669+
| PermitByRefType.SpanLike when isByrefTyconRef cenv.g tcref || isInnerByRefLike() ->
642670
onInnerByrefError ()
643671
| _ -> ()
644672

@@ -665,7 +693,13 @@ let CheckTypeAux permitByRefLike (cenv: cenv) env m ty onInnerByrefError =
665693
cenv.potentialUnboundUsesOfVals <- cenv.potentialUnboundUsesOfVals.Add(vref.Stamp, m)
666694
| _ -> ()
667695

668-
CheckTypeDeep cenv (ignore, Some visitTyconRef, Some visitAppTy, Some visitTraitSolution, Some visitTyar) cenv.g env false ty
696+
let initialCtx =
697+
match permitByRefLike with
698+
| PermitByRefType.SpanLike
699+
| PermitByRefType.NoInnerByRefLike -> TopLevelAllowingByRef
700+
| _ -> NoInfo
701+
702+
CheckTypeDeep cenv (ignore, Some visitTyconRef, Some visitAppTy, Some visitTraitSolution, Some visitTyar) cenv.g env initialCtx ty
669703

670704
let CheckType permitByRefLike cenv env m ty =
671705
CheckTypeAux permitByRefLike cenv env m ty (fun () -> errorR(Error(FSComp.SR.chkErrorUseOfByref(), m)))
@@ -1458,9 +1492,31 @@ and CheckExprOp cenv env (op, tyargs, args, m) ctxt expr =
14581492
CombineTwoLimits limit1 limit2
14591493

14601494
| TOp.ILCall (_, _, _, _, _, _, _, ilMethRef, enclTypeInst, methInst, retTypes), _, _ ->
1495+
14611496
CheckTypeInstNoByrefs cenv env m tyargs
1462-
CheckTypeInstNoByrefs cenv env m enclTypeInst
1463-
CheckTypeInstNoByrefs cenv env m methInst
1497+
1498+
match enclTypeInst,methInst with
1499+
| [],[] -> ()
1500+
| enclTypeInst,methInst ->
1501+
let tyconRef = ImportILTypeRef cenv.amap m ilMethRef.DeclaringTypeRef
1502+
match tyconRef.TypeReprInfo with
1503+
| TILObjectRepr(TILObjectReprData(scoref, _, tdef)) ->
1504+
(enclTypeInst,tdef.GenericParams)
1505+
||> List.iter2 (fun typeInst typeGeneric ->
1506+
if not typeGeneric.HasAllowsRefStruct then
1507+
CheckTypeNoByrefs cenv env m typeInst)
1508+
1509+
match methInst with
1510+
| [] -> ()
1511+
| methInst ->
1512+
let methDef = resolveILMethodRefWithRescope (rescopeILType scoref) tdef ilMethRef
1513+
(methInst,methDef.GenericParams)
1514+
||> List.iter2 (fun methInst methGeneric ->
1515+
if not methGeneric.HasAllowsRefStruct then
1516+
CheckTypeNoByrefs cenv env m methInst)
1517+
1518+
| _ -> ()
1519+
14641520
CheckTypeInstNoInnerByrefs cenv env m retTypes // permit byref returns
14651521

14661522
let hasReceiver =

src/Compiler/CodeGen/IlxGen.fs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2070,6 +2070,7 @@ type AnonTypeGenerationTable() =
20702070
HasReferenceTypeConstraint = false
20712071
HasNotNullableValueTypeConstraint = false
20722072
HasDefaultConstructorConstraint = false
2073+
HasAllowsRefStruct = false
20732074
MetadataIndex = NoMetadataIdx
20742075
}
20752076
]
@@ -5733,6 +5734,7 @@ and GenGenericParam cenv eenv (tp: Typar) =
57335734
HasReferenceTypeConstraint = refTypeConstraint
57345735
HasNotNullableValueTypeConstraint = notNullableValueTypeConstraint || emitUnmanagedInIlOutput
57355736
HasDefaultConstructorConstraint = defaultConstructorConstraint
5737+
HasAllowsRefStruct = false
57365738
}
57375739

57385740
//--------------------------------------------------------------------------

0 commit comments

Comments
 (0)