Skip to content

Commit

Permalink
alternative version using implicitNotFound
Browse files Browse the repository at this point in the history
  • Loading branch information
bishabosha committed Oct 31, 2019
1 parent 8f2ee3d commit af43f16
Show file tree
Hide file tree
Showing 6 changed files with 64 additions and 73 deletions.
1 change: 0 additions & 1 deletion compiler/src/dotty/tools/dotc/core/Definitions.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
17 changes: 7 additions & 10 deletions compiler/src/dotty/tools/dotc/typer/Typer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
29 changes: 9 additions & 20 deletions library/src/scala/util/FromDigits.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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.
*/
Expand All @@ -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)

Expand All @@ -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
Expand Down
42 changes: 0 additions & 42 deletions tests/neg-macros/generic-num-lits.check

This file was deleted.

36 changes: 36 additions & 0 deletions tests/neg/generic-num-lits.check
Original file line number Diff line number Diff line change
@@ -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.
Original file line number Diff line number Diff line change
Expand Up @@ -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
()

0 comments on commit af43f16

Please sign in to comment.