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

Optimize interpolated string with no holes #11632

Merged
merged 4 commits into from
Jun 29, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
42 changes: 25 additions & 17 deletions src/fsharp/CheckExpressions.fs
Original file line number Diff line number Diff line change
Expand Up @@ -6739,27 +6739,35 @@ and TcInterpolatedStringExpr cenv overallTy env m tpenv (parts: SynInterpolatedS

UnifyTypes cenv env m printerTupleTy printerTupleTyRequired

// Type check the expressions filling the holes
let flexes = argTys |> List.map (fun _ -> false)
let fillExprs, tpenv = TcExprs cenv env m tpenv flexes argTys synFillExprs
if List.isEmpty synFillExprs then
let str = mkString g m printfFormatString
Copy link
Contributor

Choose a reason for hiding this comment

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

In theory this changes the quotation form for an interpolated string. I don't mind that change, however could you add a test in tests/fsharp/core/quotes/test.fsx or elsewhere to test for that and pin down the new form please?

e.g. check results of <@ $"abc" @> and you may as well add a test for <@ $"abc {1} def" @> while you're at it.


let fillExprsBoxed = (argTys, fillExprs) ||> List.map2 (mkCallBox g m)

let argsExpr = mkArray (g.obj_ty, fillExprsBoxed, m)
let percentATysExpr =
if percentATys.Length = 0 then
mkNull m (mkArrayType g g.system_Type_ty)
if isString then
str, tpenv
else
let tyExprs = percentATys |> Array.map (mkCallTypeOf g m) |> Array.toList
mkArray (g.system_Type_ty, tyExprs, m)
mkCallNewFormat cenv.g m printerTy printerArgTy printerResidueTy printerResultTy printerTupleTy str, tpenv
else
// Type check the expressions filling the holes
let flexes = argTys |> List.map (fun _ -> false)
let fillExprs, tpenv = TcExprs cenv env m tpenv flexes argTys synFillExprs

let fmtExpr = MakeMethInfoCall cenv.amap m newFormatMethod [] [mkString g m printfFormatString; argsExpr; percentATysExpr]
let fillExprsBoxed = (argTys, fillExprs) ||> List.map2 (mkCallBox g m)

if isString then
// Make the call to sprintf
mkCall_sprintf g m printerTy fmtExpr [], tpenv
else
fmtExpr, tpenv
let argsExpr = mkArray (g.obj_ty, fillExprsBoxed, m)
let percentATysExpr =
if percentATys.Length = 0 then
mkNull m (mkArrayType g g.system_Type_ty)
else
let tyExprs = percentATys |> Array.map (mkCallTypeOf g m) |> Array.toList
mkArray (g.system_Type_ty, tyExprs, m)

let fmtExpr = MakeMethInfoCall cenv.amap m newFormatMethod [] [mkString g m printfFormatString; argsExpr; percentATysExpr]

if isString then
// Make the call to sprintf
mkCall_sprintf g m printerTy fmtExpr [], tpenv
else
fmtExpr, tpenv

// The case for $"..." used as type FormattableString or IFormattable
| Choice2Of2 createFormattableStringMethod ->
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information.

namespace FSharp.Compiler.ComponentTests.EmittedIL

open Xunit
open FSharp.Test.Utilities.Compiler

module ``StringFormatAndInterpolation`` =
[<Fact>]
let ``Interpolated string with no holes is reduced to a string or simple format when used in printf``() =
FSharp """
module StringFormatAndInterpolation

let stringOnly () = $"no hole"

let printed () = printf $"printed no hole"
"""
|> compile
|> shouldSucceed
|> verifyIL ["""
IL_0000: ldstr "no hole"
IL_0005: ret"""
"""
IL_0000: ldstr "printed no hole"
IL_0005: newobj instance void class [FSharp.Core]Microsoft.FSharp.Core.PrintfFormat`5<class [FSharp.Core]Microsoft.FSharp.Core.Unit,class [runtime]System.IO.TextWriter,class [FSharp.Core]Microsoft.FSharp.Core.Unit,class [FSharp.Core]Microsoft.FSharp.Core.Unit,class [FSharp.Core]Microsoft.FSharp.Core.Unit>::.ctor(string)
IL_000a: stloc.0
IL_000b: call class [netstandard]System.IO.TextWriter [netstandard]System.Console::get_Out()
IL_0010: ldloc.0
IL_0011: call !!0 [FSharp.Core]Microsoft.FSharp.Core.PrintfModule::PrintFormatToTextWriter<class [FSharp.Core]Microsoft.FSharp.Core.Unit>(class [runtime]System.IO.TextWriter,
class [FSharp.Core]Microsoft.FSharp.Core.PrintfFormat`4<!!0,class [runtime]System.IO.TextWriter,class [FSharp.Core]Microsoft.FSharp.Core.Unit,class [FSharp.Core]Microsoft.FSharp.Core.Unit>)
IL_0016: pop
IL_0017: ret"""]
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
<Compile Include="EmittedIL\Literals.fs" />
<Compile Include="EmittedIL\Misc.fs" />
<Compile Include="EmittedIL\SkipLocalsInit.fs" />
<Compile Include="EmittedIL\StringFormatAndInterpolation.fs" />
<Compile Include="EmittedIL\TailCalls.fs" />
<Compile Include="EmittedIL\TupleElimination.fs" />
<Compile Include="ErrorMessages\TypeEqualsMissingTests.fs" />
Expand Down
13 changes: 13 additions & 0 deletions tests/fsharp/core/quotes/test.fsx
Original file line number Diff line number Diff line change
Expand Up @@ -4119,6 +4119,19 @@ module CheckEliminatedConstructs =
"""IfThenElse (Call (None, op_Equality, [ValueWithName ([||], ts), Value (<null>)]),
Value (true), Value (false))"""

module Interpolation =
let interpolatedNoHoleQuoted = <@ $"abc" @>
let actual1 = interpolatedNoHoleQuoted.ToString()
checkStrings "brewbreebrwhat1" actual1 """Value ("abc")"""

let interpolatedWithLiteralQuoted = <@ $"abc {1} def" @>
let actual2 = interpolatedWithLiteralQuoted.ToString()
checkStrings "brewbreebrwhat2" actual2
"""Call (None, PrintFormatToString,
[NewObject (PrintfFormat`5, Value ("abc %P() def"),
NewArray (Object, Call (None, Box, [Value (1)])),
Value (<null>))])"""

module TestAssemblyAttributes =
let attributes = System.Reflection.Assembly.GetExecutingAssembly().GetCustomAttributes(false)

Expand Down