From ebb6719fcdfe91a32ac1f663ce80f0ff9ba6d770 Mon Sep 17 00:00:00 2001 From: kerams Date: Tue, 22 Nov 2022 14:24:16 +0100 Subject: [PATCH 1/9] Allow simple arithmetic in number literals --- src/Compiler/TypedTree/TcGlobals.fs | 2 + src/Compiler/TypedTree/TypedTreeOps.fs | 109 +++++++++++++++++++------ 2 files changed, 87 insertions(+), 24 deletions(-) diff --git a/src/Compiler/TypedTree/TcGlobals.fs b/src/Compiler/TypedTree/TcGlobals.fs index 96dc59428e..7bef3ed71a 100755 --- a/src/Compiler/TypedTree/TcGlobals.fs +++ b/src/Compiler/TypedTree/TcGlobals.fs @@ -1481,6 +1481,8 @@ type TcGlobals( member val unchecked_unary_not_vref = ValRefForIntrinsic v_unchecked_unary_not_info member val unchecked_subtraction_vref = ValRefForIntrinsic v_unchecked_subtraction_info member val unchecked_multiply_vref = ValRefForIntrinsic v_unchecked_multiply_info + member val unchecked_division_vref = ValRefForIntrinsic v_unchecked_division_info + member val unchecked_modulus_vref = ValRefForIntrinsic v_unchecked_modulus_info member val unchecked_defaultof_vref = ValRefForIntrinsic v_unchecked_defaultof_info member val refcell_deref_vref = ValRefForIntrinsic v_refcell_deref_info member val refcell_assign_vref = ValRefForIntrinsic v_refcell_assign_info diff --git a/src/Compiler/TypedTree/TypedTreeOps.fs b/src/Compiler/TypedTree/TypedTreeOps.fs index 6fa70a5903..e7ebe25c26 100644 --- a/src/Compiler/TypedTree/TypedTreeOps.fs +++ b/src/Compiler/TypedTree/TypedTreeOps.fs @@ -3742,6 +3742,14 @@ let (|SpecificUnopExpr|_|) g vrefReqd expr = | UnopExpr g (vref, arg1) when valRefEq g vref vrefReqd -> Some arg1 | _ -> None +let (|SignedIntegerConstExpr|_|) expr = + match expr with + | Expr.Const (Const.Int32 _, _, _) + | Expr.Const (Const.SByte _, _, _) + | Expr.Const (Const.Int16 _, _, _) + | Expr.Const (Const.Int64 _, _, _) -> Some () + | _ -> None + let (|SpecificBinopExpr|_|) g vrefReqd expr = match expr with | BinopExpr g (vref, arg1, arg2) when valRefEq g vref vrefReqd -> Some (arg1, arg2) @@ -9647,12 +9655,44 @@ let IsSimpleSyntacticConstantExpr g inputExpr = checkExpr vrefs e checkExpr Set.empty inputExpr - -let EvalArithBinOp (opInt8, opInt16, opInt32, opInt64, opUInt8, opUInt16, opUInt32, opUInt64) (arg1: Expr) (arg2: Expr) = - // At compile-time we check arithmetic + +let EvalArithShiftOp (opInt8, opInt16, opInt32, opInt64, opUInt8, opUInt16, opUInt32, opUInt64) (arg1: Expr) (arg2: Expr) = + // At compile-time we check arithmetic + let m = unionRanges arg1.Range arg2.Range + try + match arg1, arg2 with + | Expr.Const (Const.Int32 x1, _, ty), Expr.Const (Const.Int32 shift, _, _) -> Expr.Const (Const.Int32 (opInt32 x1 shift), m, ty) + | Expr.Const (Const.SByte x1, _, ty), Expr.Const (Const.Int32 shift, _, _) -> Expr.Const (Const.SByte (opInt8 x1 shift), m, ty) + | Expr.Const (Const.Int16 x1, _, ty), Expr.Const (Const.Int32 shift, _, _) -> Expr.Const (Const.Int16 (opInt16 x1 shift), m, ty) + | Expr.Const (Const.Int64 x1, _, ty), Expr.Const (Const.Int32 shift, _, _) -> Expr.Const (Const.Int64 (opInt64 x1 shift), m, ty) + | Expr.Const (Const.Byte x1, _, ty), Expr.Const (Const.Int32 shift, _, _) -> Expr.Const (Const.Byte (opUInt8 x1 shift), m, ty) + | Expr.Const (Const.UInt16 x1, _, ty), Expr.Const (Const.Int32 shift, _, _) -> Expr.Const (Const.UInt16 (opUInt16 x1 shift), m, ty) + | Expr.Const (Const.UInt32 x1, _, ty), Expr.Const (Const.Int32 shift, _, _) -> Expr.Const (Const.UInt32 (opUInt32 x1 shift), m, ty) + | Expr.Const (Const.UInt64 x1, _, ty), Expr.Const (Const.Int32 shift, _, _) -> Expr.Const (Const.UInt64 (opUInt64 x1 shift), m, ty) + | _ -> error (Error ( FSComp.SR.tastNotAConstantExpression(), m)) + with :? System.OverflowException -> error (Error ( FSComp.SR.tastConstantExpressionOverflow(), m)) + +let EvalArithUnOp (opInt8, opInt16, opInt32, opInt64, opUInt8, opUInt16, opUInt32, opUInt64) (arg1: Expr) = + // At compile-time we check arithmetic + let m = arg1.Range + try + match arg1 with + | Expr.Const (Const.Int32 x1, _, ty) -> Expr.Const (Const.Int32 (opInt32 x1), m, ty) + | Expr.Const (Const.SByte x1, _, ty) -> Expr.Const (Const.SByte (opInt8 x1), m, ty) + | Expr.Const (Const.Int16 x1, _, ty) -> Expr.Const (Const.Int16 (opInt16 x1), m, ty) + | Expr.Const (Const.Int64 x1, _, ty) -> Expr.Const (Const.Int64 (opInt64 x1), m, ty) + | Expr.Const (Const.Byte x1, _, ty) -> Expr.Const (Const.Byte (opUInt8 x1), m, ty) + | Expr.Const (Const.UInt16 x1, _, ty) -> Expr.Const (Const.UInt16 (opUInt16 x1), m, ty) + | Expr.Const (Const.UInt32 x1, _, ty) -> Expr.Const (Const.UInt32 (opUInt32 x1), m, ty) + | Expr.Const (Const.UInt64 x1, _, ty) -> Expr.Const (Const.UInt64 (opUInt64 x1), m, ty) + | _ -> error (Error ( FSComp.SR.tastNotAConstantExpression(), m)) + with :? System.OverflowException -> error (Error ( FSComp.SR.tastConstantExpressionOverflow(), m)) + +let EvalArithBinOp (opInt8, opInt16, opInt32, opInt64, opUInt8, opUInt16, opUInt32, opUInt64) (arg1: Expr) (arg2: Expr) = + // At compile-time we check arithmetic let m = unionRanges arg1.Range arg2.Range - try - match arg1, arg2 with + try + match arg1, arg2 with | Expr.Const (Const.Int32 x1, _, ty), Expr.Const (Const.Int32 x2, _, _) -> Expr.Const (Const.Int32 (opInt32 x1 x2), m, ty) | Expr.Const (Const.SByte x1, _, ty), Expr.Const (Const.SByte x2, _, _) -> Expr.Const (Const.SByte (opInt8 x1 x2), m, ty) | Expr.Const (Const.Int16 x1, _, ty), Expr.Const (Const.Int16 x2, _, _) -> Expr.Const (Const.Int16 (opInt16 x1 x2), m, ty) @@ -9700,29 +9740,50 @@ let rec EvalAttribArgExpr g x = // Detect bitwise or of attribute flags | AttribBitwiseOrExpr g (arg1, arg2) -> EvalArithBinOp ((|||), (|||), (|||), (|||), (|||), (|||), (|||), (|||)) (EvalAttribArgExpr g arg1) (EvalAttribArgExpr g arg2) - | SpecificBinopExpr g g.unchecked_addition_vref (arg1, arg2) -> - // At compile-time we check arithmetic - let v1, v2 = EvalAttribArgExpr g arg1, EvalAttribArgExpr g arg2 - match v1, v2 with - | Expr.Const (Const.String x1, m, ty), Expr.Const (Const.String x2, _, _) -> Expr.Const (Const.String (x1 + x2), m, ty) - | _ -> -#if ALLOW_ARITHMETIC_OPS_IN_LITERAL_EXPRESSIONS_AND_ATTRIBUTE_ARGS - EvalArithBinOp (Checked.(+), Checked.(+), Checked.(+), Checked.(+), Checked.(+), Checked.(+), Checked.(+), Checked.(+)) g v1 v2 -#else - errorR (Error ( FSComp.SR.tastNotAConstantExpression(), x.Range)) - x -#endif -#if ALLOW_ARITHMETIC_OPS_IN_LITERAL_EXPRESSIONS_AND_ATTRIBUTE_ARGS - | SpecificBinopExpr g g.unchecked_subtraction_vref (arg1, arg2) -> - EvalArithBinOp (Checked.(-), Checked.(-), Checked.(-), Checked.(-), Checked.(-), Checked.(-), Checked.(-), Checked.(-)) g (EvalAttribArgExpr g arg1) (EvalAttribArgExpr g arg2) - | SpecificBinopExpr g g.unchecked_multiply_vref (arg1, arg2) -> - EvalArithBinOp (Checked.(*), Checked.(*), Checked.(*), Checked.(*), Checked.(*), Checked.(*), Checked.(*), Checked.(*)) g (EvalAttribArgExpr g arg1) (EvalAttribArgExpr g arg2) -#endif + | SpecificBinopExpr g g.unchecked_addition_vref (arg1, arg2) -> + // At compile-time we check arithmetic + let v1, v2 = EvalAttribArgExpr g arg1, EvalAttribArgExpr g arg2 + match v1, v2 with + | Expr.Const (Const.String x1, m, ty), Expr.Const (Const.String x2, _, _) -> Expr.Const (Const.String (x1 + x2), m, ty) + | _ -> + EvalArithBinOp (Checked.(+), Checked.(+), Checked.(+), Checked.(+), Checked.(+), Checked.(+), Checked.(+), Checked.(+)) v1 v2 + | SpecificBinopExpr g g.unchecked_subtraction_vref (arg1, arg2) -> + EvalArithBinOp (Checked.(-), Checked.(-), Checked.(-), Checked.(-), Checked.(-), Checked.(-), Checked.(-), Checked.(-)) (EvalAttribArgExpr g arg1) (EvalAttribArgExpr g arg2) + | SpecificBinopExpr g g.unchecked_multiply_vref (arg1, arg2) -> + EvalArithBinOp (Checked.(*), Checked.(*), Checked.(*), Checked.(*), Checked.(*), Checked.(*), Checked.(*), Checked.(*)) (EvalAttribArgExpr g arg1) (EvalAttribArgExpr g arg2) + | SpecificBinopExpr g g.unchecked_division_vref (arg1, arg2) -> + EvalArithBinOp ((/), (/), (/), (/), (/), (/), (/), (/)) (EvalAttribArgExpr g arg1) (EvalAttribArgExpr g arg2) + | SpecificBinopExpr g g.unchecked_modulus_vref (arg1, arg2) -> + EvalArithBinOp ((%), (%), (%), (%), (%), (%), (%), (%)) (EvalAttribArgExpr g arg1) (EvalAttribArgExpr g arg2) + | SpecificBinopExpr g g.bitwise_shift_left_vref (arg1, arg2) -> + EvalArithShiftOp ((<<<), (<<<), (<<<), (<<<), (<<<), (<<<), (<<<), (<<<)) (EvalAttribArgExpr g arg1) (EvalAttribArgExpr g arg2) + | SpecificBinopExpr g g.bitwise_shift_right_vref (arg1, arg2) -> + EvalArithShiftOp ((>>>), (>>>), (>>>), (>>>), (>>>), (>>>), (>>>), (>>>)) (EvalAttribArgExpr g arg1) (EvalAttribArgExpr g arg2) + | SpecificBinopExpr g g.bitwise_and_vref (arg1, arg2) -> + EvalArithBinOp ((&&&), (&&&), (&&&), (&&&), (&&&), (&&&), (&&&), (&&&)) (EvalAttribArgExpr g arg1) (EvalAttribArgExpr g arg2) + | SpecificUnopExpr g g.unchecked_unary_minus_vref arg1 -> + let v1 = EvalAttribArgExpr g arg1 + + match v1 with + | SignedIntegerConstExpr -> + let ignore (_x: 'a) = Unchecked.defaultof<'a> + EvalArithUnOp (Checked.(~-), Checked.(~-), Checked.(~-), Checked.(~-), ignore, ignore, ignore, ignore) v1 + | _ -> + errorR (Error ( FSComp.SR.tastNotAConstantExpression(), x.Range)) + x + | SpecificUnopExpr g g.unchecked_unary_plus_vref arg1 -> + EvalArithUnOp ((~+), (~+), (~+), (~+), (~+), (~+), (~+), (~+)) (EvalAttribArgExpr g arg1) + | SpecificUnopExpr g g.unchecked_unary_not_vref arg1 -> + match EvalAttribArgExpr g arg1 with + | Expr.Const (Const.Bool value, m, ty) -> + Expr.Const (Const.Bool (not value), m, ty) + | _ -> + errorR (Error ( FSComp.SR.tastNotAConstantExpression(), x.Range)) + x | _ -> errorR (Error ( FSComp.SR.tastNotAConstantExpression(), x.Range)) x - and EvaledAttribExprEquality g e1 e2 = match e1, e2 with | Expr.Const (c1, _, _), Expr.Const (c2, _, _) -> c1 = c2 From 196d294d69ec610aeabc0de722f3c319032b2960 Mon Sep 17 00:00:00 2001 From: kerams Date: Tue, 22 Nov 2022 14:24:33 +0100 Subject: [PATCH 2/9] Allow simple arithmetic in number literals --- src/Compiler/TypedTree/TypedTreeOps.fs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Compiler/TypedTree/TypedTreeOps.fs b/src/Compiler/TypedTree/TypedTreeOps.fs index e7ebe25c26..24dbb28aa5 100644 --- a/src/Compiler/TypedTree/TypedTreeOps.fs +++ b/src/Compiler/TypedTree/TypedTreeOps.fs @@ -9769,7 +9769,7 @@ let rec EvalAttribArgExpr g x = let ignore (_x: 'a) = Unchecked.defaultof<'a> EvalArithUnOp (Checked.(~-), Checked.(~-), Checked.(~-), Checked.(~-), ignore, ignore, ignore, ignore) v1 | _ -> - errorR (Error ( FSComp.SR.tastNotAConstantExpression(), x.Range)) + errorR (Error ( FSComp.SR.tastNotAConstantExpression(), v1.Range)) x | SpecificUnopExpr g g.unchecked_unary_plus_vref arg1 -> EvalArithUnOp ((~+), (~+), (~+), (~+), (~+), (~+), (~+), (~+)) (EvalAttribArgExpr g arg1) @@ -9777,8 +9777,8 @@ let rec EvalAttribArgExpr g x = match EvalAttribArgExpr g arg1 with | Expr.Const (Const.Bool value, m, ty) -> Expr.Const (Const.Bool (not value), m, ty) - | _ -> - errorR (Error ( FSComp.SR.tastNotAConstantExpression(), x.Range)) + | expr -> + errorR (Error ( FSComp.SR.tastNotAConstantExpression(), expr.Range)) x | _ -> errorR (Error ( FSComp.SR.tastNotAConstantExpression(), x.Range)) From 0ed3c10e9d253a05421c87c7d1d83070c5299b28 Mon Sep 17 00:00:00 2001 From: kerams Date: Tue, 22 Nov 2022 19:56:36 +0100 Subject: [PATCH 3/9] Add tests --- src/Compiler/TypedTree/TypedTreeOps.fs | 7 - .../AttributeUsage/AttributeUsage.fs | 11 -- .../AttributeUsage/E_WithBitwiseAnd01.fsx | 13 -- .../EmittedIL/Literals.fs | 139 ++++++++++++++++++ 4 files changed, 139 insertions(+), 31 deletions(-) delete mode 100644 tests/FSharp.Compiler.ComponentTests/Conformance/DeclarationElements/CustomAttributes/AttributeUsage/E_WithBitwiseAnd01.fsx diff --git a/src/Compiler/TypedTree/TypedTreeOps.fs b/src/Compiler/TypedTree/TypedTreeOps.fs index 24dbb28aa5..31d3a90709 100644 --- a/src/Compiler/TypedTree/TypedTreeOps.fs +++ b/src/Compiler/TypedTree/TypedTreeOps.fs @@ -9773,13 +9773,6 @@ let rec EvalAttribArgExpr g x = x | SpecificUnopExpr g g.unchecked_unary_plus_vref arg1 -> EvalArithUnOp ((~+), (~+), (~+), (~+), (~+), (~+), (~+), (~+)) (EvalAttribArgExpr g arg1) - | SpecificUnopExpr g g.unchecked_unary_not_vref arg1 -> - match EvalAttribArgExpr g arg1 with - | Expr.Const (Const.Bool value, m, ty) -> - Expr.Const (Const.Bool (not value), m, ty) - | expr -> - errorR (Error ( FSComp.SR.tastNotAConstantExpression(), expr.Range)) - x | _ -> errorR (Error ( FSComp.SR.tastNotAConstantExpression(), x.Range)) x diff --git a/tests/FSharp.Compiler.ComponentTests/Conformance/DeclarationElements/CustomAttributes/AttributeUsage/AttributeUsage.fs b/tests/FSharp.Compiler.ComponentTests/Conformance/DeclarationElements/CustomAttributes/AttributeUsage/AttributeUsage.fs index 0161db9cd6..52c055d71a 100644 --- a/tests/FSharp.Compiler.ComponentTests/Conformance/DeclarationElements/CustomAttributes/AttributeUsage/AttributeUsage.fs +++ b/tests/FSharp.Compiler.ComponentTests/Conformance/DeclarationElements/CustomAttributes/AttributeUsage/AttributeUsage.fs @@ -127,17 +127,6 @@ module AttributeUsage = (Error 685, Line 20, Col 5, Line 20, Col 10, "The generic function 'Foo' must be given explicit type argument(s)") ] - // # SOURCE=E_WithBitwiseAnd01.fsx SCFLAGS="--test:ErrorRanges -a" # E_WithBitwiseAnd01.fsx - [] - let ``E_WithBitwiseAnd01_fsx`` compilation = - compilation - |> verifyCompile - |> shouldFail - |> withDiagnostics [ - (Error 267, Line 7, Col 25, Line 7, Col 91, "This is not a valid constant expression or custom attribute value") - (Warning 839, Line 12, Col 3, Line 12, Col 6, "Unexpected condition in imported assembly: failed to decode AttributeUsage attribute") - ] - // SOURCE=E_WithBitwiseOr01.fsx SCFLAGS="--test:ErrorRanges -a" # E_WithBitwiseOr01.fsx [] let ``E_WithBitwiseOr01_fsx`` compilation = diff --git a/tests/FSharp.Compiler.ComponentTests/Conformance/DeclarationElements/CustomAttributes/AttributeUsage/E_WithBitwiseAnd01.fsx b/tests/FSharp.Compiler.ComponentTests/Conformance/DeclarationElements/CustomAttributes/AttributeUsage/E_WithBitwiseAnd01.fsx deleted file mode 100644 index 1d541b626e..0000000000 --- a/tests/FSharp.Compiler.ComponentTests/Conformance/DeclarationElements/CustomAttributes/AttributeUsage/E_WithBitwiseAnd01.fsx +++ /dev/null @@ -1,13 +0,0 @@ -// #Regression #Conformance #DeclarationElements #Attributes -// Regression test for FSHARP1.0:4035 -// Using bitwise AND (&&&) in AttributeUsage should give a reasonable error -//Review when fixed -#light - -[] -[] -type FooAttribute() = - inherit System.Attribute() - -[] -let x = 1 diff --git a/tests/FSharp.Compiler.ComponentTests/EmittedIL/Literals.fs b/tests/FSharp.Compiler.ComponentTests/EmittedIL/Literals.fs index 40092066b3..c068041347 100644 --- a/tests/FSharp.Compiler.ComponentTests/EmittedIL/Literals.fs +++ b/tests/FSharp.Compiler.ComponentTests/EmittedIL/Literals.fs @@ -24,3 +24,142 @@ let main _ = |> verifyIL [""" .field public static literal int32 x = int32(0x00000007) .custom instance void [FSharp.Core]Microsoft.FSharp.Core.LiteralAttribute::.ctor() = ( 01 00 00 00 )"""] + + + [] + let ``Arithmetics in integer literals is evaluated at compile-time``() = + FSharp """ +module LiteralArithmetics + +let [] bytesInMegabyte = 1024L * 1024L + +let [] bytesInKilobyte = bytesInMegabyte >>> 10 + +let [] bytesInKilobyte2 = bytesInMegabyte / 1024L + +let [] secondsInDayPlusThree = 3 + (60 * 60 * 24) + +let [] bitwise = 1us &&& (3us ||| 4us) + """ + |> compile + |> shouldSucceed + |> verifyIL [ + """.field public static literal int64 bytesInMegabyte = int64(0x100000)""" + """.field public static literal int64 bytesInKilobyte = int64(0x400)""" + """.field public static literal int64 bytesInKilobyte2 = int64(0x400)""" + """.field public static literal int32 secondsInDayPlusThree = int32(0x00015183)""" + """.field public static literal uint16 bitwise = uint16(0x0001)""" + ] + + [] + let ``Arithmetics can be used for constructing enum literals``() = + FSharp """ +module LiteralArithmetics + +type E = + | A = 1 + | B = 2 + +let [] x = enum (1 + 1) + """ + |> compile + |> shouldSucceed + |> verifyIL [ + """.field public static literal valuetype LiteralArithmetics/E x = int32(0x00000002)""" + ] + + [] + let ``Arithmetics can be used for constructing literals in attributes``() = + FSharp """ +module LiteralArithmetics + +open System.Runtime.CompilerServices + +// 256 = AggressiveInlining +[] +let x () = + 3 + """ + |> compile + |> shouldSucceed + |> verifyIL [ + """.method public static int32 x() cil managed aggressiveinlining""" + ] + + [] + let ``Compilation fails when addition in literal overflows``() = + FSharp """ +module LiteralArithmetics + +let [] x = System.Int32.MaxValue + 1 + """ + |> compile + |> shouldFail + |> withResult { + Error = Error 3177 + Range = { StartLine = 4 + StartColumn = 21 + EndLine = 4 + EndColumn = 46 } + Message = "This literal expression or attribute argument results in an arithmetic overflow." + } + + [] + let ``Compilation fails when using decimal arithmetics in literal``() = + FSharp """ +module LiteralArithmetics + +let [] x = 1m + 1m + """ + |> compile + |> shouldFail + |> withResults [ + { Error = Error 267 + Range = { StartLine = 4 + StartColumn = 21 + EndLine = 4 + EndColumn = 23 } + Message = "This is not a valid constant expression or custom attribute value" } + { Error = Error 267 + Range = { StartLine = 4 + StartColumn = 26 + EndLine = 4 + EndColumn = 28 } + Message = "This is not a valid constant expression or custom attribute value" } + { Error = Error 267 + Range = { StartLine = 4 + StartColumn = 21 + EndLine = 4 + EndColumn = 28 } + Message = "This is not a valid constant expression or custom attribute value" } + ] + + [] + let ``Compilation fails when using arithmetics with a non-literal in literal``() = + FSharp """ +module LiteralArithmetics + +let [] x = 1 + System.DateTime.Now.Hour + """ + |> compile + |> shouldFail + |> withResults [ + { Error = Warning 52 + Range = { StartLine = 4 + StartColumn = 25 + EndLine = 4 + EndColumn = 49 } + Message = "The value has been copied to ensure the original is not mutated by this operation or because the copy is implicit when returning a struct from a member and another member is then accessed" } + { Error = Error 267 + Range = { StartLine = 4 + StartColumn = 25 + EndLine = 4 + EndColumn = 49 } + Message = "This is not a valid constant expression or custom attribute value" } + { Error = Error 267 + Range = { StartLine = 4 + StartColumn = 21 + EndLine = 4 + EndColumn = 49 } + Message = "This is not a valid constant expression or custom attribute value" } + ] \ No newline at end of file From 6fa39bccdc31fde21318518da715e2970e135c1d Mon Sep 17 00:00:00 2001 From: kerams Date: Tue, 22 Nov 2022 21:08:37 +0100 Subject: [PATCH 4/9] Fix test --- tests/FSharp.Compiler.ComponentTests/EmittedIL/Literals.fs | 6 ------ 1 file changed, 6 deletions(-) diff --git a/tests/FSharp.Compiler.ComponentTests/EmittedIL/Literals.fs b/tests/FSharp.Compiler.ComponentTests/EmittedIL/Literals.fs index c068041347..16c59573c8 100644 --- a/tests/FSharp.Compiler.ComponentTests/EmittedIL/Literals.fs +++ b/tests/FSharp.Compiler.ComponentTests/EmittedIL/Literals.fs @@ -144,12 +144,6 @@ let [] x = 1 + System.DateTime.Now.Hour |> compile |> shouldFail |> withResults [ - { Error = Warning 52 - Range = { StartLine = 4 - StartColumn = 25 - EndLine = 4 - EndColumn = 49 } - Message = "The value has been copied to ensure the original is not mutated by this operation or because the copy is implicit when returning a struct from a member and another member is then accessed" } { Error = Error 267 Range = { StartLine = 4 StartColumn = 25 From 4a63ad62f790dc898f5f398f169cd0e0d573b7b1 Mon Sep 17 00:00:00 2001 From: kerams Date: Tue, 22 Nov 2022 22:26:39 +0100 Subject: [PATCH 5/9] Sigh --- .../FSharp.Compiler.ComponentTests/EmittedIL/Literals.fs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/tests/FSharp.Compiler.ComponentTests/EmittedIL/Literals.fs b/tests/FSharp.Compiler.ComponentTests/EmittedIL/Literals.fs index 16c59573c8..9e147932b0 100644 --- a/tests/FSharp.Compiler.ComponentTests/EmittedIL/Literals.fs +++ b/tests/FSharp.Compiler.ComponentTests/EmittedIL/Literals.fs @@ -144,6 +144,14 @@ let [] x = 1 + System.DateTime.Now.Hour |> compile |> shouldFail |> withResults [ +#if !NETCOREAPP + { Error = Warning 52 + Range = { StartLine = 4 + StartColumn = 25 + EndLine = 4 + EndColumn = 49 } + Message = "The value has been copied to ensure the original is not mutated by this operation or because the copy is implicit when returning a struct from a member and another member is then accessed" } +#endif { Error = Error 267 Range = { StartLine = 4 StartColumn = 25 From b8206c99c3fd6d030e4644c78f92430dcbfe3cca Mon Sep 17 00:00:00 2001 From: kerams Date: Wed, 23 Nov 2022 11:54:13 +0100 Subject: [PATCH 6/9] Support logical operations --- src/Compiler/TypedTree/TypedTreeOps.fs | 24 ++++++++++++++ .../EmittedIL/Literals.fs | 31 +++++++++++++++++++ 2 files changed, 55 insertions(+) diff --git a/src/Compiler/TypedTree/TypedTreeOps.fs b/src/Compiler/TypedTree/TypedTreeOps.fs index 31d3a90709..540c0bee9a 100644 --- a/src/Compiler/TypedTree/TypedTreeOps.fs +++ b/src/Compiler/TypedTree/TypedTreeOps.fs @@ -9773,6 +9773,30 @@ let rec EvalAttribArgExpr g x = x | SpecificUnopExpr g g.unchecked_unary_plus_vref arg1 -> EvalArithUnOp ((~+), (~+), (~+), (~+), (~+), (~+), (~+), (~+)) (EvalAttribArgExpr g arg1) + | SpecificUnopExpr g g.unchecked_unary_not_vref arg1 -> + match EvalAttribArgExpr g arg1 with + | Expr.Const (Const.Bool value, m, ty) -> + Expr.Const (Const.Bool (not value), m, ty) + | expr -> + errorR (Error ( FSComp.SR.tastNotAConstantExpression(), expr.Range)) + x + // Detect logical operations on booleans, which are represented as a match expression + | Expr.Match (decision = TDSwitch (input = input; cases = [ TCase (DecisionTreeTest.Const (Const.Bool test), TDSuccess ([], targetNum)) ]); targets = [| TTarget (_, t0, _); TTarget (_, t1, _) |]) -> + match EvalAttribArgExpr g (stripDebugPoints input) with + | Expr.Const (Const.Bool value, _, _) -> + let pass, fail = + if targetNum = 0 then + t0, t1 + else + t1, t0 + + if value = test then + EvalAttribArgExpr g (stripDebugPoints pass) + else + EvalAttribArgExpr g (stripDebugPoints fail) + | _ -> + errorR (Error ( FSComp.SR.tastNotAConstantExpression(), x.Range)) + x | _ -> errorR (Error ( FSComp.SR.tastNotAConstantExpression(), x.Range)) x diff --git a/tests/FSharp.Compiler.ComponentTests/EmittedIL/Literals.fs b/tests/FSharp.Compiler.ComponentTests/EmittedIL/Literals.fs index 9e147932b0..51669e8a44 100644 --- a/tests/FSharp.Compiler.ComponentTests/EmittedIL/Literals.fs +++ b/tests/FSharp.Compiler.ComponentTests/EmittedIL/Literals.fs @@ -51,6 +51,37 @@ let [] bitwise = 1us &&& (3us ||| 4us) """.field public static literal uint16 bitwise = uint16(0x0001)""" ] + [] + let ``Logical operations on booleans are evaluated at compile-time``() = + FSharp """ +module LiteralArithmetics + +let [] flag = true + +let [] flippedFlag = not flag + +let [] simple1 = flippedFlag || false + +let [] simple2 = true && not true + +let [] complex1 = false || (flag && not flippedFlag) + +let [] complex2 = false || (flag && flippedFlag) + +let [] complex3 = true || (flag && not flippedFlag) + """ + |> compile + |> shouldSucceed + |> verifyIL [ + """.field public static literal bool flag = bool(true)""" + """.field public static literal bool flippedFlag = bool(false)""" + """.field public static literal bool simple1 = bool(false)""" + """.field public static literal bool simple2 = bool(false)""" + """.field public static literal bool complex1 = bool(true)""" + """.field public static literal bool complex2 = bool(false)""" + """.field public static literal bool complex3 = bool(true)""" + ] + [] let ``Arithmetics can be used for constructing enum literals``() = FSharp """ From eaa7b12be58a60aa7e87473e696c0c528cdb67aa Mon Sep 17 00:00:00 2001 From: kerams Date: Sun, 27 Nov 2022 10:40:09 +0100 Subject: [PATCH 7/9] Add support for char and floating point arithmetic --- src/Compiler/TypedTree/TypedTreeOps.fs | 74 +++++++++++++++---- .../EmittedIL/Literals.fs | 50 +++++++++---- 2 files changed, 94 insertions(+), 30 deletions(-) diff --git a/src/Compiler/TypedTree/TypedTreeOps.fs b/src/Compiler/TypedTree/TypedTreeOps.fs index 540c0bee9a..ac79f46139 100644 --- a/src/Compiler/TypedTree/TypedTreeOps.fs +++ b/src/Compiler/TypedTree/TypedTreeOps.fs @@ -3742,12 +3742,26 @@ let (|SpecificUnopExpr|_|) g vrefReqd expr = | UnopExpr g (vref, arg1) when valRefEq g vref vrefReqd -> Some arg1 | _ -> None -let (|SignedIntegerConstExpr|_|) expr = +let (|SignedConstExpr|_|) expr = match expr with | Expr.Const (Const.Int32 _, _, _) | Expr.Const (Const.SByte _, _, _) | Expr.Const (Const.Int16 _, _, _) - | Expr.Const (Const.Int64 _, _, _) -> Some () + | Expr.Const (Const.Int64 _, _, _) + | Expr.Const (Const.Single _, _, _) + | Expr.Const (Const.Double _, _, _) -> Some () + | _ -> None + +let (|IntegerConstExpr|_|) expr = + match expr with + | Expr.Const (Const.Int32 _, _, _) + | Expr.Const (Const.SByte _, _, _) + | Expr.Const (Const.Int16 _, _, _) + | Expr.Const (Const.Int64 _, _, _) + | Expr.Const (Const.Byte _, _, _) + | Expr.Const (Const.UInt16 _, _, _) + | Expr.Const (Const.UInt32 _, _, _) + | Expr.Const (Const.UInt64 _, _, _) -> Some () | _ -> None let (|SpecificBinopExpr|_|) g vrefReqd expr = @@ -9672,7 +9686,7 @@ let EvalArithShiftOp (opInt8, opInt16, opInt32, opInt64, opUInt8, opUInt16, opUI | _ -> error (Error ( FSComp.SR.tastNotAConstantExpression(), m)) with :? System.OverflowException -> error (Error ( FSComp.SR.tastConstantExpressionOverflow(), m)) -let EvalArithUnOp (opInt8, opInt16, opInt32, opInt64, opUInt8, opUInt16, opUInt32, opUInt64) (arg1: Expr) = +let EvalArithUnOp (opInt8, opInt16, opInt32, opInt64, opUInt8, opUInt16, opUInt32, opUInt64, opSingle, opDouble) (arg1: Expr) = // At compile-time we check arithmetic let m = arg1.Range try @@ -9685,10 +9699,12 @@ let EvalArithUnOp (opInt8, opInt16, opInt32, opInt64, opUInt8, opUInt16, opUInt3 | Expr.Const (Const.UInt16 x1, _, ty) -> Expr.Const (Const.UInt16 (opUInt16 x1), m, ty) | Expr.Const (Const.UInt32 x1, _, ty) -> Expr.Const (Const.UInt32 (opUInt32 x1), m, ty) | Expr.Const (Const.UInt64 x1, _, ty) -> Expr.Const (Const.UInt64 (opUInt64 x1), m, ty) + | Expr.Const (Const.Single x1, _, ty) -> Expr.Const (Const.Single (opSingle x1), m, ty) + | Expr.Const (Const.Double x1, _, ty) -> Expr.Const (Const.Double (opDouble x1), m, ty) | _ -> error (Error ( FSComp.SR.tastNotAConstantExpression(), m)) with :? System.OverflowException -> error (Error ( FSComp.SR.tastConstantExpressionOverflow(), m)) -let EvalArithBinOp (opInt8, opInt16, opInt32, opInt64, opUInt8, opUInt16, opUInt32, opUInt64) (arg1: Expr) (arg2: Expr) = +let EvalArithBinOp (opInt8, opInt16, opInt32, opInt64, opUInt8, opUInt16, opUInt32, opUInt64, opSingle, opDouble) (arg1: Expr) (arg2: Expr) = // At compile-time we check arithmetic let m = unionRanges arg1.Range arg2.Range try @@ -9701,11 +9717,16 @@ let EvalArithBinOp (opInt8, opInt16, opInt32, opInt64, opUInt8, opUInt16, opUInt | Expr.Const (Const.UInt16 x1, _, ty), Expr.Const (Const.UInt16 x2, _, _) -> Expr.Const (Const.UInt16 (opUInt16 x1 x2), m, ty) | Expr.Const (Const.UInt32 x1, _, ty), Expr.Const (Const.UInt32 x2, _, _) -> Expr.Const (Const.UInt32 (opUInt32 x1 x2), m, ty) | Expr.Const (Const.UInt64 x1, _, ty), Expr.Const (Const.UInt64 x2, _, _) -> Expr.Const (Const.UInt64 (opUInt64 x1 x2), m, ty) + | Expr.Const (Const.Single x1, _, ty), Expr.Const (Const.Single x2, _, _) -> Expr.Const (Const.Single (opSingle x1 x2), m, ty) + | Expr.Const (Const.Double x1, _, ty), Expr.Const (Const.Double x2, _, _) -> Expr.Const (Const.Double (opDouble x1 x2), m, ty) | _ -> error (Error ( FSComp.SR.tastNotAConstantExpression(), m)) with :? System.OverflowException -> error (Error ( FSComp.SR.tastConstantExpressionOverflow(), m)) // See also PostTypeCheckSemanticChecks.CheckAttribArgExpr, which must match this precisely let rec EvalAttribArgExpr g x = + let ignore (_x: 'a) = Unchecked.defaultof<'a> + let ignore2 (_x: 'a) (_y: 'a) = Unchecked.defaultof<'a> + match x with // Detect standard constants @@ -9739,40 +9760,61 @@ let rec EvalAttribArgExpr g x = EvalAttribArgExpr g arg1 // Detect bitwise or of attribute flags | AttribBitwiseOrExpr g (arg1, arg2) -> - EvalArithBinOp ((|||), (|||), (|||), (|||), (|||), (|||), (|||), (|||)) (EvalAttribArgExpr g arg1) (EvalAttribArgExpr g arg2) + let v1 = EvalAttribArgExpr g arg1 + + match v1 with + | IntegerConstExpr -> + EvalArithBinOp ((|||), (|||), (|||), (|||), (|||), (|||), (|||), (|||), ignore2, ignore2) v1 (EvalAttribArgExpr g arg2) + | _ -> + errorR (Error ( FSComp.SR.tastNotAConstantExpression(), x.Range)) + x | SpecificBinopExpr g g.unchecked_addition_vref (arg1, arg2) -> // At compile-time we check arithmetic let v1, v2 = EvalAttribArgExpr g arg1, EvalAttribArgExpr g arg2 match v1, v2 with - | Expr.Const (Const.String x1, m, ty), Expr.Const (Const.String x2, _, _) -> Expr.Const (Const.String (x1 + x2), m, ty) + | Expr.Const (Const.String x1, m, ty), Expr.Const (Const.String x2, _, _) -> + Expr.Const (Const.String (x1 + x2), m, ty) + | Expr.Const (Const.Char x1, m, ty), Expr.Const (Const.Char x2, _, _) -> + Expr.Const (Const.Char (x1 + x2), m, ty) | _ -> - EvalArithBinOp (Checked.(+), Checked.(+), Checked.(+), Checked.(+), Checked.(+), Checked.(+), Checked.(+), Checked.(+)) v1 v2 + EvalArithBinOp (Checked.(+), Checked.(+), Checked.(+), Checked.(+), Checked.(+), Checked.(+), Checked.(+), Checked.(+), Checked.(+), Checked.(+)) v1 v2 | SpecificBinopExpr g g.unchecked_subtraction_vref (arg1, arg2) -> - EvalArithBinOp (Checked.(-), Checked.(-), Checked.(-), Checked.(-), Checked.(-), Checked.(-), Checked.(-), Checked.(-)) (EvalAttribArgExpr g arg1) (EvalAttribArgExpr g arg2) + let v1, v2 = EvalAttribArgExpr g arg1, EvalAttribArgExpr g arg2 + match v1, v2 with + | Expr.Const (Const.Char x1, m, ty), Expr.Const (Const.Char x2, _, _) -> + Expr.Const (Const.Char (x1 - x2), m, ty) + | _ -> + EvalArithBinOp (Checked.(-), Checked.(-), Checked.(-), Checked.(-), Checked.(-), Checked.(-), Checked.(-), Checked.(-), Checked.(-), Checked.(-)) v1 v2 | SpecificBinopExpr g g.unchecked_multiply_vref (arg1, arg2) -> - EvalArithBinOp (Checked.(*), Checked.(*), Checked.(*), Checked.(*), Checked.(*), Checked.(*), Checked.(*), Checked.(*)) (EvalAttribArgExpr g arg1) (EvalAttribArgExpr g arg2) + EvalArithBinOp (Checked.(*), Checked.(*), Checked.(*), Checked.(*), Checked.(*), Checked.(*), Checked.(*), Checked.(*), Checked.(*), Checked.(*)) (EvalAttribArgExpr g arg1) (EvalAttribArgExpr g arg2) | SpecificBinopExpr g g.unchecked_division_vref (arg1, arg2) -> - EvalArithBinOp ((/), (/), (/), (/), (/), (/), (/), (/)) (EvalAttribArgExpr g arg1) (EvalAttribArgExpr g arg2) + EvalArithBinOp ((/), (/), (/), (/), (/), (/), (/), (/), (/), (/)) (EvalAttribArgExpr g arg1) (EvalAttribArgExpr g arg2) | SpecificBinopExpr g g.unchecked_modulus_vref (arg1, arg2) -> - EvalArithBinOp ((%), (%), (%), (%), (%), (%), (%), (%)) (EvalAttribArgExpr g arg1) (EvalAttribArgExpr g arg2) + EvalArithBinOp ((%), (%), (%), (%), (%), (%), (%), (%), (%), (%)) (EvalAttribArgExpr g arg1) (EvalAttribArgExpr g arg2) | SpecificBinopExpr g g.bitwise_shift_left_vref (arg1, arg2) -> EvalArithShiftOp ((<<<), (<<<), (<<<), (<<<), (<<<), (<<<), (<<<), (<<<)) (EvalAttribArgExpr g arg1) (EvalAttribArgExpr g arg2) | SpecificBinopExpr g g.bitwise_shift_right_vref (arg1, arg2) -> EvalArithShiftOp ((>>>), (>>>), (>>>), (>>>), (>>>), (>>>), (>>>), (>>>)) (EvalAttribArgExpr g arg1) (EvalAttribArgExpr g arg2) | SpecificBinopExpr g g.bitwise_and_vref (arg1, arg2) -> - EvalArithBinOp ((&&&), (&&&), (&&&), (&&&), (&&&), (&&&), (&&&), (&&&)) (EvalAttribArgExpr g arg1) (EvalAttribArgExpr g arg2) + let v1 = EvalAttribArgExpr g arg1 + + match v1 with + | IntegerConstExpr -> + EvalArithBinOp ((&&&), (&&&), (&&&), (&&&), (&&&), (&&&), (&&&), (&&&), ignore2, ignore2) v1 (EvalAttribArgExpr g arg2) + | _ -> + errorR (Error ( FSComp.SR.tastNotAConstantExpression(), x.Range)) + x | SpecificUnopExpr g g.unchecked_unary_minus_vref arg1 -> let v1 = EvalAttribArgExpr g arg1 match v1 with - | SignedIntegerConstExpr -> - let ignore (_x: 'a) = Unchecked.defaultof<'a> - EvalArithUnOp (Checked.(~-), Checked.(~-), Checked.(~-), Checked.(~-), ignore, ignore, ignore, ignore) v1 + | SignedConstExpr -> + EvalArithUnOp (Checked.(~-), Checked.(~-), Checked.(~-), Checked.(~-), ignore, ignore, ignore, ignore, Checked.(~-), Checked.(~-)) v1 | _ -> errorR (Error ( FSComp.SR.tastNotAConstantExpression(), v1.Range)) x | SpecificUnopExpr g g.unchecked_unary_plus_vref arg1 -> - EvalArithUnOp ((~+), (~+), (~+), (~+), (~+), (~+), (~+), (~+)) (EvalAttribArgExpr g arg1) + EvalArithUnOp ((~+), (~+), (~+), (~+), (~+), (~+), (~+), (~+), (~+), (~+)) (EvalAttribArgExpr g arg1) | SpecificUnopExpr g g.unchecked_unary_not_vref arg1 -> match EvalAttribArgExpr g arg1 with | Expr.Const (Const.Bool value, m, ty) -> diff --git a/tests/FSharp.Compiler.ComponentTests/EmittedIL/Literals.fs b/tests/FSharp.Compiler.ComponentTests/EmittedIL/Literals.fs index 51669e8a44..a4f635ca6e 100644 --- a/tests/FSharp.Compiler.ComponentTests/EmittedIL/Literals.fs +++ b/tests/FSharp.Compiler.ComponentTests/EmittedIL/Literals.fs @@ -27,9 +27,9 @@ let main _ = [] - let ``Arithmetics in integer literals is evaluated at compile-time``() = + let ``Arithmetic in integer literals is evaluated at compile time``() = FSharp """ -module LiteralArithmetics +module LiteralArithmetic let [] bytesInMegabyte = 1024L * 1024L @@ -52,9 +52,31 @@ let [] bitwise = 1us &&& (3us ||| 4us) ] [] - let ``Logical operations on booleans are evaluated at compile-time``() = + let ``Arithmetic in char and floating point literals is evaluated at compile time``() = FSharp """ -module LiteralArithmetics +module LiteralArithmetic + +let [] bytesInMegabyte = 1024. * 1024. + +let [] bytesInKilobyte = bytesInMegabyte / 1024. + +let [] secondsInDayPlusThree = 3f + (60f * 60f * 24f) + +let [] chars = 'a' + 'b' - 'a' + """ + |> compile + |> shouldSucceed + |> verifyIL [ + """.field public static literal float64 bytesInMegabyte = float64(1048576.)""" + """.field public static literal float64 bytesInKilobyte = float64(1024.)""" + """.field public static literal float32 secondsInDayPlusThree = float32(86403.)""" + """.field public static literal char chars = char(0x0062)""" + ] + + [] + let ``Logical operations on booleans are evaluated at compile time``() = + FSharp """ +module LiteralArithmetic let [] flag = true @@ -83,9 +105,9 @@ let [] complex3 = true || (flag && not flippedFlag) ] [] - let ``Arithmetics can be used for constructing enum literals``() = + let ``Arithmetic can be used for constructing enum literals``() = FSharp """ -module LiteralArithmetics +module LiteralArithmetic type E = | A = 1 @@ -96,13 +118,13 @@ let [] x = enum (1 + 1) |> compile |> shouldSucceed |> verifyIL [ - """.field public static literal valuetype LiteralArithmetics/E x = int32(0x00000002)""" + """.field public static literal valuetype LiteralArithmetic/E x = int32(0x00000002)""" ] [] - let ``Arithmetics can be used for constructing literals in attributes``() = + let ``Arithmetic can be used for constructing literals in attributes``() = FSharp """ -module LiteralArithmetics +module LiteralArithmetic open System.Runtime.CompilerServices @@ -120,7 +142,7 @@ let x () = [] let ``Compilation fails when addition in literal overflows``() = FSharp """ -module LiteralArithmetics +module LiteralArithmetic let [] x = System.Int32.MaxValue + 1 """ @@ -136,9 +158,9 @@ let [] x = System.Int32.MaxValue + 1 } [] - let ``Compilation fails when using decimal arithmetics in literal``() = + let ``Compilation fails when using decimal arithmetic in literal``() = FSharp """ -module LiteralArithmetics +module LiteralArithmetic let [] x = 1m + 1m """ @@ -166,9 +188,9 @@ let [] x = 1m + 1m ] [] - let ``Compilation fails when using arithmetics with a non-literal in literal``() = + let ``Compilation fails when using arithmetic with a non-literal in literal``() = FSharp """ -module LiteralArithmetics +module LiteralArithmetic let [] x = 1 + System.DateTime.Now.Hour """ From 45e33cb822423c000b544fac8feb022d3efff676 Mon Sep 17 00:00:00 2001 From: kerams Date: Sun, 27 Nov 2022 11:13:45 +0100 Subject: [PATCH 8/9] Make test consitent across operating systems --- .../EmittedIL/Literals.fs | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/tests/FSharp.Compiler.ComponentTests/EmittedIL/Literals.fs b/tests/FSharp.Compiler.ComponentTests/EmittedIL/Literals.fs index a4f635ca6e..51fa6bb745 100644 --- a/tests/FSharp.Compiler.ComponentTests/EmittedIL/Literals.fs +++ b/tests/FSharp.Compiler.ComponentTests/EmittedIL/Literals.fs @@ -53,23 +53,25 @@ let [] bitwise = 1us &&& (3us ||| 4us) [] let ``Arithmetic in char and floating point literals is evaluated at compile time``() = + // on Linux and Mac floats with no decimal parts are printed without the decimal point (unlike Windows) + // let's add some fractions so that the tests are consistent FSharp """ module LiteralArithmetic -let [] bytesInMegabyte = 1024. * 1024. +let [] bytesInMegabyte = 1024. * 1024. + 0.1 -let [] bytesInKilobyte = bytesInMegabyte / 1024. +let [] bytesInKilobyte = bytesInMegabyte / 1024. + 0.1 -let [] secondsInDayPlusThree = 3f + (60f * 60f * 24f) +let [] secondsInDayPlusThree = 3.1f + (60f * 60f * 24f) let [] chars = 'a' + 'b' - 'a' """ |> compile |> shouldSucceed |> verifyIL [ - """.field public static literal float64 bytesInMegabyte = float64(1048576.)""" - """.field public static literal float64 bytesInKilobyte = float64(1024.)""" - """.field public static literal float32 secondsInDayPlusThree = float32(86403.)""" + """.field public static literal float64 bytesInMegabyte = float64(1048576.1000000001)""" + """.field public static literal float64 bytesInKilobyte = float64(1024.10009765625)""" + """.field public static literal float32 secondsInDayPlusThree = float32(86403.102)""" """.field public static literal char chars = char(0x0062)""" ] From ff127278f9f307edd653796bb709a51d11cf5545 Mon Sep 17 00:00:00 2001 From: kerams Date: Tue, 29 Nov 2022 16:55:03 +0100 Subject: [PATCH 9/9] Add lang feature flag --- src/Compiler/FSComp.txt | 1 + src/Compiler/Facilities/LanguageFeatures.fs | 7 ++-- src/Compiler/Facilities/LanguageFeatures.fsi | 1 + src/Compiler/TypedTree/TypedTreeOps.fs | 34 +++++++++++-------- src/Compiler/xlf/FSComp.txt.cs.xlf | 5 +++ src/Compiler/xlf/FSComp.txt.de.xlf | 5 +++ src/Compiler/xlf/FSComp.txt.es.xlf | 5 +++ src/Compiler/xlf/FSComp.txt.fr.xlf | 5 +++ src/Compiler/xlf/FSComp.txt.it.xlf | 5 +++ src/Compiler/xlf/FSComp.txt.ja.xlf | 5 +++ src/Compiler/xlf/FSComp.txt.ko.xlf | 5 +++ src/Compiler/xlf/FSComp.txt.pl.xlf | 5 +++ src/Compiler/xlf/FSComp.txt.pt-BR.xlf | 5 +++ src/Compiler/xlf/FSComp.txt.ru.xlf | 5 +++ src/Compiler/xlf/FSComp.txt.tr.xlf | 5 +++ src/Compiler/xlf/FSComp.txt.zh-Hans.xlf | 5 +++ src/Compiler/xlf/FSComp.txt.zh-Hant.xlf | 5 +++ .../EmittedIL/Literals.fs | 8 +++++ 18 files changed, 100 insertions(+), 16 deletions(-) diff --git a/src/Compiler/FSComp.txt b/src/Compiler/FSComp.txt index 2fca7afeb6..d6033b8230 100644 --- a/src/Compiler/FSComp.txt +++ b/src/Compiler/FSComp.txt @@ -1558,6 +1558,7 @@ featureLowercaseDUWhenRequireQualifiedAccess,"Allow lowercase DU when RequireQua featureMatchNotAllowedForUnionCaseWithNoData,"Pattern match discard is not allowed for union case that takes no data." featureCSharpExtensionAttributeNotRequired,"Allow implicit Extension attribute on declaring types, modules" featureErrorForNonVirtualMembersOverrides,"Raises errors for non-virtual members overrides" +featureArithmeticInLiterals,"Allow arithmetic and logical operations in literals" 3353,fsiInvalidDirective,"Invalid directive '#%s %s'" 3354,tcNotAFunctionButIndexerNamedIndexingNotYetEnabled,"This value supports indexing, e.g. '%s.[index]'. The syntax '%s[index]' requires /langversion:preview. See https://aka.ms/fsharp-index-notation." 3354,tcNotAFunctionButIndexerIndexingNotYetEnabled,"This expression supports indexing, e.g. 'expr.[index]'. The syntax 'expr[index]' requires /langversion:preview. See https://aka.ms/fsharp-index-notation." diff --git a/src/Compiler/Facilities/LanguageFeatures.fs b/src/Compiler/Facilities/LanguageFeatures.fs index 14302b0513..6b543bc9b6 100644 --- a/src/Compiler/Facilities/LanguageFeatures.fs +++ b/src/Compiler/Facilities/LanguageFeatures.fs @@ -57,6 +57,7 @@ type LanguageFeature = | MatchNotAllowedForUnionCaseWithNoData | CSharpExtensionAttributeNotRequired | ErrorForNonVirtualMembersOverrides + | ArithmeticInLiterals /// LanguageVersion management type LanguageVersion(versionText) = @@ -130,6 +131,7 @@ type LanguageVersion(versionText) = LanguageFeature.MatchNotAllowedForUnionCaseWithNoData, previewVersion LanguageFeature.CSharpExtensionAttributeNotRequired, previewVersion LanguageFeature.ErrorForNonVirtualMembersOverrides, previewVersion + LanguageFeature.ArithmeticInLiterals, previewVersion ] @@ -238,8 +240,9 @@ type LanguageVersion(versionText) = | LanguageFeature.InterfacesWithAbstractStaticMembers -> FSComp.SR.featureInterfacesWithAbstractStaticMembers () | LanguageFeature.SelfTypeConstraints -> FSComp.SR.featureSelfTypeConstraints () | LanguageFeature.MatchNotAllowedForUnionCaseWithNoData -> FSComp.SR.featureMatchNotAllowedForUnionCaseWithNoData () - | LanguageFeature.CSharpExtensionAttributeNotRequired -> FSComp.SR.featureCSharpExtensionAttributeNotRequired () - | LanguageFeature.ErrorForNonVirtualMembersOverrides -> FSComp.SR.featureErrorForNonVirtualMembersOverrides () + | LanguageFeature.CSharpExtensionAttributeNotRequired -> FSComp.SR.featureCSharpExtensionAttributeNotRequired () + | LanguageFeature.ErrorForNonVirtualMembersOverrides -> FSComp.SR.featureErrorForNonVirtualMembersOverrides () + | LanguageFeature.ArithmeticInLiterals -> FSComp.SR.featureArithmeticInLiterals () /// Get a version string associated with the given feature. static member GetFeatureVersionString feature = diff --git a/src/Compiler/Facilities/LanguageFeatures.fsi b/src/Compiler/Facilities/LanguageFeatures.fsi index f471f371b9..616093ecb3 100644 --- a/src/Compiler/Facilities/LanguageFeatures.fsi +++ b/src/Compiler/Facilities/LanguageFeatures.fsi @@ -47,6 +47,7 @@ type LanguageFeature = | MatchNotAllowedForUnionCaseWithNoData | CSharpExtensionAttributeNotRequired | ErrorForNonVirtualMembersOverrides + | ArithmeticInLiterals /// LanguageVersion management type LanguageVersion = diff --git a/src/Compiler/TypedTree/TypedTreeOps.fs b/src/Compiler/TypedTree/TypedTreeOps.fs index ac79f46139..1ff908d445 100644 --- a/src/Compiler/TypedTree/TypedTreeOps.fs +++ b/src/Compiler/TypedTree/TypedTreeOps.fs @@ -9723,10 +9723,12 @@ let EvalArithBinOp (opInt8, opInt16, opInt32, opInt64, opUInt8, opUInt16, opUInt with :? System.OverflowException -> error (Error ( FSComp.SR.tastConstantExpressionOverflow(), m)) // See also PostTypeCheckSemanticChecks.CheckAttribArgExpr, which must match this precisely -let rec EvalAttribArgExpr g x = +let rec EvalAttribArgExpr (g: TcGlobals) x = let ignore (_x: 'a) = Unchecked.defaultof<'a> let ignore2 (_x: 'a) (_y: 'a) = Unchecked.defaultof<'a> + let arithmeticInLiteralsEnabled = g.langVersion.SupportsFeature LanguageFeature.ArithmeticInLiterals + match x with // Detect standard constants @@ -9774,28 +9776,32 @@ let rec EvalAttribArgExpr g x = match v1, v2 with | Expr.Const (Const.String x1, m, ty), Expr.Const (Const.String x2, _, _) -> Expr.Const (Const.String (x1 + x2), m, ty) - | Expr.Const (Const.Char x1, m, ty), Expr.Const (Const.Char x2, _, _) -> + | Expr.Const (Const.Char x1, m, ty), Expr.Const (Const.Char x2, _, _) when arithmeticInLiteralsEnabled -> Expr.Const (Const.Char (x1 + x2), m, ty) | _ -> - EvalArithBinOp (Checked.(+), Checked.(+), Checked.(+), Checked.(+), Checked.(+), Checked.(+), Checked.(+), Checked.(+), Checked.(+), Checked.(+)) v1 v2 - | SpecificBinopExpr g g.unchecked_subtraction_vref (arg1, arg2) -> + if arithmeticInLiteralsEnabled then + EvalArithBinOp (Checked.(+), Checked.(+), Checked.(+), Checked.(+), Checked.(+), Checked.(+), Checked.(+), Checked.(+), Checked.(+), Checked.(+)) v1 v2 + else + errorR (Error ( FSComp.SR.tastNotAConstantExpression(), x.Range)) + x + | SpecificBinopExpr g g.unchecked_subtraction_vref (arg1, arg2) when arithmeticInLiteralsEnabled -> let v1, v2 = EvalAttribArgExpr g arg1, EvalAttribArgExpr g arg2 match v1, v2 with | Expr.Const (Const.Char x1, m, ty), Expr.Const (Const.Char x2, _, _) -> Expr.Const (Const.Char (x1 - x2), m, ty) | _ -> EvalArithBinOp (Checked.(-), Checked.(-), Checked.(-), Checked.(-), Checked.(-), Checked.(-), Checked.(-), Checked.(-), Checked.(-), Checked.(-)) v1 v2 - | SpecificBinopExpr g g.unchecked_multiply_vref (arg1, arg2) -> + | SpecificBinopExpr g g.unchecked_multiply_vref (arg1, arg2) when arithmeticInLiteralsEnabled -> EvalArithBinOp (Checked.(*), Checked.(*), Checked.(*), Checked.(*), Checked.(*), Checked.(*), Checked.(*), Checked.(*), Checked.(*), Checked.(*)) (EvalAttribArgExpr g arg1) (EvalAttribArgExpr g arg2) - | SpecificBinopExpr g g.unchecked_division_vref (arg1, arg2) -> + | SpecificBinopExpr g g.unchecked_division_vref (arg1, arg2) when arithmeticInLiteralsEnabled -> EvalArithBinOp ((/), (/), (/), (/), (/), (/), (/), (/), (/), (/)) (EvalAttribArgExpr g arg1) (EvalAttribArgExpr g arg2) - | SpecificBinopExpr g g.unchecked_modulus_vref (arg1, arg2) -> + | SpecificBinopExpr g g.unchecked_modulus_vref (arg1, arg2) when arithmeticInLiteralsEnabled -> EvalArithBinOp ((%), (%), (%), (%), (%), (%), (%), (%), (%), (%)) (EvalAttribArgExpr g arg1) (EvalAttribArgExpr g arg2) - | SpecificBinopExpr g g.bitwise_shift_left_vref (arg1, arg2) -> + | SpecificBinopExpr g g.bitwise_shift_left_vref (arg1, arg2) when arithmeticInLiteralsEnabled -> EvalArithShiftOp ((<<<), (<<<), (<<<), (<<<), (<<<), (<<<), (<<<), (<<<)) (EvalAttribArgExpr g arg1) (EvalAttribArgExpr g arg2) - | SpecificBinopExpr g g.bitwise_shift_right_vref (arg1, arg2) -> + | SpecificBinopExpr g g.bitwise_shift_right_vref (arg1, arg2) when arithmeticInLiteralsEnabled -> EvalArithShiftOp ((>>>), (>>>), (>>>), (>>>), (>>>), (>>>), (>>>), (>>>)) (EvalAttribArgExpr g arg1) (EvalAttribArgExpr g arg2) - | SpecificBinopExpr g g.bitwise_and_vref (arg1, arg2) -> + | SpecificBinopExpr g g.bitwise_and_vref (arg1, arg2) when arithmeticInLiteralsEnabled -> let v1 = EvalAttribArgExpr g arg1 match v1 with @@ -9804,7 +9810,7 @@ let rec EvalAttribArgExpr g x = | _ -> errorR (Error ( FSComp.SR.tastNotAConstantExpression(), x.Range)) x - | SpecificUnopExpr g g.unchecked_unary_minus_vref arg1 -> + | SpecificUnopExpr g g.unchecked_unary_minus_vref arg1 when arithmeticInLiteralsEnabled -> let v1 = EvalAttribArgExpr g arg1 match v1 with @@ -9813,9 +9819,9 @@ let rec EvalAttribArgExpr g x = | _ -> errorR (Error ( FSComp.SR.tastNotAConstantExpression(), v1.Range)) x - | SpecificUnopExpr g g.unchecked_unary_plus_vref arg1 -> + | SpecificUnopExpr g g.unchecked_unary_plus_vref arg1 when arithmeticInLiteralsEnabled -> EvalArithUnOp ((~+), (~+), (~+), (~+), (~+), (~+), (~+), (~+), (~+), (~+)) (EvalAttribArgExpr g arg1) - | SpecificUnopExpr g g.unchecked_unary_not_vref arg1 -> + | SpecificUnopExpr g g.unchecked_unary_not_vref arg1 when arithmeticInLiteralsEnabled -> match EvalAttribArgExpr g arg1 with | Expr.Const (Const.Bool value, m, ty) -> Expr.Const (Const.Bool (not value), m, ty) @@ -9823,7 +9829,7 @@ let rec EvalAttribArgExpr g x = errorR (Error ( FSComp.SR.tastNotAConstantExpression(), expr.Range)) x // Detect logical operations on booleans, which are represented as a match expression - | Expr.Match (decision = TDSwitch (input = input; cases = [ TCase (DecisionTreeTest.Const (Const.Bool test), TDSuccess ([], targetNum)) ]); targets = [| TTarget (_, t0, _); TTarget (_, t1, _) |]) -> + | Expr.Match (decision = TDSwitch (input = input; cases = [ TCase (DecisionTreeTest.Const (Const.Bool test), TDSuccess ([], targetNum)) ]); targets = [| TTarget (_, t0, _); TTarget (_, t1, _) |]) when arithmeticInLiteralsEnabled -> match EvalAttribArgExpr g (stripDebugPoints input) with | Expr.Const (Const.Bool value, _, _) -> let pass, fail = diff --git a/src/Compiler/xlf/FSComp.txt.cs.xlf b/src/Compiler/xlf/FSComp.txt.cs.xlf index 9f1b557acb..8d85893b37 100644 --- a/src/Compiler/xlf/FSComp.txt.cs.xlf +++ b/src/Compiler/xlf/FSComp.txt.cs.xlf @@ -142,6 +142,11 @@ aplikativní výpočetní výrazy + + Allow arithmetic and logical operations in literals + Allow arithmetic and logical operations in literals + + attributes to the right of the 'module' keyword atributy napravo od klíčového slova Module diff --git a/src/Compiler/xlf/FSComp.txt.de.xlf b/src/Compiler/xlf/FSComp.txt.de.xlf index fad70824e3..e87c63bfcd 100644 --- a/src/Compiler/xlf/FSComp.txt.de.xlf +++ b/src/Compiler/xlf/FSComp.txt.de.xlf @@ -142,6 +142,11 @@ applikative Berechnungsausdrücke + + Allow arithmetic and logical operations in literals + Allow arithmetic and logical operations in literals + + attributes to the right of the 'module' keyword Attribute rechts vom "Module"-Schlüsselwort diff --git a/src/Compiler/xlf/FSComp.txt.es.xlf b/src/Compiler/xlf/FSComp.txt.es.xlf index 5402a968af..3a1456ea3d 100644 --- a/src/Compiler/xlf/FSComp.txt.es.xlf +++ b/src/Compiler/xlf/FSComp.txt.es.xlf @@ -142,6 +142,11 @@ expresiones de cálculo aplicativas + + Allow arithmetic and logical operations in literals + Allow arithmetic and logical operations in literals + + attributes to the right of the 'module' keyword atributos a la derecha de la palabra clave “módulo” diff --git a/src/Compiler/xlf/FSComp.txt.fr.xlf b/src/Compiler/xlf/FSComp.txt.fr.xlf index aadf83ade0..afb7f7f19a 100644 --- a/src/Compiler/xlf/FSComp.txt.fr.xlf +++ b/src/Compiler/xlf/FSComp.txt.fr.xlf @@ -142,6 +142,11 @@ expressions de calcul applicatives + + Allow arithmetic and logical operations in literals + Allow arithmetic and logical operations in literals + + attributes to the right of the 'module' keyword attributs à droite du mot clé 'module' diff --git a/src/Compiler/xlf/FSComp.txt.it.xlf b/src/Compiler/xlf/FSComp.txt.it.xlf index 8b681eb4a1..740e6a00e5 100644 --- a/src/Compiler/xlf/FSComp.txt.it.xlf +++ b/src/Compiler/xlf/FSComp.txt.it.xlf @@ -142,6 +142,11 @@ espressioni di calcolo applicativo + + Allow arithmetic and logical operations in literals + Allow arithmetic and logical operations in literals + + attributes to the right of the 'module' keyword attributi a destra della parola chiave 'module' diff --git a/src/Compiler/xlf/FSComp.txt.ja.xlf b/src/Compiler/xlf/FSComp.txt.ja.xlf index 4b6c944481..86cc15b831 100644 --- a/src/Compiler/xlf/FSComp.txt.ja.xlf +++ b/src/Compiler/xlf/FSComp.txt.ja.xlf @@ -142,6 +142,11 @@ 適用できる計算式 + + Allow arithmetic and logical operations in literals + Allow arithmetic and logical operations in literals + + attributes to the right of the 'module' keyword 'module' キーワードの右側の属性 diff --git a/src/Compiler/xlf/FSComp.txt.ko.xlf b/src/Compiler/xlf/FSComp.txt.ko.xlf index 40973f9e1a..8646d198ed 100644 --- a/src/Compiler/xlf/FSComp.txt.ko.xlf +++ b/src/Compiler/xlf/FSComp.txt.ko.xlf @@ -142,6 +142,11 @@ 적용 가능한 계산 식 + + Allow arithmetic and logical operations in literals + Allow arithmetic and logical operations in literals + + attributes to the right of the 'module' keyword 'module' 키워드 오른쪽에 있는 특성 diff --git a/src/Compiler/xlf/FSComp.txt.pl.xlf b/src/Compiler/xlf/FSComp.txt.pl.xlf index 9b4b9dab2e..dc3641eb63 100644 --- a/src/Compiler/xlf/FSComp.txt.pl.xlf +++ b/src/Compiler/xlf/FSComp.txt.pl.xlf @@ -142,6 +142,11 @@ praktyczne wyrażenia obliczeniowe + + Allow arithmetic and logical operations in literals + Allow arithmetic and logical operations in literals + + attributes to the right of the 'module' keyword atrybuty po prawej stronie słowa kluczowego "module" diff --git a/src/Compiler/xlf/FSComp.txt.pt-BR.xlf b/src/Compiler/xlf/FSComp.txt.pt-BR.xlf index 3307d01761..61a9f9ea4d 100644 --- a/src/Compiler/xlf/FSComp.txt.pt-BR.xlf +++ b/src/Compiler/xlf/FSComp.txt.pt-BR.xlf @@ -142,6 +142,11 @@ expressões de computação aplicáveis + + Allow arithmetic and logical operations in literals + Allow arithmetic and logical operations in literals + + attributes to the right of the 'module' keyword atributos à direita da palavra-chave 'módulo' diff --git a/src/Compiler/xlf/FSComp.txt.ru.xlf b/src/Compiler/xlf/FSComp.txt.ru.xlf index d9de5f772a..1ba03c032d 100644 --- a/src/Compiler/xlf/FSComp.txt.ru.xlf +++ b/src/Compiler/xlf/FSComp.txt.ru.xlf @@ -142,6 +142,11 @@ применимые вычислительные выражения + + Allow arithmetic and logical operations in literals + Allow arithmetic and logical operations in literals + + attributes to the right of the 'module' keyword атрибуты справа от ключевого слова "module" diff --git a/src/Compiler/xlf/FSComp.txt.tr.xlf b/src/Compiler/xlf/FSComp.txt.tr.xlf index 3cd9f827dc..0f032aabdc 100644 --- a/src/Compiler/xlf/FSComp.txt.tr.xlf +++ b/src/Compiler/xlf/FSComp.txt.tr.xlf @@ -142,6 +142,11 @@ uygulama hesaplama ifadeleri + + Allow arithmetic and logical operations in literals + Allow arithmetic and logical operations in literals + + attributes to the right of the 'module' keyword 'modül' anahtar sözcüğünün sağındaki öznitelikler diff --git a/src/Compiler/xlf/FSComp.txt.zh-Hans.xlf b/src/Compiler/xlf/FSComp.txt.zh-Hans.xlf index d733d0c103..73811a9fe0 100644 --- a/src/Compiler/xlf/FSComp.txt.zh-Hans.xlf +++ b/src/Compiler/xlf/FSComp.txt.zh-Hans.xlf @@ -142,6 +142,11 @@ 适用的计算表达式 + + Allow arithmetic and logical operations in literals + Allow arithmetic and logical operations in literals + + attributes to the right of the 'module' keyword "module" 关键字右侧的属性 diff --git a/src/Compiler/xlf/FSComp.txt.zh-Hant.xlf b/src/Compiler/xlf/FSComp.txt.zh-Hant.xlf index 1d7aec7be9..9fef8008df 100644 --- a/src/Compiler/xlf/FSComp.txt.zh-Hant.xlf +++ b/src/Compiler/xlf/FSComp.txt.zh-Hant.xlf @@ -142,6 +142,11 @@ 適用的計算運算式 + + Allow arithmetic and logical operations in literals + Allow arithmetic and logical operations in literals + + attributes to the right of the 'module' keyword 'module' 關鍵字右邊的屬性 diff --git a/tests/FSharp.Compiler.ComponentTests/EmittedIL/Literals.fs b/tests/FSharp.Compiler.ComponentTests/EmittedIL/Literals.fs index 51fa6bb745..570f22d137 100644 --- a/tests/FSharp.Compiler.ComponentTests/EmittedIL/Literals.fs +++ b/tests/FSharp.Compiler.ComponentTests/EmittedIL/Literals.fs @@ -41,6 +41,7 @@ let [] secondsInDayPlusThree = 3 + (60 * 60 * 24) let [] bitwise = 1us &&& (3us ||| 4us) """ + |> withLangVersionPreview |> compile |> shouldSucceed |> verifyIL [ @@ -66,6 +67,7 @@ let [] secondsInDayPlusThree = 3.1f + (60f * 60f * 24f) let [] chars = 'a' + 'b' - 'a' """ + |> withLangVersionPreview |> compile |> shouldSucceed |> verifyIL [ @@ -94,6 +96,7 @@ let [] complex2 = false || (flag && flippedFlag) let [] complex3 = true || (flag && not flippedFlag) """ + |> withLangVersionPreview |> compile |> shouldSucceed |> verifyIL [ @@ -117,6 +120,7 @@ type E = let [] x = enum (1 + 1) """ + |> withLangVersionPreview |> compile |> shouldSucceed |> verifyIL [ @@ -135,6 +139,7 @@ open System.Runtime.CompilerServices let x () = 3 """ + |> withLangVersionPreview |> compile |> shouldSucceed |> verifyIL [ @@ -148,6 +153,7 @@ module LiteralArithmetic let [] x = System.Int32.MaxValue + 1 """ + |> withLangVersionPreview |> compile |> shouldFail |> withResult { @@ -166,6 +172,7 @@ module LiteralArithmetic let [] x = 1m + 1m """ + |> withLangVersionPreview |> compile |> shouldFail |> withResults [ @@ -196,6 +203,7 @@ module LiteralArithmetic let [] x = 1 + System.DateTime.Now.Hour """ + |> withLangVersionPreview |> compile |> shouldFail |> withResults [