Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support CallerArgumentExpression (without #line) #17519

Open
wants to merge 70 commits into
base: main
Choose a base branch
from
Open
Changes from all commits
Commits
Show all changes
70 commits
Select commit Hold shift + click to select a range
9d3917a
Support CallerArgumentExpression
ijklam Jun 11, 2024
b698a3b
Merge remote-tracking branch 'upstream/main' into SupportCallerArgume…
ijklam Aug 11, 2024
dec3d3e
fix for fsi
ijklam Aug 11, 2024
e5d7763
revert unnecessary changes
ijklam Aug 11, 2024
5119bb6
Read and store file content before compilation
ijklam Aug 11, 2024
c914672
Change test; Add release note
ijklam Aug 11, 2024
5fe3495
Merge branch 'main' into SupportCallerArgumentExpression
ijklam Aug 11, 2024
536cf34
Add tests
ijklam Aug 11, 2024
b270e16
Merge branch 'SupportCallerArgumentExpression' of https://github.com/…
ijklam Aug 11, 2024
3fc4642
fix build; format
ijklam Aug 11, 2024
550daa4
Support `#line`
ijklam Aug 15, 2024
5cecb20
format
ijklam Aug 15, 2024
1b6cd56
support callee side optional arg
ijklam Aug 16, 2024
08d958b
Merge branch 'main' into SupportCallerArgumentExpression
ijklam Aug 16, 2024
68f2f61
format code
ijklam Aug 16, 2024
ec3b97f
Merge branch 'SupportCallerArgumentExpression' of https://github.com/…
ijklam Aug 16, 2024
7e93270
fix build
ijklam Aug 16, 2024
5790db9
fix test
ijklam Aug 16, 2024
46c4d46
try fix tests
ijklam Aug 25, 2024
8d792fa
fix build
ijklam Aug 25, 2024
65a4acc
test
ijklam Aug 25, 2024
1bdae47
Merge remote-tracking branch 'upstream/main' into SupportCallerArgume…
ijklam Sep 26, 2024
9e46116
Merge remote-tracking branch 'upstream/main' into SupportCallerArgume…
ijklam Feb 3, 2025
f379b7f
simplify code
ijklam Feb 3, 2025
843c71a
Merge branch 'main' into SupportCallerArgumentExpression
ijklam Feb 3, 2025
6d1a30d
simplify code; fix test
ijklam Feb 4, 2025
5f499a5
fix range
ijklam Feb 4, 2025
470b993
fix tests
ijklam Feb 4, 2025
477ed6e
fix tests
ijklam Feb 4, 2025
6fb6548
fix
ijklam Feb 4, 2025
c8ca45b
test
ijklam Feb 4, 2025
b5f22fb
test
ijklam Feb 4, 2025
271985b
test
ijklam Feb 4, 2025
31e7bb2
fix tests
ijklam Feb 26, 2025
d47ee7e
Merge remote-tracking branch 'upstream/main' into SupportCallerArgume…
ijklam Feb 26, 2025
424b83c
update baselines
ijklam Feb 26, 2025
77697cd
fix tests
ijklam Feb 26, 2025
1e43eff
test
ijklam Feb 26, 2025
2d38b2a
Merge remote-tracking branch 'upstream/main' into SupportCallerArgume…
ijklam Mar 11, 2025
52a0fc0
fix named arguments didn't trigger the feature
ijklam Mar 11, 2025
24a9494
baseline
ijklam Mar 11, 2025
935af15
Merge branch 'main' into SupportCallerArgumentExpression
ijklam Mar 11, 2025
31125f5
add new tests; improve err msg
ijklam Mar 13, 2025
49c46b1
adjust code; add and update tests
ijklam Mar 13, 2025
983f180
baseline
ijklam Mar 13, 2025
cf2085b
fix test
ijklam Mar 14, 2025
52a4d53
fmt
ijklam Mar 14, 2025
6e6dd39
Support for user defined CallerArgumentExpressionAttribute
ijklam Mar 15, 2025
9c5d917
replace the way get substring text
ijklam Mar 15, 2025
795c64a
test
ijklam Mar 15, 2025
98d8708
fix build
ijklam Mar 15, 2025
d68a6f7
fix test
ijklam Mar 15, 2025
9ac37e6
test
ijklam Mar 15, 2025
2cfba8d
Merge branch 'main' into SupportCallerArgumentExpression
psfinaki Mar 17, 2025
262ed24
revert the modify to `range`
ijklam Mar 17, 2025
9c24123
fix test and add new test
ijklam Mar 17, 2025
081a36b
Merge branch 'main' into SupportCallerArgumentExpression
ijklam Mar 17, 2025
1999506
Merge remote-tracking branch 'upstream/main' into SupportCallerArgume…
ijklam Mar 20, 2025
2e7121d
add a new test
ijklam Mar 20, 2025
fb8a3d0
add test
ijklam Mar 20, 2025
70f5630
change the position to get code file content
ijklam Mar 21, 2025
1cc4395
refractor
ijklam Mar 21, 2025
3c1deb4
new test
ijklam Mar 21, 2025
a5a4b8b
refactor
ijklam Mar 25, 2025
5cba8ca
fmt
ijklam Mar 25, 2025
0b02f01
Merge remote-tracking branch 'upstream/main' into SupportCallerArgume…
ijklam Mar 25, 2025
1258816
baseline; fix test
ijklam Mar 25, 2025
95a4930
fix test
ijklam Mar 25, 2025
c3baed5
Merge branch 'main' into SupportCallerArgumentExpression
ijklam Mar 25, 2025
5e1e27c
try fix cannot determine in C# method with non BCL attr
ijklam Mar 25, 2025
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
1 change: 1 addition & 0 deletions docs/release-notes/.FSharp.Compiler.Service/9.0.300.md
Original file line number Diff line number Diff line change
@@ -30,6 +30,7 @@
* Type parameter constraint `null` in generic code will now automatically imply `not struct` ([Issue #18320](https://github.com/dotnet/fsharp/issues/18320), [PR #18323](https://github.com/dotnet/fsharp/pull/18323))
* Add a switch to determine whether to generate a default implementation body for overridden method when completing. [PR #18341](https://github.com/dotnet/fsharp/pull/18341)

* Support `CallerArgumentExpression` ([Language Suggestion #966](https://github.com/fsharp/fslang-suggestions/issues/966), [PR #17519](https://github.com/dotnet/fsharp/pull/17519))

### Changed
* FSharpCheckFileResults.ProjectContext.ProjectOptions will not be available when using the experimental Transparent Compiler feature. ([PR #18205](https://github.com/dotnet/fsharp/pull/18205))
1 change: 1 addition & 0 deletions docs/release-notes/.Language/preview.md
Original file line number Diff line number Diff line change
@@ -5,6 +5,7 @@
* Added type conversions cache, only enabled for compiler runs ([PR#17668](https://github.com/dotnet/fsharp/pull/17668))
* Support ValueOption + Struct attribute as optional parameter for methods ([Language suggestion #1136](https://github.com/fsharp/fslang-suggestions/issues/1136), [PR #18098](https://github.com/dotnet/fsharp/pull/18098))
* Warn when `unit` is passed to an `obj`-typed argument ([PR #18330](https://github.com/dotnet/fsharp/pull/18330))
* Support `CallerArgumentExpression` ([Language Suggestion #966](https://github.com/fsharp/fslang-suggestions/issues/966), [PR #17519](https://github.com/dotnet/fsharp/pull/17519))

### Fixed

45 changes: 36 additions & 9 deletions src/Compiler/Checking/MethodCalls.fs
Original file line number Diff line number Diff line change
@@ -1437,9 +1437,20 @@ let AdjustCallerArgExpr tcVal (g: TcGlobals) amap infoReader ad isOutArg calledA
/// matter what order they are applied in as long as they are all composed together.
let emptyPreBinder (e: Expr) = e

/// Try to pick the code text of an argument with the given parameter name from a list of assigned arguments.
let tryPickArgumentCodeText assignedArgs paramName =
assignedArgs
|> List.tryPick (fun { CalledArg=called; CallerArg=caller } ->
match called.NameOpt with
| Some x when x.idText = paramName ->
let code = FileContent.getCodeText caller.Range
if System.String.IsNullOrEmpty code then None
else Some code
| _ -> None)

/// Get the expression that must be inserted on the caller side for a CallerSide optional arg,
/// i.e. one where there is no corresponding caller arg.
let rec GetDefaultExpressionForCallerSideOptionalArg tcFieldInit g (calledArg: CalledArg) currCalledArgTy currDfltVal eCallerMemberName mMethExpr =
let rec GetDefaultExpressionForCallerSideOptionalArg tcFieldInit g (calledArg: CalledArg) currCalledArgTy currDfltVal eCallerMemberName mMethExpr assignedArgs =
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.
@@ -1456,7 +1467,7 @@ let rec GetDefaultExpressionForCallerSideOptionalArg tcFieldInit g (calledArg: C
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 ->
GetDefaultExpressionForCallerSideOptionalArg tcFieldInit g calledArg inst (PassByRef(inst, currDfltVal)) eCallerMemberName mMethExpr
GetDefaultExpressionForCallerSideOptionalArg tcFieldInit g calledArg inst (PassByRef(inst, currDfltVal)) eCallerMemberName mMethExpr assignedArgs
| _ ->
match calledArg.CallerInfo, eCallerMemberName with
| CallerLineNumber, _ when typeEquiv g currCalledArgTy g.int_ty ->
@@ -1466,6 +1477,14 @@ let rec GetDefaultExpressionForCallerSideOptionalArg tcFieldInit g (calledArg: C
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)

| CallerArgumentExpression param, _ when g.langVersion.SupportsFeature LanguageFeature.SupportCallerArgumentExpression && typeEquiv g currCalledArgTy g.string_ty ->
let stringConst =
match tryPickArgumentCodeText assignedArgs param with
| Some code -> Const.String code
| None -> tcFieldInit mMethExpr fieldInit
emptyPreBinder, Expr.Const (stringConst, mMethExpr, currCalledArgTy)

| _ ->
emptyPreBinder, Expr.Const (tcFieldInit mMethExpr fieldInit, mMethExpr, currCalledArgTy)

@@ -1489,13 +1508,13 @@ let rec GetDefaultExpressionForCallerSideOptionalArg tcFieldInit g (calledArg: C

| PassByRef (ty, dfltVal2) ->
let v, _ = mkCompGenLocal mMethExpr "defaultByrefArg" ty
let wrapper2, rhs = GetDefaultExpressionForCallerSideOptionalArg tcFieldInit g calledArg currCalledArgTy dfltVal2 eCallerMemberName mMethExpr
let wrapper2, rhs = GetDefaultExpressionForCallerSideOptionalArg tcFieldInit g calledArg currCalledArgTy dfltVal2 eCallerMemberName mMethExpr assignedArgs
(wrapper2 >> mkCompGenLet mMethExpr v rhs), mkValAddr mMethExpr false (mkLocalValRef v)

/// Get the expression that must be inserted on the caller side for a CalleeSide optional arg where
/// no caller argument has been provided. Normally this is 'None', however CallerMemberName and friends
/// can be used with 'CalleeSide' optional arguments
let GetDefaultExpressionForCalleeSideOptionalArg g (calledArg: CalledArg) eCallerMemberName (mMethExpr: range) =
let GetDefaultExpressionForCalleeSideOptionalArg g (calledArg: CalledArg) eCallerMemberName (mMethExpr: range) assignedArgs =
let calledArgTy = calledArg.CalledArgumentType
let calledNonOptTy = tryDestOptionalTy g calledArgTy

@@ -1510,24 +1529,32 @@ let GetDefaultExpressionForCalleeSideOptionalArg g (calledArg: CalledArg) eCalle
| CallerMemberName, Some(callerName) when typeEquiv g calledNonOptTy g.string_ty ->
let memberNameExpr = Expr.Const (Const.String callerName, mMethExpr, calledNonOptTy)
mkSome g calledNonOptTy memberNameExpr mMethExpr

| CallerArgumentExpression param, _ when g.langVersion.SupportsFeature LanguageFeature.SupportCallerArgumentExpression && typeEquiv g calledNonOptTy g.string_ty ->
match tryPickArgumentCodeText assignedArgs param with
| Some code ->
let expr = Expr.Const(Const.String code, mMethExpr, calledNonOptTy)
mkSome g calledNonOptTy expr mMethExpr
| None -> mkNone g calledNonOptTy mMethExpr

| _ ->
mkOptionalNone g calledArgTy calledNonOptTy mMethExpr


/// Get the expression that must be inserted on the caller side for an optional arg where
/// no caller argument has been provided.
let GetDefaultExpressionForOptionalArg tcFieldInit g (calledArg: CalledArg) eCallerMemberName mItem (mMethExpr: range) =
let GetDefaultExpressionForOptionalArg tcFieldInit g (calledArg: CalledArg) eCallerMemberName mItem (mMethExpr: range) assignedArgs =
let calledArgTy = calledArg.CalledArgumentType
let preBinder, expr =
match calledArg.OptArgInfo with
| NotOptional ->
error(InternalError("Unexpected NotOptional", mItem))

| CallerSide dfltVal ->
GetDefaultExpressionForCallerSideOptionalArg tcFieldInit g calledArg calledArgTy dfltVal eCallerMemberName mMethExpr
GetDefaultExpressionForCallerSideOptionalArg tcFieldInit g calledArg calledArgTy dfltVal eCallerMemberName mMethExpr assignedArgs

| CalleeSide ->
emptyPreBinder, GetDefaultExpressionForCalleeSideOptionalArg g calledArg eCallerMemberName mMethExpr
emptyPreBinder, GetDefaultExpressionForCalleeSideOptionalArg g calledArg eCallerMemberName mMethExpr assignedArgs

// Combine the variable allocators (if any)
let callerArg = CallerArg(calledArgTy, mMethExpr, false, expr)
@@ -1581,7 +1608,7 @@ let AdjustCallerArgForOptional tcVal tcFieldInit eCallerMemberName (infoReader:
mkOptionToNullable g m (destOptionTy g callerArgTy) callerArgExpr
else
// CSharpMethod(?x=b) when 'b' has optional type and 'x' has non-nullable type --> CSharpMethod(x=Option.defaultValue DEFAULT v)
let _wrapper, defaultExpr = GetDefaultExpressionForCallerSideOptionalArg tcFieldInit g calledArg calledArgTy dfltVal eCallerMemberName m
let _wrapper, defaultExpr = GetDefaultExpressionForCallerSideOptionalArg tcFieldInit g calledArg calledArgTy dfltVal eCallerMemberName m [assignedArg]
let ty = destOptionTy g callerArgTy
mkOptionDefaultValue g m ty defaultExpr callerArgExpr
else
@@ -1645,7 +1672,7 @@ let AdjustCallerArgsForOptionals tcVal tcFieldInit eCallerMemberName (infoReader
// i.e. there is no corresponding caller arg.
let optArgs, optArgPreBinder =
(emptyPreBinder, calledMeth.UnnamedCalledOptArgs) ||> List.mapFold (fun preBinder calledArg ->
let preBinder2, arg = GetDefaultExpressionForOptionalArg tcFieldInit g calledArg eCallerMemberName mItem mMethExpr
let preBinder2, arg = GetDefaultExpressionForOptionalArg tcFieldInit g calledArg eCallerMemberName mItem mMethExpr (assignedNamedArgs @ unnamedArgs)
arg, (preBinder >> preBinder2))

let adjustedNormalUnnamedArgs = List.map (AdjustCallerArgForOptional tcVal tcFieldInit eCallerMemberName infoReader ad) unnamedArgs
27 changes: 25 additions & 2 deletions src/Compiler/Checking/PostInferenceChecks.fs
Original file line number Diff line number Diff line change
@@ -2391,8 +2391,10 @@ let CheckEntityDefn cenv env (tycon: Entity) =
if numCurriedArgSets > 1 && others |> List.exists (fun minfo2 -> not (IsAbstractDefaultPair2 minfo minfo2)) then
errorR(Error(FSComp.SR.chkDuplicateMethodCurried(nm, NicePrint.minimalStringOfType cenv.denv ty), m))

let paramDatas = minfo.GetParamDatas(cenv.amap, m, minfo.FormalMethodInst)

if numCurriedArgSets > 1 &&
(minfo.GetParamDatas(cenv.amap, m, minfo.FormalMethodInst)
(paramDatas
|> List.existsSquared (fun (ParamData(isParamArrayArg, _isInArg, isOutArg, optArgInfo, callerInfo, _, reflArgInfo, ty)) ->
isParamArrayArg || isOutArg || reflArgInfo.AutoQuote || optArgInfo.IsOptional || callerInfo <> NoCallerInfo || isByrefLikeTy g m ty)) then
errorR(Error(FSComp.SR.chkCurriedMethodsCantHaveOutParams(), m))
@@ -2406,7 +2408,20 @@ let CheckEntityDefn cenv env (tycon: Entity) =
if not ((isOptionTy g ty) && (typeEquiv g g.string_ty (destOptionTy g ty))) then
errorR(Error(FSComp.SR.tcCallerInfoWrongType(callerInfo |> string, "string", NicePrint.minimalStringOfType cenv.denv (destOptionTy g ty)), m))

minfo.GetParamDatas(cenv.amap, m, minfo.FormalMethodInst)
let paramNames = HashSet()
paramDatas
|> List.iterSquared (fun (ParamData(_, _, _, _, _, nameOpt, _, _)) ->
nameOpt |> Option.iter (fun name -> paramNames.Add name.idText |> ignore))

let checkArgOfCallerArgumentExpression m arg (nameOpt: Ident option) =
match nameOpt with
| Some ident when arg = ident.idText ->
warning(Error(FSComp.SR.tcCallerArgumentExpressionSelfReferential(), m))
| _ when not (paramNames.Contains arg) ->
warning(Error(FSComp.SR.tcCallerArgumentExpressionHasInvalidParameterName(), m))
| _ -> ()

paramDatas
|> List.iterSquared (fun (ParamData(_, isInArg, _, optArgInfo, callerInfo, nameOpt, _, ty)) ->
ignore isInArg

@@ -2426,6 +2441,14 @@ let CheckEntityDefn cenv env (tycon: Entity) =
errorR(Error(FSComp.SR.tcCallerInfoWrongType(callerInfo |> string, "int", NicePrint.minimalStringOfType cenv.denv (destOptionTy g ty)), m))
| CallerSide _, (CallerFilePath | CallerMemberName) -> errorIfNotStringTy m ty callerInfo
| CalleeSide, (CallerFilePath | CallerMemberName) -> errorIfNotStringOptionTy m ty callerInfo
| CallerSide _, CallerArgumentExpression arg ->
checkLanguageFeatureAndRecover g.langVersion LanguageFeature.SupportCallerArgumentExpression m
errorIfNotStringTy m ty callerInfo
checkArgOfCallerArgumentExpression m arg nameOpt
| CalleeSide, CallerArgumentExpression arg ->
checkLanguageFeatureAndRecover g.langVersion LanguageFeature.SupportCallerArgumentExpression m
errorIfNotStringOptionTy m ty callerInfo
checkArgOfCallerArgumentExpression m arg nameOpt
)

for pinfo in immediateProps do
38 changes: 25 additions & 13 deletions src/Compiler/Checking/infos.fs
Original file line number Diff line number Diff line change
@@ -240,6 +240,7 @@ type CallerInfo =
| CallerLineNumber
| CallerMemberName
| CallerFilePath
| CallerArgumentExpression of paramName: string

override x.ToString() = sprintf "%+A" x

@@ -317,20 +318,25 @@ let CrackParamAttribsInfo g (ty: TType, argInfo: ArgReprInfo) =
let isCallerLineNumberArg = HasFSharpAttribute g g.attrib_CallerLineNumberAttribute argInfo.Attribs
let isCallerFilePathArg = HasFSharpAttribute g g.attrib_CallerFilePathAttribute argInfo.Attribs
let isCallerMemberNameArg = HasFSharpAttribute g g.attrib_CallerMemberNameAttribute argInfo.Attribs
let callerArgumentExpressionArg =
TryFindFSharpAttributeOpt g g.attrib_CallerArgumentExpressionAttribute argInfo.Attribs
|> Option.orElseWith (fun () -> TryFindFSharpAttributeByName "System.Runtime.CompilerServices.CallerArgumentExpressionAttribute" argInfo.Attribs)

let callerInfo =
match isCallerLineNumberArg, isCallerFilePathArg, isCallerMemberNameArg with
| false, false, false -> NoCallerInfo
| true, false, false -> CallerLineNumber
| false, true, false -> CallerFilePath
| false, false, true -> CallerMemberName
| false, true, true ->
match isCallerLineNumberArg, isCallerFilePathArg, isCallerMemberNameArg, callerArgumentExpressionArg with
| false, false, false, None -> NoCallerInfo
| true, false, false, None -> CallerLineNumber
| false, true, false, None -> CallerFilePath
| false, false, true, None -> CallerMemberName
| false, false, false, Some(Attrib(_, _, (AttribStringArg x :: _), _, _, _, _)) ->
CallerArgumentExpression(x)
| false, true, true, _ ->
match TryFindFSharpAttribute g g.attrib_CallerMemberNameAttribute argInfo.Attribs with
| Some(Attrib(_, _, _, _, _, _, callerMemberNameAttributeRange)) ->
warning(Error(FSComp.SR.CallerMemberNameIsOverridden(argInfo.Name.Value.idText), callerMemberNameAttributeRange))
CallerFilePath
| _ -> failwith "Impossible"
| _, _, _ ->
| _, _, _, _ ->
// if multiple caller info attributes are specified, pick the "wrong" one here
// so that we get an error later
match tryDestOptionTy g ty with
@@ -1280,14 +1286,20 @@ type MethInfo =
let isCallerLineNumberArg = TryFindILAttribute g.attrib_CallerLineNumberAttribute attrs
let isCallerFilePathArg = TryFindILAttribute g.attrib_CallerFilePathAttribute attrs
let isCallerMemberNameArg = TryFindILAttribute g.attrib_CallerMemberNameAttribute attrs
let isCallerArgumentExpressionArg =
g.attrib_CallerArgumentExpressionAttribute
|> Option.bind (fun (AttribInfo(tref, _)) -> TryDecodeILAttribute tref attrs)
|> Option.orElseWith (fun () -> TryDecodeILAttributeByName "System.Runtime.CompilerServices.CallerArgumentExpressionAttribute" attrs)

let callerInfo =
match isCallerLineNumberArg, isCallerFilePathArg, isCallerMemberNameArg with
| false, false, false -> NoCallerInfo
| true, false, false -> CallerLineNumber
| false, true, false -> CallerFilePath
| false, false, true -> CallerMemberName
| _, _, _ ->
match isCallerLineNumberArg, isCallerFilePathArg, isCallerMemberNameArg, isCallerArgumentExpressionArg with
| false, false, false, None -> NoCallerInfo
| true, false, false, None -> CallerLineNumber
| false, true, false, None -> CallerFilePath
| false, false, true, None -> CallerMemberName
| false, false, false, Some ([ILAttribElem.String (Some name) ], _) -> CallerArgumentExpression(name)
| false, false, false, _ -> NoCallerInfo
| _, _, _, _ ->
// if multiple caller info attributes are specified, pick the "wrong" one here
// so that we get an error later
if p.Type.TypeRef.FullName = "System.Int32" then CallerFilePath
1 change: 1 addition & 0 deletions src/Compiler/Checking/infos.fsi
Original file line number Diff line number Diff line change
@@ -101,6 +101,7 @@ type CallerInfo =
| CallerLineNumber
| CallerMemberName
| CallerFilePath
| CallerArgumentExpression of paramName: string

[<RequireQualifiedAccess>]
type ReflectedArgInfo =
11 changes: 11 additions & 0 deletions src/Compiler/Driver/CompilerConfig.fs
Original file line number Diff line number Diff line change
@@ -1539,3 +1539,14 @@ type TcConfigProvider =
TcConfigProvider(fun _ctok -> TcConfig.Create(tcConfigB, validate = false))

let GetFSharpCoreLibraryName () = getFSharpCoreLibraryName

/// Read and store the source file content for the `CallerArgumentExpression` feature
let readAndStoreFileContents (tcConfig: TcConfig) (sourceFiles: #seq<string>) =
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pls add a remark about this only being for CLI standalone compilation + fsi
(so that future contributors do not attempt to hook this into design-time scenarios, especially because of allocations and file-reads this would have massive consequences for typing)

for fileName in sourceFiles do
if FSharpImplFileSuffixes |> List.exists (FileSystemUtils.checkSuffix fileName) then
try
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about FileContent storing not allocated strings, but rather lazy objects?
Assumption is that majority of content will never be needed,

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I still wonder there is any way we could pass in an existing ISourceText somehow, as suggested in #17519 (comment). But maybe it's just too hard to thread it through to where it needs to be.

use fileStream = FileSystem.OpenFileForReadShim fileName
use reader = fileStream.GetReader(tcConfig.inputCodePage)
FileContent.update fileName (reader.ReadToEnd())
with _ ->
()
3 changes: 3 additions & 0 deletions src/Compiler/Driver/CompilerConfig.fsi
Original file line number Diff line number Diff line change
@@ -961,3 +961,6 @@ val FSharpMLCompatFileSuffixes: string list

/// Indicates whether experimental features should be enabled automatically
val FSharpExperimentalFeaturesEnabledAutomatically: bool

/// Read and store the source file content for the `CallerArgumentExpression` feature
val readAndStoreFileContents: tcConfig: TcConfig -> sourceFiles: #seq<string> -> unit
3 changes: 3 additions & 0 deletions src/Compiler/Driver/fsc.fs
Original file line number Diff line number Diff line change
@@ -671,6 +671,9 @@ let main1
// Build the initial type checking environment
ReportTime tcConfig "Typecheck"

// Read the source file content for the `CallerArgumentExpression` feature
readAndStoreFileContents tcConfig sourceFiles
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you also add a "ReportTime" section here so that we can see impact on timing in selected compilations?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Especially something bigger, like when the compiler compiles itself (this will need the --times flag to report the timings)


use unwindParsePhase = UseBuildPhase BuildPhase.TypeCheck

let tcEnv0, openDecls0 =
3 changes: 3 additions & 0 deletions src/Compiler/FSComp.txt
Original file line number Diff line number Diff line change
@@ -1798,3 +1798,6 @@ featureDontWarnOnUppercaseIdentifiersInBindingPatterns,"Don't warn on uppercase
featureDeprecatePlacesWhereSeqCanBeOmitted,"Deprecate places where 'seq' can be omitted"
featureSupportValueOptionsAsOptionalParameters,"Support ValueOption as valid type for optional member parameters"
featureSupportWarnWhenUnitPassedToObjArg,"Warn when unit is passed to a member accepting `obj` argument, e.g. `Method(o:obj)` will warn if called via `Method()`."
featureSupportCallerArgumentExpression,"Support `CallerArgumentExpression`"
3875,tcCallerArgumentExpressionSelfReferential,"The CallerArgumentExpression on this parameter will have no effect because it's self-referential."
3875,tcCallerArgumentExpressionHasInvalidParameterName,"The CallerArgumentExpression on this parameter will have no effect because it's applied with an invalid parameter name."
Loading