From af43f16015b920b1362f6db49998fe486718e256 Mon Sep 17 00:00:00 2001 From: Jamie Thompson Date: Thu, 31 Oct 2019 10:33:00 +0100 Subject: [PATCH] alternative version using implicitNotFound --- .../dotty/tools/dotc/core/Definitions.scala | 1 - .../src/dotty/tools/dotc/typer/Typer.scala | 17 ++++---- library/src/scala/util/FromDigits.scala | 29 ++++--------- tests/neg-macros/generic-num-lits.check | 42 ------------------- tests/neg/generic-num-lits.check | 36 ++++++++++++++++ .../generic-num-lits.scala | 12 ++++++ 6 files changed, 64 insertions(+), 73 deletions(-) delete mode 100644 tests/neg-macros/generic-num-lits.check create mode 100644 tests/neg/generic-num-lits.check rename tests/{neg-macros => neg}/generic-num-lits.scala (65%) diff --git a/compiler/src/dotty/tools/dotc/core/Definitions.scala b/compiler/src/dotty/tools/dotc/core/Definitions.scala index a6e4c05183b3..dad30ffc7297 100644 --- a/compiler/src/dotty/tools/dotc/core/Definitions.scala +++ b/compiler/src/dotty/tools/dotc/core/Definitions.scala @@ -689,7 +689,6 @@ class Definitions { @tu lazy val Stats_doRecord: Symbol = StatsModule.requiredMethod("doRecord") @tu lazy val FromDigitsClass: ClassSymbol = ctx.requiredClass("scala.util.FromDigits") - @tu lazy val FromDigits_WithRadixClass: ClassSymbol = ctx.requiredClass("scala.util.FromDigits.WithRadix") @tu lazy val FromDigitsModule: TermSymbol = ctx.requiredModule("scala.util.FromDigits") @tu lazy val FromDigits_fromDigits: Symbol = FromDigitsModule.requiredMethod(nme.fromDigits) @tu lazy val FromDigits_fromRadixDigits: Symbol = FromDigitsModule.requiredMethod(nme.fromRadixDigits) diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index e0639df3dbc3..1c660e753e8a 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -534,17 +534,14 @@ class Typer extends Namer case Decimal => defn.FromDigits_fromDecimalDigits case Floating => defn.FromDigits_fromFloatingDigits inferImplicit(defn.FromDigitsClass.typeRef.appliedTo(target), EmptyTree, tree.span) match { - case SearchSuccess(arg, _, _) => - val summoned = untpd.Apply(untpd.TypedSplice(ref(summoner)), untpd.TypedSplice(arg) :: Nil).setGivenApply() - var fromDigits: untpd.Tree = untpd.Select(summoned, nme.fromDigits).withSpan(tree.span) + case _: SearchSuccess => + val summoned = untpd.TypedSplice(ref(summoner).appliedToType(target)) + val fromDigits = untpd.Select(summoned, nme.fromDigits).withSpan(tree.span) val firstArg = Literal(Constant(digits)) - val otherArgs = - if arg.tpe.widen.classSymbol.isSubClass(defn.FromDigits_WithRadixClass) then - tree.kind match - case Whole(r) if r != 10 => Literal(Constant(r)) :: Nil - case _ => Nil - else - Nil + val otherArgs = tree.kind match { + case Whole(r) if r != 10 => Literal(Constant(r)) :: Nil + case _ => Nil + } var app: untpd.Tree = untpd.Apply(fromDigits, firstArg :: otherArgs) if (ctx.mode.is(Mode.Pattern)) app = untpd.Block(Nil, app) return typed(app, pt) diff --git a/library/src/scala/util/FromDigits.scala b/library/src/scala/util/FromDigits.scala index 27785ac8aec2..dfaca1b8a5a0 100644 --- a/library/src/scala/util/FromDigits.scala +++ b/library/src/scala/util/FromDigits.scala @@ -4,6 +4,7 @@ import quoted._ import quoted.matching._ import internal.Chars.digit2int import annotation.internal.sharable +import annotation.implicitNotFound /** A typeclass for types that admit numeric literals. */ @@ -27,6 +28,7 @@ object FromDigits { /** A subclass of `FromDigits` that also allows to convert whole number literals * with a radix other than 10 */ + @implicitNotFound("Type ${T} does not have a FromDigits instance for whole numbers with radix other than 10.") trait WithRadix[T] extends FromDigits[T] { def fromDigits(digits: String): T = fromDigits(digits, 10) @@ -39,36 +41,23 @@ object FromDigits { /** A subclass of `FromDigits` that also allows to convert number * literals containing a decimal point ".". */ + @implicitNotFound("Type ${T} does not have a FromDigits instance for numbers with a decimal point.") trait Decimal[T] extends FromDigits[T] /** A subclass of `FromDigits`that allows also to convert number * literals containing a decimal point "." or an * exponent `('e' | 'E')['+' | '-']digit digit*`. */ + @implicitNotFound("Type ${T} does not have a FromDigits instance for floating-point numbers.") trait Floating[T] extends Decimal[T] inline def fromDigits[T](given x: FromDigits[T]): x.type = x - inline def fromRadixDigits[T](given x: FromDigits[T]): x.type = - ${summonDigitsImpl[x.type, FromDigits.WithRadix[T], T]('x, "whole numbers with radix other than 10")} - - inline def fromDecimalDigits[T](given x: FromDigits[T]): x.type = - ${summonDigitsImpl[x.type, FromDigits.Decimal[T], T]('x, "numbers with a decimal point")} - - inline def fromFloatingDigits[T](given x: FromDigits[T]): x.type = - ${summonDigitsImpl[x.type, FromDigits.Floating[T], T]('x, "floating-point numbers")} - - private def summonDigitsImpl[Inst <: FromDigits[T], Expected <: FromDigits[T], T](x: Expr[Inst], descriptor: String) - (given qctx: QuoteContext, instance: Type[Inst], t: Type[T], expected: Type[Expected]): Expr[Inst] = - import qctx.tasty.{_, given} - if typeOf[Inst] <:< typeOf[Expected] then - x - else - val msg = s"""|Type ${t.show} does not have a FromDigits instance for $descriptor. - | Found: ${instance.show}(${x.show}) - | Expected: ${expected.show}""".stripMargin - qctx.error(msg, x) - Expr.nullExpr.cast[Inst] + inline def fromRadixDigits[T](given x: FromDigits.WithRadix[T]): x.type = x + + inline def fromDecimalDigits[T](given x: FromDigits.Decimal[T]): x.type = x + + inline def fromFloatingDigits[T](given x: FromDigits.Floating[T]): x.type = x /** The base type for exceptions that can be thrown from * `fromDigits` conversions diff --git a/tests/neg-macros/generic-num-lits.check b/tests/neg-macros/generic-num-lits.check deleted file mode 100644 index f7d726c45784..000000000000 --- a/tests/neg-macros/generic-num-lits.check +++ /dev/null @@ -1,42 +0,0 @@ --- Error: tests/neg-macros/generic-num-lits.scala:2:8 ------------------------------------------------------------------ -2 |val b = 0xcafebabe: BigDecimal // error - | ^ - | Type scala.math.BigDecimal does not have a FromDigits instance for whole numbers with radix other than 10. - | Found: scala.util.FromDigits.BigDecimalFromDigits.type(scala.util.FromDigits.BigDecimalFromDigits) - | Expected: scala.util.FromDigits.WithRadix[scala.math.BigDecimal] - | This location is in code that was inlined at generic-num-lits.scala:2 --- Error: tests/neg-macros/generic-num-lits.scala:3:8 ------------------------------------------------------------------ -3 |val c = 1.3: BigInt // error - | ^ - | Type scala.math.BigInt does not have a FromDigits instance for numbers with a decimal point. - | Found: scala.util.FromDigits.BigIntFromDigits.type(scala.util.FromDigits.BigIntFromDigits) - | Expected: scala.util.FromDigits.Decimal[scala.math.BigInt] - | This location is in code that was inlined at generic-num-lits.scala:3 --- Error: tests/neg-macros/generic-num-lits.scala:4:8 ------------------------------------------------------------------ -4 |val d = 2e500: BigInt // error - | ^ - | Type scala.math.BigInt does not have a FromDigits instance for floating-point numbers. - | Found: scala.util.FromDigits.BigIntFromDigits.type(scala.util.FromDigits.BigIntFromDigits) - | Expected: scala.util.FromDigits.Floating[scala.math.BigInt] - | This location is in code that was inlined at generic-num-lits.scala:4 --- Error: tests/neg-macros/generic-num-lits.scala:7:7 ------------------------------------------------------------------ -7 | case 0xcafebabe: BigDecimal => // error - | ^ - | Type scala.math.BigDecimal does not have a FromDigits instance for whole numbers with radix other than 10. - | Found: scala.util.FromDigits.BigDecimalFromDigits.type(scala.util.FromDigits.BigDecimalFromDigits) - | Expected: scala.util.FromDigits.WithRadix[scala.math.BigDecimal] - | This location is in code that was inlined at generic-num-lits.scala:7 --- Error: tests/neg-macros/generic-num-lits.scala:11:7 ----------------------------------------------------------------- -11 | case 1.3: BigInt => // error - | ^ - | Type scala.math.BigInt does not have a FromDigits instance for numbers with a decimal point. - | Found: scala.util.FromDigits.BigIntFromDigits.type(scala.util.FromDigits.BigIntFromDigits) - | Expected: scala.util.FromDigits.Decimal[scala.math.BigInt] - | This location is in code that was inlined at generic-num-lits.scala:11 --- Error: tests/neg-macros/generic-num-lits.scala:15:7 ----------------------------------------------------------------- -15 | case 2e500: BigInt => // error - | ^ - | Type scala.math.BigInt does not have a FromDigits instance for floating-point numbers. - | Found: scala.util.FromDigits.BigIntFromDigits.type(scala.util.FromDigits.BigIntFromDigits) - | Expected: scala.util.FromDigits.Floating[scala.math.BigInt] - | This location is in code that was inlined at generic-num-lits.scala:15 diff --git a/tests/neg/generic-num-lits.check b/tests/neg/generic-num-lits.check new file mode 100644 index 000000000000..fdcf2d64c5d4 --- /dev/null +++ b/tests/neg/generic-num-lits.check @@ -0,0 +1,36 @@ +-- Error: tests/neg/generic-num-lits.scala:2:8 ------------------------------------------------------------------------- +2 |val b = 0xcafebabe: BigDecimal // error + | ^ + | Type BigDecimal does not have a FromDigits instance for whole numbers with radix other than 10. +-- Error: tests/neg/generic-num-lits.scala:3:8 ------------------------------------------------------------------------- +3 |val c = 1.3: BigInt // error + | ^ + | Type BigInt does not have a FromDigits instance for numbers with a decimal point. +-- Error: tests/neg/generic-num-lits.scala:4:8 ------------------------------------------------------------------------- +4 |val d = 2e500: BigInt // error + | ^ + | Type BigInt does not have a FromDigits instance for floating-point numbers. +-- Error: tests/neg/generic-num-lits.scala:7:7 ------------------------------------------------------------------------- +7 | case 0xcafebabe: BigDecimal => // error + | ^ + | Type BigDecimal does not have a FromDigits instance for whole numbers with radix other than 10. +-- Error: tests/neg/generic-num-lits.scala:11:7 ------------------------------------------------------------------------ +11 | case 1.3: BigInt => // error + | ^ + | Type BigInt does not have a FromDigits instance for numbers with a decimal point. +-- Error: tests/neg/generic-num-lits.scala:15:7 ------------------------------------------------------------------------ +15 | case 2e500: BigInt => // error + | ^ + | Type BigInt does not have a FromDigits instance for floating-point numbers. +-- Error: tests/neg/generic-num-lits.scala:19:7 ------------------------------------------------------------------------ +19 | case 0xa => // error + | ^ + | Type BigDecimal does not have a FromDigits instance for whole numbers with radix other than 10. +-- Error: tests/neg/generic-num-lits.scala:23:7 ------------------------------------------------------------------------ +23 | case 1.2 => // error + | ^ + | Type BigInt does not have a FromDigits instance for numbers with a decimal point. +-- Error: tests/neg/generic-num-lits.scala:27:7 ------------------------------------------------------------------------ +27 | case 5e10 => // error + | ^ + | Type BigInt does not have a FromDigits instance for floating-point numbers. diff --git a/tests/neg-macros/generic-num-lits.scala b/tests/neg/generic-num-lits.scala similarity index 65% rename from tests/neg-macros/generic-num-lits.scala rename to tests/neg/generic-num-lits.scala index 355873deb293..d1e7e139b6c3 100644 --- a/tests/neg-macros/generic-num-lits.scala +++ b/tests/neg/generic-num-lits.scala @@ -14,3 +14,15 @@ val f = (??? : Any) match val g = (??? : Any) match case 2e500: BigInt => // error () + +val h = (1.3: BigDecimal) match + case 0xa => // error + () + +val i = (1: BigInt) match + case 1.2 => // error + () + +val j = (1: BigInt) match + case 5e10 => // error + ()