Skip to content

Commit 0bfd490

Browse files
authored
[FS-1081] - Extended fixed bindings (#15697)
Co-authored-by: John Wostenberg <[email protected]>
1 parent 5bc809e commit 0bfd490

File tree

79 files changed

+2278
-92
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

79 files changed

+2278
-92
lines changed

src/Compiler/Checking/CheckExpressions.fs

Lines changed: 84 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -10307,22 +10307,73 @@ and TcAndBuildFixedExpr (cenv: cenv) env (overallPatTy, fixedExpr, overallExprTy
1030710307

1030810308
let g = cenv.g
1030910309

10310+
// Search for GetPinnableReference (like https://learn.microsoft.com/en-us/dotnet/api/system.span-1.getpinnablereference?view=net-7.0)
10311+
// on the target expression, and, if it exists, call it
10312+
let tryBuildGetPinnableReferenceCall () =
10313+
let getPinnableReferenceMInfo =
10314+
TryFindIntrinsicOrExtensionMethInfo ResultCollectionSettings.AllResults cenv env mBinding env.eAccessRights "GetPinnableReference" overallExprTy
10315+
|> List.tryFind (fun mInfo ->
10316+
// GetPinnableReference must be a parameterless method with a byref or inref return value
10317+
match mInfo.GetParamDatas(cenv.amap, mBinding, mInfo.FormalMethodInst), mInfo.GetFSharpReturnType(cenv.amap, mBinding, mInfo.FormalMethodInst) with
10318+
| [[]], retTy when isByrefTy g retTy && mInfo.IsInstance -> true
10319+
| _ -> false
10320+
)
10321+
10322+
match getPinnableReferenceMInfo with
10323+
| Some mInfo ->
10324+
checkLanguageFeatureAndRecover g.langVersion LanguageFeature.ExtendedFixedBindings mBinding
10325+
10326+
let mInst = FreshenMethInfo mBinding mInfo
10327+
let pinnableReference, actualRetTy = BuildPossiblyConditionalMethodCall cenv env NeverMutates mBinding false mInfo NormalValUse mInst [ fixedExpr ] [] None
10328+
10329+
let elemTy = destByrefTy g actualRetTy
10330+
UnifyTypes cenv env mBinding (mkNativePtrTy g elemTy) overallPatTy
10331+
10332+
// For value types:
10333+
// let ptr: nativeptr<elem> =
10334+
// let pinned x = &(expr: 'a).GetPinnableReference()
10335+
// (nativeint) x
10336+
10337+
// For reference types:
10338+
// let ptr: nativeptr<elem> =
10339+
// if isNull expr then
10340+
// (nativeint) expr
10341+
// else
10342+
// let pinned x = &(expr: 'a).GetPinnableReference()
10343+
// (nativeint) x
10344+
10345+
let pinnedBinding =
10346+
mkCompGenLetIn mBinding "pinnedByref" actualRetTy pinnableReference (fun (v, ve) ->
10347+
v.SetIsFixed()
10348+
mkConvToNativeInt g ve mBinding)
10349+
10350+
if isStructTy g overallExprTy then
10351+
Some pinnedBinding
10352+
else
10353+
Some (mkNullTest g mBinding fixedExpr pinnedBinding fixedExpr)
10354+
| None ->
10355+
None
10356+
1031010357
warning(PossibleUnverifiableCode mBinding)
1031110358

1031210359
match overallExprTy with
1031310360
| ty when isByrefTy g ty ->
10314-
let okByRef =
10315-
match stripDebugPoints (stripExpr fixedExpr) with
10316-
| Expr.Op (op, tyargs, args, _) ->
10361+
// Feature ExtendedFixedBindings allows *any* byref to be used with fixed bindings, whereas the old logic only allowed a specific
10362+
// subset. This preserves the old logic when the feature is turned off.
10363+
if not (g.langVersion.SupportsFeature LanguageFeature.ExtendedFixedBindings) then
10364+
let okByRef =
10365+
match stripDebugPoints (stripExpr fixedExpr) with
10366+
| Expr.Op (op, tyargs, args, _) ->
1031710367
match op, tyargs, args with
1031810368
| TOp.ValFieldGetAddr (rfref, _), _, [_] -> not rfref.Tycon.IsStructOrEnumTycon
1031910369
| TOp.ILAsm ([ I_ldflda fspec], _), _, _ -> fspec.DeclaringType.Boxity = ILBoxity.AsObject
1032010370
| TOp.ILAsm ([ I_ldelema _], _), _, _ -> true
1032110371
| TOp.RefAddrGet _, _, _ -> true
1032210372
| _ -> false
10323-
| _ -> false
10324-
if not okByRef then
10325-
error(Error(FSComp.SR.tcFixedNotAllowed(), mBinding))
10373+
| _ -> false
10374+
10375+
if not okByRef then
10376+
errorR (languageFeatureError g.langVersion LanguageFeature.ExtendedFixedBindings mBinding)
1032610377

1032710378
let elemTy = destByrefTy g overallExprTy
1032810379
UnifyTypes cenv env mBinding (mkNativePtrTy g elemTy) overallPatTy
@@ -10331,20 +10382,29 @@ and TcAndBuildFixedExpr (cenv: cenv) env (overallPatTy, fixedExpr, overallExprTy
1033110382
mkConvToNativeInt g ve mBinding)
1033210383

1033310384
| ty when isStringTy g ty ->
10334-
let charPtrTy = mkNativePtrTy g g.char_ty
10335-
UnifyTypes cenv env mBinding charPtrTy overallPatTy
10336-
//
10337-
// let ptr: nativeptr<char> =
10338-
// let pinned s = str
10339-
// (nativeptr)s + get_OffsettoStringData()
10340-
10341-
mkCompGenLetIn mBinding "pinnedString" g.string_ty fixedExpr (fun (v, ve) ->
10342-
v.SetIsFixed()
10343-
let addrOffset = BuildOffsetToStringData cenv env mBinding
10344-
let stringAsNativeInt = mkConvToNativeInt g ve mBinding
10345-
let plusOffset = Expr.Op (TOp.ILAsm ([ AI_add ], [ g.nativeint_ty ]), [], [stringAsNativeInt; addrOffset], mBinding)
10346-
// check for non-null
10347-
mkNullTest g mBinding ve plusOffset ve)
10385+
let getPinnableRefCall =
10386+
if g.langVersion.SupportsFeature LanguageFeature.PreferStringGetPinnableReference then
10387+
tryBuildGetPinnableReferenceCall ()
10388+
else
10389+
None
10390+
10391+
match getPinnableRefCall with
10392+
| Some expr -> expr
10393+
| None ->
10394+
let charPtrTy = mkNativePtrTy g g.char_ty
10395+
UnifyTypes cenv env mBinding charPtrTy overallPatTy
10396+
//
10397+
// let ptr: nativeptr<char> =
10398+
// let pinned s = str
10399+
// (nativeptr)s + get_OffsettoStringData()
10400+
10401+
mkCompGenLetIn mBinding "pinnedString" g.string_ty fixedExpr (fun (v, ve) ->
10402+
v.SetIsFixed()
10403+
let addrOffset = BuildOffsetToStringData cenv env mBinding
10404+
let stringAsNativeInt = mkConvToNativeInt g ve mBinding
10405+
let plusOffset = Expr.Op (TOp.ILAsm ([ AI_add ], [ g.nativeint_ty ]), [], [stringAsNativeInt; addrOffset], mBinding)
10406+
// check for non-null
10407+
mkNullTest g mBinding ve plusOffset ve)
1034810408

1034910409
| ty when isArray1DTy g ty ->
1035010410
let elemTy = destArrayTy g overallExprTy
@@ -10377,7 +10437,10 @@ and TcAndBuildFixedExpr (cenv: cenv) env (overallPatTy, fixedExpr, overallExprTy
1037710437
zero)
1037810438
zero)
1037910439

10380-
| _ -> error(Error(FSComp.SR.tcFixedNotAllowed(), mBinding))
10440+
| _ ->
10441+
match tryBuildGetPinnableReferenceCall () with
10442+
| Some expr -> expr
10443+
| None -> error(Error(FSComp.SR.tcFixedNotAllowed(), mBinding))
1038110444

1038210445

1038310446
/// Binding checking code, for all bindings including let bindings, let-rec bindings, member bindings and object-expression bindings and

src/Compiler/FSComp.txt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1381,7 +1381,7 @@ tcTupleStructMismatch,"One tuple type is a struct tuple, the other is a referenc
13811381
3203,parsInvalidUseOfRec,"Invalid use of 'rec' keyword"
13821382
3204,tcStructUnionMultiCaseDistinctFields,"If a multicase union type is a struct, then all union cases must have unique names. For example: 'type A = B of b: int | C of c: int'."
13831383
3206,CallerMemberNameIsOverriden,"The CallerMemberNameAttribute applied to parameter '%s' will have no effect. It is overridden by the CallerFilePathAttribute."
1384-
3207,tcFixedNotAllowed,"Invalid use of 'fixed'. 'fixed' may only be used in a declaration of the form 'use x = fixed expr' where the expression is an array, the address of a field, the address of an array element or a string'"
1384+
3207,tcFixedNotAllowed,"Invalid use of 'fixed'. 'fixed' may only be used in a declaration of the form 'use x = fixed expr' where the expression is one of the following: an array, the address of an array element, a string, a byref, an inref, or a type implementing GetPinnableReference()"
13851385
3208,tcCouldNotFindOffsetToStringData,"Could not find method System.Runtime.CompilerServices.OffsetToStringData in references when building 'fixed' expression."
13861386
3209,chkNoByrefAddressOfLocal,"The address of the variable '%s' or a related expression cannot be used at this point. This is to ensure the address of the local value does not escape its scope."
13871387
3210,tcNamedActivePattern,"%s is an active pattern and cannot be treated as a discriminated union case with named fields."
@@ -1586,6 +1586,8 @@ featureStrictIndentation,"Raises errors on incorrect indentation, allows better
15861586
featureConstraintIntersectionOnFlexibleTypes,"Constraint intersection on flexible types"
15871587
featureChkNotTailRecursive,"Raises warnings if a member or function has the 'TailCall' attribute, but is not being used in a tail recursive way."
15881588
featureWhileBang,"'while!' expression"
1589+
featureExtendedFixedBindings,"extended fixed bindings for byref and GetPinnableReference"
1590+
featurePreferStringGetPinnableReference,"prefer String.GetPinnableReference in fixed bindings"
15891591
3353,fsiInvalidDirective,"Invalid directive '#%s %s'"
15901592
3354,tcNotAFunctionButIndexerNamedIndexingNotYetEnabled,"This value supports indexing, e.g. '%s.[index]'. The syntax '%s[index]' requires /langversion:preview. See https://aka.ms/fsharp-index-notation."
15911593
3354,tcNotAFunctionButIndexerIndexingNotYetEnabled,"This expression supports indexing, e.g. 'expr.[index]'. The syntax 'expr[index]' requires /langversion:preview. See https://aka.ms/fsharp-index-notation."

src/Compiler/Facilities/DiagnosticsLogger.fs

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -817,12 +817,15 @@ type internal SuppressLanguageFeatureCheck =
817817
| Yes
818818
| No
819819

820+
let internal languageFeatureError (langVersion: LanguageVersion) (langFeature: LanguageFeature) (m: range) =
821+
let featureStr = LanguageVersion.GetFeatureString langFeature
822+
let currentVersionStr = langVersion.SpecifiedVersionString
823+
let suggestedVersionStr = LanguageVersion.GetFeatureVersionString langFeature
824+
Error(FSComp.SR.chkFeatureNotLanguageSupported (featureStr, currentVersionStr, suggestedVersionStr), m)
825+
820826
let private tryLanguageFeatureErrorAux (langVersion: LanguageVersion) (langFeature: LanguageFeature) (m: range) =
821827
if not (langVersion.SupportsFeature langFeature) then
822-
let featureStr = LanguageVersion.GetFeatureString langFeature
823-
let currentVersionStr = langVersion.SpecifiedVersionString
824-
let suggestedVersionStr = LanguageVersion.GetFeatureVersionString langFeature
825-
Some(Error(FSComp.SR.chkFeatureNotLanguageSupported (featureStr, currentVersionStr, suggestedVersionStr), m))
828+
Some(languageFeatureError langVersion langFeature m)
826829
else
827830
None
828831

src/Compiler/Facilities/DiagnosticsLogger.fsi

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -432,6 +432,8 @@ type SuppressLanguageFeatureCheck =
432432
| Yes
433433
| No
434434

435+
val languageFeatureError: langVersion: LanguageVersion -> langFeature: LanguageFeature -> m: range -> exn
436+
435437
val checkLanguageFeatureError: langVersion: LanguageVersion -> langFeature: LanguageFeature -> m: range -> unit
436438

437439
val checkLanguageFeatureAndRecover: langVersion: LanguageVersion -> langFeature: LanguageFeature -> m: range -> unit

src/Compiler/Facilities/LanguageFeatures.fs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,8 @@ type LanguageFeature =
7777
| WarningWhenTailRecAttributeButNonTailRecUsage
7878
| UnmanagedConstraintCsharpInterop
7979
| WhileBang
80+
| ExtendedFixedBindings
81+
| PreferStringGetPinnableReference
8082

8183
/// LanguageVersion management
8284
type LanguageVersion(versionText) =
@@ -177,6 +179,8 @@ type LanguageVersion(versionText) =
177179
LanguageFeature.ConstraintIntersectionOnFlexibleTypes, previewVersion
178180
LanguageFeature.UnmanagedConstraintCsharpInterop, previewVersion
179181
LanguageFeature.WhileBang, previewVersion
182+
LanguageFeature.ExtendedFixedBindings, previewVersion
183+
LanguageFeature.PreferStringGetPinnableReference, previewVersion
180184
]
181185

182186
static let defaultLanguageVersion = LanguageVersion("default")
@@ -309,6 +313,8 @@ type LanguageVersion(versionText) =
309313
| LanguageFeature.WarningWhenTailRecAttributeButNonTailRecUsage -> FSComp.SR.featureChkNotTailRecursive ()
310314
| LanguageFeature.UnmanagedConstraintCsharpInterop -> FSComp.SR.featureUnmanagedConstraintCsharpInterop ()
311315
| LanguageFeature.WhileBang -> FSComp.SR.featureWhileBang ()
316+
| LanguageFeature.ExtendedFixedBindings -> FSComp.SR.featureExtendedFixedBindings ()
317+
| LanguageFeature.PreferStringGetPinnableReference -> FSComp.SR.featurePreferStringGetPinnableReference ()
312318

313319
/// Get a version string associated with the given feature.
314320
static member GetFeatureVersionString feature =

src/Compiler/Facilities/LanguageFeatures.fsi

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,8 @@ type LanguageFeature =
6767
| WarningWhenTailRecAttributeButNonTailRecUsage
6868
| UnmanagedConstraintCsharpInterop
6969
| WhileBang
70+
| ExtendedFixedBindings
71+
| PreferStringGetPinnableReference
7072

7173
/// LanguageVersion management
7274
type LanguageVersion =

src/Compiler/xlf/FSComp.txt.cs.xlf

Lines changed: 12 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/Compiler/xlf/FSComp.txt.de.xlf

Lines changed: 12 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/Compiler/xlf/FSComp.txt.es.xlf

Lines changed: 12 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)