Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
62 commits
Select commit Hold shift + click to select a range
3b8bf45
Add compiler tests for currently allowed fixed expressions
jwosty Jul 22, 2023
fa02474
Make typechecker accept arbitrary byref pinning in `fixed` expressions
jwosty Jul 22, 2023
819890b
Make typechecker accept GetPinnableReference() in fixed expressions
jwosty Jul 26, 2023
c0fca86
Add IL compiler tests for currently allowed fixed expressions
jwosty Jul 27, 2023
d523744
Write codegen tests for fixed expressions with byrefs
jwosty Jul 27, 2023
56b9eb0
Write codegen for pinning custom byref type via GetPinnableReference
jwosty Jul 27, 2023
c5e9fbd
Make fixed expressions work properly with Span and Span-like types
jwosty Jul 27, 2023
4d00bb5
Make fixed expr work with generic extension GetPinnableReference method
jwosty Jul 28, 2023
53b5920
Reject fixed expressions when used with static GetPinnableReference m…
jwosty Jul 28, 2023
653daca
Write more fixed expression codegen tests
jwosty Jul 28, 2023
fda843a
Try to make test pass in CI
jwosty Jul 28, 2023
e479305
Merge remote-tracking branch 'upstream/main' into jw-fs1081-extend-fi…
jwosty Jul 28, 2023
aadc045
Fix test mistakes - all new tests should be green now
jwosty Jul 28, 2023
cd4c932
Create extended-fixed-bindings language switch
jwosty Jul 28, 2023
b52e1b4
Update FS3207 error message text to include new criteria
jwosty Jul 28, 2023
a202926
Exclude some tests for .net framework, which lacks Span<T>
jwosty Jul 28, 2023
771f021
Fix more CI test issues
jwosty Jul 28, 2023
224bae9
Add a fixed expr struct test that works under .net framework
jwosty Jul 28, 2023
33e595b
Update baselines
jwosty Jul 28, 2023
287caa9
Add more tests to cover original baselines
jwosty Jul 28, 2023
2a1f50a
Attempt more CI test fixes
jwosty Jul 28, 2023
e57d48a
Feature-flag fixed expr codegen tests
jwosty Jul 28, 2023
cd3d09b
Add missing [<Fact>] to activate a test
jwosty Jul 28, 2023
98d114d
Parameterize all fixed expr tests with feature flags
jwosty Jul 28, 2023
69281a6
Add negative tests for fixed expr feature flagging
jwosty Jul 28, 2023
f827244
Make extended fixed bindings obey feature flag
jwosty Jul 28, 2023
2e08260
Add a test case highlighting a new (fixed?) behavior
jwosty Jul 28, 2023
79817ca
Raise FS3350 (feature unavailable) when necessary for fixed bindings
jwosty Jul 28, 2023
1e4b10b
Use GetPinnableReference method on strings when available
jwosty Jul 29, 2023
1ecf841
Prevent method inlining to make tests clearer
jwosty Jul 29, 2023
7647990
Add a generated null check for reference types
jwosty Jul 29, 2023
1ca8485
Refactor
jwosty Jul 29, 2023
2b8e64e
Merge remote-tracking branch 'upstream/main' into jw-fs1081-extend-fi…
jwosty Jul 31, 2023
8cb46a4
Fix baselines
jwosty Aug 1, 2023
71e1146
Merge branch 'main' into jw-fs1081-extend-fixed-expressions
jwosty Aug 1, 2023
8445ffd
Merge branch 'main' into jw-fs1081-extend-fixed-expressions
jwosty Aug 1, 2023
091684f
Merge branch 'main' into jw-fs1081-extend-fixed-expressions
jwosty Aug 3, 2023
e55e801
Merge branch 'main' into jw-fs1081-extend-fixed-expressions
jwosty Aug 4, 2023
e4d552b
Merge branch 'main' into jw-fs1081-extend-fixed-expressions
jwosty Aug 8, 2023
6de6e00
Rewrite to increase comprehensibility, and add some clarifying comments
jwosty Aug 9, 2023
ba2d08a
Rename fixed binding test files
jwosty Aug 9, 2023
2d13cfc
More renames
jwosty Aug 9, 2023
9b47c4c
Rename all instances of 'fixed expressions' -> 'fixed bindings'
jwosty Aug 9, 2023
a3b980f
Extract fixed binding IL test cases to their own files
jwosty Aug 9, 2023
4873ec4
Extract fixed binding Language test cases to their own files
jwosty Aug 10, 2023
01cbd18
Split fixed binding Language test cases
jwosty Aug 10, 2023
8c74b6d
Raise recoverable errors
jwosty Aug 10, 2023
bb99236
Add more negative fixed binding tests
jwosty Aug 10, 2023
0c4ea8b
Add test verifying that null guard in fixed bindings against GetPinna…
jwosty Aug 10, 2023
44f1e09
Adjust wording
jwosty Aug 10, 2023
cfaf3e3
Split one remaining test case
jwosty Aug 10, 2023
351b155
Small test refactor
jwosty Aug 10, 2023
25a064c
Merge branch 'main' into jw-fs1081-extend-fixed-expressions
jwosty Aug 10, 2023
45db0e6
Add an F# style extension method test for GetPinnableReference
jwosty Aug 10, 2023
557ef00
Add some more fixed binding negative tests for sanity
jwosty Aug 10, 2023
f39284d
Merge branch 'main' into jw-fs1081-extend-fixed-expressions
jwosty Aug 10, 2023
ef318f7
Apply fantomas
jwosty Aug 10, 2023
ebd143f
Merge branch 'main' into jw-fs1081-extend-fixed-expressions
jwosty Aug 14, 2023
dbdc459
Change an IL test to hopefully make it compile consistently in CI
jwosty Aug 14, 2023
43d0d5f
Change method name to try to appease CI
jwosty Aug 14, 2023
597e29d
Fix test file name
jwosty Aug 14, 2023
09c38c9
Merge branch 'main' into jw-fs1081-extend-fixed-expressions
jwosty Aug 15, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
105 changes: 84 additions & 21 deletions src/Compiler/Checking/CheckExpressions.fs
Original file line number Diff line number Diff line change
Expand Up @@ -10307,22 +10307,73 @@ and TcAndBuildFixedExpr (cenv: cenv) env (overallPatTy, fixedExpr, overallExprTy

let g = cenv.g

// Search for GetPinnableReference (like https://learn.microsoft.com/en-us/dotnet/api/system.span-1.getpinnablereference?view=net-7.0)
// on the target expression, and, if it exists, call it
let tryBuildGetPinnableReferenceCall () =
let getPinnableReferenceMInfo =
TryFindIntrinsicOrExtensionMethInfo ResultCollectionSettings.AllResults cenv env mBinding env.eAccessRights "GetPinnableReference" overallExprTy
|> List.tryFind (fun mInfo ->
// GetPinnableReference must be a parameterless method with a byref or inref return value
match mInfo.GetParamDatas(cenv.amap, mBinding, mInfo.FormalMethodInst), mInfo.GetFSharpReturnType(cenv.amap, mBinding, mInfo.FormalMethodInst) with
| [[]], retTy when isByrefTy g retTy && mInfo.IsInstance -> true
| _ -> false
)

match getPinnableReferenceMInfo with
| Some mInfo ->
checkLanguageFeatureAndRecover g.langVersion LanguageFeature.ExtendedFixedBindings mBinding

let mInst = FreshenMethInfo mBinding mInfo
let pinnableReference, actualRetTy = BuildPossiblyConditionalMethodCall cenv env NeverMutates mBinding false mInfo NormalValUse mInst [ fixedExpr ] [] None

let elemTy = destByrefTy g actualRetTy
UnifyTypes cenv env mBinding (mkNativePtrTy g elemTy) overallPatTy

// For value types:
// let ptr: nativeptr<elem> =
// let pinned x = &(expr: 'a).GetPinnableReference()
// (nativeint) x

// For reference types:
// let ptr: nativeptr<elem> =
// if isNull expr then
// (nativeint) expr
// else
// let pinned x = &(expr: 'a).GetPinnableReference()
// (nativeint) x

let pinnedBinding =
mkCompGenLetIn mBinding "pinnedByref" actualRetTy pinnableReference (fun (v, ve) ->
v.SetIsFixed()
mkConvToNativeInt g ve mBinding)

if isStructTy g overallExprTy then
Some pinnedBinding
else
Some (mkNullTest g mBinding fixedExpr pinnedBinding fixedExpr)
| None ->
None

warning(PossibleUnverifiableCode mBinding)

match overallExprTy with
| ty when isByrefTy g ty ->
let okByRef =
match stripDebugPoints (stripExpr fixedExpr) with
| Expr.Op (op, tyargs, args, _) ->
// Feature ExtendedFixedBindings allows *any* byref to be used with fixed bindings, whereas the old logic only allowed a specific
// subset. This preserves the old logic when the feature is turned off.
if not (g.langVersion.SupportsFeature LanguageFeature.ExtendedFixedBindings) then
let okByRef =
match stripDebugPoints (stripExpr fixedExpr) with
| Expr.Op (op, tyargs, args, _) ->
match op, tyargs, args with
| TOp.ValFieldGetAddr (rfref, _), _, [_] -> not rfref.Tycon.IsStructOrEnumTycon
| TOp.ILAsm ([ I_ldflda fspec], _), _, _ -> fspec.DeclaringType.Boxity = ILBoxity.AsObject
| TOp.ILAsm ([ I_ldelema _], _), _, _ -> true
| TOp.RefAddrGet _, _, _ -> true
| _ -> false
| _ -> false
if not okByRef then
error(Error(FSComp.SR.tcFixedNotAllowed(), mBinding))
| _ -> false

if not okByRef then
errorR (languageFeatureError g.langVersion LanguageFeature.ExtendedFixedBindings mBinding)

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

| ty when isStringTy g ty ->
let charPtrTy = mkNativePtrTy g g.char_ty
UnifyTypes cenv env mBinding charPtrTy overallPatTy
//
// let ptr: nativeptr<char> =
// let pinned s = str
// (nativeptr)s + get_OffsettoStringData()

mkCompGenLetIn mBinding "pinnedString" g.string_ty fixedExpr (fun (v, ve) ->
v.SetIsFixed()
let addrOffset = BuildOffsetToStringData cenv env mBinding
let stringAsNativeInt = mkConvToNativeInt g ve mBinding
let plusOffset = Expr.Op (TOp.ILAsm ([ AI_add ], [ g.nativeint_ty ]), [], [stringAsNativeInt; addrOffset], mBinding)
// check for non-null
mkNullTest g mBinding ve plusOffset ve)
let getPinnableRefCall =
if g.langVersion.SupportsFeature LanguageFeature.PreferStringGetPinnableReference then
tryBuildGetPinnableReferenceCall ()
else
None

match getPinnableRefCall with
| Some expr -> expr
| None ->
let charPtrTy = mkNativePtrTy g g.char_ty
UnifyTypes cenv env mBinding charPtrTy overallPatTy
//
// let ptr: nativeptr<char> =
// let pinned s = str
// (nativeptr)s + get_OffsettoStringData()

mkCompGenLetIn mBinding "pinnedString" g.string_ty fixedExpr (fun (v, ve) ->
v.SetIsFixed()
let addrOffset = BuildOffsetToStringData cenv env mBinding
let stringAsNativeInt = mkConvToNativeInt g ve mBinding
let plusOffset = Expr.Op (TOp.ILAsm ([ AI_add ], [ g.nativeint_ty ]), [], [stringAsNativeInt; addrOffset], mBinding)
// check for non-null
mkNullTest g mBinding ve plusOffset ve)

| ty when isArray1DTy g ty ->
let elemTy = destArrayTy g overallExprTy
Expand Down Expand Up @@ -10377,7 +10437,10 @@ and TcAndBuildFixedExpr (cenv: cenv) env (overallPatTy, fixedExpr, overallExprTy
zero)
zero)

| _ -> error(Error(FSComp.SR.tcFixedNotAllowed(), mBinding))
| _ ->
match tryBuildGetPinnableReferenceCall () with
| Some expr -> expr
| None -> error(Error(FSComp.SR.tcFixedNotAllowed(), mBinding))


/// Binding checking code, for all bindings including let bindings, let-rec bindings, member bindings and object-expression bindings and
Expand Down
4 changes: 3 additions & 1 deletion src/Compiler/FSComp.txt
Original file line number Diff line number Diff line change
Expand Up @@ -1381,7 +1381,7 @@ tcTupleStructMismatch,"One tuple type is a struct tuple, the other is a referenc
3203,parsInvalidUseOfRec,"Invalid use of 'rec' keyword"
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'."
3206,CallerMemberNameIsOverriden,"The CallerMemberNameAttribute applied to parameter '%s' will have no effect. It is overridden by the CallerFilePathAttribute."
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'"
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()"
3208,tcCouldNotFindOffsetToStringData,"Could not find method System.Runtime.CompilerServices.OffsetToStringData in references when building 'fixed' expression."
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."
3210,tcNamedActivePattern,"%s is an active pattern and cannot be treated as a discriminated union case with named fields."
Expand Down Expand Up @@ -1586,6 +1586,8 @@ featureStrictIndentation,"Raises errors on incorrect indentation, allows better
featureConstraintIntersectionOnFlexibleTypes,"Constraint intersection on flexible types"
featureChkNotTailRecursive,"Raises warnings if a member or function has the 'TailCall' attribute, but is not being used in a tail recursive way."
featureWhileBang,"'while!' expression"
featureExtendedFixedBindings,"extended fixed bindings for byref and GetPinnableReference"
featurePreferStringGetPinnableReference,"prefer String.GetPinnableReference in fixed bindings"
3353,fsiInvalidDirective,"Invalid directive '#%s %s'"
3354,tcNotAFunctionButIndexerNamedIndexingNotYetEnabled,"This value supports indexing, e.g. '%s.[index]'. The syntax '%s[index]' requires /langversion:preview. See https://aka.ms/fsharp-index-notation."
3354,tcNotAFunctionButIndexerIndexingNotYetEnabled,"This expression supports indexing, e.g. 'expr.[index]'. The syntax 'expr[index]' requires /langversion:preview. See https://aka.ms/fsharp-index-notation."
Expand Down
11 changes: 7 additions & 4 deletions src/Compiler/Facilities/DiagnosticsLogger.fs
Original file line number Diff line number Diff line change
Expand Up @@ -817,12 +817,15 @@ type internal SuppressLanguageFeatureCheck =
| Yes
| No

let internal languageFeatureError (langVersion: LanguageVersion) (langFeature: LanguageFeature) (m: range) =
let featureStr = LanguageVersion.GetFeatureString langFeature
let currentVersionStr = langVersion.SpecifiedVersionString
let suggestedVersionStr = LanguageVersion.GetFeatureVersionString langFeature
Error(FSComp.SR.chkFeatureNotLanguageSupported (featureStr, currentVersionStr, suggestedVersionStr), m)

let private tryLanguageFeatureErrorAux (langVersion: LanguageVersion) (langFeature: LanguageFeature) (m: range) =
if not (langVersion.SupportsFeature langFeature) then
let featureStr = LanguageVersion.GetFeatureString langFeature
let currentVersionStr = langVersion.SpecifiedVersionString
let suggestedVersionStr = LanguageVersion.GetFeatureVersionString langFeature
Some(Error(FSComp.SR.chkFeatureNotLanguageSupported (featureStr, currentVersionStr, suggestedVersionStr), m))
Some(languageFeatureError langVersion langFeature m)
else
None

Expand Down
2 changes: 2 additions & 0 deletions src/Compiler/Facilities/DiagnosticsLogger.fsi
Original file line number Diff line number Diff line change
Expand Up @@ -432,6 +432,8 @@ type SuppressLanguageFeatureCheck =
| Yes
| No

val languageFeatureError: langVersion: LanguageVersion -> langFeature: LanguageFeature -> m: range -> exn

val checkLanguageFeatureError: langVersion: LanguageVersion -> langFeature: LanguageFeature -> m: range -> unit

val checkLanguageFeatureAndRecover: langVersion: LanguageVersion -> langFeature: LanguageFeature -> m: range -> unit
Expand Down
6 changes: 6 additions & 0 deletions src/Compiler/Facilities/LanguageFeatures.fs
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,8 @@ type LanguageFeature =
| WarningWhenTailRecAttributeButNonTailRecUsage
| UnmanagedConstraintCsharpInterop
| WhileBang
| ExtendedFixedBindings
| PreferStringGetPinnableReference

/// LanguageVersion management
type LanguageVersion(versionText) =
Expand Down Expand Up @@ -177,6 +179,8 @@ type LanguageVersion(versionText) =
LanguageFeature.ConstraintIntersectionOnFlexibleTypes, previewVersion
LanguageFeature.UnmanagedConstraintCsharpInterop, previewVersion
LanguageFeature.WhileBang, previewVersion
LanguageFeature.ExtendedFixedBindings, previewVersion
LanguageFeature.PreferStringGetPinnableReference, previewVersion
]

static let defaultLanguageVersion = LanguageVersion("default")
Expand Down Expand Up @@ -309,6 +313,8 @@ type LanguageVersion(versionText) =
| LanguageFeature.WarningWhenTailRecAttributeButNonTailRecUsage -> FSComp.SR.featureChkNotTailRecursive ()
| LanguageFeature.UnmanagedConstraintCsharpInterop -> FSComp.SR.featureUnmanagedConstraintCsharpInterop ()
| LanguageFeature.WhileBang -> FSComp.SR.featureWhileBang ()
| LanguageFeature.ExtendedFixedBindings -> FSComp.SR.featureExtendedFixedBindings ()
| LanguageFeature.PreferStringGetPinnableReference -> FSComp.SR.featurePreferStringGetPinnableReference ()

/// Get a version string associated with the given feature.
static member GetFeatureVersionString feature =
Expand Down
2 changes: 2 additions & 0 deletions src/Compiler/Facilities/LanguageFeatures.fsi
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,8 @@ type LanguageFeature =
| WarningWhenTailRecAttributeButNonTailRecUsage
| UnmanagedConstraintCsharpInterop
| WhileBang
| ExtendedFixedBindings
| PreferStringGetPinnableReference

/// LanguageVersion management
type LanguageVersion =
Expand Down
14 changes: 12 additions & 2 deletions src/Compiler/xlf/FSComp.txt.cs.xlf

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

14 changes: 12 additions & 2 deletions src/Compiler/xlf/FSComp.txt.de.xlf

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

14 changes: 12 additions & 2 deletions src/Compiler/xlf/FSComp.txt.es.xlf

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading