From b27b4e9ab1a8ce938de1376ae39e3e1f8d09cb30 Mon Sep 17 00:00:00 2001 From: Don Syme Date: Thu, 7 Apr 2022 21:22:59 +1000 Subject: [PATCH 01/13] initial flag --- src/fsharp/IlxGen.fs | 9 +++++++-- src/fsharp/IlxGen.fsi | 3 +++ 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/fsharp/IlxGen.fs b/src/fsharp/IlxGen.fs index 0c4fe94a434..5e082c21f38 100644 --- a/src/fsharp/IlxGen.fs +++ b/src/fsharp/IlxGen.fs @@ -231,6 +231,9 @@ type IlxGenOptions = /// storage, even though 'it' is not logically mutable isInteractiveItExpr: bool + /// Suppress ToString emit + noReflectionCodeGen: bool + /// Whenever possible, use callvirt instead of call alwaysCallVirt: bool } @@ -7776,8 +7779,10 @@ and GenToStringMethod cenv eenv ilThisTy m = /// Generate a ToString/get_Message method that calls 'sprintf "%A"' and GenPrintingMethod cenv eenv methName ilThisTy m = - let g = cenv.g - [ match (eenv.valsInScope.TryFind g.sprintf_vref.Deref, + let g = cenv.g + [ if not cenv.opts.noReflectionCodeGen then + + match (eenv.valsInScope.TryFind g.sprintf_vref.Deref, eenv.valsInScope.TryFind g.new_format_vref.Deref) with | Some(Lazy(Method(_, _, sprintfMethSpec, _, _, _, _, _, _, _, _, _))), Some(Lazy(Method(_, _, newFormatMethSpec, _, _, _, _, _, _, _, _, _))) -> // The type returned by the 'sprintf' call diff --git a/src/fsharp/IlxGen.fsi b/src/fsharp/IlxGen.fsi index c8a33c49587..0ede5892d57 100644 --- a/src/fsharp/IlxGen.fsi +++ b/src/fsharp/IlxGen.fsi @@ -53,6 +53,9 @@ type internal IlxGenOptions = /// storage, even though 'it' is not logically mutable isInteractiveItExpr: bool + /// Suppress ToString emit + noReflectionCodeGen: bool + /// Indicates that, whenever possible, use callvirt instead of call alwaysCallVirt: bool } From 3e6e36bf0d44c8170aa00860d4a696eca025d4f1 Mon Sep 17 00:00:00 2001 From: Don Syme Date: Fri, 8 Apr 2022 12:23:41 +1000 Subject: [PATCH 02/13] reflection free flag --- src/fsharp/CheckFormatStrings.fs | 82 +++++++++++-------- src/fsharp/CheckFormatStrings.fsi | 9 +- src/fsharp/CompilerConfig.fs | 5 ++ src/fsharp/CompilerConfig.fsi | 2 + src/fsharp/CompilerImports.fs | 17 +++- src/fsharp/CompilerOptions.fs | 5 ++ src/fsharp/FSComp.txt | 2 + src/fsharp/FSharp.Build/Fsc.fs | 8 ++ src/fsharp/FSharp.Build/Fsi.fs | 7 +- .../Microsoft.FSharp.NetSdk.props | 1 + .../FSharp.Build/Microsoft.FSharp.Targets | 1 + src/fsharp/FSharp.Core/Linq.fs | 2 +- src/fsharp/FSharp.Core/Query.fs | 31 ++++--- src/fsharp/FSharp.Core/QueryExtensions.fs | 2 +- src/fsharp/FSharp.Core/quotations.fs | 9 +- src/fsharp/IlxGen.fs | 4 +- src/fsharp/IlxGen.fsi | 2 +- src/fsharp/OptimizeInputs.fs | 19 +++-- src/fsharp/OptimizeInputs.fsi | 31 +++++-- src/fsharp/TcGlobals.fs | 21 +++-- src/fsharp/xlf/FSComp.txt.cs.xlf | 10 +++ src/fsharp/xlf/FSComp.txt.de.xlf | 10 +++ src/fsharp/xlf/FSComp.txt.es.xlf | 10 +++ src/fsharp/xlf/FSComp.txt.fr.xlf | 10 +++ src/fsharp/xlf/FSComp.txt.it.xlf | 10 +++ src/fsharp/xlf/FSComp.txt.ja.xlf | 10 +++ src/fsharp/xlf/FSComp.txt.ko.xlf | 10 +++ src/fsharp/xlf/FSComp.txt.pl.xlf | 10 +++ src/fsharp/xlf/FSComp.txt.pt-BR.xlf | 10 +++ src/fsharp/xlf/FSComp.txt.ru.xlf | 10 +++ src/fsharp/xlf/FSComp.txt.tr.xlf | 10 +++ src/fsharp/xlf/FSComp.txt.zh-Hans.xlf | 10 +++ src/fsharp/xlf/FSComp.txt.zh-Hant.xlf | 10 +++ .../fsc/help/help40.437.1033.bsl | 2 + .../fsi/exename/help40.437.1033.bsl | 2 + 35 files changed, 307 insertions(+), 87 deletions(-) diff --git a/src/fsharp/CheckFormatStrings.fs b/src/fsharp/CheckFormatStrings.fs index f4be062b8bf..2fa351a9422 100644 --- a/src/fsharp/CheckFormatStrings.fs +++ b/src/fsharp/CheckFormatStrings.fs @@ -48,7 +48,16 @@ let newInfo () = addZeros = false precision = false} -let parseFormatStringInternal (m: range) (fragRanges: range list) (g: TcGlobals) isInterpolated isFormattableString (context: FormatStringCheckContext option) fmt printerArgTy printerResidueTy = +let parseFormatStringInternal + (m: range) + (fragRanges: range list) + (g: TcGlobals) + isInterpolated + isFormattableString + (context: FormatStringCheckContext option) + fmt + printerArgTy + printerResidueTy = // As background: the F# compiler tokenizes strings on the assumption that the only thing you need from // them is the actual corresponding text, e.g. of a string literal. This means many different textual input strings @@ -200,7 +209,7 @@ let parseFormatStringInternal (m: range) (fragRanges: range list) (g: TcGlobals) if acc |> List.forall (fun (p, _) -> p = None) then // without positional specifiers acc |> List.map snd |> List.rev else - failwithf "%s" <| FSComp.SR.forPositionalSpecifiersNotPermitted() + failwith (FSComp.SR.forPositionalSpecifiersNotPermitted()) argtys elif System.Char.IsSurrogatePair(fmt,i) then appendToDotnetFormatString fmt.[i..i+1] @@ -212,65 +221,65 @@ let parseFormatStringInternal (m: range) (fragRanges: range list) (g: TcGlobals) let startFragCol = fragCol let fragCol = fragCol+1 let i = i+1 - if i >= len then failwithf "%s" <| FSComp.SR.forMissingFormatSpecifier() + if i >= len then failwith (FSComp.SR.forMissingFormatSpecifier()) let info = newInfo() let rec flags i = - if i >= len then failwithf "%s" <| FSComp.SR.forMissingFormatSpecifier() + if i >= len then failwith (FSComp.SR.forMissingFormatSpecifier()) match fmt.[i] with | '-' -> - if info.leftJustify then failwithf "%s" <| FSComp.SR.forFlagSetTwice("-") + if info.leftJustify then failwith (FSComp.SR.forFlagSetTwice("-")) info.leftJustify <- true flags(i+1) | '+' -> - if info.numPrefixIfPos <> None then failwithf "%s" <| FSComp.SR.forPrefixFlagSpacePlusSetTwice() + if info.numPrefixIfPos <> None then failwith (FSComp.SR.forPrefixFlagSpacePlusSetTwice()) info.numPrefixIfPos <- Some '+' flags(i+1) | '0' -> - if info.addZeros then failwithf "%s" <| FSComp.SR.forFlagSetTwice("0") + if info.addZeros then failwith (FSComp.SR.forFlagSetTwice("0")) info.addZeros <- true flags(i+1) | ' ' -> - if info.numPrefixIfPos <> None then failwithf "%s" <| FSComp.SR.forPrefixFlagSpacePlusSetTwice() + if info.numPrefixIfPos <> None then failwith (FSComp.SR.forPrefixFlagSpacePlusSetTwice()) info.numPrefixIfPos <- Some ' ' flags(i+1) - | '#' -> failwithf "%s" <| FSComp.SR.forHashSpecifierIsInvalid() + | '#' -> failwith (FSComp.SR.forHashSpecifierIsInvalid()) | _ -> i let rec digitsPrecision i = - if i >= len then failwithf "%s" <| FSComp.SR.forBadPrecision() + if i >= len then failwith (FSComp.SR.forBadPrecision()) match fmt.[i] with | c when System.Char.IsDigit c -> digitsPrecision (i+1) | _ -> i let precision i = - if i >= len then failwithf "%s" <| FSComp.SR.forBadWidth() + if i >= len then failwith (FSComp.SR.forBadWidth()) match fmt.[i] with | c when System.Char.IsDigit c -> info.precision <- true; false,digitsPrecision (i+1) | '*' -> info.precision <- true; true,(i+1) - | _ -> failwithf "%s" <| FSComp.SR.forPrecisionMissingAfterDot() + | _ -> failwith (FSComp.SR.forPrecisionMissingAfterDot()) let optionalDotAndPrecision i = - if i >= len then failwithf "%s" <| FSComp.SR.forBadPrecision() + if i >= len then failwith (FSComp.SR.forBadPrecision()) match fmt.[i] with | '.' -> precision (i+1) | _ -> false,i let rec digitsWidthAndPrecision n i = - if i >= len then failwithf "%s" <| FSComp.SR.forBadPrecision() + if i >= len then failwith (FSComp.SR.forBadPrecision()) match fmt.[i] with | c when System.Char.IsDigit c -> digitsWidthAndPrecision (n*10 + int c - int '0') (i+1) | _ -> Some n, optionalDotAndPrecision i let widthAndPrecision i = - if i >= len then failwithf "%s" <| FSComp.SR.forBadPrecision() + if i >= len then failwith (FSComp.SR.forBadPrecision()) match fmt.[i] with | c when System.Char.IsDigit c -> false,digitsWidthAndPrecision 0 i | '*' -> true, (None, optionalDotAndPrecision (i+1)) | _ -> false, (None, optionalDotAndPrecision i) let rec digitsPosition n i = - if i >= len then failwithf "%s" <| FSComp.SR.forBadPrecision() + if i >= len then failwith (FSComp.SR.forBadPrecision()) match fmt.[i] with | c when System.Char.IsDigit c -> digitsPosition (n*10 + int c - int '0') (i+1) | '$' -> Some n, i+1 @@ -295,21 +304,21 @@ let parseFormatStringInternal (m: range) (fragRanges: range list) (g: TcGlobals) let widthArg,(widthValue, (precisionArg,i)) = widthAndPrecision i let fragCol = fragCol + i - oldI - if i >= len then failwithf "%s" <| FSComp.SR.forBadPrecision() + if i >= len then failwith (FSComp.SR.forBadPrecision()) let acc = if precisionArg then (Option.map ((+)1) posi, g.int_ty) :: acc else acc let acc = if widthArg then (Option.map ((+)1) posi, g.int_ty) :: acc else acc let checkNoPrecision c = - if info.precision then failwithf "%s" <| FSComp.SR.forFormatDoesntSupportPrecision(c.ToString()) + if info.precision then failwith (FSComp.SR.forFormatDoesntSupportPrecision(c.ToString())) let checkNoZeroFlag c = - if info.addZeros then failwithf "%s" <| FSComp.SR.forDoesNotSupportZeroFlag(c.ToString()) + if info.addZeros then failwith (FSComp.SR.forDoesNotSupportZeroFlag(c.ToString())) let checkNoNumericPrefix c = match info.numPrefixIfPos with - | Some n -> failwithf "%s" <| FSComp.SR.forDoesNotSupportPrefixFlag(c.ToString(), n.ToString()) + | Some n -> failwith (FSComp.SR.forDoesNotSupportPrefixFlag(c.ToString(), n.ToString())) | None -> () let checkOtherFlags c = @@ -324,12 +333,12 @@ let parseFormatStringInternal (m: range) (fragRanges: range list) (g: TcGlobals) let i = i + 2 if i+1 < len && fmt.[i] = '(' && fmt.[i+1] = ')' then if isFormattableString then - failwithf "%s" <| FSComp.SR.forFormatInvalidForInterpolated4() + failwith (FSComp.SR.forFormatInvalidForInterpolated4()) i + 2 else - failwithf "%s" <| FSComp.SR.forFormatInvalidForInterpolated2() + failwith (FSComp.SR.forFormatInvalidForInterpolated2()) else - failwithf "%s" <| FSComp.SR.forFormatInvalidForInterpolated() + failwith (FSComp.SR.forFormatInvalidForInterpolated()) else i // Implicitly typed holes in interpolated strings are translated to '... %P(...)...' in the @@ -338,7 +347,7 @@ let parseFormatStringInternal (m: range) (fragRanges: range list) (g: TcGlobals) if i < len && fmt.[i] = '(' then let i2 = fmt.IndexOf(")", i+1) if i2 = -1 then - failwithf "%s" <| FSComp.SR.forFormatInvalidForInterpolated3() + failwith (FSComp.SR.forFormatInvalidForInterpolated3()) else let dotnetAlignment = match widthValue with None -> "" | Some w -> "," + (if info.leftJustify then "-" else "") + string w let dotnetNumberFormat = match fmt.[i+1..i2-1] with "" -> "" | s -> ":" + s @@ -346,7 +355,7 @@ let parseFormatStringInternal (m: range) (fragRanges: range list) (g: TcGlobals) dotnetFormatStringInterpolationHoleCount <- dotnetFormatStringInterpolationHoleCount + 1 i2+1 else - failwithf "%s" <| FSComp.SR.forFormatInvalidForInterpolated3() + failwith (FSComp.SR.forFormatInvalidForInterpolated3()) let collectSpecifierLocation fragLine fragCol numStdArgs = match context with @@ -368,30 +377,30 @@ let parseFormatStringInternal (m: range) (fragRanges: range list) (g: TcGlobals) | 'd' | 'i' | 'u' | 'B' | 'o' | 'x' | 'X' -> if ch = 'B' then ErrorLogger.checkLanguageFeatureError g.langVersion Features.LanguageFeature.PrintfBinaryFormat m - if info.precision then failwithf "%s" <| FSComp.SR.forFormatDoesntSupportPrecision(ch.ToString()) + if info.precision then failwith (FSComp.SR.forFormatDoesntSupportPrecision(ch.ToString())) collectSpecifierLocation fragLine fragCol 1 let i = skipPossibleInterpolationHole (i+1) parseLoop ((posi, mkFlexibleIntFormatTypar g m) :: acc) (i, fragLine, fragCol+1) fragments | 'l' | 'L' -> - if info.precision then failwithf "%s" <| FSComp.SR.forFormatDoesntSupportPrecision(ch.ToString()) + if info.precision then failwith (FSComp.SR.forFormatDoesntSupportPrecision(ch.ToString())) let fragCol = fragCol+1 let i = i+1 // "bad format specifier ... In F# code you can use %d, %x, %o or %u instead ..." if i >= len then - raise (Failure (FSComp.SR.forBadFormatSpecifier())) + failwith (FSComp.SR.forBadFormatSpecifier()) // Always error for %l and %Lx - failwithf "%s" <| FSComp.SR.forLIsUnnecessary() + failwith (FSComp.SR.forLIsUnnecessary()) match fmt.[i] with | 'd' | 'i' | 'o' | 'u' | 'x' | 'X' -> collectSpecifierLocation fragLine fragCol 1 let i = skipPossibleInterpolationHole (i+1) parseLoop ((posi, mkFlexibleIntFormatTypar g m) :: acc) (i, fragLine, fragCol+1) fragments - | _ -> failwithf "%s" <| FSComp.SR.forBadFormatSpecifier() + | _ -> failwith (FSComp.SR.forBadFormatSpecifier()) | 'h' | 'H' -> - failwithf "%s" <| FSComp.SR.forHIsUnnecessary() + failwith (FSComp.SR.forHIsUnnecessary()) | 'M' -> collectSpecifierLocation fragLine fragCol 1 @@ -435,6 +444,9 @@ let parseFormatStringInternal (m: range) (fragRanges: range list) (g: TcGlobals) parseLoop ((posi, NewInferenceType g) :: acc) (i, fragLine, startFragCol) fragments | 'A' -> + if g.useReflectionFreeCodeGen then + failwithf "%s" (FSComp.SR.forPercentAInReflectionFreeCode()) + match info.numPrefixIfPos with | None // %A has BindingFlags=Public, %+A has BindingFlags=Public | NonPublic | Some '+' -> @@ -443,7 +455,8 @@ let parseFormatStringInternal (m: range) (fragRanges: range list) (g: TcGlobals) let xty = NewInferenceType g percentATys.Add(xty) parseLoop ((posi, xty) :: acc) (i, fragLine, fragCol+1) fragments - | Some n -> failwithf "%s" <| FSComp.SR.forDoesNotSupportPrefixFlag(ch.ToString(), n.ToString()) + | Some n -> + failwith (FSComp.SR.forDoesNotSupportPrefixFlag(ch.ToString(), n.ToString())) | 'a' -> checkOtherFlags ch @@ -459,7 +472,7 @@ let parseFormatStringInternal (m: range) (fragRanges: range list) (g: TcGlobals) let i = skipPossibleInterpolationHole (i+1) parseLoop ((posi, mkFunTy g printerArgTy printerResidueTy) :: acc) (i, fragLine, fragCol+1) fragments - | c -> failwithf "%s" <| FSComp.SR.forBadFormatSpecifierGeneral(String.make 1 c) + | c -> failwith (FSComp.SR.forBadFormatSpecifierGeneral(String.make 1 c)) | '\n' -> appendToDotnetFormatString fmt.[i..i] @@ -472,7 +485,8 @@ let parseFormatStringInternal (m: range) (fragRanges: range list) (g: TcGlobals) results, Seq.toList specifierLocations, dotnetFormatString.ToString(), percentATys.ToArray() let ParseFormatString m fragmentRanges g isInterpolated isFormattableString formatStringCheckContext fmt printerArgTy printerResidueTy printerResultTy = - let argTys, specifierLocations, dotnetFormatString, percentATys = parseFormatStringInternal m fragmentRanges g isInterpolated isFormattableString formatStringCheckContext fmt printerArgTy printerResidueTy + let argTys, specifierLocations, dotnetFormatString, percentATys = + parseFormatStringInternal m fragmentRanges g isInterpolated isFormattableString formatStringCheckContext fmt printerArgTy printerResidueTy let printerTy = List.foldBack (mkFunTy g) argTys printerResultTy let printerTupleTy = mkRefTupledTy g argTys argTys, printerTy, printerTupleTy, percentATys, specifierLocations, dotnetFormatString diff --git a/src/fsharp/CheckFormatStrings.fsi b/src/fsharp/CheckFormatStrings.fsi index 83586c97e20..97dc0ed4899 100644 --- a/src/fsharp/CheckFormatStrings.fsi +++ b/src/fsharp/CheckFormatStrings.fsi @@ -25,4 +25,11 @@ val ParseFormatString: -> printerResultTy: TType -> TType list * TType * TType * TType[] * (range * int) list * string -val TryCountFormatStringArguments: m: range -> g: TcGlobals -> isInterpolated: bool -> fmt:string -> printerArgTy:TType -> printerResidueTy:TType -> int option +val TryCountFormatStringArguments: + m: range + -> g: TcGlobals + -> isInterpolated: bool + -> fmt: string + -> printerArgTy: TType + -> printerResidueTy: TType + -> int option diff --git a/src/fsharp/CompilerConfig.fs b/src/fsharp/CompilerConfig.fs index a130e7b2404..a6e447da859 100644 --- a/src/fsharp/CompilerConfig.fs +++ b/src/fsharp/CompilerConfig.fs @@ -462,6 +462,9 @@ type TcConfigBuilder = /// If true, strip away data that would not be of use to end users, but is useful to us for debugging mutable noDebugAttributes: bool + /// If true, do not emit ToString implementations for unions, records, structs, exceptions + mutable useReflectionFreeCodeGen: bool + /// If true, indicates all type checking and code generation is in the context of fsi.exe isInteractive: bool @@ -660,6 +663,7 @@ type TcConfigBuilder = pause = false alwaysCallVirt = true noDebugAttributes = false + useReflectionFreeCodeGen = false emitDebugInfoInQuotations = false exename = None shadowCopyReferences = false @@ -1048,6 +1052,7 @@ type TcConfig private (data: TcConfigBuilder, validate: bool) = member x.pause = data.pause member x.alwaysCallVirt = data.alwaysCallVirt member x.noDebugAttributes = data.noDebugAttributes + member _.useReflectionFreeCodeGen = data.useReflectionFreeCodeGen member x.isInteractive = data.isInteractive member x.isInvalidationSupported = data.isInvalidationSupported member x.emitDebugInfoInQuotations = data.emitDebugInfoInQuotations diff --git a/src/fsharp/CompilerConfig.fsi b/src/fsharp/CompilerConfig.fsi index 11224395b92..774bd2608e5 100644 --- a/src/fsharp/CompilerConfig.fsi +++ b/src/fsharp/CompilerConfig.fsi @@ -262,6 +262,7 @@ type TcConfigBuilder = mutable pause: bool mutable alwaysCallVirt: bool mutable noDebugAttributes: bool + mutable useReflectionFreeCodeGen: bool /// If true, indicates all type checking and code generation is in the context of fsi.exe isInteractive: bool @@ -454,6 +455,7 @@ type TcConfig = member pause: bool member alwaysCallVirt: bool member noDebugAttributes: bool + member useReflectionFreeCodeGen: bool /// If true, indicates all type checking and code generation is in the context of fsi.exe member isInteractive: bool diff --git a/src/fsharp/CompilerImports.fs b/src/fsharp/CompilerImports.fs index 15b56de32a3..a3512ee4910 100644 --- a/src/fsharp/CompilerImports.fs +++ b/src/fsharp/CompilerImports.fs @@ -1914,10 +1914,19 @@ and [] TcImports(tcConfigP: TcConfigProvider, initialResolutions: TcAsse let ilGlobals = mkILGlobals (primaryScopeRef, assembliesThatForwardToPrimaryAssembly, fsharpCoreAssemblyScopeRef) // OK, now we have both mscorlib.dll and FSharp.Core.dll we can create TcGlobals - let tcGlobals = TcGlobals(tcConfig.compilingFslib, ilGlobals, fslibCcu, - tcConfig.implicitIncludeDir, tcConfig.mlCompatibility, - tcConfig.isInteractive, tryFindSysTypeCcu, tcConfig.emitDebugInfoInQuotations, - tcConfig.noDebugAttributes, tcConfig.pathMap, tcConfig.langVersion) + let tcGlobals = + TcGlobals(tcConfig.compilingFslib, + ilGlobals, + fslibCcu, + tcConfig.implicitIncludeDir, + tcConfig.mlCompatibility, + tcConfig.isInteractive, + tcConfig.useReflectionFreeCodeGen, + tryFindSysTypeCcu, + tcConfig.emitDebugInfoInQuotations, + tcConfig.noDebugAttributes, + tcConfig.pathMap, + tcConfig.langVersion) #if DEBUG // the global_g reference cell is used only for debug printing diff --git a/src/fsharp/CompilerOptions.fs b/src/fsharp/CompilerOptions.fs index e076d7fdaf1..b7e2752c328 100644 --- a/src/fsharp/CompilerOptions.fs +++ b/src/fsharp/CompilerOptions.fs @@ -820,6 +820,11 @@ let codeGenerationFlags isFsi (tcConfigB: TcConfigBuilder) = ("crossoptimize", tagNone, OptionSwitch (crossOptimizeSwitch tcConfigB), None, Some (FSComp.SR.optsCrossoptimize())) + + CompilerOption + ("reflectionfree", tagNone, + OptionUnit (fun () -> tcConfigB.useReflectionFreeCodeGen <- true), None, + Some (FSComp.SR.optsReflectionFree())) ] if isFsi then debug @ codegen else debug @ embed @ codegen diff --git a/src/fsharp/FSComp.txt b/src/fsharp/FSComp.txt index c8fb201700c..dae12c121e8 100644 --- a/src/fsharp/FSComp.txt +++ b/src/fsharp/FSComp.txt @@ -235,6 +235,7 @@ forLIsUnnecessary,"The 'l' or 'L' in this format specifier is unnecessary. In F# forHIsUnnecessary,"The 'h' or 'H' in this format specifier is unnecessary. You can use %%d, %%x, %%o or %%u instead, which are overloaded to work with all basic integer types." forDoesNotSupportPrefixFlag,"'%s' does not support prefix '%s' flag" forBadFormatSpecifierGeneral,"Bad format specifier: '%s'" +forPercentAInReflectionFreeCode,"The '%%A' format specifier may not be used in an assembly being compiled with option '--reflectionfree'. This construct implicitly uses reflection." elSysEnvExitDidntExit,"System.Environment.Exit did not exit" elDeprecatedOperator,"The treatment of this operator is now handled directly by the F# compiler and its meaning cannot be redefined" 405,chkProtectedOrBaseCalled,"A protected member is called or 'base' is being used. This is only allowed in the direct implementation of members since they could escape their object scope." @@ -876,6 +877,7 @@ optsTailcalls,"Enable or disable tailcalls" optsDeterministic,"Produce a deterministic assembly (including module version GUID and timestamp)" optsPathMap,"Maps physical paths to source path names output by the compiler" optsCrossoptimize,"Enable or disable cross-module optimizations" +optsReflectionFree,"Disable implicit generation of constructs using reflection" optsWarnaserrorPM,"Report all warnings as errors" optsWarnaserror,"Report specific warnings as errors" optsWarn,"Set a warning level (0-5)" diff --git a/src/fsharp/FSharp.Build/Fsc.fs b/src/fsharp/FSharp.Build/Fsc.fs index e9c61149c0a..6a573a19374 100644 --- a/src/fsharp/FSharp.Build/Fsc.fs +++ b/src/fsharp/FSharp.Build/Fsc.fs @@ -84,6 +84,7 @@ type public Fsc () as this = let mutable vserrors: bool = false let mutable vslcid: string MaybeNull = null let mutable utf8output: bool = false + let mutable useReflectionFreeCodeGen: bool = false /// Trim whitespace ... spaces, tabs, newlines,returns, Double quotes and single quotes let wsCharsToTrim = [| ' '; '\t'; '\"'; '\'' |] @@ -257,6 +258,9 @@ type public Fsc () as this = if utf8output then builder.AppendSwitch("--utf8output") + if useReflectionFreeCodeGen then + builder.AppendSwitch("--reflectionfree") + // When building using the fsc task, always emit the "fullpaths" flag to make the output easier // for the user to parse builder.AppendSwitch("--fullpaths") @@ -544,6 +548,10 @@ type public Fsc () as this = with get() = utf8output and set(p) = utf8output <- p + member _.ReflectionFree + with get() = useReflectionFreeCodeGen + and set(p) = useReflectionFreeCodeGen <- p + member _.SubsystemVersion with get() = subsystemVersion and set(p) = subsystemVersion <- p diff --git a/src/fsharp/FSharp.Build/Fsi.fs b/src/fsharp/FSharp.Build/Fsi.fs index c5da10a645d..9d1144f2c62 100644 --- a/src/fsharp/FSharp.Build/Fsi.fs +++ b/src/fsharp/FSharp.Build/Fsi.fs @@ -59,6 +59,7 @@ type public Fsi () as this = let mutable warningLevel: string MaybeNull = null let mutable vslcid: string MaybeNull = null let mutable utf8output: bool = false + let mutable useReflectionFreeCodeGen: bool = false // See bug 6483; this makes parallel build faster, and is fine to set unconditionally do this.YieldDuringToolExecution <- true @@ -108,7 +109,11 @@ type public Fsi () as this = builder.AppendSwitchIfNotNull("--preferreduilang:", preferredUILang) - if utf8output then builder.AppendSwitch("--utf8output") + if utf8output then + builder.AppendSwitch("--utf8output") + + if useReflectionFreeCodeGen then + builder.AppendSwitch("--reflectionfree") builder.AppendSwitch("--fullpaths") builder.AppendSwitch("--flaterrors") diff --git a/src/fsharp/FSharp.Build/Microsoft.FSharp.NetSdk.props b/src/fsharp/FSharp.Build/Microsoft.FSharp.NetSdk.props index f6de7d70481..b2252e9e136 100644 --- a/src/fsharp/FSharp.Build/Microsoft.FSharp.NetSdk.props +++ b/src/fsharp/FSharp.Build/Microsoft.FSharp.NetSdk.props @@ -42,6 +42,7 @@ WARNING: DO NOT MODIFY this file unless you are knowledgeable about MSBuild and 3239;$(WarningsAsErrors) true true + false diff --git a/src/fsharp/FSharp.Build/Microsoft.FSharp.Targets b/src/fsharp/FSharp.Build/Microsoft.FSharp.Targets index e200ed8a7b0..bab825525f8 100644 --- a/src/fsharp/FSharp.Build/Microsoft.FSharp.Targets +++ b/src/fsharp/FSharp.Build/Microsoft.FSharp.Targets @@ -335,6 +335,7 @@ this file. LCID="$(LCID)" NoFramework="true" Optimize="$(Optimize)" + ReflectionFree="$(ReflectionFree)" OtherFlags="$(FscOtherFlags)" OutputAssembly="@(IntermediateAssembly)" PathMap="$(PathMap)" diff --git a/src/fsharp/FSharp.Core/Linq.fs b/src/fsharp/FSharp.Core/Linq.fs index d46d9eedb56..5b383f2c3d2 100644 --- a/src/fsharp/FSharp.Core/Linq.fs +++ b/src/fsharp/FSharp.Core/Linq.fs @@ -663,7 +663,7 @@ module LeafExpressionConverter = failConvert inp and failConvert inp = - raise (new NotSupportedException(Printf.sprintf "Could not convert the following F# Quotation to a LINQ Expression Tree\n--------\n%A\n-------------\n" inp)) + raise (new NotSupportedException(Printf.sprintf "Could not convert the following F# Quotation to a LINQ Expression Tree\n--------\n%s\n-------------\n" (inp.ToString()))) and transBinOp inp env addConvertLeft args addConvertRight (exprErasedConstructor : _ * _ -> _) = match args with diff --git a/src/fsharp/FSharp.Core/Query.fs b/src/fsharp/FSharp.Core/Query.fs index 8d54ff85228..241c178e883 100644 --- a/src/fsharp/FSharp.Core/Query.fs +++ b/src/fsharp/FSharp.Core/Query.fs @@ -1799,27 +1799,26 @@ module Query = let linqQuery = TransInnerWithFinalConsume canElim queryProducingSequence let linqQueryAfterEliminatingNestedQueries = EliminateNestedQueries linqQuery -#if DEBUG - let debug() = - Printf.printfn "----------------------queryProducingSequence-------------------------" - Printf.printfn "%A" queryProducingSequence - Printf.printfn "--------------------------linqQuery (before nested)------------------" - Printf.printfn "%A" linqQuery - Printf.printfn "--------------------------linqQuery (after nested)-------------------" - Printf.printfn "%A" linqQueryAfterEliminatingNestedQueries -#endif - +//#if DEBUG +// let debug() = +// Printf.printfn "----------------------queryProducingSequence-------------------------" +// Printf.printfn "%A" queryProducingSequence +// Printf.printfn "--------------------------linqQuery (before nested)------------------" +// Printf.printfn "%A" linqQuery +// Printf.printfn "--------------------------linqQuery (after nested)-------------------" +// Printf.printfn "%A" linqQueryAfterEliminatingNestedQueries +//#endif let result = try LeafExpressionConverter.EvaluateQuotation linqQueryAfterEliminatingNestedQueries with e -> -#if DEBUG - debug() - Printf.printfn "--------------------------error--------------------------------------" - Printf.printfn "%A" (e.ToString()) - Printf.printfn "---------------------------------------------------------------------" -#endif +//#if DEBUG +// debug() +// Printf.printfn "--------------------------error--------------------------------------" +// Printf.printfn "%A" (e.ToString()) +// Printf.printfn "---------------------------------------------------------------------" +//#endif reraise () diff --git a/src/fsharp/FSharp.Core/QueryExtensions.fs b/src/fsharp/FSharp.Core/QueryExtensions.fs index f21c93ba648..7da5831e478 100644 --- a/src/fsharp/FSharp.Core/QueryExtensions.fs +++ b/src/fsharp/FSharp.Core/QueryExtensions.fs @@ -161,7 +161,7 @@ module internal Adapters = mutableTupleType.MakeGenericType (ty.GetGenericArguments() |> Array.toList |> conv |> Array.ofList) | _ -> assert false - Printf.failwithf "unreachable, ty = %A" ty + failwith "unreachable" let (|RecordFieldGetSimplification|_|) (expr:Expr) = match expr with diff --git a/src/fsharp/FSharp.Core/quotations.fs b/src/fsharp/FSharp.Core/quotations.fs index 40148181633..e9144abaed0 100644 --- a/src/fsharp/FSharp.Core/quotations.fs +++ b/src/fsharp/FSharp.Core/quotations.fs @@ -328,14 +328,11 @@ and [] match e with | NLambdas nargs (vs, e) -> combL "NewDelegate" ([typeL ty] @ (vs |> List.map varL) @ [expr e]) | _ -> combL "NewDelegate" [typeL ty; expr e] - //| CombTerm(_, args) -> combL "??" (exprs args) | VarTerm v -> wordL (tagLocal v.Name) | LambdaTerm(v, b) -> combL "Lambda" [varL v; expr b] | HoleTerm _ -> wordL (tagLocal "_") | CombTerm(QuoteOp _, args) -> combL "Quote" (exprs args) - | _ -> failwithf "Unexpected term in layout %A" x.Tree - - + | _ -> failwithf "Unexpected term" and [] Expr<'T>(term:Tree, attribs) = @@ -1411,7 +1408,7 @@ module Patterns = // For some reason we can get 'null' returned here even when a type with the right name exists... Hence search the slow way... match (assembly.GetTypes() |> Array.tryFind (fun a -> a.FullName = tcName)) with | Some ty -> ty - | None -> invalidArg "tcName" (String.Format(SR.GetString(SR.QfailedToBindTypeInAssembly), tcName, assembly.FullName)) // "Available types are:\n%A" tcName assembly (assembly.GetTypes() |> Array.map (fun a -> a.FullName)) + | None -> invalidArg "tcName" (String.Format(SR.GetString(SR.QfailedToBindTypeInAssembly), tcName, assembly.FullName)) | ty -> ty let decodeNamedTy tc tsR = mkNamedType (tc, tsR) @@ -1711,7 +1708,7 @@ module Patterns = if minfo.IsStatic then StaticMethodCallWOp(minfo, minfoW, n) else InstanceMethodCallWOp(minfo, minfoW, n)) // 51 taken above - | _ -> failwithf "u_constSpec, unrecognized tag %d" tag + | _ -> failwith ("u_constSpec, unrecognized tag " + string tag) Unique constSpec let u_ReflectedDefinition = u_tup2 u_MethodBase u_Expr diff --git a/src/fsharp/IlxGen.fs b/src/fsharp/IlxGen.fs index 5e082c21f38..b2a7e4f90f8 100644 --- a/src/fsharp/IlxGen.fs +++ b/src/fsharp/IlxGen.fs @@ -232,7 +232,7 @@ type IlxGenOptions = isInteractiveItExpr: bool /// Suppress ToString emit - noReflectionCodeGen: bool + useReflectionFreeCodeGen: bool /// Whenever possible, use callvirt instead of call alwaysCallVirt: bool @@ -7780,7 +7780,7 @@ and GenToStringMethod cenv eenv ilThisTy m = /// Generate a ToString/get_Message method that calls 'sprintf "%A"' and GenPrintingMethod cenv eenv methName ilThisTy m = let g = cenv.g - [ if not cenv.opts.noReflectionCodeGen then + [ if not cenv.opts.useReflectionFreeCodeGen then match (eenv.valsInScope.TryFind g.sprintf_vref.Deref, eenv.valsInScope.TryFind g.new_format_vref.Deref) with diff --git a/src/fsharp/IlxGen.fsi b/src/fsharp/IlxGen.fsi index 0ede5892d57..5fe1519df1d 100644 --- a/src/fsharp/IlxGen.fsi +++ b/src/fsharp/IlxGen.fsi @@ -54,7 +54,7 @@ type internal IlxGenOptions = isInteractiveItExpr: bool /// Suppress ToString emit - noReflectionCodeGen: bool + useReflectionFreeCodeGen: bool /// Indicates that, whenever possible, use callvirt instead of call alwaysCallVirt: bool diff --git a/src/fsharp/OptimizeInputs.fs b/src/fsharp/OptimizeInputs.fs index 09eda095ee8..773630055f7 100644 --- a/src/fsharp/OptimizeInputs.fs +++ b/src/fsharp/OptimizeInputs.fs @@ -157,10 +157,16 @@ let CreateIlxAssemblyGenerator (_tcConfig:TcConfig, tcImports:TcImports, tcGloba ilxGenerator.AddExternalCcus ccus ilxGenerator -let GenerateIlxCode - (ilxBackend, isInteractiveItExpr, isInteractiveOnMono, - tcConfig:TcConfig, topAttrs: TopAttribs, optimizedImpls, - fragName, ilxGenerator: IlxAssemblyGenerator) = +let GenerateIlxCode ( + ilxBackend, + isInteractiveItExpr, + isInteractiveOnMono, + tcConfig:TcConfig, + topAttrs: TopAttribs, + optimizedImpls, + fragName, + ilxGenerator: IlxAssemblyGenerator + ) = let mainMethodInfo = if (tcConfig.target = CompilerTarget.Dll) || (tcConfig.target = CompilerTarget.Module) then @@ -170,14 +176,15 @@ let GenerateIlxCode let ilxGenOpts: IlxGenOptions = { generateFilterBlocks = tcConfig.generateFilterBlocks emitConstantArraysUsingStaticDataBlobs = not isInteractiveOnMono - workAroundReflectionEmitBugs=tcConfig.isInteractive // REVIEW: is this still required? - generateDebugSymbols= tcConfig.debuginfo + workAroundReflectionEmitBugs = tcConfig.isInteractive // REVIEW: is this still required? + generateDebugSymbols = tcConfig.debuginfo fragName = fragName localOptimizationsEnabled= tcConfig.optSettings.LocalOptimizationsEnabled testFlagEmitFeeFeeAs100001 = tcConfig.testFlagEmitFeeFeeAs100001 mainMethodInfo= mainMethodInfo ilxBackend = ilxBackend fsiMultiAssemblyEmit = tcConfig.fsiMultiAssemblyEmit + useReflectionFreeCodeGen = tcConfig.useReflectionFreeCodeGen isInteractive = tcConfig.isInteractive isInteractiveItExpr = isInteractiveItExpr alwaysCallVirt = tcConfig.alwaysCallVirt } diff --git a/src/fsharp/OptimizeInputs.fsi b/src/fsharp/OptimizeInputs.fsi index 5a41c3f9bd4..bcd9d44ba12 100644 --- a/src/fsharp/OptimizeInputs.fsi +++ b/src/fsharp/OptimizeInputs.fsi @@ -13,11 +13,11 @@ open FSharp.Compiler.Optimizer open FSharp.Compiler.TcGlobals open FSharp.Compiler.TypedTree -val GetGeneratedILModuleName : CompilerTarget -> string -> string +val GetGeneratedILModuleName: CompilerTarget -> string -> string -val GetInitialOptimizationEnv : TcImports * TcGlobals -> IncrementalOptimizationEnv +val GetInitialOptimizationEnv: TcImports * TcGlobals -> IncrementalOptimizationEnv -val AddExternalCcuToOptimizationEnv : TcGlobals -> IncrementalOptimizationEnv -> ImportedAssembly -> IncrementalOptimizationEnv +val AddExternalCcuToOptimizationEnv: TcGlobals -> IncrementalOptimizationEnv -> ImportedAssembly -> IncrementalOptimizationEnv val ApplyAllOptimizations: TcConfig * @@ -31,11 +31,26 @@ val ApplyAllOptimizations: TypedImplFile list -> TypedAssemblyAfterOptimization * LazyModuleInfo * IncrementalOptimizationEnv -val CreateIlxAssemblyGenerator : TcConfig * TcImports * TcGlobals * ConstraintSolver.TcValF * CcuThunk -> IlxAssemblyGenerator - -val GenerateIlxCode : IlxGenBackend * isInteractiveItExpr:bool * isInteractiveOnMono:bool * TcConfig * TopAttribs * TypedAssemblyAfterOptimization * fragName:string * IlxAssemblyGenerator -> IlxGenResults +val CreateIlxAssemblyGenerator: + TcConfig * + TcImports * + TcGlobals * + ConstraintSolver.TcValF * + CcuThunk + -> IlxAssemblyGenerator + +val GenerateIlxCode: + ilxBackend: IlxGenBackend * + isInteractiveItExpr:bool * + isInteractiveOnMono:bool * + tcConfig: TcConfig * + topAttrs: TopAttribs * + optimizedImpls: TypedAssemblyAfterOptimization * + fragName:string * + ilxGenerator: IlxAssemblyGenerator + -> IlxGenResults // Used during static linking -val NormalizeAssemblyRefs : CompilationThreadToken * ILGlobals * TcImports -> (ILScopeRef -> ILScopeRef) +val NormalizeAssemblyRefs: CompilationThreadToken * ILGlobals * TcImports -> (ILScopeRef -> ILScopeRef) -val GetGeneratedILModuleName : CompilerTarget -> string -> string +val GetGeneratedILModuleName: CompilerTarget -> string -> string diff --git a/src/fsharp/TcGlobals.fs b/src/fsharp/TcGlobals.fs index d0142d9a824..0c137df3df9 100755 --- a/src/fsharp/TcGlobals.fs +++ b/src/fsharp/TcGlobals.fs @@ -182,12 +182,20 @@ let tname_IsByRefLikeAttribute = "System.Runtime.CompilerServices.IsByRefLikeAtt // Table of all these "globals" //------------------------------------------------------------------------- -type public TcGlobals(compilingFslib: bool, ilg:ILGlobals, fslibCcu: CcuThunk, directoryToResolveRelativePaths, - mlCompatibility: bool, isInteractive:bool, - // The helper to find system types amongst referenced DLLs - tryFindSysTypeCcu, - emitDebugInfoInQuotations: bool, noDebugAttributes: bool, - pathMap: PathMap, langVersion: LanguageVersion) = +type TcGlobals( + compilingFslib: bool, + ilg:ILGlobals, + fslibCcu: CcuThunk, + directoryToResolveRelativePaths, + mlCompatibility: bool, + isInteractive: bool, + useReflectionFreeCodeGen: bool, + // The helper to find system types amongst referenced DLLs + tryFindSysTypeCcu, + emitDebugInfoInQuotations: bool, + noDebugAttributes: bool, + pathMap: PathMap, + langVersion: LanguageVersion) = let vara = Construct.NewRigidTypar "a" envRange let varb = Construct.NewRigidTypar "b" envRange @@ -986,6 +994,7 @@ type public TcGlobals(compilingFslib: bool, ilg:ILGlobals, fslibCcu: CcuThunk, d // better the job we do of mapping from provided expressions back to FSharp.Core F# functions and values. member _.knownFSharpCoreModules = v_knownFSharpCoreModules member _.compilingFslib = compilingFslib + member _.useReflectionFreeCodeGen = useReflectionFreeCodeGen member _.mlCompatibility = mlCompatibility member _.emitDebugInfoInQuotations = emitDebugInfoInQuotations member _.directoryToResolveRelativePaths = directoryToResolveRelativePaths diff --git a/src/fsharp/xlf/FSComp.txt.cs.xlf b/src/fsharp/xlf/FSComp.txt.cs.xlf index 603662589f4..3340e3fc1a1 100644 --- a/src/fsharp/xlf/FSComp.txt.cs.xlf +++ b/src/fsharp/xlf/FSComp.txt.cs.xlf @@ -292,6 +292,11 @@ Interpolované řetězce, které se používají jako typ IFormattable nebo FormattableString, nemůžou používat specifikátory %. Použít je možné jen interpolované výrazy ve stylu .NET, třeba {{expr}}, {{expr,3}} nebo {{expr:N5}}. + + The '%A' format specifier may not be used in an assembly being compiled with option '--reflectionfree'. This construct implicitly uses reflection. + The '%A' format specifier may not be used in an assembly being compiled with option '--reflectionfree'. This construct implicitly uses reflection. + + - {0} – {0} @@ -427,6 +432,11 @@ Zobrazte si povolené hodnoty verze jazyka a pak zadejte požadovanou verzi, například latest nebo preview. + + Disable implicit generation of constructs using reflection + Disable implicit generation of constructs using reflection + + Supported language versions: Podporované jazykové verze: diff --git a/src/fsharp/xlf/FSComp.txt.de.xlf b/src/fsharp/xlf/FSComp.txt.de.xlf index 38700d023c0..d4096f79fa4 100644 --- a/src/fsharp/xlf/FSComp.txt.de.xlf +++ b/src/fsharp/xlf/FSComp.txt.de.xlf @@ -292,6 +292,11 @@ Interpolierte Zeichenfolgen, die als Typ "IFormattable" oder "FormattableString" verwendet werden, dürfen keine Spezifizierer vom Typ "%" verwenden. Es dürfen nur Interpolanten im .NET-Format wie "{{expr}}", "{{expr,3}}" oder "{{expr:N5}}" verwendet werden. + + The '%A' format specifier may not be used in an assembly being compiled with option '--reflectionfree'. This construct implicitly uses reflection. + The '%A' format specifier may not be used in an assembly being compiled with option '--reflectionfree'. This construct implicitly uses reflection. + + - {0} - {0} @@ -427,6 +432,11 @@ Zeigen Sie die zulässigen Werte für die Sprachversion an. Geben Sie die Sprachversion als "latest" oder "preview" an. + + Disable implicit generation of constructs using reflection + Disable implicit generation of constructs using reflection + + Supported language versions: Unterstützte Sprachversionen: diff --git a/src/fsharp/xlf/FSComp.txt.es.xlf b/src/fsharp/xlf/FSComp.txt.es.xlf index f6cff0722b0..024857af6bf 100644 --- a/src/fsharp/xlf/FSComp.txt.es.xlf +++ b/src/fsharp/xlf/FSComp.txt.es.xlf @@ -292,6 +292,11 @@ Las cadenas interpoladas que se usan como tipo IFormattable o FormattableString no pueden usar los especificadores "%"; solo pueden utilizar operandos de interpolación de estilo .NET, como "{{expr}}", "{{expr,3}}" o "{{expr:N5}}". + + The '%A' format specifier may not be used in an assembly being compiled with option '--reflectionfree'. This construct implicitly uses reflection. + The '%A' format specifier may not be used in an assembly being compiled with option '--reflectionfree'. This construct implicitly uses reflection. + + - {0} - {0} @@ -427,6 +432,11 @@ Mostrar los valores permitidos para la versión de idioma, especificar la versión de idioma como "latest" "preview" + + Disable implicit generation of constructs using reflection + Disable implicit generation of constructs using reflection + + Supported language versions: Versiones de lenguaje admitidas: diff --git a/src/fsharp/xlf/FSComp.txt.fr.xlf b/src/fsharp/xlf/FSComp.txt.fr.xlf index 4fc68f7b7cd..ae178d201b1 100644 --- a/src/fsharp/xlf/FSComp.txt.fr.xlf +++ b/src/fsharp/xlf/FSComp.txt.fr.xlf @@ -292,6 +292,11 @@ Les chaînes interpolées utilisées en tant que type IFormattable ou FormattableString ne peuvent pas utiliser les spécificateurs '%'. Seuls les spécificateurs de style .NET tels que '{{expr}}', '{{expr,3}}' et '{{expr:N5}}' peuvent être utilisés. + + The '%A' format specifier may not be used in an assembly being compiled with option '--reflectionfree'. This construct implicitly uses reflection. + The '%A' format specifier may not be used in an assembly being compiled with option '--reflectionfree'. This construct implicitly uses reflection. + + - {0} - {0} @@ -427,6 +432,11 @@ Afficher les valeurs autorisées pour la version du langage, spécifier la version du langage comme 'dernière' ou 'préversion' + + Disable implicit generation of constructs using reflection + Disable implicit generation of constructs using reflection + + Supported language versions: Versions linguistiques prises en charge : diff --git a/src/fsharp/xlf/FSComp.txt.it.xlf b/src/fsharp/xlf/FSComp.txt.it.xlf index 40d14d8b227..d3fb2dc92b0 100644 --- a/src/fsharp/xlf/FSComp.txt.it.xlf +++ b/src/fsharp/xlf/FSComp.txt.it.xlf @@ -292,6 +292,11 @@ Nelle stringhe interpolate usate come tipo IFormattable o FormattableString non è possibile usare gli identificatori '%', ma è possibile usare solo interpolanti di tipo .NET, come '{{expr}}', '{{expr,3}}' o '{{expr:N5}}'. + + The '%A' format specifier may not be used in an assembly being compiled with option '--reflectionfree'. This construct implicitly uses reflection. + The '%A' format specifier may not be used in an assembly being compiled with option '--reflectionfree'. This construct implicitly uses reflection. + + - {0} - {0} @@ -427,6 +432,11 @@ Visualizza i valori consentiti per la versione del linguaggio. Specificare la versione del linguaggio, ad esempio 'latest' o 'preview' + + Disable implicit generation of constructs using reflection + Disable implicit generation of constructs using reflection + + Supported language versions: Versioni del linguaggio supportate: diff --git a/src/fsharp/xlf/FSComp.txt.ja.xlf b/src/fsharp/xlf/FSComp.txt.ja.xlf index 66bbfcdb229..7d836cb99d3 100644 --- a/src/fsharp/xlf/FSComp.txt.ja.xlf +++ b/src/fsharp/xlf/FSComp.txt.ja.xlf @@ -292,6 +292,11 @@ IFormattable 型または FormattableString 型として使用される補間された文字列では、'%' 指定子を使用できません。'{{expr}}'、'{{expr,3}}'、'{{expr:N5}}' などの .NET 形式の補間のみ使用できます。 + + The '%A' format specifier may not be used in an assembly being compiled with option '--reflectionfree'. This construct implicitly uses reflection. + The '%A' format specifier may not be used in an assembly being compiled with option '--reflectionfree'. This construct implicitly uses reflection. + + - {0} - {0} @@ -427,6 +432,11 @@ 言語バージョンで許可された値を表示し、'最新' や 'プレビュー' などの言語バージョンを指定する + + Disable implicit generation of constructs using reflection + Disable implicit generation of constructs using reflection + + Supported language versions: サポートされる言語バージョン: diff --git a/src/fsharp/xlf/FSComp.txt.ko.xlf b/src/fsharp/xlf/FSComp.txt.ko.xlf index add2ab5f909..5ea415bc684 100644 --- a/src/fsharp/xlf/FSComp.txt.ko.xlf +++ b/src/fsharp/xlf/FSComp.txt.ko.xlf @@ -292,6 +292,11 @@ 형식 IFormattable 또는 형식 FormattableString으로 사용된 보간 문자열은 '%' 지정자를 사용할 수 없으며 '{{expr}}', '{{expr,3}}' 또는 '{{expr:N5}}' 등의 .NET 스타일 인터폴란드를 사용할 수 있습니다. + + The '%A' format specifier may not be used in an assembly being compiled with option '--reflectionfree'. This construct implicitly uses reflection. + The '%A' format specifier may not be used in an assembly being compiled with option '--reflectionfree'. This construct implicitly uses reflection. + + - {0} - {0} @@ -427,6 +432,11 @@ 언어 버전의 허용된 값을 표시하고 '최신' 또는 '미리 보기'와 같은 언어 버전을 지정합니다. + + Disable implicit generation of constructs using reflection + Disable implicit generation of constructs using reflection + + Supported language versions: 지원되는 언어 버전: diff --git a/src/fsharp/xlf/FSComp.txt.pl.xlf b/src/fsharp/xlf/FSComp.txt.pl.xlf index 1242de81b70..68a31cfd350 100644 --- a/src/fsharp/xlf/FSComp.txt.pl.xlf +++ b/src/fsharp/xlf/FSComp.txt.pl.xlf @@ -292,6 +292,11 @@ W interpolowanych ciągach używanych jako typ IFormattable lub FormattableString nie można używać specyfikatorów „%”. Można używać tylko operatorów interpolacji, takich jak „{{expr}}”, „{{expr,3}}” lub „{{expr:N5}}”. + + The '%A' format specifier may not be used in an assembly being compiled with option '--reflectionfree'. This construct implicitly uses reflection. + The '%A' format specifier may not be used in an assembly being compiled with option '--reflectionfree'. This construct implicitly uses reflection. + + - {0} — {0} @@ -427,6 +432,11 @@ Wyświetl dozwolone wartości dla wersji językowej; określ wersję językową, np. „latest” lub „preview” + + Disable implicit generation of constructs using reflection + Disable implicit generation of constructs using reflection + + Supported language versions: Obsługiwane wersje językowe: diff --git a/src/fsharp/xlf/FSComp.txt.pt-BR.xlf b/src/fsharp/xlf/FSComp.txt.pt-BR.xlf index c17ab8cea14..71f3b6242dc 100644 --- a/src/fsharp/xlf/FSComp.txt.pt-BR.xlf +++ b/src/fsharp/xlf/FSComp.txt.pt-BR.xlf @@ -292,6 +292,11 @@ As cadeias de caracteres interpoladas usadas como tipo IFormattable ou tipo FormattableString não podem usar especificadores '%'. Apenas interpoladores de estilo .NET, como '{{expr}}', '{{expr,3}}' ou '{{expr:N5}}' podem ser usados. + + The '%A' format specifier may not be used in an assembly being compiled with option '--reflectionfree'. This construct implicitly uses reflection. + The '%A' format specifier may not be used in an assembly being compiled with option '--reflectionfree'. This construct implicitly uses reflection. + + - {0} - {0} @@ -427,6 +432,11 @@ Exibe os valores permitidos para a versão do idioma, especifica a versão do idioma, como 'mais recente ' ou 'prévia' + + Disable implicit generation of constructs using reflection + Disable implicit generation of constructs using reflection + + Supported language versions: Versões de linguagens com suporte: diff --git a/src/fsharp/xlf/FSComp.txt.ru.xlf b/src/fsharp/xlf/FSComp.txt.ru.xlf index 6c0111bf3c1..cbcd1275e7a 100644 --- a/src/fsharp/xlf/FSComp.txt.ru.xlf +++ b/src/fsharp/xlf/FSComp.txt.ru.xlf @@ -292,6 +292,11 @@ Интерполированные строки, используемые в качестве типа IFormattable или FormattableString, не могут использовать описатели "%". Можно использовать только описатели в стиле .NET, такие как "{{expr}}", "{{expr,3}}" или "{{expr:N5}}". + + The '%A' format specifier may not be used in an assembly being compiled with option '--reflectionfree'. This construct implicitly uses reflection. + The '%A' format specifier may not be used in an assembly being compiled with option '--reflectionfree'. This construct implicitly uses reflection. + + - {0} - {0} @@ -427,6 +432,11 @@ Отображение допустимых значений для версии языка. Укажите версию языка, например, "latest" или "preview". + + Disable implicit generation of constructs using reflection + Disable implicit generation of constructs using reflection + + Supported language versions: Поддерживаемые языковые версии: diff --git a/src/fsharp/xlf/FSComp.txt.tr.xlf b/src/fsharp/xlf/FSComp.txt.tr.xlf index 82e15d70092..0cf72580dd0 100644 --- a/src/fsharp/xlf/FSComp.txt.tr.xlf +++ b/src/fsharp/xlf/FSComp.txt.tr.xlf @@ -292,6 +292,11 @@ IFormattable veya FormattableString türü olarak kullanılan düz metin arasına kod eklenmiş dizeler '%' belirticilerini kullanamaz. Yalnızca '{{expr}}', '{{expr,3}}' veya '{{expr:N5}}' gibi .NET stili düz metin arasına kod ekleme işlemleri kullanılabilir. + + The '%A' format specifier may not be used in an assembly being compiled with option '--reflectionfree'. This construct implicitly uses reflection. + The '%A' format specifier may not be used in an assembly being compiled with option '--reflectionfree'. This construct implicitly uses reflection. + + - {0} - {0} @@ -427,6 +432,11 @@ Dil sürümü için izin verilen değerleri görüntüleyin, dil sürümünü 'en son' veya 'önizleme' örneklerindeki gibi belirtin + + Disable implicit generation of constructs using reflection + Disable implicit generation of constructs using reflection + + Supported language versions: Desteklenen dil sürümleri: diff --git a/src/fsharp/xlf/FSComp.txt.zh-Hans.xlf b/src/fsharp/xlf/FSComp.txt.zh-Hans.xlf index 5ffe569f28e..0c22932b850 100644 --- a/src/fsharp/xlf/FSComp.txt.zh-Hans.xlf +++ b/src/fsharp/xlf/FSComp.txt.zh-Hans.xlf @@ -292,6 +292,11 @@ 作为类型 IFormattable 或类型 FormattableString 使用的内插字符串不能使用 "%" 说明符,只能使用 .NET 样式的插植,如 "{{expr}}"、"{{expr,3}}" 或 "{{expr:N5}}"。 + + The '%A' format specifier may not be used in an assembly being compiled with option '--reflectionfree'. This construct implicitly uses reflection. + The '%A' format specifier may not be used in an assembly being compiled with option '--reflectionfree'. This construct implicitly uses reflection. + + - {0} - {0} @@ -427,6 +432,11 @@ 显示语言版本的允许值,指定语言版本,如“最新”或“预览” + + Disable implicit generation of constructs using reflection + Disable implicit generation of constructs using reflection + + Supported language versions: 支持的语言版本: diff --git a/src/fsharp/xlf/FSComp.txt.zh-Hant.xlf b/src/fsharp/xlf/FSComp.txt.zh-Hant.xlf index d6bcfaa2ed7..31cd7e13b7c 100644 --- a/src/fsharp/xlf/FSComp.txt.zh-Hant.xlf +++ b/src/fsharp/xlf/FSComp.txt.zh-Hant.xlf @@ -292,6 +292,11 @@ 用作類型 IFormattable 或類型 FormattableString 的插補字串不能使用 '%' 指定名稱,只能使用 .NET 樣式的插補值,例如 '{{expr}}'、'{{expr,3}}' 或 '{{expr:N5}}'。 + + The '%A' format specifier may not be used in an assembly being compiled with option '--reflectionfree'. This construct implicitly uses reflection. + The '%A' format specifier may not be used in an assembly being compiled with option '--reflectionfree'. This construct implicitly uses reflection. + + - {0} - {0} @@ -427,6 +432,11 @@ 顯示語言版本允許的值,指定 'latest' 或 'preview' 等語言版本 + + Disable implicit generation of constructs using reflection + Disable implicit generation of constructs using reflection + + Supported language versions: 支援的語言版本: diff --git a/tests/fsharpqa/Source/CompilerOptions/fsc/help/help40.437.1033.bsl b/tests/fsharpqa/Source/CompilerOptions/fsc/help/help40.437.1033.bsl index 853541eca4d..862ee4182f5 100644 --- a/tests/fsharpqa/Source/CompilerOptions/fsc/help/help40.437.1033.bsl +++ b/tests/fsharpqa/Source/CompilerOptions/fsc/help/help40.437.1033.bsl @@ -90,6 +90,8 @@ Copyright (c) Microsoft Corporation. All Rights Reserved. names output by the compiler --crossoptimize[+|-] Enable or disable cross-module optimizations +--reflectionfree Disable implicit generation of + constructs using reflection - ERRORS AND WARNINGS - diff --git a/tests/fsharpqa/Source/CompilerOptions/fsi/exename/help40.437.1033.bsl b/tests/fsharpqa/Source/CompilerOptions/fsi/exename/help40.437.1033.bsl index 845f9443070..166a579b88f 100644 --- a/tests/fsharpqa/Source/CompilerOptions/fsi/exename/help40.437.1033.bsl +++ b/tests/fsharpqa/Source/CompilerOptions/fsi/exename/help40.437.1033.bsl @@ -38,6 +38,8 @@ Usage: fsharpi [script.fsx []] names output by the compiler --crossoptimize[+|-] Enable or disable cross-module optimizations +--reflectionfree Disable implicit generation of + constructs using reflection - ERRORS AND WARNINGS - From 39ca174f61d7ac7823b06c81f4707c313a74b84b Mon Sep 17 00:00:00 2001 From: Don Syme Date: Fri, 8 Apr 2022 13:33:13 +1000 Subject: [PATCH 03/13] reflection free flag --- src/fsharp/IlxGen.fs | 6 +++++- .../CompilerOptions/fsi/help/help40-nologo.437.1033.bsl | 2 ++ .../Source/CompilerOptions/fsi/help/help40.437.1033.bsl | 2 ++ 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/fsharp/IlxGen.fs b/src/fsharp/IlxGen.fs index b2a7e4f90f8..7dcbcc8c4c2 100644 --- a/src/fsharp/IlxGen.fs +++ b/src/fsharp/IlxGen.fs @@ -7938,7 +7938,11 @@ and GenTypeDef cenv mgbuf lazyInitInfo eenv m (tycon: Tycon) = // DebugDisplayAttribute gets copied to the subtypes generated as part of DU compilation let debugDisplayAttrs, normalAttrs = tycon.Attribs |> List.partition (IsMatchingFSharpAttribute g g.attrib_DebuggerDisplayAttribute) let securityAttrs, normalAttrs = normalAttrs |> List.partition (fun a -> IsSecurityAttribute g cenv.amap cenv.casApplied a m) - let generateDebugDisplayAttribute = not g.compilingFslib && tycon.IsUnionTycon && isNil debugDisplayAttrs + let generateDebugDisplayAttribute = + not g.useReflectionFreeCodeGen && + not g.compilingFslib && + tycon.IsUnionTycon && + isNil debugDisplayAttrs let generateDebugProxies = not (tyconRefEq g tcref g.unit_tcr_canon) && diff --git a/tests/fsharpqa/Source/CompilerOptions/fsi/help/help40-nologo.437.1033.bsl b/tests/fsharpqa/Source/CompilerOptions/fsi/help/help40-nologo.437.1033.bsl index f0af4efa4b2..06f0acdb1c4 100644 --- a/tests/fsharpqa/Source/CompilerOptions/fsi/help/help40-nologo.437.1033.bsl +++ b/tests/fsharpqa/Source/CompilerOptions/fsi/help/help40-nologo.437.1033.bsl @@ -38,6 +38,8 @@ Usage: fsiAnyCpu [script.fsx []] names output by the compiler --crossoptimize[+|-] Enable or disable cross-module optimizations +--reflectionfree Disable implicit generation of + constructs using reflection - ERRORS AND WARNINGS - diff --git a/tests/fsharpqa/Source/CompilerOptions/fsi/help/help40.437.1033.bsl b/tests/fsharpqa/Source/CompilerOptions/fsi/help/help40.437.1033.bsl index 34c66f728ed..f2651c5c8a1 100644 --- a/tests/fsharpqa/Source/CompilerOptions/fsi/help/help40.437.1033.bsl +++ b/tests/fsharpqa/Source/CompilerOptions/fsi/help/help40.437.1033.bsl @@ -40,6 +40,8 @@ Usage: fsiAnyCpu [script.fsx []] names output by the compiler --crossoptimize[+|-] Enable or disable cross-module optimizations +--reflectionfree Disable implicit generation of + constructs using reflection - ERRORS AND WARNINGS - From f5c2b98c23ea11f93163a0120341cb1543f7fc9d Mon Sep 17 00:00:00 2001 From: Don Syme Date: Sun, 10 Apr 2022 14:00:38 +1000 Subject: [PATCH 04/13] Update src/fsharp/CheckFormatStrings.fs Co-authored-by: Ilja Nosik --- src/fsharp/CheckFormatStrings.fs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/fsharp/CheckFormatStrings.fs b/src/fsharp/CheckFormatStrings.fs index 2fa351a9422..ab25a3822fd 100644 --- a/src/fsharp/CheckFormatStrings.fs +++ b/src/fsharp/CheckFormatStrings.fs @@ -445,7 +445,7 @@ let parseFormatStringInternal | 'A' -> if g.useReflectionFreeCodeGen then - failwithf "%s" (FSComp.SR.forPercentAInReflectionFreeCode()) + failwith (FSComp.SR.forPercentAInReflectionFreeCode()) match info.numPrefixIfPos with | None // %A has BindingFlags=Public, %+A has BindingFlags=Public | NonPublic From 0c128f3aa86e64c804cad5fa7f7be7b8e8fc0158 Mon Sep 17 00:00:00 2001 From: Don Syme Date: Wed, 11 May 2022 23:37:11 +0100 Subject: [PATCH 05/13] merge fix --- src/Compiler/Driver/CompilerOptions.fs | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/Compiler/Driver/CompilerOptions.fs b/src/Compiler/Driver/CompilerOptions.fs index b107aa9d7e6..b57592d25af 100644 --- a/src/Compiler/Driver/CompilerOptions.fs +++ b/src/Compiler/Driver/CompilerOptions.fs @@ -848,13 +848,10 @@ let codeGenerationFlags isFsi (tcConfigB: TcConfigBuilder) = OptionSwitch (crossOptimizeSwitch tcConfigB), None, Some (FSComp.SR.optsCrossoptimize())) -<<<<<<< HEAD CompilerOption ("reflectionfree", tagNone, OptionUnit (fun () -> tcConfigB.useReflectionFreeCodeGen <- true), None, Some (FSComp.SR.optsReflectionFree())) -======= ->>>>>>> e063dd2a7005faf953619ab4f232d1e3606c7ed2 ] if isFsi then debug @ codegen else debug @ embed @ codegen From a4ca7f0aec124388d6cd898d1192ecc12a4a3527 Mon Sep 17 00:00:00 2001 From: Don Syme Date: Wed, 11 May 2022 23:59:04 +0100 Subject: [PATCH 06/13] fix merge --- src/Compiler/CodeGen/IlxGen.fs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Compiler/CodeGen/IlxGen.fs b/src/Compiler/CodeGen/IlxGen.fs index 25acb693aa7..aee365bb716 100644 --- a/src/Compiler/CodeGen/IlxGen.fs +++ b/src/Compiler/CodeGen/IlxGen.fs @@ -7808,7 +7808,7 @@ and GenToStringMethod cenv eenv ilThisTy m = /// Generate a ToString/get_Message method that calls 'sprintf "%A"' and GenPrintingMethod cenv eenv methName ilThisTy m = let g = cenv.g - [ if not cenv.opts.useReflectionFreeCodeGen then + [ if not g.useReflectionFreeCodeGen then match (eenv.valsInScope.TryFind g.sprintf_vref.Deref, eenv.valsInScope.TryFind g.new_format_vref.Deref) with | Some(Lazy(Method(_, _, sprintfMethSpec, _, _, _, _, _, _, _, _, _))), Some(Lazy(Method(_, _, newFormatMethSpec, _, _, _, _, _, _, _, _, _))) -> From d46d7f3ba9e82566127beb238bfdb019a33ab4ab Mon Sep 17 00:00:00 2001 From: Don Syme Date: Mon, 11 Jul 2022 23:16:44 +0100 Subject: [PATCH 07/13] merge main --- src/Compiler/CodeGen/IlxGen.fs | 128 ++++++++++++------------- src/Compiler/Driver/CompilerOptions.fs | 21 ++-- 2 files changed, 71 insertions(+), 78 deletions(-) diff --git a/src/Compiler/CodeGen/IlxGen.fs b/src/Compiler/CodeGen/IlxGen.fs index 17eecfc2639..d385c1c1d76 100644 --- a/src/Compiler/CodeGen/IlxGen.fs +++ b/src/Compiler/CodeGen/IlxGen.fs @@ -10323,65 +10323,67 @@ and GenToStringMethod cenv eenv ilThisTy m = /// Generate a ToString/get_Message method that calls 'sprintf "%A"' and GenPrintingMethod cenv eenv methName ilThisTy m = let g = cenv.g - [ if not g.useReflectionFreeCodeGen then - match (eenv.valsInScope.TryFind g.sprintf_vref.Deref, eenv.valsInScope.TryFind g.new_format_vref.Deref) with - | Some (Lazy (Method (_, _, sprintfMethSpec, _, _, _, _, _, _, _, _, _))), - Some (Lazy (Method (_, _, newFormatMethSpec, _, _, _, _, _, _, _, _, _))) -> - // The type returned by the 'sprintf' call - let funcTy = EraseClosures.mkILFuncTy cenv.ilxPubCloEnv ilThisTy g.ilg.typ_String - - // Give the instantiation of the printf format object, i.e. a Format`5 object compatible with StringFormat - let newFormatMethSpec = - mkILMethSpec ( - newFormatMethSpec.MethodRef, - AsObject, - [ // 'T -> string' - funcTy - // rest follow from 'StringFormat' - GenUnitTy cenv eenv m - g.ilg.typ_String - g.ilg.typ_String - ilThisTy - ], - [] - ) - // Instantiate with our own type - let sprintfMethSpec = - mkILMethSpec (sprintfMethSpec.MethodRef, AsObject, [], [ funcTy ]) + [ + if not g.useReflectionFreeCodeGen then + match (eenv.valsInScope.TryFind g.sprintf_vref.Deref, eenv.valsInScope.TryFind g.new_format_vref.Deref) with + | Some (Lazy (Method (_, _, sprintfMethSpec, _, _, _, _, _, _, _, _, _))), + Some (Lazy (Method (_, _, newFormatMethSpec, _, _, _, _, _, _, _, _, _))) -> + // The type returned by the 'sprintf' call + let funcTy = EraseClosures.mkILFuncTy cenv.ilxPubCloEnv ilThisTy g.ilg.typ_String + + // Give the instantiation of the printf format object, i.e. a Format`5 object compatible with StringFormat + let newFormatMethSpec = + mkILMethSpec ( + newFormatMethSpec.MethodRef, + AsObject, + [ // 'T -> string' + funcTy + // rest follow from 'StringFormat' + GenUnitTy cenv eenv m + g.ilg.typ_String + g.ilg.typ_String + ilThisTy + ], + [] + ) - // Here's the body of the method. Call printf, then invoke the function it returns - let callInstrs = - EraseClosures.mkCallFunc - cenv.ilxPubCloEnv - (fun _ -> 0us) - eenv.tyenv.Count - Normalcall - (Apps_app(ilThisTy, Apps_done g.ilg.typ_String)) - - let ilInstrs = - [ // load the hardwired format string - I_ldstr "%+A" - // make the printf format object - mkNormalNewobj newFormatMethSpec - // call sprintf - mkNormalCall sprintfMethSpec - // call the function returned by sprintf - mkLdarg0 - if ilThisTy.Boxity = ILBoxity.AsValue then - mkNormalLdobj ilThisTy - yield! callInstrs - ] + // Instantiate with our own type + let sprintfMethSpec = + mkILMethSpec (sprintfMethSpec.MethodRef, AsObject, [], [ funcTy ]) + + // Here's the body of the method. Call printf, then invoke the function it returns + let callInstrs = + EraseClosures.mkCallFunc + cenv.ilxPubCloEnv + (fun _ -> 0us) + eenv.tyenv.Count + Normalcall + (Apps_app(ilThisTy, Apps_done g.ilg.typ_String)) + + let ilInstrs = + [ // load the hardwired format string + I_ldstr "%+A" + // make the printf format object + mkNormalNewobj newFormatMethSpec + // call sprintf + mkNormalCall sprintfMethSpec + // call the function returned by sprintf + mkLdarg0 + if ilThisTy.Boxity = ILBoxity.AsValue then + mkNormalLdobj ilThisTy + yield! callInstrs + ] - let ilMethodBody = - mkMethodBody (true, [], 2, nonBranchingInstrsToCode ilInstrs, None, eenv.imports) + let ilMethodBody = + mkMethodBody (true, [], 2, nonBranchingInstrsToCode ilInstrs, None, eenv.imports) - let mdef = - mkILNonGenericVirtualMethod (methName, ILMemberAccess.Public, [], mkILReturn g.ilg.typ_String, ilMethodBody) + let mdef = + mkILNonGenericVirtualMethod (methName, ILMemberAccess.Public, [], mkILReturn g.ilg.typ_String, ilMethodBody) - let mdef = mdef.With(customAttrs = mkILCustomAttrs [ g.CompilerGeneratedAttribute ]) - yield mdef - | _ -> () + let mdef = mdef.With(customAttrs = mkILCustomAttrs [ g.CompilerGeneratedAttribute ]) + yield mdef + | _ -> () ] and GenTypeDef cenv mgbuf lazyInitInfo eenv m (tycon: Tycon) = @@ -10523,6 +10525,8 @@ and GenTypeDef cenv mgbuf lazyInitInfo eenv m (tycon: Tycon) = let tyconRepr = tycon.TypeReprInfo + let reprAccess = ComputeMemberAccess hiddenRepr + // DebugDisplayAttribute gets copied to the subtypes generated as part of DU compilation let debugDisplayAttrs, normalAttrs = tycon.Attribs @@ -10533,7 +10537,10 @@ and GenTypeDef cenv mgbuf lazyInitInfo eenv m (tycon: Tycon) = |> List.partition (fun a -> IsSecurityAttribute g cenv.amap cenv.casApplied a m) let generateDebugDisplayAttribute = - not g.compilingFSharpCore && tycon.IsUnionTycon && isNil debugDisplayAttrs + not g.useReflectionFreeCodeGen + && not g.compilingFSharpCore + && tycon.IsUnionTycon + && isNil debugDisplayAttrs let generateDebugProxies = not (tyconRefEq g tcref g.unit_tcr_canon) @@ -10564,17 +10571,6 @@ and GenTypeDef cenv mgbuf lazyInitInfo eenv m (tycon: Tycon) = yield! ilDebugDisplayAttributes ] - let reprAccess = ComputeMemberAccess hiddenRepr - - // DebugDisplayAttribute gets copied to the subtypes generated as part of DU compilation - let debugDisplayAttrs, normalAttrs = tycon.Attribs |> List.partition (IsMatchingFSharpAttribute g g.attrib_DebuggerDisplayAttribute) - let securityAttrs, normalAttrs = normalAttrs |> List.partition (fun a -> IsSecurityAttribute g cenv.amap cenv.casApplied a m) - let generateDebugDisplayAttribute = - not g.useReflectionFreeCodeGen && - not g.compilingFSharpCore && - tycon.IsUnionTycon && - isNil debugDisplayAttrs - let ilTypeDefKind = match tyconRepr with | TFSharpObjectRepr o -> diff --git a/src/Compiler/Driver/CompilerOptions.fs b/src/Compiler/Driver/CompilerOptions.fs index 7d5d7f92922..37d5a2172eb 100644 --- a/src/Compiler/Driver/CompilerOptions.fs +++ b/src/Compiler/Driver/CompilerOptions.fs @@ -1070,18 +1070,15 @@ let codeGenerationFlags isFsi (tcConfigB: TcConfigBuilder) = CompilerOption("pathmap", tagPathMap, OptionStringList(AddPathMapping tcConfigB), None, Some(FSComp.SR.optsPathMap ())) - CompilerOption( - "crossoptimize", - tagNone, - OptionSwitch(crossOptimizeSwitch tcConfigB), - None, - Some(FSComp.SR.optsCrossoptimize ()) - ) - - CompilerOption - ("reflectionfree", tagNone, - OptionUnit (fun () -> tcConfigB.useReflectionFreeCodeGen <- true), None, - Some (FSComp.SR.optsReflectionFree())) + CompilerOption + ("crossoptimize", tagNone, OptionSwitch(crossOptimizeSwitch tcConfigB), None, Some(FSComp.SR.optsCrossoptimize ())) + + CompilerOption + ("reflectionfree", + tagNone, + OptionUnit(fun () -> tcConfigB.useReflectionFreeCodeGen <- true), + None, + Some(FSComp.SR.optsReflectionFree ())) ] if isFsi then debug @ codegen else debug @ embed @ codegen From 14ad55694375f905d852bf4b33cbe14de473b47a Mon Sep 17 00:00:00 2001 From: Don Syme Date: Mon, 11 Jul 2022 23:18:59 +0100 Subject: [PATCH 08/13] merge main --- src/Compiler/Driver/CompilerOptions.fs | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/src/Compiler/Driver/CompilerOptions.fs b/src/Compiler/Driver/CompilerOptions.fs index 37d5a2172eb..20b3ca606f9 100644 --- a/src/Compiler/Driver/CompilerOptions.fs +++ b/src/Compiler/Driver/CompilerOptions.fs @@ -1070,15 +1070,21 @@ let codeGenerationFlags isFsi (tcConfigB: TcConfigBuilder) = CompilerOption("pathmap", tagPathMap, OptionStringList(AddPathMapping tcConfigB), None, Some(FSComp.SR.optsPathMap ())) - CompilerOption - ("crossoptimize", tagNone, OptionSwitch(crossOptimizeSwitch tcConfigB), None, Some(FSComp.SR.optsCrossoptimize ())) - - CompilerOption - ("reflectionfree", - tagNone, - OptionUnit(fun () -> tcConfigB.useReflectionFreeCodeGen <- true), - None, - Some(FSComp.SR.optsReflectionFree ())) + CompilerOption( + "crossoptimize", + tagNone, + OptionSwitch(crossOptimizeSwitch tcConfigB), + None, + Some(FSComp.SR.optsCrossoptimize ()) + ) + + CompilerOption( + "reflectionfree", + tagNone, + OptionUnit(fun () -> tcConfigB.useReflectionFreeCodeGen <- true), + None, + Some(FSComp.SR.optsReflectionFree ()) + ) ] if isFsi then debug @ codegen else debug @ embed @ codegen From 2ad5a78096bb48185cb33c9f20a435c1ad61dcf2 Mon Sep 17 00:00:00 2001 From: Petr Pokorny Date: Mon, 8 Aug 2022 15:22:50 +0200 Subject: [PATCH 09/13] Merge fix --- src/Compiler/xlf/FSComp.txt.cs.xlf | 10 ++++++++++ src/Compiler/xlf/FSComp.txt.de.xlf | 10 ++++++++++ src/Compiler/xlf/FSComp.txt.es.xlf | 10 ++++++++++ src/Compiler/xlf/FSComp.txt.fr.xlf | 10 ++++++++++ src/Compiler/xlf/FSComp.txt.it.xlf | 10 ++++++++++ src/Compiler/xlf/FSComp.txt.ja.xlf | 10 ++++++++++ src/Compiler/xlf/FSComp.txt.ko.xlf | 10 ++++++++++ src/Compiler/xlf/FSComp.txt.pl.xlf | 10 ++++++++++ src/Compiler/xlf/FSComp.txt.pt-BR.xlf | 10 ++++++++++ src/Compiler/xlf/FSComp.txt.ru.xlf | 10 ++++++++++ src/Compiler/xlf/FSComp.txt.tr.xlf | 10 ++++++++++ src/Compiler/xlf/FSComp.txt.zh-Hans.xlf | 10 ++++++++++ src/Compiler/xlf/FSComp.txt.zh-Hant.xlf | 10 ++++++++++ src/FSharp.Build/FSharp.Build.fsproj | 1 - 14 files changed, 130 insertions(+), 1 deletion(-) diff --git a/src/Compiler/xlf/FSComp.txt.cs.xlf b/src/Compiler/xlf/FSComp.txt.cs.xlf index 8052182cecb..21d8999165c 100644 --- a/src/Compiler/xlf/FSComp.txt.cs.xlf +++ b/src/Compiler/xlf/FSComp.txt.cs.xlf @@ -317,6 +317,11 @@ Interpolované řetězce, které se používají jako typ IFormattable nebo FormattableString, nemůžou používat specifikátory %. Použít je možné jen interpolované výrazy ve stylu .NET, třeba {{expr}}, {{expr,3}} nebo {{expr:N5}}. + + The '%A' format specifier may not be used in an assembly being compiled with option '--reflectionfree'. This construct implicitly uses reflection. + The '%A' format specifier may not be used in an assembly being compiled with option '--reflectionfree'. This construct implicitly uses reflection. + + - {0} – {0} @@ -482,6 +487,11 @@ Produce a reference assembly with the specified file path. + + Disable implicit generation of constructs using reflection + Disable implicit generation of constructs using reflection + + Supported language versions: Podporované jazykové verze: diff --git a/src/Compiler/xlf/FSComp.txt.de.xlf b/src/Compiler/xlf/FSComp.txt.de.xlf index 3d3dc8c2604..6942378fe46 100644 --- a/src/Compiler/xlf/FSComp.txt.de.xlf +++ b/src/Compiler/xlf/FSComp.txt.de.xlf @@ -317,6 +317,11 @@ Interpolierte Zeichenfolgen, die als Typ "IFormattable" oder "FormattableString" verwendet werden, dürfen keine Spezifizierer vom Typ "%" verwenden. Es dürfen nur Interpolanten im .NET-Format wie "{{expr}}", "{{expr,3}}" oder "{{expr:N5}}" verwendet werden. + + The '%A' format specifier may not be used in an assembly being compiled with option '--reflectionfree'. This construct implicitly uses reflection. + The '%A' format specifier may not be used in an assembly being compiled with option '--reflectionfree'. This construct implicitly uses reflection. + + - {0} - {0} @@ -482,6 +487,11 @@ Produce a reference assembly with the specified file path. + + Disable implicit generation of constructs using reflection + Disable implicit generation of constructs using reflection + + Supported language versions: Unterstützte Sprachversionen: diff --git a/src/Compiler/xlf/FSComp.txt.es.xlf b/src/Compiler/xlf/FSComp.txt.es.xlf index ba19316436e..51f3f3b80f6 100644 --- a/src/Compiler/xlf/FSComp.txt.es.xlf +++ b/src/Compiler/xlf/FSComp.txt.es.xlf @@ -317,6 +317,11 @@ Las cadenas interpoladas que se usan como tipo IFormattable o FormattableString no pueden usar los especificadores "%"; solo pueden utilizar operandos de interpolación de estilo .NET, como "{{expr}}", "{{expr,3}}" o "{{expr:N5}}". + + The '%A' format specifier may not be used in an assembly being compiled with option '--reflectionfree'. This construct implicitly uses reflection. + The '%A' format specifier may not be used in an assembly being compiled with option '--reflectionfree'. This construct implicitly uses reflection. + + - {0} - {0} @@ -482,6 +487,11 @@ Produce a reference assembly with the specified file path. + + Disable implicit generation of constructs using reflection + Disable implicit generation of constructs using reflection + + Supported language versions: Versiones de lenguaje admitidas: diff --git a/src/Compiler/xlf/FSComp.txt.fr.xlf b/src/Compiler/xlf/FSComp.txt.fr.xlf index 4ed2c0da2a9..db7ab13eae9 100644 --- a/src/Compiler/xlf/FSComp.txt.fr.xlf +++ b/src/Compiler/xlf/FSComp.txt.fr.xlf @@ -317,6 +317,11 @@ Les chaînes interpolées utilisées en tant que type IFormattable ou FormattableString ne peuvent pas utiliser les spécificateurs '%'. Seuls les spécificateurs de style .NET tels que '{{expr}}', '{{expr,3}}' et '{{expr:N5}}' peuvent être utilisés. + + The '%A' format specifier may not be used in an assembly being compiled with option '--reflectionfree'. This construct implicitly uses reflection. + The '%A' format specifier may not be used in an assembly being compiled with option '--reflectionfree'. This construct implicitly uses reflection. + + - {0} - {0} @@ -482,6 +487,11 @@ Produce a reference assembly with the specified file path. + + Disable implicit generation of constructs using reflection + Disable implicit generation of constructs using reflection + + Supported language versions: Versions linguistiques prises en charge : diff --git a/src/Compiler/xlf/FSComp.txt.it.xlf b/src/Compiler/xlf/FSComp.txt.it.xlf index 01af391840a..6b53a37aa0c 100644 --- a/src/Compiler/xlf/FSComp.txt.it.xlf +++ b/src/Compiler/xlf/FSComp.txt.it.xlf @@ -317,6 +317,11 @@ Nelle stringhe interpolate usate come tipo IFormattable o FormattableString non è possibile usare gli identificatori '%', ma è possibile usare solo interpolanti di tipo .NET, come '{{expr}}', '{{expr,3}}' o '{{expr:N5}}'. + + The '%A' format specifier may not be used in an assembly being compiled with option '--reflectionfree'. This construct implicitly uses reflection. + The '%A' format specifier may not be used in an assembly being compiled with option '--reflectionfree'. This construct implicitly uses reflection. + + - {0} - {0} @@ -482,6 +487,11 @@ Produce a reference assembly with the specified file path. + + Disable implicit generation of constructs using reflection + Disable implicit generation of constructs using reflection + + Supported language versions: Versioni del linguaggio supportate: diff --git a/src/Compiler/xlf/FSComp.txt.ja.xlf b/src/Compiler/xlf/FSComp.txt.ja.xlf index 54fbab4b514..c378f4445ab 100644 --- a/src/Compiler/xlf/FSComp.txt.ja.xlf +++ b/src/Compiler/xlf/FSComp.txt.ja.xlf @@ -317,6 +317,11 @@ IFormattable 型または FormattableString 型として使用される補間された文字列では、'%' 指定子を使用できません。'{{expr}}'、'{{expr,3}}'、'{{expr:N5}}' などの .NET 形式の補間のみ使用できます。 + + The '%A' format specifier may not be used in an assembly being compiled with option '--reflectionfree'. This construct implicitly uses reflection. + The '%A' format specifier may not be used in an assembly being compiled with option '--reflectionfree'. This construct implicitly uses reflection. + + - {0} - {0} @@ -482,6 +487,11 @@ Produce a reference assembly with the specified file path. + + Disable implicit generation of constructs using reflection + Disable implicit generation of constructs using reflection + + Supported language versions: サポートされる言語バージョン: diff --git a/src/Compiler/xlf/FSComp.txt.ko.xlf b/src/Compiler/xlf/FSComp.txt.ko.xlf index 7ed437b2763..a20578b3c22 100644 --- a/src/Compiler/xlf/FSComp.txt.ko.xlf +++ b/src/Compiler/xlf/FSComp.txt.ko.xlf @@ -317,6 +317,11 @@ 형식 IFormattable 또는 형식 FormattableString으로 사용된 보간 문자열은 '%' 지정자를 사용할 수 없으며 '{{expr}}', '{{expr,3}}' 또는 '{{expr:N5}}' 등의 .NET 스타일 인터폴란드를 사용할 수 있습니다. + + The '%A' format specifier may not be used in an assembly being compiled with option '--reflectionfree'. This construct implicitly uses reflection. + The '%A' format specifier may not be used in an assembly being compiled with option '--reflectionfree'. This construct implicitly uses reflection. + + - {0} - {0} @@ -482,6 +487,11 @@ Produce a reference assembly with the specified file path. + + Disable implicit generation of constructs using reflection + Disable implicit generation of constructs using reflection + + Supported language versions: 지원되는 언어 버전: diff --git a/src/Compiler/xlf/FSComp.txt.pl.xlf b/src/Compiler/xlf/FSComp.txt.pl.xlf index 19e01c91485..8a525741e3b 100644 --- a/src/Compiler/xlf/FSComp.txt.pl.xlf +++ b/src/Compiler/xlf/FSComp.txt.pl.xlf @@ -317,6 +317,11 @@ W interpolowanych ciągach używanych jako typ IFormattable lub FormattableString nie można używać specyfikatorów „%”. Można używać tylko operatorów interpolacji, takich jak „{{expr}}”, „{{expr,3}}” lub „{{expr:N5}}”. + + The '%A' format specifier may not be used in an assembly being compiled with option '--reflectionfree'. This construct implicitly uses reflection. + The '%A' format specifier may not be used in an assembly being compiled with option '--reflectionfree'. This construct implicitly uses reflection. + + - {0} — {0} @@ -482,6 +487,11 @@ Produce a reference assembly with the specified file path. + + Disable implicit generation of constructs using reflection + Disable implicit generation of constructs using reflection + + Supported language versions: Obsługiwane wersje językowe: diff --git a/src/Compiler/xlf/FSComp.txt.pt-BR.xlf b/src/Compiler/xlf/FSComp.txt.pt-BR.xlf index 615e716bd0f..4a7fb908b72 100644 --- a/src/Compiler/xlf/FSComp.txt.pt-BR.xlf +++ b/src/Compiler/xlf/FSComp.txt.pt-BR.xlf @@ -317,6 +317,11 @@ As cadeias de caracteres interpoladas usadas como tipo IFormattable ou tipo FormattableString não podem usar especificadores '%'. Apenas interpoladores de estilo .NET, como '{{expr}}', '{{expr,3}}' ou '{{expr:N5}}' podem ser usados. + + The '%A' format specifier may not be used in an assembly being compiled with option '--reflectionfree'. This construct implicitly uses reflection. + The '%A' format specifier may not be used in an assembly being compiled with option '--reflectionfree'. This construct implicitly uses reflection. + + - {0} - {0} @@ -482,6 +487,11 @@ Produce a reference assembly with the specified file path. + + Disable implicit generation of constructs using reflection + Disable implicit generation of constructs using reflection + + Supported language versions: Versões de linguagens com suporte: diff --git a/src/Compiler/xlf/FSComp.txt.ru.xlf b/src/Compiler/xlf/FSComp.txt.ru.xlf index f534378e99f..c32dae284f6 100644 --- a/src/Compiler/xlf/FSComp.txt.ru.xlf +++ b/src/Compiler/xlf/FSComp.txt.ru.xlf @@ -317,6 +317,11 @@ Интерполированные строки, используемые в качестве типа IFormattable или FormattableString, не могут использовать описатели "%". Можно использовать только описатели в стиле .NET, такие как "{{expr}}", "{{expr,3}}" или "{{expr:N5}}". + + The '%A' format specifier may not be used in an assembly being compiled with option '--reflectionfree'. This construct implicitly uses reflection. + The '%A' format specifier may not be used in an assembly being compiled with option '--reflectionfree'. This construct implicitly uses reflection. + + - {0} - {0} @@ -482,6 +487,11 @@ Produce a reference assembly with the specified file path. + + Disable implicit generation of constructs using reflection + Disable implicit generation of constructs using reflection + + Supported language versions: Поддерживаемые языковые версии: diff --git a/src/Compiler/xlf/FSComp.txt.tr.xlf b/src/Compiler/xlf/FSComp.txt.tr.xlf index 70335d09d9d..32c30a895d6 100644 --- a/src/Compiler/xlf/FSComp.txt.tr.xlf +++ b/src/Compiler/xlf/FSComp.txt.tr.xlf @@ -317,6 +317,11 @@ IFormattable veya FormattableString türü olarak kullanılan düz metin arasına kod eklenmiş dizeler '%' belirticilerini kullanamaz. Yalnızca '{{expr}}', '{{expr,3}}' veya '{{expr:N5}}' gibi .NET stili düz metin arasına kod ekleme işlemleri kullanılabilir. + + The '%A' format specifier may not be used in an assembly being compiled with option '--reflectionfree'. This construct implicitly uses reflection. + The '%A' format specifier may not be used in an assembly being compiled with option '--reflectionfree'. This construct implicitly uses reflection. + + - {0} - {0} @@ -482,6 +487,11 @@ Produce a reference assembly with the specified file path. + + Disable implicit generation of constructs using reflection + Disable implicit generation of constructs using reflection + + Supported language versions: Desteklenen dil sürümleri: diff --git a/src/Compiler/xlf/FSComp.txt.zh-Hans.xlf b/src/Compiler/xlf/FSComp.txt.zh-Hans.xlf index 4e7e55a375c..fd71187c35b 100644 --- a/src/Compiler/xlf/FSComp.txt.zh-Hans.xlf +++ b/src/Compiler/xlf/FSComp.txt.zh-Hans.xlf @@ -317,6 +317,11 @@ 作为类型 IFormattable 或类型 FormattableString 使用的内插字符串不能使用 "%" 说明符,只能使用 .NET 样式的插植,如 "{{expr}}"、"{{expr,3}}" 或 "{{expr:N5}}"。 + + The '%A' format specifier may not be used in an assembly being compiled with option '--reflectionfree'. This construct implicitly uses reflection. + The '%A' format specifier may not be used in an assembly being compiled with option '--reflectionfree'. This construct implicitly uses reflection. + + - {0} - {0} @@ -482,6 +487,11 @@ Produce a reference assembly with the specified file path. + + Disable implicit generation of constructs using reflection + Disable implicit generation of constructs using reflection + + Supported language versions: 支持的语言版本: diff --git a/src/Compiler/xlf/FSComp.txt.zh-Hant.xlf b/src/Compiler/xlf/FSComp.txt.zh-Hant.xlf index 965a9c231b6..64cf1551bd1 100644 --- a/src/Compiler/xlf/FSComp.txt.zh-Hant.xlf +++ b/src/Compiler/xlf/FSComp.txt.zh-Hant.xlf @@ -317,6 +317,11 @@ 用作類型 IFormattable 或類型 FormattableString 的插補字串不能使用 '%' 指定名稱,只能使用 .NET 樣式的插補值,例如 '{{expr}}'、'{{expr,3}}' 或 '{{expr:N5}}'。 + + The '%A' format specifier may not be used in an assembly being compiled with option '--reflectionfree'. This construct implicitly uses reflection. + The '%A' format specifier may not be used in an assembly being compiled with option '--reflectionfree'. This construct implicitly uses reflection. + + - {0} - {0} @@ -482,6 +487,11 @@ Produce a reference assembly with the specified file path. + + Disable implicit generation of constructs using reflection + Disable implicit generation of constructs using reflection + + Supported language versions: 支援的語言版本: diff --git a/src/FSharp.Build/FSharp.Build.fsproj b/src/FSharp.Build/FSharp.Build.fsproj index d1eafe2b15d..98bf38f572e 100644 --- a/src/FSharp.Build/FSharp.Build.fsproj +++ b/src/FSharp.Build/FSharp.Build.fsproj @@ -63,7 +63,6 @@ - From 09c356f3f5e477e03b4751480f72a301da813a74 Mon Sep 17 00:00:00 2001 From: Petr Pokorny Date: Tue, 9 Aug 2022 14:58:47 +0200 Subject: [PATCH 10/13] Added some tests --- .../CompilerOptions/fsc/reflectionfree.fs | 53 ++++++ .../InequalityComparison02.fs.il.bsl | 4 +- .../FSharp.Compiler.ComponentTests.fsproj | 1 + tests/FSharp.Test.Utilities/Compiler.fs | 8 +- tests/FSharp.Test.Utilities/ILChecker.fs | 158 +++++++++--------- 5 files changed, 142 insertions(+), 82 deletions(-) create mode 100644 tests/FSharp.Compiler.ComponentTests/CompilerOptions/fsc/reflectionfree.fs diff --git a/tests/FSharp.Compiler.ComponentTests/CompilerOptions/fsc/reflectionfree.fs b/tests/FSharp.Compiler.ComponentTests/CompilerOptions/fsc/reflectionfree.fs new file mode 100644 index 00000000000..2cffacf65cf --- /dev/null +++ b/tests/FSharp.Compiler.ComponentTests/CompilerOptions/fsc/reflectionfree.fs @@ -0,0 +1,53 @@ +// Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information. + +module FSharp.Compiler.ComponentTests.CompilerOptions.ReflectionFree + +open Xunit +open FSharp.Test.Compiler + +[] +let ``Gives error when %A is used`` () = + FSharp """printfn "Hello, %A" "world" """ + |> asExe + |> withOptions [ "--reflectionfree" ] + |> compile + |> shouldFail + |> withDiagnostics [ + Error 741, Line 1, Col 9, Line 1, Col 20, + "Unable to parse format string 'The '%A' format specifier may not be used in an assembly being compiled with option '--reflectionfree'. This construct implicitly uses reflection.'" ] + +let someCode = + FSharp """ + module Test + + type MyRecord = { A: int } + type MyUnion = A of int | B of string + type MyClass() = member val A = 42 + + let poke thing = $"Thing says: {thing}" + + [] + let doStuff _ = + poke { A = 3 } |> printfn "%s" + poke <| B "foo" |> printfn "%s" + poke <| MyClass() |> printfn "%s" + 0 + """ + +[] +let ``Records and DUs don't have generated ToString`` () = + someCode + |> withOptions [ "--reflectionfree" ] + |> compileExeAndRun + |> shouldSucceed + |> withStdOutContains "Thing says: Test+MyRecord" + |> withStdOutContains "Thing says: Test+MyUnion+B" + |> withStdOutContains "Thing says: Test+MyClass" + +[] +let ``No debug display attribute`` () = + someCode + |> withOptions [ "--reflectionfree" ] + |> compile + |> shouldSucceed + |> verifyILNotPresent [ "[runtime]System.Diagnostics.DebuggerDisplayAttribute" ] diff --git a/tests/FSharp.Compiler.ComponentTests/EmittedIL/InequalityComparison/InequalityComparison02.fs.il.bsl b/tests/FSharp.Compiler.ComponentTests/EmittedIL/InequalityComparison/InequalityComparison02.fs.il.bsl index 84f29017fb8..f25d03e0939 100644 --- a/tests/FSharp.Compiler.ComponentTests/EmittedIL/InequalityComparison/InequalityComparison02.fs.il.bsl +++ b/tests/FSharp.Compiler.ComponentTests/EmittedIL/InequalityComparison/InequalityComparison02.fs.il.bsl @@ -51,11 +51,11 @@ .class public abstract auto ansi sealed InequalityComparison02 extends [mscorlib]System.Object { - .custom instance void [FSharp.Core]Microsoft.FSharp.Core.CompilationMappingAttribute::.ctor(valuetype [FSharp.Core]Microsoft.FSharp.Core.SourceConstructFlags) = ( 01 00 07 00 00 00 00 00 ) + .custom instance void [FSharp.Core]Microsoft.FSharp.Core.CompilationMappingAttribute::.ctor(valuetype [FSharp.Core]Microsoft.FSharp.Core.SourceConstructFlags) = ( 01 00 07 00 00 00 00 00 ) .method public static bool f2(int32 x, int32 y) cil managed { - .custom instance void [FSharp.Core]Microsoft.FSharp.Core.CompilationArgumentCountsAttribute::.ctor(int32[]) = ( 01 00 02 00 00 00 01 00 00 00 01 00 00 00 00 00 ) + .custom instance void [FSharp.Core]Microsoft.FSharp.Core.CompilationArgumentCountsAttribute::.ctor(int32[]) = ( 01 00 02 00 00 00 01 00 00 00 01 00 00 00 00 00 ) // Code size 8 (0x8) .maxstack 8 IL_0000: ldarg.0 diff --git a/tests/FSharp.Compiler.ComponentTests/FSharp.Compiler.ComponentTests.fsproj b/tests/FSharp.Compiler.ComponentTests/FSharp.Compiler.ComponentTests.fsproj index 9c6e84e4a0c..c59c8545bc7 100644 --- a/tests/FSharp.Compiler.ComponentTests/FSharp.Compiler.ComponentTests.fsproj +++ b/tests/FSharp.Compiler.ComponentTests/FSharp.Compiler.ComponentTests.fsproj @@ -175,6 +175,7 @@ + diff --git a/tests/FSharp.Test.Utilities/Compiler.fs b/tests/FSharp.Test.Utilities/Compiler.fs index 574d7dae1ee..d1f2b2864b2 100644 --- a/tests/FSharp.Test.Utilities/Compiler.fs +++ b/tests/FSharp.Test.Utilities/Compiler.fs @@ -863,14 +863,18 @@ module rec Compiler = cUnit - let verifyIL (il: string list) (result: CompilationResult) : unit = + let private doILCheck func (il: string list) result = match result with | CompilationResult.Success s -> match s.OutputPath with | None -> failwith "Operation didn't produce any output!" - | Some p -> ILChecker.checkIL p il + | Some p -> func p il | CompilationResult.Failure _ -> failwith "Result should be \"Success\" in order to get IL." + let verifyIL = doILCheck ILChecker.checkIL + + let verifyILNotPresent = doILCheck ILChecker.checkILNotPresent + let verifyILBinary (il: string list) (dll: string)= ILChecker.checkIL dll il let private verifyFSILBaseline (baseline: Baseline option) (result: CompilationOutput) : unit = diff --git a/tests/FSharp.Test.Utilities/ILChecker.fs b/tests/FSharp.Test.Utilities/ILChecker.fs index a0b1ddefdfe..546deb0a2e6 100644 --- a/tests/FSharp.Test.Utilities/ILChecker.fs +++ b/tests/FSharp.Test.Utilities/ILChecker.fs @@ -4,7 +4,6 @@ namespace FSharp.Test open System open System.IO -open System.Diagnostics open System.Text.RegularExpressions open NUnit.Framework @@ -30,14 +29,48 @@ module ILChecker = (fun me -> String.Empty) ) - let private checkILPrim ildasmArgs dllFilePath expectedIL = + let private normalizeILText assemblyName (ilCode: string) = + let blockComments = @"/\*(.*?)\*/" + let lineComments = @"//(.*?)\r?\n" + let lineCommentsEof = @"//(.*?)$" + let strings = @"""((\\[^\n]|[^""\n])*)""" + let verbatimStrings = @"@(""[^""]*"")+" + let stripComments (text:string) = + Regex.Replace(text, + $"{blockComments}|{lineComments}|{lineCommentsEof}|{strings}|{verbatimStrings}", + (fun me -> + if (me.Value.StartsWith("/*") || me.Value.StartsWith("//")) then + if me.Value.StartsWith("//") then Environment.NewLine else String.Empty + else + me.Value), RegexOptions.Singleline) + |> filterSpecialComment + + let replace input (pattern, replacement: string) = Regex.Replace(input, pattern, replacement, RegexOptions.Singleline) + + let unifyRuntimeAssemblyName ilCode = + List.fold replace ilCode [ + "\[System\.Runtime\]|\[System\.Console\]|\[System\.Runtime\.Extensions\]|\[mscorlib\]|\[System\.Memory\]", "[runtime]" + "(\.assembly extern (System\.Runtime|System\.Console|System\.Runtime\.Extensions|mscorlib|System\.Memory)){1}([^\}]*)\}", ".assembly extern runtime { }" + "(\.assembly extern (FSharp.Core)){1}([^\}]*)\}", ".assembly extern FSharp.Core { }" ] + + let unifyImageBase ilCode = replace ilCode ("\.imagebase\s*0x\d*", ".imagebase {value}") + + let unifyingAssemblyNames (text: string) = + match assemblyName with + | Some name -> text.Replace(name, "assembly") + | None -> text + |> unifyRuntimeAssemblyName + |> unifyImageBase + + ilCode.Trim() |> stripComments |> unifyingAssemblyNames + + + let private generateIlFile dllFilePath ildasmArgs = let ilFilePath = Path.ChangeExtension(dllFilePath, ".il") - let mutable errorMsgOpt = None - let mutable actualIL = String.Empty let ildasmPath = config.ILDASM - let ildasmFullArgs = [ yield dllFilePath; yield sprintf "-out=%s" ilFilePath; yield! ildasmArgs ] + let ildasmFullArgs = [ dllFilePath; $"-out=%s{ilFilePath}"; yield! ildasmArgs ] let stdErr, exitCode = let ildasmCommandPath = Path.ChangeExtension(dllFilePath, ".ildasmCommandPath") @@ -45,100 +78,55 @@ module ILChecker = exec ildasmPath ildasmFullArgs if exitCode <> 0 then - failwith (sprintf "ILASM Expected exit code \"0\", got \"%d\"\nSTDERR: %s" exitCode stdErr) + failwith $"ILASM Expected exit code \"0\", got \"%d{exitCode}\"\nSTDERR: %s{stdErr}" if not (String.IsNullOrWhiteSpace stdErr) then - failwith (sprintf "ILASM Stderr is not empty:\n %s" stdErr) + failwith $"ILASM Stderr is not empty:\n %s{stdErr}" - let blockComments = @"/\*(.*?)\*/" - let lineComments = @"//(.*?)\r?\n" - let lineCommentsEof = @"//(.*?)$" - let strings = @"""((\\[^\n]|[^""\n])*)""" - let verbatimStrings = @"@(""[^""]*"")+" - let stripComments (text:string) = - System.Text.RegularExpressions.Regex.Replace(text, - blockComments + "|" + lineComments + "|" + lineCommentsEof + "|" + strings + "|" + verbatimStrings, - (fun me -> - if (me.Value.StartsWith("/*") || me.Value.StartsWith("//")) then - if me.Value.StartsWith("//") then Environment.NewLine else String.Empty - else - me.Value), System.Text.RegularExpressions.RegexOptions.Singleline) - |> filterSpecialComment + ilFilePath - let unifyRuntimeAssemblyName ilCode = - let pass1 = - Regex.Replace( - ilCode, - "\[System\.Runtime\]|\[System\.Console\]|\[System\.Runtime\.Extensions\]|\[mscorlib\]|\[System\.Memory\]","[runtime]", - RegexOptions.Singleline) - let pass2 = - Regex.Replace( - pass1, - "(\.assembly extern (System\.Runtime|System\.Console|System\.Runtime\.Extensions|mscorlib|System\.Memory)){1}([^\}]*)\}",".assembly extern runtime { }", - RegexOptions.Singleline) - let pass3 = - Regex.Replace( - pass2, - "(\.assembly extern (FSharp.Core)){1}([^\}]*)\}",".assembly extern FSharp.Core { }", - RegexOptions.Singleline) - pass3 - - let unifyImageBase ilCode = - Regex.Replace( - ilCode, - "\.imagebase\s*0x\d*",".imagebase {value}", - RegexOptions.Singleline) - - let unifyIlText (text:string) = - let unifyingAssemblyNames (text:string)= - let asmName = Path.GetFileNameWithoutExtension(dllFilePath) - text.Replace(asmName, "assembly") - |> unifyRuntimeAssemblyName - |> unifyImageBase - - text.Trim() |> stripComments |> unifyingAssemblyNames - - let raw = File.ReadAllText(ilFilePath) - let unifiedInputText = raw |> unifyIlText + let private generateIL (dllFilePath: string) = + let assemblyName = Some (Path.GetFileNameWithoutExtension dllFilePath) + generateIlFile dllFilePath >> File.ReadAllText >> normalizeILText assemblyName - expectedIL - |> List.map (fun (ilCode: string) -> ilCode.Trim()) - |> List.iter (fun (ilCode: string) -> - let expectedLines = - (ilCode |> unifyIlText).Split('\n') + let private compareIL assemblyName (actualIL: string) expectedIL = + + let mutable errorMsgOpt = None + + let prepareLines (s: string) = + s.Split('\n') |> Array.map(fun e -> e.Trim('\r')) - |> Array.skipWhile(fun s -> String.IsNullOrWhiteSpace(s)) + |> Array.skipWhile(String.IsNullOrWhiteSpace) |> Array.rev - |> Array.skipWhile(fun s -> String.IsNullOrWhiteSpace(s)) + |> Array.skipWhile(String.IsNullOrWhiteSpace) |> Array.rev + expectedIL + |> List.map (fun (ilCode: string) -> ilCode.Trim()) + |> List.iter (fun (ilCode: string) -> + let expectedLines = ilCode |> normalizeILText (Some assemblyName) |> prepareLines + if expectedLines.Length = 0 then errorMsgOpt <- Some("ExpectedLines length invalid: 0") else let startIndex = - let index = unifiedInputText.IndexOf(expectedLines[0].Trim()) + let index = actualIL.IndexOf(expectedLines[0].Trim()) if index > 0 then index else 0 - let actualLines = - unifiedInputText.Substring(startIndex).Split('\n') - |> Array.map(fun e -> e.Trim('\r')) - |> Array.skipWhile(fun s -> String.IsNullOrWhiteSpace(s)) - |> Array.rev - |> Array.skipWhile(fun s -> String.IsNullOrWhiteSpace(s)) - |> Array.rev + let actualLines = actualIL.Substring(startIndex) |> prepareLines let errors = ResizeArray() if actualLines.Length < expectedLines.Length then - let msg = sprintf "\nExpected at least %d lines but found only %d\n" expectedLines.Length actualLines.Length + let msg = $"\nExpected at least %d{expectedLines.Length} lines but found only %d{actualLines.Length}\n" errorMsgOpt <- Some(msg + "\nExpected:\n" + ilCode + "\n") else for i = 0 to expectedLines.Length - 1 do let expected = expectedLines[i].Trim() let actual = actualLines[i].Trim() if expected <> actual then - errors.Add(sprintf "\n==\nName: '%s'\n\nExpected:\t %s\nActual:\t\t %s\n==" actualLines[0] expected actual) + errors.Add $"\n==\nName: '%s{actualLines[0]}'\n\nExpected:\t %s{expected}\nActual:\t\t %s{actual}\n==" if errors.Count > 0 then let msg = String.concat "\n" errors + "\n\n\Expected:\n" + ilCode + "\n" @@ -148,21 +136,22 @@ module ILChecker = if expectedIL.Length = 0 then errorMsgOpt <- Some ("No Expected IL") - actualIL <- unifiedInputText - match errorMsgOpt with - | Some(msg) -> errorMsgOpt <- Some(msg + "\n\n\nEntire actual:\n" + unifiedInputText) + | Some(msg) -> errorMsgOpt <- Some(msg + "\n\n\nEntire actual:\n" + actualIL) | _ -> () match errorMsgOpt with | Some(errorMsg) -> (false, errorMsg, actualIL) | _ -> (true, String.Empty, String.Empty) + let private checkILPrim ildasmArgs dllFilePath = + let actualIL = generateIL dllFilePath ildasmArgs + compareIL (Path.GetFileNameWithoutExtension dllFilePath) actualIL + let private checkILAux ildasmArgs dllFilePath expectedIL = let (success, errorMsg, _) = checkILPrim ildasmArgs dllFilePath expectedIL if not success then Assert.Fail(errorMsg) - else () // This doesn't work because the '/linenum' is being ignored by // the version of ILDASM we are using, which we acquire from a nuget package @@ -178,7 +167,20 @@ module ILChecker = let verifyILAndReturnActual (dllFilePath: string) (expectedIL: string) = checkILPrim [] dllFilePath [expectedIL] + let checkILNotPresent dllFilePath unexpectedIL = + let actualIL = generateIL dllFilePath [] + if unexpectedIL = [] then + Assert.Fail $"No unexpected IL given. This is actual IL: \n{actualIL}" + let errors = + unexpectedIL + |> Seq.map (normalizeILText None) + |> Seq.filter actualIL.Contains + |> Seq.map (sprintf "Found in actual IL: '%s'") + |> String.concat "\n" + if errors <> "" then + Assert.Fail $"{errors}\n\n\nEntire actual:\n{actualIL}" + let reassembleIL ilFilePath dllFilePath = let ilasmPath = config.ILASM - let errors, _ = exec ilasmPath ([ sprintf "%s /output=%s /dll" ilFilePath dllFilePath ]) + let errors, _ = exec ilasmPath [ $"%s{ilFilePath} /output=%s{dllFilePath} /dll" ] errors From 4a1f2a907e5ebd55ef46205e7365fab9d54edc6d Mon Sep 17 00:00:00 2001 From: 0101 <0101@innit.cz> Date: Wed, 10 Aug 2022 12:32:45 +0200 Subject: [PATCH 11/13] Fixed capturing console outputs on NET FW --- tests/FSharp.Test.Utilities/CompilerAssert.fs | 63 ++++++++++--------- 1 file changed, 34 insertions(+), 29 deletions(-) diff --git a/tests/FSharp.Test.Utilities/CompilerAssert.fs b/tests/FSharp.Test.Utilities/CompilerAssert.fs index daab582a61b..a006215ddc3 100644 --- a/tests/FSharp.Test.Utilities/CompilerAssert.fs +++ b/tests/FSharp.Test.Utilities/CompilerAssert.fs @@ -254,6 +254,11 @@ module rec CompilerAssertHelpers = yield Array.empty |] + let executeAssemblyEntryPoint (asm: Assembly) = + let entryPoint = asm.EntryPoint + let args = mkDefaultArgs entryPoint + captureConsoleOutputs (fun () -> entryPoint.Invoke(Unchecked.defaultof, args) |> ignore) + #if NETCOREAPP let executeBuiltApp assembly deps = let ctxt = AssemblyLoadContext("ContextName", true) @@ -264,10 +269,7 @@ module rec CompilerAssertHelpers = |> Option.map ctxt.LoadFromAssemblyPath |> Option.defaultValue null) - let asm = ctxt.LoadFromAssemblyPath(assembly) - let entryPoint = asm.EntryPoint - let args = mkDefaultArgs entryPoint - (entryPoint.Invoke(Unchecked.defaultof, args)) |> ignore + ctxt.LoadFromAssemblyPath assembly |> executeAssemblyEntryPoint finally ctxt.Unload() #else @@ -281,10 +283,8 @@ module rec CompilerAssertHelpers = |> Option.bind (fun x -> if FileSystem.FileExistsShim x then Some x else None) |> Option.map Assembly.LoadFile |> Option.defaultValue null)) - let asm = Assembly.LoadFrom(assemblyPath) - let entryPoint = asm.EntryPoint - let args = mkDefaultArgs entryPoint - (entryPoint.Invoke(Unchecked.defaultof, args)) |> ignore + + Assembly.LoadFrom assemblyPath |> executeAssemblyEntryPoint let adSetup = let setup = new System.AppDomainSetup () @@ -297,7 +297,7 @@ module rec CompilerAssertHelpers = let worker = use _ = new AlreadyLoadedAppDomainResolver() (ad.CreateInstanceFromAndUnwrap(typeof.Assembly.CodeBase, typeof.FullName)) :?> Worker - worker.ExecuteTestCase assembly (deps |> Array.ofList) |>ignore + worker.ExecuteTestCase assembly (deps |> Array.ofList) #endif let defaultProjectOptions = @@ -515,34 +515,39 @@ module rec CompilerAssertHelpers = outputDirectory.Create() compileCompilationAux outputDirectory (ResizeArray()) ignoreWarnings cmpl - let executeBuiltAppAndReturnResult (outputFilePath: string) (deps: string list) : (int * string * string) = + let captureConsoleOutputs (func: unit -> unit) = let out = Console.Out let err = Console.Error let stdout = StringBuilder () let stderr = StringBuilder () - let outWriter = new StringWriter (stdout) - let errWriter = new StringWriter (stderr) - - let mutable exitCode = 0 + use outWriter = new StringWriter (stdout) + use errWriter = new StringWriter (stderr) - try + let succeeded = try - Console.SetOut(outWriter) - Console.SetError(errWriter) - (executeBuiltApp outputFilePath deps) |> ignore - with e -> - let errorMessage = if e.InnerException <> null then (e.InnerException.ToString()) else (e.ToString()) - stderr.Append (errorMessage) |> ignore - exitCode <- -1 - finally - Console.SetOut(out) - Console.SetError(err) - outWriter.Close() - errWriter.Close() + try + Console.SetOut outWriter + Console.SetError errWriter + func () + true + with e -> + let errorMessage = if e.InnerException <> null then e.InnerException.ToString() else e.ToString() + stderr.Append errorMessage |> ignore + false + finally + Console.SetOut out + Console.SetError err + outWriter.Close() + errWriter.Close() + + succeeded, stdout.ToString(), stderr.ToString() - (exitCode, stdout.ToString(), stderr.ToString()) + let executeBuiltAppAndReturnResult (outputFilePath: string) (deps: string list) : (int * string * string) = + let succeeded, stdout, stderr = executeBuiltApp outputFilePath deps + let exitCode = if succeeded then 0 else -1 + exitCode, stdout, stderr let executeBuiltAppNewProcessAndReturnResult (outputFilePath: string) : (int * string * string) = #if !NETCOREAPP @@ -672,7 +677,7 @@ Updated automatically, please check diffs in your pull request, changes must be Assert.Fail errors onOutput output else - executeBuiltApp outputFilePath deps) + executeBuiltApp outputFilePath deps |> ignore) static member ExecutionHasOutput(cmpl: Compilation, expectedOutput: string) = CompilerAssert.Execute(cmpl, newProcess = true, onOutput = (fun output -> Assert.AreEqual(expectedOutput, output, sprintf "'%s' = '%s'" expectedOutput output))) From e5a175f4c090a3a2641cf16e9dd546e974182555 Mon Sep 17 00:00:00 2001 From: Petr Pokorny Date: Wed, 10 Aug 2022 14:18:30 +0200 Subject: [PATCH 12/13] Fix tests --- tests/FSharp.Test.Utilities/CompilerAssert.fs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/FSharp.Test.Utilities/CompilerAssert.fs b/tests/FSharp.Test.Utilities/CompilerAssert.fs index a006215ddc3..b0027ef74ec 100644 --- a/tests/FSharp.Test.Utilities/CompilerAssert.fs +++ b/tests/FSharp.Test.Utilities/CompilerAssert.fs @@ -587,7 +587,7 @@ type CompilerAssert private () = if errors.Length > 0 then Assert.Fail (sprintf "Compile had warnings and/or errors: %A" errors) - executeBuiltApp outputExe [] + executeBuiltApp outputExe [] |> ignore ) static let compileLibraryAndVerifyILWithOptions options (source: SourceCodeFileKind) (f: ILVerifier -> unit) = From c2be2b3294cbd0dbbf2ea2306b90f76767978dfd Mon Sep 17 00:00:00 2001 From: Petr Pokorny Date: Wed, 10 Aug 2022 18:12:06 +0200 Subject: [PATCH 13/13] Fixed test --- tests/FSharp.Test.Utilities/CompilerAssert.fs | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/tests/FSharp.Test.Utilities/CompilerAssert.fs b/tests/FSharp.Test.Utilities/CompilerAssert.fs index b0027ef74ec..48bd3047cba 100644 --- a/tests/FSharp.Test.Utilities/CompilerAssert.fs +++ b/tests/FSharp.Test.Utilities/CompilerAssert.fs @@ -525,27 +525,27 @@ module rec CompilerAssertHelpers = use outWriter = new StringWriter (stdout) use errWriter = new StringWriter (stderr) - let succeeded = + let succeeded, exn = try try Console.SetOut outWriter Console.SetError errWriter func () - true + true, None with e -> let errorMessage = if e.InnerException <> null then e.InnerException.ToString() else e.ToString() stderr.Append errorMessage |> ignore - false + false, Some e finally Console.SetOut out Console.SetError err outWriter.Close() errWriter.Close() - succeeded, stdout.ToString(), stderr.ToString() + succeeded, stdout.ToString(), stderr.ToString(), exn let executeBuiltAppAndReturnResult (outputFilePath: string) (deps: string list) : (int * string * string) = - let succeeded, stdout, stderr = executeBuiltApp outputFilePath deps + let succeeded, stdout, stderr, _ = executeBuiltApp outputFilePath deps let exitCode = if succeeded then 0 else -1 exitCode, stdout, stderr @@ -677,7 +677,8 @@ Updated automatically, please check diffs in your pull request, changes must be Assert.Fail errors onOutput output else - executeBuiltApp outputFilePath deps |> ignore) + let _succeeded, _stdout, _stderr, exn = executeBuiltApp outputFilePath deps + exn |> Option.iter raise) static member ExecutionHasOutput(cmpl: Compilation, expectedOutput: string) = CompilerAssert.Execute(cmpl, newProcess = true, onOutput = (fun output -> Assert.AreEqual(expectedOutput, output, sprintf "'%s' = '%s'" expectedOutput output)))