From 821ae60c9bb1d9027b7c0fc5672a41c73b1ef9c1 Mon Sep 17 00:00:00 2001 From: kerams Date: Sat, 11 Feb 2023 20:15:13 +0100 Subject: [PATCH 1/4] Improve diagnostics for ArithmeticInLiterals --- src/Compiler/Checking/CheckDeclarations.fs | 4 +- src/Compiler/FSComp.txt | 2 +- src/Compiler/TypedTree/TypedTreeOps.fs | 94 +++++++++++-------- src/Compiler/xlf/FSComp.txt.cs.xlf | 4 +- src/Compiler/xlf/FSComp.txt.de.xlf | 4 +- src/Compiler/xlf/FSComp.txt.es.xlf | 4 +- src/Compiler/xlf/FSComp.txt.fr.xlf | 4 +- src/Compiler/xlf/FSComp.txt.it.xlf | 4 +- src/Compiler/xlf/FSComp.txt.ja.xlf | 4 +- src/Compiler/xlf/FSComp.txt.ko.xlf | 4 +- src/Compiler/xlf/FSComp.txt.pl.xlf | 4 +- src/Compiler/xlf/FSComp.txt.pt-BR.xlf | 4 +- src/Compiler/xlf/FSComp.txt.ru.xlf | 4 +- src/Compiler/xlf/FSComp.txt.tr.xlf | 4 +- src/Compiler/xlf/FSComp.txt.zh-Hans.xlf | 4 +- src/Compiler/xlf/FSComp.txt.zh-Hant.xlf | 4 +- .../EmittedIL/Literals.fs | 29 +++++- 17 files changed, 110 insertions(+), 71 deletions(-) diff --git a/src/Compiler/Checking/CheckDeclarations.fs b/src/Compiler/Checking/CheckDeclarations.fs index a65745059fa..5c424ed0f40 100644 --- a/src/Compiler/Checking/CheckDeclarations.fs +++ b/src/Compiler/Checking/CheckDeclarations.fs @@ -579,15 +579,13 @@ module TcRecdUnionAndEnumDeclarations = | SynExpr.Const (synConst, _) -> let konst = TcConst cenv fieldTy valueRange env synConst MakeEnumCaseSpec cenv env parent attrs thisTy caseRange id xmldoc konst - | _ when cenv.g.langVersion.SupportsFeature LanguageFeature.ArithmeticInLiterals -> + | _ -> let expr, actualTy, _ = TcExprOfUnknownType cenv env tpenv valueExpr UnifyTypes cenv env valueRange fieldTy actualTy match EvalLiteralExprOrAttribArg cenv.g expr with | Expr.Const (konst, _, _) -> MakeEnumCaseSpec cenv env parent attrs thisTy caseRange id xmldoc konst | _ -> error(Error(FSComp.SR.tcInvalidEnumerationLiteral(), valueRange)) - | _ -> - error(Error(FSComp.SR.tcInvalidEnumerationLiteral(), valueRange)) let TcEnumDecls (cenv: cenv) env tpenv parent thisTy enumCases = let g = cenv.g diff --git a/src/Compiler/FSComp.txt b/src/Compiler/FSComp.txt index 02bc779ab35..be84b8ad027 100644 --- a/src/Compiler/FSComp.txt +++ b/src/Compiler/FSComp.txt @@ -1559,7 +1559,7 @@ featureMatchNotAllowedForUnionCaseWithNoData,"Pattern match discard is not allow featureCSharpExtensionAttributeNotRequired,"Allow implicit Extension attribute on declaring types, modules" featureErrorForNonVirtualMembersOverrides,"Raises errors for non-virtual members overrides" featureWarningWhenInliningMethodImplNoInlineMarkedFunction,"Raises warnings when 'let inline ... =' is used together with [] attribute. Function is not getting inlined." -featureArithmeticInLiterals,"Allow arithmetic and logical operations in literals" +featureArithmeticInLiterals,"Arithmetic and logical operations in literals, enum definitions and attributes" featureErrorReportingOnStaticClasses,"Error reporting on static classes" featureTryWithInSeqExpressions,"Support for try-with in sequence expressions" featureWarningWhenCopyAndUpdateRecordChangesAllFields,"Raises warnings when an copy-and-update record expression changes all fields of a record." diff --git a/src/Compiler/TypedTree/TypedTreeOps.fs b/src/Compiler/TypedTree/TypedTreeOps.fs index 1873a63abee..c0ddf3b2034 100644 --- a/src/Compiler/TypedTree/TypedTreeOps.fs +++ b/src/Compiler/TypedTree/TypedTreeOps.fs @@ -9657,11 +9657,13 @@ 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: TcGlobals) x = +let rec EvalAttribArgExpr checkArithmeticsFeature (g: TcGlobals) (x: Expr) = let ignore (_x: 'a) = Unchecked.defaultof<'a> let ignore2 (_x: 'a) (_y: 'a) = Unchecked.defaultof<'a> - let arithmeticInLiteralsEnabled = g.langVersion.SupportsFeature LanguageFeature.ArithmeticInLiterals + let inline checkFeature() = + if checkArithmeticsFeature then + checkLanguageFeatureAndRecover g.langVersion LanguageFeature.ArithmeticInLiterals x.Range match x with @@ -9691,61 +9693,68 @@ let rec EvalAttribArgExpr (g: TcGlobals) x = | TypeOfExpr g _ -> x | TypeDefOfExpr g _ -> x | Expr.Op (TOp.Coerce, _, [arg], _) -> - EvalAttribArgExpr g arg + EvalAttribArgExpr true g arg | EnumExpr g arg1 -> - EvalAttribArgExpr g arg1 + EvalAttribArgExpr true g arg1 // Detect bitwise or of attribute flags | AttribBitwiseOrExpr g (arg1, arg2) -> - let v1 = EvalAttribArgExpr g arg1 + let v1 = EvalAttribArgExpr false g arg1 match v1 with | IntegerConstExpr -> - EvalArithBinOp ((|||), (|||), (|||), (|||), (|||), (|||), (|||), (|||), ignore2, ignore2) v1 (EvalAttribArgExpr g arg2) + EvalArithBinOp ((|||), (|||), (|||), (|||), (|||), (|||), (|||), (|||), ignore2, ignore2) v1 (EvalAttribArgExpr false 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 + let v1, v2 = EvalAttribArgExpr false g arg1, EvalAttribArgExpr false 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.Char x1, m, ty), Expr.Const (Const.Char x2, _, _) when arithmeticInLiteralsEnabled -> + | Expr.Const (Const.Char x1, m, ty), Expr.Const (Const.Char x2, _, _) -> + checkFeature() Expr.Const (Const.Char (x1 + x2), m, ty) | _ -> - 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 + checkFeature() + EvalArithBinOp (Checked.(+), Checked.(+), Checked.(+), Checked.(+), Checked.(+), Checked.(+), Checked.(+), Checked.(+), Checked.(+), Checked.(+)) v1 v2 + | SpecificBinopExpr g g.unchecked_subtraction_vref (arg1, arg2) -> + checkFeature() + let v1, v2 = EvalAttribArgExpr false g arg1, EvalAttribArgExpr false 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) 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) when arithmeticInLiteralsEnabled -> - EvalArithBinOp ((/), (/), (/), (/), (/), (/), (/), (/), (/), (/)) (EvalAttribArgExpr g arg1) (EvalAttribArgExpr g 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) when arithmeticInLiteralsEnabled -> - EvalArithShiftOp ((<<<), (<<<), (<<<), (<<<), (<<<), (<<<), (<<<), (<<<)) (EvalAttribArgExpr g arg1) (EvalAttribArgExpr g 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) when arithmeticInLiteralsEnabled -> - let v1 = EvalAttribArgExpr g arg1 + | SpecificBinopExpr g g.unchecked_multiply_vref (arg1, arg2) -> + checkFeature() + EvalArithBinOp (Checked.(*), Checked.(*), Checked.(*), Checked.(*), Checked.(*), Checked.(*), Checked.(*), Checked.(*), Checked.(*), Checked.(*)) (EvalAttribArgExpr false g arg1) (EvalAttribArgExpr false g arg2) + | SpecificBinopExpr g g.unchecked_division_vref (arg1, arg2) -> + checkFeature() + EvalArithBinOp ((/), (/), (/), (/), (/), (/), (/), (/), (/), (/)) (EvalAttribArgExpr false g arg1) (EvalAttribArgExpr false g arg2) + | SpecificBinopExpr g g.unchecked_modulus_vref (arg1, arg2) -> + checkFeature() + EvalArithBinOp ((%), (%), (%), (%), (%), (%), (%), (%), (%), (%)) (EvalAttribArgExpr false g arg1) (EvalAttribArgExpr false g arg2) + | SpecificBinopExpr g g.bitwise_shift_left_vref (arg1, arg2) -> + checkFeature() + EvalArithShiftOp ((<<<), (<<<), (<<<), (<<<), (<<<), (<<<), (<<<), (<<<)) (EvalAttribArgExpr false g arg1) (EvalAttribArgExpr false g arg2) + | SpecificBinopExpr g g.bitwise_shift_right_vref (arg1, arg2) -> + checkFeature() + EvalArithShiftOp ((>>>), (>>>), (>>>), (>>>), (>>>), (>>>), (>>>), (>>>)) (EvalAttribArgExpr false g arg1) (EvalAttribArgExpr false g arg2) + | SpecificBinopExpr g g.bitwise_and_vref (arg1, arg2) -> + checkFeature() + let v1 = EvalAttribArgExpr false g arg1 match v1 with | IntegerConstExpr -> - EvalArithBinOp ((&&&), (&&&), (&&&), (&&&), (&&&), (&&&), (&&&), (&&&), ignore2, ignore2) v1 (EvalAttribArgExpr g arg2) + EvalArithBinOp ((&&&), (&&&), (&&&), (&&&), (&&&), (&&&), (&&&), (&&&), ignore2, ignore2) v1 (EvalAttribArgExpr false g arg2) | _ -> errorR (Error ( FSComp.SR.tastNotAConstantExpression(), x.Range)) x - | SpecificUnopExpr g g.unchecked_unary_minus_vref arg1 when arithmeticInLiteralsEnabled -> - let v1 = EvalAttribArgExpr g arg1 + | SpecificUnopExpr g g.unchecked_unary_minus_vref arg1 -> + checkFeature() + let v1 = EvalAttribArgExpr false g arg1 match v1 with | SignedConstExpr -> @@ -9753,18 +9762,23 @@ let rec EvalAttribArgExpr (g: TcGlobals) x = | _ -> errorR (Error ( FSComp.SR.tastNotAConstantExpression(), v1.Range)) x - | SpecificUnopExpr g g.unchecked_unary_plus_vref arg1 when arithmeticInLiteralsEnabled -> - EvalArithUnOp ((~+), (~+), (~+), (~+), (~+), (~+), (~+), (~+), (~+), (~+)) (EvalAttribArgExpr g arg1) - | SpecificUnopExpr g g.unchecked_unary_not_vref arg1 when arithmeticInLiteralsEnabled -> - match EvalAttribArgExpr g arg1 with + | SpecificUnopExpr g g.unchecked_unary_plus_vref arg1 -> + checkFeature() + EvalArithUnOp ((~+), (~+), (~+), (~+), (~+), (~+), (~+), (~+), (~+), (~+)) (EvalAttribArgExpr false g arg1) + | SpecificUnopExpr g g.unchecked_unary_not_vref arg1 -> + checkFeature() + + match EvalAttribArgExpr false 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, _) |]) when arithmeticInLiteralsEnabled -> - match EvalAttribArgExpr g (stripDebugPoints input) with + | Expr.Match (decision = TDSwitch (input = input; cases = [ TCase (DecisionTreeTest.Const (Const.Bool test), TDSuccess ([], targetNum)) ]); targets = [| TTarget (_, t0, _); TTarget (_, t1, _) |]) -> + checkFeature() + + match EvalAttribArgExpr false g (stripDebugPoints input) with | Expr.Const (Const.Bool value, _, _) -> let pass, fail = if targetNum = 0 then @@ -9773,9 +9787,9 @@ let rec EvalAttribArgExpr (g: TcGlobals) x = t1, t0 if value = test then - EvalAttribArgExpr g (stripDebugPoints pass) + EvalAttribArgExpr false g (stripDebugPoints pass) else - EvalAttribArgExpr g (stripDebugPoints fail) + EvalAttribArgExpr false g (stripDebugPoints fail) | _ -> errorR (Error ( FSComp.SR.tastNotAConstantExpression(), x.Range)) x @@ -9812,10 +9826,10 @@ let EvalLiteralExprOrAttribArg g x = match x with | Expr.Op (TOp.Coerce, _, [Expr.Op (TOp.Array, [elemTy], args, m)], _) | Expr.Op (TOp.Array, [elemTy], args, m) -> - let args = args |> List.map (EvalAttribArgExpr g) + let args = args |> List.map (EvalAttribArgExpr true g) Expr.Op (TOp.Array, [elemTy], args, m) | _ -> - EvalAttribArgExpr g x + EvalAttribArgExpr true g x // Take into account the fact that some "instance" members are compiled as static // members when using CompilationRepresentation.Static, or any non-virtual instance members diff --git a/src/Compiler/xlf/FSComp.txt.cs.xlf b/src/Compiler/xlf/FSComp.txt.cs.xlf index cf7f2ad933c..03ac50147e0 100644 --- a/src/Compiler/xlf/FSComp.txt.cs.xlf +++ b/src/Compiler/xlf/FSComp.txt.cs.xlf @@ -188,8 +188,8 @@ - Allow arithmetic and logical operations in literals - Povolit aritmetické a logické operace v literálech + Arithmetic and logical operations in literals, enum definitions and attributes + Povolit aritmetické a logické operace v literálech diff --git a/src/Compiler/xlf/FSComp.txt.de.xlf b/src/Compiler/xlf/FSComp.txt.de.xlf index 6af5f7b5edf..0b2590f4072 100644 --- a/src/Compiler/xlf/FSComp.txt.de.xlf +++ b/src/Compiler/xlf/FSComp.txt.de.xlf @@ -188,8 +188,8 @@ - Allow arithmetic and logical operations in literals - Arithmetische und logische Vorgänge in Literalen zulassen + Arithmetic and logical operations in literals, enum definitions and attributes + Arithmetische und logische Vorgänge in Literalen zulassen diff --git a/src/Compiler/xlf/FSComp.txt.es.xlf b/src/Compiler/xlf/FSComp.txt.es.xlf index fee5bdbc84a..7670b2158c1 100644 --- a/src/Compiler/xlf/FSComp.txt.es.xlf +++ b/src/Compiler/xlf/FSComp.txt.es.xlf @@ -188,8 +188,8 @@ - Allow arithmetic and logical operations in literals - Permitir operaciones aritméticas y lógicas en literales + Arithmetic and logical operations in literals, enum definitions and attributes + Permitir operaciones aritméticas y lógicas en literales diff --git a/src/Compiler/xlf/FSComp.txt.fr.xlf b/src/Compiler/xlf/FSComp.txt.fr.xlf index 7b10a5441cf..2ba0075f8a4 100644 --- a/src/Compiler/xlf/FSComp.txt.fr.xlf +++ b/src/Compiler/xlf/FSComp.txt.fr.xlf @@ -188,8 +188,8 @@ - Allow arithmetic and logical operations in literals - Autoriser les opérations arithmétiques et logiques dans les littéraux + Arithmetic and logical operations in literals, enum definitions and attributes + Autoriser les opérations arithmétiques et logiques dans les littéraux diff --git a/src/Compiler/xlf/FSComp.txt.it.xlf b/src/Compiler/xlf/FSComp.txt.it.xlf index 1446a0e9513..821ad3fcb50 100644 --- a/src/Compiler/xlf/FSComp.txt.it.xlf +++ b/src/Compiler/xlf/FSComp.txt.it.xlf @@ -188,8 +188,8 @@ - Allow arithmetic and logical operations in literals - Consentire operazioni aritmetiche e logiche in valori letterali + Arithmetic and logical operations in literals, enum definitions and attributes + Consentire operazioni aritmetiche e logiche in valori letterali diff --git a/src/Compiler/xlf/FSComp.txt.ja.xlf b/src/Compiler/xlf/FSComp.txt.ja.xlf index fefe3ec525a..39dfae650c5 100644 --- a/src/Compiler/xlf/FSComp.txt.ja.xlf +++ b/src/Compiler/xlf/FSComp.txt.ja.xlf @@ -188,8 +188,8 @@ - Allow arithmetic and logical operations in literals - リテラルで算術演算と論理演算を許可する + Arithmetic and logical operations in literals, enum definitions and attributes + リテラルで算術演算と論理演算を許可する diff --git a/src/Compiler/xlf/FSComp.txt.ko.xlf b/src/Compiler/xlf/FSComp.txt.ko.xlf index db13251e6e7..29c7c2f483c 100644 --- a/src/Compiler/xlf/FSComp.txt.ko.xlf +++ b/src/Compiler/xlf/FSComp.txt.ko.xlf @@ -188,8 +188,8 @@ - Allow arithmetic and logical operations in literals - 리터럴에서 산술 및 논리 연산 허용 + Arithmetic and logical operations in literals, enum definitions and attributes + 리터럴에서 산술 및 논리 연산 허용 diff --git a/src/Compiler/xlf/FSComp.txt.pl.xlf b/src/Compiler/xlf/FSComp.txt.pl.xlf index 73455911504..42e74bfbd26 100644 --- a/src/Compiler/xlf/FSComp.txt.pl.xlf +++ b/src/Compiler/xlf/FSComp.txt.pl.xlf @@ -188,8 +188,8 @@ - Allow arithmetic and logical operations in literals - Zezwalaj na operacje arytmetyczne i logiczne w literałach + Arithmetic and logical operations in literals, enum definitions and attributes + Zezwalaj na operacje arytmetyczne i logiczne w literałach diff --git a/src/Compiler/xlf/FSComp.txt.pt-BR.xlf b/src/Compiler/xlf/FSComp.txt.pt-BR.xlf index 2070a1bf6ce..9511eef0b67 100644 --- a/src/Compiler/xlf/FSComp.txt.pt-BR.xlf +++ b/src/Compiler/xlf/FSComp.txt.pt-BR.xlf @@ -188,8 +188,8 @@ - Allow arithmetic and logical operations in literals - Permitir operações aritméticas e lógicas em literais + Arithmetic and logical operations in literals, enum definitions and attributes + Permitir operações aritméticas e lógicas em literais diff --git a/src/Compiler/xlf/FSComp.txt.ru.xlf b/src/Compiler/xlf/FSComp.txt.ru.xlf index 3762829b9c5..ac8ed8d9b5f 100644 --- a/src/Compiler/xlf/FSComp.txt.ru.xlf +++ b/src/Compiler/xlf/FSComp.txt.ru.xlf @@ -188,8 +188,8 @@ - Allow arithmetic and logical operations in literals - Разрешить арифметические и логические операции в литералах + Arithmetic and logical operations in literals, enum definitions and attributes + Разрешить арифметические и логические операции в литералах diff --git a/src/Compiler/xlf/FSComp.txt.tr.xlf b/src/Compiler/xlf/FSComp.txt.tr.xlf index 3a1ca285157..3d64ae3b873 100644 --- a/src/Compiler/xlf/FSComp.txt.tr.xlf +++ b/src/Compiler/xlf/FSComp.txt.tr.xlf @@ -188,8 +188,8 @@ - Allow arithmetic and logical operations in literals - Sabit değerlerle aritmetik ve mantıksal işlemlere izin ver + Arithmetic and logical operations in literals, enum definitions and attributes + Sabit değerlerle aritmetik ve mantıksal işlemlere izin ver diff --git a/src/Compiler/xlf/FSComp.txt.zh-Hans.xlf b/src/Compiler/xlf/FSComp.txt.zh-Hans.xlf index 731b4d15713..8572fab1b39 100644 --- a/src/Compiler/xlf/FSComp.txt.zh-Hans.xlf +++ b/src/Compiler/xlf/FSComp.txt.zh-Hans.xlf @@ -188,8 +188,8 @@ - Allow arithmetic and logical operations in literals - 允许在文本中进行算术和逻辑运算 + Arithmetic and logical operations in literals, enum definitions and attributes + 允许在文本中进行算术和逻辑运算 diff --git a/src/Compiler/xlf/FSComp.txt.zh-Hant.xlf b/src/Compiler/xlf/FSComp.txt.zh-Hant.xlf index 5374ae02eb6..8cd04aab6ec 100644 --- a/src/Compiler/xlf/FSComp.txt.zh-Hant.xlf +++ b/src/Compiler/xlf/FSComp.txt.zh-Hant.xlf @@ -188,8 +188,8 @@ - Allow arithmetic and logical operations in literals - 允許常值中的算術和邏輯運算 + Arithmetic and logical operations in literals, enum definitions and attributes + 允許常值中的算術和邏輯運算 diff --git a/tests/FSharp.Compiler.ComponentTests/EmittedIL/Literals.fs b/tests/FSharp.Compiler.ComponentTests/EmittedIL/Literals.fs index 570f22d1377..3faf6868908 100644 --- a/tests/FSharp.Compiler.ComponentTests/EmittedIL/Literals.fs +++ b/tests/FSharp.Compiler.ComponentTests/EmittedIL/Literals.fs @@ -227,4 +227,31 @@ let [] x = 1 + System.DateTime.Now.Hour EndLine = 4 EndColumn = 49 } Message = "This is not a valid constant expression or custom attribute value" } - ] \ No newline at end of file + ] + + [] + let ``Arithmetic cannot be used in enums, literals and attributes in lang version70``() = + FSharp """ +module LiteralArithmetic + +open System.Runtime.CompilerServices + +[] +let x () = 3 + +let [] lit = 1 <<< (7 * 10) + +type E = + | A = (1 <<< 2) + | B = 1 + | C = (5 / 3 * 4) + """ + |> withLangVersion70 + |> typecheck + |> shouldFail + |> withDiagnostics [ + (Error 3350, Line 6, Col 19, Line 6, Col 30, "Feature 'Arithmetic and logical operations in literals, enum definitions and attributes' is not available in F# 7.0. Please use language version 'PREVIEW' or greater.") + (Error 3350, Line 9, Col 23, Line 9, Col 37, "Feature 'Arithmetic and logical operations in literals, enum definitions and attributes' is not available in F# 7.0. Please use language version 'PREVIEW' or greater.") + (Error 3350, Line 12, Col 12, Line 12, Col 19, "Feature 'Arithmetic and logical operations in literals, enum definitions and attributes' is not available in F# 7.0. Please use language version 'PREVIEW' or greater.") + (Error 3350, Line 14, Col 12, Line 14, Col 21, "Feature 'Arithmetic and logical operations in literals, enum definitions and attributes' is not available in F# 7.0. Please use language version 'PREVIEW' or greater.") + ] From 98b48ba06036aa37e8a4003a429a1610d06e196a Mon Sep 17 00:00:00 2001 From: kerams Date: Tue, 21 Feb 2023 10:31:56 +0100 Subject: [PATCH 2/4] Refactor --- src/Compiler/Facilities/DiagnosticsLogger.fs | 7 +++ src/Compiler/Facilities/DiagnosticsLogger.fsi | 7 +++ src/Compiler/TypedTree/TypedTreeOps.fs | 48 +++++++++---------- 3 files changed, 38 insertions(+), 24 deletions(-) diff --git a/src/Compiler/Facilities/DiagnosticsLogger.fs b/src/Compiler/Facilities/DiagnosticsLogger.fs index 83f59e52162..4f94d6b4fbb 100644 --- a/src/Compiler/Facilities/DiagnosticsLogger.fs +++ b/src/Compiler/Facilities/DiagnosticsLogger.fs @@ -810,6 +810,13 @@ let NormalizeErrorString (text: string MaybeNull) = buf.ToString() +/// Indicates whether a language feature check should be skipped. Typically used in recursive functions +/// where we don't want repeated recursive calls to raise the same diagnostic multiple times. +[] +type internal SuppressLanguageFeatureCheck = + | Yes + | No + let private tryLanguageFeatureErrorAux (langVersion: LanguageVersion) (langFeature: LanguageFeature) (m: range) = if not (langVersion.SupportsFeature langFeature) then let featureStr = LanguageVersion.GetFeatureString langFeature diff --git a/src/Compiler/Facilities/DiagnosticsLogger.fsi b/src/Compiler/Facilities/DiagnosticsLogger.fsi index 8357c003321..03b27b922a0 100644 --- a/src/Compiler/Facilities/DiagnosticsLogger.fsi +++ b/src/Compiler/Facilities/DiagnosticsLogger.fsi @@ -425,6 +425,13 @@ val NewlineifyErrorString: message: string -> string /// which is decoded by the IDE with 'NewlineifyErrorString' back into newlines, so that multi-line errors can be displayed in QuickInfo val NormalizeErrorString: text: string -> string +/// Indicates whether a language feature check should be skipped. Typically used in recursive functions +/// where we don't want repeated recursive calls to raise the same diagnostic multiple times. +[] +type SuppressLanguageFeatureCheck = + | Yes + | No + val checkLanguageFeatureError: langVersion: LanguageVersion -> langFeature: LanguageFeature -> m: range -> unit val checkLanguageFeatureAndRecover: langVersion: LanguageVersion -> langFeature: LanguageFeature -> m: range -> unit diff --git a/src/Compiler/TypedTree/TypedTreeOps.fs b/src/Compiler/TypedTree/TypedTreeOps.fs index c0ddf3b2034..167742ee343 100644 --- a/src/Compiler/TypedTree/TypedTreeOps.fs +++ b/src/Compiler/TypedTree/TypedTreeOps.fs @@ -9657,12 +9657,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 checkArithmeticsFeature (g: TcGlobals) (x: Expr) = +let rec EvalAttribArgExpr suppressLangFeatureCheck (g: TcGlobals) (x: Expr) = let ignore (_x: 'a) = Unchecked.defaultof<'a> let ignore2 (_x: 'a) (_y: 'a) = Unchecked.defaultof<'a> let inline checkFeature() = - if checkArithmeticsFeature then + if suppressLangFeatureCheck = SuppressLanguageFeatureCheck.No then checkLanguageFeatureAndRecover g.langVersion LanguageFeature.ArithmeticInLiterals x.Range match x with @@ -9693,22 +9693,22 @@ let rec EvalAttribArgExpr checkArithmeticsFeature (g: TcGlobals) (x: Expr) = | TypeOfExpr g _ -> x | TypeDefOfExpr g _ -> x | Expr.Op (TOp.Coerce, _, [arg], _) -> - EvalAttribArgExpr true g arg + EvalAttribArgExpr SuppressLanguageFeatureCheck.No g arg | EnumExpr g arg1 -> - EvalAttribArgExpr true g arg1 + EvalAttribArgExpr SuppressLanguageFeatureCheck.No g arg1 // Detect bitwise or of attribute flags | AttribBitwiseOrExpr g (arg1, arg2) -> - let v1 = EvalAttribArgExpr false g arg1 + let v1 = EvalAttribArgExpr SuppressLanguageFeatureCheck.No g arg1 match v1 with | IntegerConstExpr -> - EvalArithBinOp ((|||), (|||), (|||), (|||), (|||), (|||), (|||), (|||), ignore2, ignore2) v1 (EvalAttribArgExpr false g arg2) + EvalArithBinOp ((|||), (|||), (|||), (|||), (|||), (|||), (|||), (|||), ignore2, ignore2) v1 (EvalAttribArgExpr SuppressLanguageFeatureCheck.No 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 false g arg1, EvalAttribArgExpr false g arg2 + let v1, v2 = EvalAttribArgExpr SuppressLanguageFeatureCheck.No g arg1, EvalAttribArgExpr SuppressLanguageFeatureCheck.No 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) @@ -9720,7 +9720,7 @@ let rec EvalAttribArgExpr checkArithmeticsFeature (g: TcGlobals) (x: Expr) = EvalArithBinOp (Checked.(+), Checked.(+), Checked.(+), Checked.(+), Checked.(+), Checked.(+), Checked.(+), Checked.(+), Checked.(+), Checked.(+)) v1 v2 | SpecificBinopExpr g g.unchecked_subtraction_vref (arg1, arg2) -> checkFeature() - let v1, v2 = EvalAttribArgExpr false g arg1, EvalAttribArgExpr false g arg2 + let v1, v2 = EvalAttribArgExpr SuppressLanguageFeatureCheck.Yes g arg1, EvalAttribArgExpr SuppressLanguageFeatureCheck.Yes g arg2 match v1, v2 with | Expr.Const (Const.Char x1, m, ty), Expr.Const (Const.Char x2, _, _) -> @@ -9729,32 +9729,32 @@ let rec EvalAttribArgExpr checkArithmeticsFeature (g: TcGlobals) (x: Expr) = EvalArithBinOp (Checked.(-), Checked.(-), Checked.(-), Checked.(-), Checked.(-), Checked.(-), Checked.(-), Checked.(-), Checked.(-), Checked.(-)) v1 v2 | SpecificBinopExpr g g.unchecked_multiply_vref (arg1, arg2) -> checkFeature() - EvalArithBinOp (Checked.(*), Checked.(*), Checked.(*), Checked.(*), Checked.(*), Checked.(*), Checked.(*), Checked.(*), Checked.(*), Checked.(*)) (EvalAttribArgExpr false g arg1) (EvalAttribArgExpr false g arg2) + EvalArithBinOp (Checked.(*), Checked.(*), Checked.(*), Checked.(*), Checked.(*), Checked.(*), Checked.(*), Checked.(*), Checked.(*), Checked.(*)) (EvalAttribArgExpr SuppressLanguageFeatureCheck.Yes g arg1) (EvalAttribArgExpr SuppressLanguageFeatureCheck.Yes g arg2) | SpecificBinopExpr g g.unchecked_division_vref (arg1, arg2) -> checkFeature() - EvalArithBinOp ((/), (/), (/), (/), (/), (/), (/), (/), (/), (/)) (EvalAttribArgExpr false g arg1) (EvalAttribArgExpr false g arg2) + EvalArithBinOp ((/), (/), (/), (/), (/), (/), (/), (/), (/), (/)) (EvalAttribArgExpr SuppressLanguageFeatureCheck.Yes g arg1) (EvalAttribArgExpr SuppressLanguageFeatureCheck.Yes g arg2) | SpecificBinopExpr g g.unchecked_modulus_vref (arg1, arg2) -> checkFeature() - EvalArithBinOp ((%), (%), (%), (%), (%), (%), (%), (%), (%), (%)) (EvalAttribArgExpr false g arg1) (EvalAttribArgExpr false g arg2) + EvalArithBinOp ((%), (%), (%), (%), (%), (%), (%), (%), (%), (%)) (EvalAttribArgExpr SuppressLanguageFeatureCheck.Yes g arg1) (EvalAttribArgExpr SuppressLanguageFeatureCheck.Yes g arg2) | SpecificBinopExpr g g.bitwise_shift_left_vref (arg1, arg2) -> checkFeature() - EvalArithShiftOp ((<<<), (<<<), (<<<), (<<<), (<<<), (<<<), (<<<), (<<<)) (EvalAttribArgExpr false g arg1) (EvalAttribArgExpr false g arg2) + EvalArithShiftOp ((<<<), (<<<), (<<<), (<<<), (<<<), (<<<), (<<<), (<<<)) (EvalAttribArgExpr SuppressLanguageFeatureCheck.Yes g arg1) (EvalAttribArgExpr SuppressLanguageFeatureCheck.Yes g arg2) | SpecificBinopExpr g g.bitwise_shift_right_vref (arg1, arg2) -> checkFeature() - EvalArithShiftOp ((>>>), (>>>), (>>>), (>>>), (>>>), (>>>), (>>>), (>>>)) (EvalAttribArgExpr false g arg1) (EvalAttribArgExpr false g arg2) + EvalArithShiftOp ((>>>), (>>>), (>>>), (>>>), (>>>), (>>>), (>>>), (>>>)) (EvalAttribArgExpr SuppressLanguageFeatureCheck.Yes g arg1) (EvalAttribArgExpr SuppressLanguageFeatureCheck.Yes g arg2) | SpecificBinopExpr g g.bitwise_and_vref (arg1, arg2) -> checkFeature() - let v1 = EvalAttribArgExpr false g arg1 + let v1 = EvalAttribArgExpr SuppressLanguageFeatureCheck.Yes g arg1 match v1 with | IntegerConstExpr -> - EvalArithBinOp ((&&&), (&&&), (&&&), (&&&), (&&&), (&&&), (&&&), (&&&), ignore2, ignore2) v1 (EvalAttribArgExpr false g arg2) + EvalArithBinOp ((&&&), (&&&), (&&&), (&&&), (&&&), (&&&), (&&&), (&&&), ignore2, ignore2) v1 (EvalAttribArgExpr SuppressLanguageFeatureCheck.Yes g arg2) | _ -> errorR (Error ( FSComp.SR.tastNotAConstantExpression(), x.Range)) x | SpecificUnopExpr g g.unchecked_unary_minus_vref arg1 -> checkFeature() - let v1 = EvalAttribArgExpr false g arg1 + let v1 = EvalAttribArgExpr SuppressLanguageFeatureCheck.Yes g arg1 match v1 with | SignedConstExpr -> @@ -9764,11 +9764,11 @@ let rec EvalAttribArgExpr checkArithmeticsFeature (g: TcGlobals) (x: Expr) = x | SpecificUnopExpr g g.unchecked_unary_plus_vref arg1 -> checkFeature() - EvalArithUnOp ((~+), (~+), (~+), (~+), (~+), (~+), (~+), (~+), (~+), (~+)) (EvalAttribArgExpr false g arg1) + EvalArithUnOp ((~+), (~+), (~+), (~+), (~+), (~+), (~+), (~+), (~+), (~+)) (EvalAttribArgExpr SuppressLanguageFeatureCheck.Yes g arg1) | SpecificUnopExpr g g.unchecked_unary_not_vref arg1 -> checkFeature() - match EvalAttribArgExpr false g arg1 with + match EvalAttribArgExpr SuppressLanguageFeatureCheck.Yes g arg1 with | Expr.Const (Const.Bool value, m, ty) -> Expr.Const (Const.Bool (not value), m, ty) | expr -> @@ -9778,7 +9778,7 @@ let rec EvalAttribArgExpr checkArithmeticsFeature (g: TcGlobals) (x: Expr) = | Expr.Match (decision = TDSwitch (input = input; cases = [ TCase (DecisionTreeTest.Const (Const.Bool test), TDSuccess ([], targetNum)) ]); targets = [| TTarget (_, t0, _); TTarget (_, t1, _) |]) -> checkFeature() - match EvalAttribArgExpr false g (stripDebugPoints input) with + match EvalAttribArgExpr SuppressLanguageFeatureCheck.Yes g (stripDebugPoints input) with | Expr.Const (Const.Bool value, _, _) -> let pass, fail = if targetNum = 0 then @@ -9787,9 +9787,9 @@ let rec EvalAttribArgExpr checkArithmeticsFeature (g: TcGlobals) (x: Expr) = t1, t0 if value = test then - EvalAttribArgExpr false g (stripDebugPoints pass) + EvalAttribArgExpr SuppressLanguageFeatureCheck.Yes g (stripDebugPoints pass) else - EvalAttribArgExpr false g (stripDebugPoints fail) + EvalAttribArgExpr SuppressLanguageFeatureCheck.Yes g (stripDebugPoints fail) | _ -> errorR (Error ( FSComp.SR.tastNotAConstantExpression(), x.Range)) x @@ -9826,10 +9826,10 @@ let EvalLiteralExprOrAttribArg g x = match x with | Expr.Op (TOp.Coerce, _, [Expr.Op (TOp.Array, [elemTy], args, m)], _) | Expr.Op (TOp.Array, [elemTy], args, m) -> - let args = args |> List.map (EvalAttribArgExpr true g) + let args = args |> List.map (EvalAttribArgExpr SuppressLanguageFeatureCheck.No g) Expr.Op (TOp.Array, [elemTy], args, m) | _ -> - EvalAttribArgExpr true g x + EvalAttribArgExpr SuppressLanguageFeatureCheck.No g x // Take into account the fact that some "instance" members are compiled as static // members when using CompilationRepresentation.Static, or any non-virtual instance members From f4921119612ec12c0c6f96a94994910603ec0745 Mon Sep 17 00:00:00 2001 From: kerams Date: Wed, 22 Feb 2023 09:47:45 +0100 Subject: [PATCH 3/4] Do not reenable suppressed feature check in recursive calls --- src/Compiler/TypedTree/TypedTreeOps.fs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Compiler/TypedTree/TypedTreeOps.fs b/src/Compiler/TypedTree/TypedTreeOps.fs index 167742ee343..0f37feeece8 100644 --- a/src/Compiler/TypedTree/TypedTreeOps.fs +++ b/src/Compiler/TypedTree/TypedTreeOps.fs @@ -9693,21 +9693,21 @@ let rec EvalAttribArgExpr suppressLangFeatureCheck (g: TcGlobals) (x: Expr) = | TypeOfExpr g _ -> x | TypeDefOfExpr g _ -> x | Expr.Op (TOp.Coerce, _, [arg], _) -> - EvalAttribArgExpr SuppressLanguageFeatureCheck.No g arg + EvalAttribArgExpr suppressLangFeatureCheck g arg | EnumExpr g arg1 -> - EvalAttribArgExpr SuppressLanguageFeatureCheck.No g arg1 + EvalAttribArgExpr suppressLangFeatureCheck g arg1 // Detect bitwise or of attribute flags | AttribBitwiseOrExpr g (arg1, arg2) -> - let v1 = EvalAttribArgExpr SuppressLanguageFeatureCheck.No g arg1 + let v1 = EvalAttribArgExpr suppressLangFeatureCheck g arg1 match v1 with | IntegerConstExpr -> - EvalArithBinOp ((|||), (|||), (|||), (|||), (|||), (|||), (|||), (|||), ignore2, ignore2) v1 (EvalAttribArgExpr SuppressLanguageFeatureCheck.No g arg2) + EvalArithBinOp ((|||), (|||), (|||), (|||), (|||), (|||), (|||), (|||), ignore2, ignore2) v1 (EvalAttribArgExpr suppressLangFeatureCheck g arg2) | _ -> errorR (Error ( FSComp.SR.tastNotAConstantExpression(), x.Range)) x | SpecificBinopExpr g g.unchecked_addition_vref (arg1, arg2) -> - let v1, v2 = EvalAttribArgExpr SuppressLanguageFeatureCheck.No g arg1, EvalAttribArgExpr SuppressLanguageFeatureCheck.No g arg2 + let v1, v2 = EvalAttribArgExpr suppressLangFeatureCheck g arg1, EvalAttribArgExpr suppressLangFeatureCheck g arg2 match v1, v2 with | Expr.Const (Const.String x1, m, ty), Expr.Const (Const.String x2, _, _) -> From e4eb73e3f7544e393142db4a7bcd82bb4ad85a35 Mon Sep 17 00:00:00 2001 From: kerams Date: Wed, 22 Feb 2023 11:32:39 +0100 Subject: [PATCH 4/4] Fix lang 4.6 test --- tests/fsharp/core/nameof/version46/test.fsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/fsharp/core/nameof/version46/test.fsx b/tests/fsharp/core/nameof/version46/test.fsx index 214bb498d90..0652ca028eb 100644 --- a/tests/fsharp/core/nameof/version46/test.fsx +++ b/tests/fsharp/core/nameof/version46/test.fsx @@ -32,7 +32,7 @@ //The value or constructor 'nameof' is not defined. //The value or constructor 'nameof' is not defined. //This is not a valid constant expression or custom attribute value -//This is not a valid constant expression or custom attribute value +//Feature 'Arithmetic and logical operations in literals, enum definitions and attributes' is not available in F# 4.6. Please use language version 'PREVIEW' or greater. //The value or constructor 'nameof' is not defined. //This is not a valid constant expression or custom attribute value //This is not a valid constant expression or custom attribute value