diff --git a/core/src/main/scala/shapeless/typeable.scala b/core/src/main/scala/shapeless/typeable.scala index 781979520..6a9590831 100644 --- a/core/src/main/scala/shapeless/typeable.scala +++ b/core/src/main/scala/shapeless/typeable.scala @@ -119,6 +119,14 @@ object Typeable extends TupleTypeableInstances with LowPriorityTypeable { def describe = name } + /** Typeable instance defined by a partial function and given an explicit name */ + def partialFunctionTypeable[T](pf: PartialFunction[Any, T], name: => String): Typeable[T] = + new Typeable[T] { + val caster = pf.lift + def cast(t: Any) = caster(t) + def describe = name + } + /** Typeable instance for singleton value types */ def valueSingletonTypeable[T](value: T, name: String): Typeable[T] = new Typeable[T] { @@ -454,8 +462,11 @@ class TypeableMacros(val c: blackbox.Context) extends SingletonTypeUtils { } else { c.abort(c.enclosingPosition, s"No default Typeable for type $tpe capturing an outer type variable") } + } else if (tsym.isStatic || tsym.isFinal || (tsym.isClass && tsym.asClass.isTrait)) { + // scala/bug#4440 Final inner classes and traits have no outer accessor. + q"_root_.shapeless.Typeable.namedSimpleTypeable(classOf[$tpe], ${nameOf(tsym)})" } else { - q"""_root_.shapeless.Typeable.namedSimpleTypeable(_root_.scala.Predef.classOf[$tpe], ${nameOf(tsym)})""" + q"_root_.shapeless.Typeable.partialFunctionTypeable({ case x: $tpe => x }, ${nameOf(tsym)})" } } } diff --git a/core/src/test/scala/shapeless/typeable.scala b/core/src/test/scala/shapeless/typeable.scala index 9440b5785..46b4dc59b 100644 --- a/core/src/test/scala/shapeless/typeable.scala +++ b/core/src/test/scala/shapeless/typeable.scala @@ -25,6 +25,11 @@ class TypeableTests { import syntax.typeable._ import test._ + class Outer { + class Inner + val inner = new Inner + } + @Test def testPrimitives: Unit = { val b: Any = 23.toByte @@ -684,4 +689,12 @@ class TypeableTests { assertEquals("|+|.type", Typeable[|+|.type].describe) assertEquals("Symbol('witness)", Typeable[witness.T].describe) } + + @Test + def testInnerClasses(): Unit = { + val outer1 = new Outer + val outer2 = new Outer + assertEquals(None, outer1.inner.cast[outer2.Inner]) + assertEquals(Some(outer1.inner), outer1.inner.cast[outer1.Inner]) + } }