Skip to content

Commit

Permalink
Merge pull request #26 from estatico/25-rm-classtag
Browse files Browse the repository at this point in the history
refs #25: Removing ClassTag instances
  • Loading branch information
carymrobbins authored Apr 21, 2018
2 parents 7e9afaa + 8d5c0d3 commit bbfac0e
Show file tree
Hide file tree
Showing 2 changed files with 30 additions and 47 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ private[macros] class NewTypeMacros(val c: blackbox.Context)
def mkBaseTypeDef(clsDef: ClassDef, reprType: Tree, subtype: Boolean) = {
val refinementName = TypeName(clsDef.name.decodedName.toString + "$newtype")
(clsDef.tparams, subtype) match {
case (_, false) => q"type Base = { type $refinementName } "
case (_, false) => q"type Base = Any { type $refinementName } "
case (Nil, true) => q"type Base = $reprType"
case (tparams, true) => q"type Base[..$tparams] = $reprType"
}
Expand All @@ -118,14 +118,12 @@ private[macros] class NewTypeMacros(val c: blackbox.Context)
val reprType = valDef.tpt
val typesTraitName = TypeName(clsName.toString + '$' + "Types")
val tparams = clsDef.tparams
val classTagName = TermName(clsName + "$classTag")
val companionExtraDefs =
generateClassTag(classTagName, valDef, tparamsNoVar, tparamNames, subtype) ::
maybeGenerateApplyMethod(clsDef, valDef, tparamsNoVar, tparamNames) :::
maybeGenerateUnapplyMethod(clsDef, valDef, tparamsNoVar, tparamNames) :::
maybeGenerateOpsDef(clsDef, valDef, tparamsNoVar, tparamNames) :::
generateCoercibleInstances(tparamsNoVar, tparamNames, tparamsWild) :::
generateDerivingMethods(tparamsNoVar, tparamNames, tparamsWild)
maybeGenerateApplyMethod(clsDef, valDef, tparamsNoVar, tparamNames) :::
maybeGenerateUnapplyMethod(clsDef, valDef, tparamsNoVar, tparamNames) :::
maybeGenerateOpsDef(clsDef, valDef, tparamsNoVar, tparamNames) :::
generateCoercibleInstances(tparamsNoVar, tparamNames, tparamsWild) :::
generateDerivingMethods(tparamsNoVar, tparamNames, tparamsWild)

val newtypeObjParents = objParents :+ tq"$typesTraitName"
val newtypeObjDef = q"""
Expand All @@ -152,7 +150,7 @@ private[macros] class NewTypeMacros(val c: blackbox.Context)
trait $typesTraitName {
type Repr = $reprType
$baseTypeDef
trait Tag
trait Tag extends Any
${mkTypeTypeDef(clsDef, tparamNames, subtype)}
}
$newtypeObjDef
Expand All @@ -163,7 +161,7 @@ private[macros] class NewTypeMacros(val c: blackbox.Context)
trait $typesTraitName {
type Repr[..$tparams] = $reprType
$baseTypeDef
trait Tag[..$tparams]
trait Tag[..$tparams] extends Any
$typeTypeDef
}
$newtypeObjDef
Expand Down Expand Up @@ -352,28 +350,4 @@ private[macros] class NewTypeMacros(val c: blackbox.Context)
fail(s"newtypes do not support inheritance; illegal supertypes: ${unsupported.mkString(", ")}")
}
}

// The erasure of opaque newtypes is always Object.
def generateClassTag(
name: TermName, valDef: ValDef, tparamsNoVar: List[TypeDef], tparamNames: List[TypeName],
subtype: Boolean
): Tree = {
def mkClassTag(t: Tree) = q"implicitly[$ClassTagCls[$t]]"
def objClassTag = mkClassTag(tq"$ObjectCls")
(tparamsNoVar, subtype) match {
case (Nil, false) =>
q"implicit def $name: $ClassTagCls[Type] = $objClassTag.asInstanceOf[$ClassTagCls[Type]]"
case (ts, false) =>
q"""implicit def $name[..$ts]: $ClassTagCls[Type[..$tparamNames]] =
$objClassTag.asInstanceOf[$ClassTagCls[Type[..$tparamNames]]]"""
case (Nil, true) =>
q"""implicit def $name(implicit ct: $ClassTagCls[${valDef.tpt}]): $ClassTagCls[Type] =
ct.asInstanceOf[$ClassTagCls[Type]]"""
case (ts, true) =>
q"""implicit def $name[..$ts](
implicit ct: $ClassTagCls[${valDef.tpt}]
): $ClassTagCls[Type[..$tparamNames]] =
ct.asInstanceOf[$ClassTagCls[Type[..$tparamNames]]]"""
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,6 @@ class NewTypeMacrosTest extends FlatSpec with Matchers {
res shouldBe Foo(1)
}

// The newtype should be implemented in such a way as to prevent type pattern matching.
// Unfortunately, assertDoesNotCompile and assertTypeError fail at compile time with this,
// so we're not able to make a test case for it yet.
// res match {
// case _: Foo => ???
// case _ => ???
// }

it should "generate an accessor extension method" in {
Foo(1).value shouldBe 1
}
Expand All @@ -44,9 +36,12 @@ class NewTypeMacrosTest extends FlatSpec with Matchers {
val res: Bar = Bar(2).twice
res shouldBe 4
}

it should "work in arrays" in {
val foo = Foo(313)
Array(foo).head shouldBe foo
// See https://github.com/estatico/scala-newtype/issues/25
// Array(foo).head shouldBe foo
Array[Int](313).asInstanceOf[Array[Foo]].head shouldBe foo
}

behavior of "@newtype class"
Expand Down Expand Up @@ -106,7 +101,9 @@ class NewTypeMacrosTest extends FlatSpec with Matchers {
it should "work in arrays" in {
val repr = Set(Option("newtypes"))
val ot = OptionT(repr)
Array(ot).head shouldBe ot
// See https://github.com/estatico/scala-newtype/issues/25
// Array(ot).head shouldBe ot
Array(repr).asInstanceOf[Array[OptionT[Set, String]]].head shouldBe ot
}

behavior of "@newtype with type bounds"
Expand Down Expand Up @@ -255,7 +252,7 @@ class NewTypeMacrosTest extends FlatSpec with Matchers {
val x1 = x0.filter(_.contains("a"))
x1.isEmpty shouldBe true
x1 shouldBe Maybe.empty
x1 shouldBe null
x1 shouldBe (null: Any)

val y0 = Maybe("apple")
val y1 = y0.filter(_.contains("a"))
Expand All @@ -266,13 +263,13 @@ class NewTypeMacrosTest extends FlatSpec with Matchers {
val z1 = z0.filter(_.contains("z"))
z1.isEmpty shouldBe true
z1 shouldBe Maybe.empty
z1 shouldBe null
z1 shouldBe (null: Any)

val n0 = Maybe(0)
val n1 = n0.filter(_ > 0)
n1.isEmpty shouldBe true
n1 shouldBe Maybe.empty
n1 shouldBe null
n1 shouldBe (null: Any)
}

behavior of "nested @newtypes"
Expand Down Expand Up @@ -352,6 +349,18 @@ class NewTypeMacrosTest extends FlatSpec with Matchers {
assertTypeError(""" "x" match { case Y1(x) => x }""")
assertTypeError(""" 1 match { case Y1(x) => x }""")
}

// Unfortunately, we don't have a way to assert on compiler warnings, which is
// what happens with the code below. If we run with -Xfatal-warnings, the test
// won't compile at all, so leaving here to do manual checking until scalatest
// can provide support for this.
// See https://github.com/scalatest/scalatest/issues/1352
//"type-based pattern matching" should "emit compiler warnings" in {
// assertDoesNotCompile("Foo(1) match { case x: Foo => x }")
// assertDoesNotCompile("1 match { case x: Foo => x }")
// assertDoesNotCompile(""" "foo" match { case x: Foo => x }""")
// assertDoesNotCompile("(1: Any) match { case x: Foo => x }")
//}
}

object NewTypeMacrosTest {
Expand Down

0 comments on commit bbfac0e

Please sign in to comment.