diff --git a/src/fsharp/CheckExpressions.fs b/src/fsharp/CheckExpressions.fs index 7487225f33e..6c6355b5bd0 100644 --- a/src/fsharp/CheckExpressions.fs +++ b/src/fsharp/CheckExpressions.fs @@ -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 - 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 -> diff --git a/tests/FSharp.Compiler.ComponentTests/EmittedIL/StringFormatAndInterpolation.fs b/tests/FSharp.Compiler.ComponentTests/EmittedIL/StringFormatAndInterpolation.fs new file mode 100644 index 00000000000..716568cf837 --- /dev/null +++ b/tests/FSharp.Compiler.ComponentTests/EmittedIL/StringFormatAndInterpolation.fs @@ -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`` = + [] + 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::.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 [runtime]System.IO.TextWriter, + class [FSharp.Core]Microsoft.FSharp.Core.PrintfFormat`4) +IL_0016: pop +IL_0017: ret"""] diff --git a/tests/FSharp.Compiler.ComponentTests/FSharp.Compiler.ComponentTests.fsproj b/tests/FSharp.Compiler.ComponentTests/FSharp.Compiler.ComponentTests.fsproj index 912181214b3..a3ac88c1c90 100644 --- a/tests/FSharp.Compiler.ComponentTests/FSharp.Compiler.ComponentTests.fsproj +++ b/tests/FSharp.Compiler.ComponentTests/FSharp.Compiler.ComponentTests.fsproj @@ -23,6 +23,7 @@ + diff --git a/tests/fsharp/core/quotes/test.fsx b/tests/fsharp/core/quotes/test.fsx index 6415c466a65..35d1a057b45 100644 --- a/tests/fsharp/core/quotes/test.fsx +++ b/tests/fsharp/core/quotes/test.fsx @@ -4119,6 +4119,19 @@ module CheckEliminatedConstructs = """IfThenElse (Call (None, op_Equality, [ValueWithName ([||], ts), Value ()]), 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 ())])""" + module TestAssemblyAttributes = let attributes = System.Reflection.Assembly.GetExecutingAssembly().GetCustomAttributes(false)