Skip to content

Commit dec4bef

Browse files
keramsT-Gro
andauthored
Improve diagnostics for ArithmeticInLiterals (#14735)
Co-authored-by: Tomas Grosup <[email protected]>
1 parent af463ef commit dec4bef

File tree

20 files changed

+126
-73
lines changed

20 files changed

+126
-73
lines changed

src/Compiler/Checking/CheckDeclarations.fs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -579,15 +579,13 @@ module TcRecdUnionAndEnumDeclarations =
579579
| SynExpr.Const (synConst, _) ->
580580
let konst = TcConst cenv fieldTy valueRange env synConst
581581
MakeEnumCaseSpec cenv env parent attrs thisTy caseRange id xmldoc konst
582-
| _ when cenv.g.langVersion.SupportsFeature LanguageFeature.ArithmeticInLiterals ->
582+
| _ ->
583583
let expr, actualTy, _ = TcExprOfUnknownType cenv env tpenv valueExpr
584584
UnifyTypes cenv env valueRange fieldTy actualTy
585585

586586
match EvalLiteralExprOrAttribArg cenv.g expr with
587587
| Expr.Const (konst, _, _) -> MakeEnumCaseSpec cenv env parent attrs thisTy caseRange id xmldoc konst
588588
| _ -> error(Error(FSComp.SR.tcInvalidEnumerationLiteral(), valueRange))
589-
| _ ->
590-
error(Error(FSComp.SR.tcInvalidEnumerationLiteral(), valueRange))
591589

592590
let TcEnumDecls (cenv: cenv) env tpenv parent thisTy enumCases =
593591
let g = cenv.g

src/Compiler/FSComp.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1559,7 +1559,7 @@ featureMatchNotAllowedForUnionCaseWithNoData,"Pattern match discard is not allow
15591559
featureCSharpExtensionAttributeNotRequired,"Allow implicit Extension attribute on declaring types, modules"
15601560
featureErrorForNonVirtualMembersOverrides,"Raises errors for non-virtual members overrides"
15611561
featureWarningWhenInliningMethodImplNoInlineMarkedFunction,"Raises warnings when 'let inline ... =' is used together with [<MethodImpl(MethodImplOptions.NoInlining)>] attribute. Function is not getting inlined."
1562-
featureArithmeticInLiterals,"Allow arithmetic and logical operations in literals"
1562+
featureArithmeticInLiterals,"Arithmetic and logical operations in literals, enum definitions and attributes"
15631563
featureErrorReportingOnStaticClasses,"Error reporting on static classes"
15641564
featureTryWithInSeqExpressions,"Support for try-with in sequence expressions"
15651565
featureWarningWhenCopyAndUpdateRecordChangesAllFields,"Raises warnings when an copy-and-update record expression changes all fields of a record."

src/Compiler/Facilities/DiagnosticsLogger.fs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -810,6 +810,13 @@ let NormalizeErrorString (text: string MaybeNull) =
810810

811811
buf.ToString()
812812

813+
/// Indicates whether a language feature check should be skipped. Typically used in recursive functions
814+
/// where we don't want repeated recursive calls to raise the same diagnostic multiple times.
815+
[<RequireQualifiedAccess>]
816+
type internal SuppressLanguageFeatureCheck =
817+
| Yes
818+
| No
819+
813820
let private tryLanguageFeatureErrorAux (langVersion: LanguageVersion) (langFeature: LanguageFeature) (m: range) =
814821
if not (langVersion.SupportsFeature langFeature) then
815822
let featureStr = LanguageVersion.GetFeatureString langFeature

src/Compiler/Facilities/DiagnosticsLogger.fsi

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -425,6 +425,13 @@ val NewlineifyErrorString: message: string -> string
425425
/// which is decoded by the IDE with 'NewlineifyErrorString' back into newlines, so that multi-line errors can be displayed in QuickInfo
426426
val NormalizeErrorString: text: string -> string
427427

428+
/// Indicates whether a language feature check should be skipped. Typically used in recursive functions
429+
/// where we don't want repeated recursive calls to raise the same diagnostic multiple times.
430+
[<RequireQualifiedAccess>]
431+
type SuppressLanguageFeatureCheck =
432+
| Yes
433+
| No
434+
428435
val checkLanguageFeatureError: langVersion: LanguageVersion -> langFeature: LanguageFeature -> m: range -> unit
429436

430437
val checkLanguageFeatureAndRecover: langVersion: LanguageVersion -> langFeature: LanguageFeature -> m: range -> unit

src/Compiler/TypedTree/TypedTreeOps.fs

Lines changed: 55 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -9659,11 +9659,13 @@ let EvalArithBinOp (opInt8, opInt16, opInt32, opInt64, opUInt8, opUInt16, opUInt
96599659
with :? System.OverflowException -> error (Error ( FSComp.SR.tastConstantExpressionOverflow(), m))
96609660

96619661
// See also PostTypeCheckSemanticChecks.CheckAttribArgExpr, which must match this precisely
9662-
let rec EvalAttribArgExpr (g: TcGlobals) x =
9662+
let rec EvalAttribArgExpr suppressLangFeatureCheck (g: TcGlobals) (x: Expr) =
96639663
let ignore (_x: 'a) = Unchecked.defaultof<'a>
96649664
let ignore2 (_x: 'a) (_y: 'a) = Unchecked.defaultof<'a>
96659665

9666-
let arithmeticInLiteralsEnabled = g.langVersion.SupportsFeature LanguageFeature.ArithmeticInLiterals
9666+
let inline checkFeature() =
9667+
if suppressLangFeatureCheck = SuppressLanguageFeatureCheck.No then
9668+
checkLanguageFeatureAndRecover g.langVersion LanguageFeature.ArithmeticInLiterals x.Range
96679669

96689670
match x with
96699671

@@ -9693,80 +9695,92 @@ let rec EvalAttribArgExpr (g: TcGlobals) x =
96939695
| TypeOfExpr g _ -> x
96949696
| TypeDefOfExpr g _ -> x
96959697
| Expr.Op (TOp.Coerce, _, [arg], _) ->
9696-
EvalAttribArgExpr g arg
9698+
EvalAttribArgExpr suppressLangFeatureCheck g arg
96979699
| EnumExpr g arg1 ->
9698-
EvalAttribArgExpr g arg1
9700+
EvalAttribArgExpr suppressLangFeatureCheck g arg1
96999701
// Detect bitwise or of attribute flags
97009702
| AttribBitwiseOrExpr g (arg1, arg2) ->
9701-
let v1 = EvalAttribArgExpr g arg1
9703+
let v1 = EvalAttribArgExpr suppressLangFeatureCheck g arg1
97029704

97039705
match v1 with
97049706
| IntegerConstExpr ->
9705-
EvalArithBinOp ((|||), (|||), (|||), (|||), (|||), (|||), (|||), (|||), ignore2, ignore2) v1 (EvalAttribArgExpr g arg2)
9707+
EvalArithBinOp ((|||), (|||), (|||), (|||), (|||), (|||), (|||), (|||), ignore2, ignore2) v1 (EvalAttribArgExpr suppressLangFeatureCheck g arg2)
97069708
| _ ->
97079709
errorR (Error ( FSComp.SR.tastNotAConstantExpression(), x.Range))
97089710
x
97099711
| SpecificBinopExpr g g.unchecked_addition_vref (arg1, arg2) ->
9710-
// At compile-time we check arithmetic
9711-
let v1, v2 = EvalAttribArgExpr g arg1, EvalAttribArgExpr g arg2
9712+
let v1, v2 = EvalAttribArgExpr suppressLangFeatureCheck g arg1, EvalAttribArgExpr suppressLangFeatureCheck g arg2
9713+
97129714
match v1, v2 with
97139715
| Expr.Const (Const.String x1, m, ty), Expr.Const (Const.String x2, _, _) ->
97149716
Expr.Const (Const.String (x1 + x2), m, ty)
9715-
| Expr.Const (Const.Char x1, m, ty), Expr.Const (Const.Char x2, _, _) when arithmeticInLiteralsEnabled ->
9717+
| Expr.Const (Const.Char x1, m, ty), Expr.Const (Const.Char x2, _, _) ->
9718+
checkFeature()
97169719
Expr.Const (Const.Char (x1 + x2), m, ty)
97179720
| _ ->
9718-
if arithmeticInLiteralsEnabled then
9719-
EvalArithBinOp (Checked.(+), Checked.(+), Checked.(+), Checked.(+), Checked.(+), Checked.(+), Checked.(+), Checked.(+), Checked.(+), Checked.(+)) v1 v2
9720-
else
9721-
errorR (Error ( FSComp.SR.tastNotAConstantExpression(), x.Range))
9722-
x
9723-
| SpecificBinopExpr g g.unchecked_subtraction_vref (arg1, arg2) when arithmeticInLiteralsEnabled ->
9724-
let v1, v2 = EvalAttribArgExpr g arg1, EvalAttribArgExpr g arg2
9721+
checkFeature()
9722+
EvalArithBinOp (Checked.(+), Checked.(+), Checked.(+), Checked.(+), Checked.(+), Checked.(+), Checked.(+), Checked.(+), Checked.(+), Checked.(+)) v1 v2
9723+
| SpecificBinopExpr g g.unchecked_subtraction_vref (arg1, arg2) ->
9724+
checkFeature()
9725+
let v1, v2 = EvalAttribArgExpr SuppressLanguageFeatureCheck.Yes g arg1, EvalAttribArgExpr SuppressLanguageFeatureCheck.Yes g arg2
9726+
97259727
match v1, v2 with
97269728
| Expr.Const (Const.Char x1, m, ty), Expr.Const (Const.Char x2, _, _) ->
97279729
Expr.Const (Const.Char (x1 - x2), m, ty)
97289730
| _ ->
97299731
EvalArithBinOp (Checked.(-), Checked.(-), Checked.(-), Checked.(-), Checked.(-), Checked.(-), Checked.(-), Checked.(-), Checked.(-), Checked.(-)) v1 v2
9730-
| SpecificBinopExpr g g.unchecked_multiply_vref (arg1, arg2) when arithmeticInLiteralsEnabled ->
9731-
EvalArithBinOp (Checked.(*), Checked.(*), Checked.(*), Checked.(*), Checked.(*), Checked.(*), Checked.(*), Checked.(*), Checked.(*), Checked.(*)) (EvalAttribArgExpr g arg1) (EvalAttribArgExpr g arg2)
9732-
| SpecificBinopExpr g g.unchecked_division_vref (arg1, arg2) when arithmeticInLiteralsEnabled ->
9733-
EvalArithBinOp ((/), (/), (/), (/), (/), (/), (/), (/), (/), (/)) (EvalAttribArgExpr g arg1) (EvalAttribArgExpr g arg2)
9734-
| SpecificBinopExpr g g.unchecked_modulus_vref (arg1, arg2) when arithmeticInLiteralsEnabled ->
9735-
EvalArithBinOp ((%), (%), (%), (%), (%), (%), (%), (%), (%), (%)) (EvalAttribArgExpr g arg1) (EvalAttribArgExpr g arg2)
9736-
| SpecificBinopExpr g g.bitwise_shift_left_vref (arg1, arg2) when arithmeticInLiteralsEnabled ->
9737-
EvalArithShiftOp ((<<<), (<<<), (<<<), (<<<), (<<<), (<<<), (<<<), (<<<)) (EvalAttribArgExpr g arg1) (EvalAttribArgExpr g arg2)
9738-
| SpecificBinopExpr g g.bitwise_shift_right_vref (arg1, arg2) when arithmeticInLiteralsEnabled ->
9739-
EvalArithShiftOp ((>>>), (>>>), (>>>), (>>>), (>>>), (>>>), (>>>), (>>>)) (EvalAttribArgExpr g arg1) (EvalAttribArgExpr g arg2)
9740-
| SpecificBinopExpr g g.bitwise_and_vref (arg1, arg2) when arithmeticInLiteralsEnabled ->
9741-
let v1 = EvalAttribArgExpr g arg1
9732+
| SpecificBinopExpr g g.unchecked_multiply_vref (arg1, arg2) ->
9733+
checkFeature()
9734+
EvalArithBinOp (Checked.(*), Checked.(*), Checked.(*), Checked.(*), Checked.(*), Checked.(*), Checked.(*), Checked.(*), Checked.(*), Checked.(*)) (EvalAttribArgExpr SuppressLanguageFeatureCheck.Yes g arg1) (EvalAttribArgExpr SuppressLanguageFeatureCheck.Yes g arg2)
9735+
| SpecificBinopExpr g g.unchecked_division_vref (arg1, arg2) ->
9736+
checkFeature()
9737+
EvalArithBinOp ((/), (/), (/), (/), (/), (/), (/), (/), (/), (/)) (EvalAttribArgExpr SuppressLanguageFeatureCheck.Yes g arg1) (EvalAttribArgExpr SuppressLanguageFeatureCheck.Yes g arg2)
9738+
| SpecificBinopExpr g g.unchecked_modulus_vref (arg1, arg2) ->
9739+
checkFeature()
9740+
EvalArithBinOp ((%), (%), (%), (%), (%), (%), (%), (%), (%), (%)) (EvalAttribArgExpr SuppressLanguageFeatureCheck.Yes g arg1) (EvalAttribArgExpr SuppressLanguageFeatureCheck.Yes g arg2)
9741+
| SpecificBinopExpr g g.bitwise_shift_left_vref (arg1, arg2) ->
9742+
checkFeature()
9743+
EvalArithShiftOp ((<<<), (<<<), (<<<), (<<<), (<<<), (<<<), (<<<), (<<<)) (EvalAttribArgExpr SuppressLanguageFeatureCheck.Yes g arg1) (EvalAttribArgExpr SuppressLanguageFeatureCheck.Yes g arg2)
9744+
| SpecificBinopExpr g g.bitwise_shift_right_vref (arg1, arg2) ->
9745+
checkFeature()
9746+
EvalArithShiftOp ((>>>), (>>>), (>>>), (>>>), (>>>), (>>>), (>>>), (>>>)) (EvalAttribArgExpr SuppressLanguageFeatureCheck.Yes g arg1) (EvalAttribArgExpr SuppressLanguageFeatureCheck.Yes g arg2)
9747+
| SpecificBinopExpr g g.bitwise_and_vref (arg1, arg2) ->
9748+
checkFeature()
9749+
let v1 = EvalAttribArgExpr SuppressLanguageFeatureCheck.Yes g arg1
97429750

97439751
match v1 with
97449752
| IntegerConstExpr ->
9745-
EvalArithBinOp ((&&&), (&&&), (&&&), (&&&), (&&&), (&&&), (&&&), (&&&), ignore2, ignore2) v1 (EvalAttribArgExpr g arg2)
9753+
EvalArithBinOp ((&&&), (&&&), (&&&), (&&&), (&&&), (&&&), (&&&), (&&&), ignore2, ignore2) v1 (EvalAttribArgExpr SuppressLanguageFeatureCheck.Yes g arg2)
97469754
| _ ->
97479755
errorR (Error ( FSComp.SR.tastNotAConstantExpression(), x.Range))
97489756
x
9749-
| SpecificUnopExpr g g.unchecked_unary_minus_vref arg1 when arithmeticInLiteralsEnabled ->
9750-
let v1 = EvalAttribArgExpr g arg1
9757+
| SpecificUnopExpr g g.unchecked_unary_minus_vref arg1 ->
9758+
checkFeature()
9759+
let v1 = EvalAttribArgExpr SuppressLanguageFeatureCheck.Yes g arg1
97519760

97529761
match v1 with
97539762
| SignedConstExpr ->
97549763
EvalArithUnOp (Checked.(~-), Checked.(~-), Checked.(~-), Checked.(~-), ignore, ignore, ignore, ignore, Checked.(~-), Checked.(~-)) v1
97559764
| _ ->
97569765
errorR (Error ( FSComp.SR.tastNotAConstantExpression(), v1.Range))
97579766
x
9758-
| SpecificUnopExpr g g.unchecked_unary_plus_vref arg1 when arithmeticInLiteralsEnabled ->
9759-
EvalArithUnOp ((~+), (~+), (~+), (~+), (~+), (~+), (~+), (~+), (~+), (~+)) (EvalAttribArgExpr g arg1)
9760-
| SpecificUnopExpr g g.unchecked_unary_not_vref arg1 when arithmeticInLiteralsEnabled ->
9761-
match EvalAttribArgExpr g arg1 with
9767+
| SpecificUnopExpr g g.unchecked_unary_plus_vref arg1 ->
9768+
checkFeature()
9769+
EvalArithUnOp ((~+), (~+), (~+), (~+), (~+), (~+), (~+), (~+), (~+), (~+)) (EvalAttribArgExpr SuppressLanguageFeatureCheck.Yes g arg1)
9770+
| SpecificUnopExpr g g.unchecked_unary_not_vref arg1 ->
9771+
checkFeature()
9772+
9773+
match EvalAttribArgExpr SuppressLanguageFeatureCheck.Yes g arg1 with
97629774
| Expr.Const (Const.Bool value, m, ty) ->
97639775
Expr.Const (Const.Bool (not value), m, ty)
97649776
| expr ->
97659777
errorR (Error ( FSComp.SR.tastNotAConstantExpression(), expr.Range))
97669778
x
97679779
// Detect logical operations on booleans, which are represented as a match expression
9768-
| Expr.Match (decision = TDSwitch (input = input; cases = [ TCase (DecisionTreeTest.Const (Const.Bool test), TDSuccess ([], targetNum)) ]); targets = [| TTarget (_, t0, _); TTarget (_, t1, _) |]) when arithmeticInLiteralsEnabled ->
9769-
match EvalAttribArgExpr g (stripDebugPoints input) with
9780+
| Expr.Match (decision = TDSwitch (input = input; cases = [ TCase (DecisionTreeTest.Const (Const.Bool test), TDSuccess ([], targetNum)) ]); targets = [| TTarget (_, t0, _); TTarget (_, t1, _) |]) ->
9781+
checkFeature()
9782+
9783+
match EvalAttribArgExpr SuppressLanguageFeatureCheck.Yes g (stripDebugPoints input) with
97709784
| Expr.Const (Const.Bool value, _, _) ->
97719785
let pass, fail =
97729786
if targetNum = 0 then
@@ -9775,9 +9789,9 @@ let rec EvalAttribArgExpr (g: TcGlobals) x =
97759789
t1, t0
97769790

97779791
if value = test then
9778-
EvalAttribArgExpr g (stripDebugPoints pass)
9792+
EvalAttribArgExpr SuppressLanguageFeatureCheck.Yes g (stripDebugPoints pass)
97799793
else
9780-
EvalAttribArgExpr g (stripDebugPoints fail)
9794+
EvalAttribArgExpr SuppressLanguageFeatureCheck.Yes g (stripDebugPoints fail)
97819795
| _ ->
97829796
errorR (Error ( FSComp.SR.tastNotAConstantExpression(), x.Range))
97839797
x
@@ -9814,10 +9828,10 @@ let EvalLiteralExprOrAttribArg g x =
98149828
match x with
98159829
| Expr.Op (TOp.Coerce, _, [Expr.Op (TOp.Array, [elemTy], args, m)], _)
98169830
| Expr.Op (TOp.Array, [elemTy], args, m) ->
9817-
let args = args |> List.map (EvalAttribArgExpr g)
9831+
let args = args |> List.map (EvalAttribArgExpr SuppressLanguageFeatureCheck.No g)
98189832
Expr.Op (TOp.Array, [elemTy], args, m)
98199833
| _ ->
9820-
EvalAttribArgExpr g x
9834+
EvalAttribArgExpr SuppressLanguageFeatureCheck.No g x
98219835

98229836
// Take into account the fact that some "instance" members are compiled as static
98239837
// members when using CompilationRepresentation.Static, or any non-virtual instance members

src/Compiler/xlf/FSComp.txt.cs.xlf

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -188,8 +188,8 @@
188188
<note />
189189
</trans-unit>
190190
<trans-unit id="featureArithmeticInLiterals">
191-
<source>Allow arithmetic and logical operations in literals</source>
192-
<target state="translated">Povolit aritmetické a logické operace v literálech</target>
191+
<source>Arithmetic and logical operations in literals, enum definitions and attributes</source>
192+
<target state="needs-review-translation">Povolit aritmetické a logické operace v literálech</target>
193193
<note />
194194
</trans-unit>
195195
<trans-unit id="featureAttributesToRightOfModuleKeyword">

src/Compiler/xlf/FSComp.txt.de.xlf

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -188,8 +188,8 @@
188188
<note />
189189
</trans-unit>
190190
<trans-unit id="featureArithmeticInLiterals">
191-
<source>Allow arithmetic and logical operations in literals</source>
192-
<target state="translated">Arithmetische und logische Vorgänge in Literalen zulassen</target>
191+
<source>Arithmetic and logical operations in literals, enum definitions and attributes</source>
192+
<target state="needs-review-translation">Arithmetische und logische Vorgänge in Literalen zulassen</target>
193193
<note />
194194
</trans-unit>
195195
<trans-unit id="featureAttributesToRightOfModuleKeyword">

src/Compiler/xlf/FSComp.txt.es.xlf

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -188,8 +188,8 @@
188188
<note />
189189
</trans-unit>
190190
<trans-unit id="featureArithmeticInLiterals">
191-
<source>Allow arithmetic and logical operations in literals</source>
192-
<target state="translated">Permitir operaciones aritméticas y lógicas en literales</target>
191+
<source>Arithmetic and logical operations in literals, enum definitions and attributes</source>
192+
<target state="needs-review-translation">Permitir operaciones aritméticas y lógicas en literales</target>
193193
<note />
194194
</trans-unit>
195195
<trans-unit id="featureAttributesToRightOfModuleKeyword">

src/Compiler/xlf/FSComp.txt.fr.xlf

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -188,8 +188,8 @@
188188
<note />
189189
</trans-unit>
190190
<trans-unit id="featureArithmeticInLiterals">
191-
<source>Allow arithmetic and logical operations in literals</source>
192-
<target state="translated">Autoriser les opérations arithmétiques et logiques dans les littéraux</target>
191+
<source>Arithmetic and logical operations in literals, enum definitions and attributes</source>
192+
<target state="needs-review-translation">Autoriser les opérations arithmétiques et logiques dans les littéraux</target>
193193
<note />
194194
</trans-unit>
195195
<trans-unit id="featureAttributesToRightOfModuleKeyword">

src/Compiler/xlf/FSComp.txt.it.xlf

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -188,8 +188,8 @@
188188
<note />
189189
</trans-unit>
190190
<trans-unit id="featureArithmeticInLiterals">
191-
<source>Allow arithmetic and logical operations in literals</source>
192-
<target state="translated">Consentire operazioni aritmetiche e logiche in valori letterali</target>
191+
<source>Arithmetic and logical operations in literals, enum definitions and attributes</source>
192+
<target state="needs-review-translation">Consentire operazioni aritmetiche e logiche in valori letterali</target>
193193
<note />
194194
</trans-unit>
195195
<trans-unit id="featureAttributesToRightOfModuleKeyword">

0 commit comments

Comments
 (0)