From a42588df2e5564d720026c3a2912254088b784da Mon Sep 17 00:00:00 2001 From: Seth Tisue Date: Thu, 21 Oct 2021 11:20:57 -0700 Subject: [PATCH] Warn on lossy conversion of literals & constants Co-authored-by: Dale Wijnand --- .../tools/dotc/reporting/ErrorMessageID.scala | 3 +- .../dotty/tools/dotc/reporting/messages.scala | 7 +++++ .../src/dotty/tools/dotc/typer/Typer.scala | 12 +++++++- .../fatal-warnings/i11333.check | 30 +++++++++++++++++++ .../fatal-warnings/i11333.scala | 12 ++++++++ 5 files changed, 62 insertions(+), 2 deletions(-) create mode 100644 tests/neg-custom-args/fatal-warnings/i11333.check create mode 100644 tests/neg-custom-args/fatal-warnings/i11333.scala diff --git a/compiler/src/dotty/tools/dotc/reporting/ErrorMessageID.scala b/compiler/src/dotty/tools/dotc/reporting/ErrorMessageID.scala index d1b95d31dd62..342e640ca819 100644 --- a/compiler/src/dotty/tools/dotc/reporting/ErrorMessageID.scala +++ b/compiler/src/dotty/tools/dotc/reporting/ErrorMessageID.scala @@ -174,7 +174,8 @@ enum ErrorMessageID extends java.lang.Enum[ErrorMessageID]: OverrideTypeMismatchErrorID, OverrideErrorID, MatchableWarningID, - CannotExtendFunctionID + CannotExtendFunctionID, + LossyWideningConstantConversionID def errorNumber = ordinal - 2 diff --git a/compiler/src/dotty/tools/dotc/reporting/messages.scala b/compiler/src/dotty/tools/dotc/reporting/messages.scala index 063ba96410c8..43f7f6f5ddb3 100644 --- a/compiler/src/dotty/tools/dotc/reporting/messages.scala +++ b/compiler/src/dotty/tools/dotc/reporting/messages.scala @@ -807,6 +807,13 @@ import transform.SymUtils._ |""" } + class LossyWideningConstantConversion(sourceType: Type, targetType: Type)(using Context) + extends Message(LossyWideningConstantConversionID): + def kind = "Lossy Conversion" + def msg = em"""|Widening conversion from $sourceType to $targetType loses precision. + |Write `.to$targetType` instead.""".stripMargin + def explain = "" + class PatternMatchExhaustivity(uncoveredFn: => String, hasMore: Boolean)(using Context) extends Message(PatternMatchExhaustivityID) { def kind = "Pattern Match Exhaustivity" diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index 433075f73fd9..db50ea4d0cb2 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -719,7 +719,11 @@ class Typer extends Namer else if (target.isRef(defn.FloatClass)) tree.kind match { case Whole(16) => // cant parse hex literal as float - case _ => return lit(floatFromDigits(digits)) + case _ => + val float = floatFromDigits(digits) + if digits.toIntOption.exists(_ != float.toInt) then + report.warning(LossyWideningConstantConversion(defn.IntType, target), tree.srcPos) + return lit(float) } else if (target.isRef(defn.DoubleClass)) tree.kind match { @@ -3733,6 +3737,12 @@ class Typer extends Namer case ConstantType(x) => val converted = x.convertTo(pt) if converted != null && (converted ne x) then + val cls = pt.classSymbol + if x.tag == IntTag && cls == defn.FloatClass && x.intValue.toFloat.toInt != x.intValue + || x.tag == LongTag && cls == defn.FloatClass && x.longValue.toFloat.toLong != x.longValue + || x.tag == LongTag && cls == defn.DoubleClass && x.longValue.toDouble.toLong != x.longValue + then + report.warning(LossyWideningConstantConversion(x.tpe, pt), tree.srcPos) return adaptConstant(tree, ConstantType(converted)) case _ => diff --git a/tests/neg-custom-args/fatal-warnings/i11333.check b/tests/neg-custom-args/fatal-warnings/i11333.check new file mode 100644 index 000000000000..beef37c6460a --- /dev/null +++ b/tests/neg-custom-args/fatal-warnings/i11333.check @@ -0,0 +1,30 @@ +-- [E167] Lossy Conversion Error: tests/neg-custom-args/fatal-warnings/i11333.scala:2:19 ------------------------------- +2 | val f1: Float = 123456789 // error + | ^^^^^^^^^ + | Widening conversion from Int to Float loses precision. + | Write `.toFloat` instead. +-- [E167] Lossy Conversion Error: tests/neg-custom-args/fatal-warnings/i11333.scala:3:19 ------------------------------- +3 | val d1: Double = 1234567890123456789L // error + | ^^^^^^^^^^^^^^^^^^^^ + | Widening conversion from Long to Double loses precision. + | Write `.toDouble` instead. +-- [E167] Lossy Conversion Error: tests/neg-custom-args/fatal-warnings/i11333.scala:4:19 ------------------------------- +4 | val f2: Float = 123456789L // error + | ^^^^^^^^^^ + | Widening conversion from Long to Float loses precision. + | Write `.toFloat` instead. +-- [E167] Lossy Conversion Error: tests/neg-custom-args/fatal-warnings/i11333.scala:10:21 ------------------------------ +10 | val f1_b: Float = i1 // error + | ^^ + | Widening conversion from Int to Float loses precision. + | Write `.toFloat` instead. +-- [E167] Lossy Conversion Error: tests/neg-custom-args/fatal-warnings/i11333.scala:11:21 ------------------------------ +11 | val d1_b: Double = l1 // error + | ^^ + | Widening conversion from Long to Double loses precision. + | Write `.toDouble` instead. +-- [E167] Lossy Conversion Error: tests/neg-custom-args/fatal-warnings/i11333.scala:12:21 ------------------------------ +12 | val f2_b: Float = l2 // error + | ^^ + | Widening conversion from Long to Float loses precision. + | Write `.toFloat` instead. diff --git a/tests/neg-custom-args/fatal-warnings/i11333.scala b/tests/neg-custom-args/fatal-warnings/i11333.scala new file mode 100644 index 000000000000..3ba39efeb29e --- /dev/null +++ b/tests/neg-custom-args/fatal-warnings/i11333.scala @@ -0,0 +1,12 @@ +class C: + val f1: Float = 123456789 // error + val d1: Double = 1234567890123456789L // error + val f2: Float = 123456789L // error + + inline val i1 = 123456789 + inline val l1 = 1234567890123456789L + inline val l2 = 123456789L + + val f1_b: Float = i1 // error + val d1_b: Double = l1 // error + val f2_b: Float = l2 // error