diff --git a/.travis.yml b/.travis.yml index af287d607..c1f587f03 100644 --- a/.travis.yml +++ b/.travis.yml @@ -15,6 +15,7 @@ jdk: cache: directories: + - $HOME/.cache/coursier - $HOME/.ivy2/cache - $HOME/.sbt diff --git a/: b/: new file mode 100644 index 000000000..e69de29bb diff --git a/core/src/main/scala/shapeless/hlists.scala b/core/src/main/scala/shapeless/hlists.scala index fee600b01..ab378462c 100644 --- a/core/src/main/scala/shapeless/hlists.scala +++ b/core/src/main/scala/shapeless/hlists.scala @@ -47,7 +47,7 @@ final case class ::[+H, +T <: HList](head : H, tail : T) extends HList { * @author Miles Sabin */ sealed trait HNil extends HList { - def ::[H](h : H) = shapeless.::(h, this) + def ::[H](h: H): H :: HNil = new ::(h, this) override def toString = "HNil" } diff --git a/core/src/main/scala/shapeless/hmap.scala b/core/src/main/scala/shapeless/hmap.scala index e1687d12a..cdba5c2b3 100644 --- a/core/src/main/scala/shapeless/hmap.scala +++ b/core/src/main/scala/shapeless/hmap.scala @@ -19,11 +19,16 @@ package shapeless import poly._ /** - * Heterogenous map with type-level key/value associations that are fixed by an arbitrary - * relation `R`. + * Heterogeneous map with type-level key/value associations that are fixed by an arbitrary relation `R`. * - * `HMap`s extend `Poly` and hence are also polymorphic function values with type-specific - * cases corresponding to the map's type-level key/value associations. + * `HMap`s extend `Poly` and hence are also polymorphic function values with type-specific cases + * corresponding to the map's type-level key/value associations. + * + * Note: keys and values are stored in erased form in an `HMap`. + * Therefore one should be careful when using parameterized types as keys, because it might lead to unsoundness. + * For a parameterized key type `K[T]` it is important that `T` is part of the `hashCode` / `equals` contract: + * - Good: `case class Key[T](id: T)` - `Key[Int](1)` and `Key[String]("1")` have different hash codes. + * - Bad: `case class Key[T](id: Int)` - `Key[Int](1)` and `Key[String](1)` have different types but the same hash code. */ class HMap[R[_, _]](underlying : Map[Any, Any] = Map.empty) extends Poly1 { def get[K, V](k : K)(implicit ev : R[K, V]) : Option[V] = underlying.get(k).asInstanceOf[Option[V]] diff --git a/core/src/main/scala/shapeless/syntax/std/tuples.scala b/core/src/main/scala/shapeless/syntax/std/tuples.scala index 2bde7db13..778399acb 100644 --- a/core/src/main/scala/shapeless/syntax/std/tuples.scala +++ b/core/src/main/scala/shapeless/syntax/std/tuples.scala @@ -20,16 +20,11 @@ package std import shapeless.ops.hlist.ProductToHList -trait LowPriorityTuple { - implicit def productTupleOps[P <: Product](p: P): TupleOps[P] = new TupleOps(p) -} - -object tuple extends LowPriorityTuple { - implicit def unitTupleOps(u: Unit): TupleOps[Unit] = new TupleOps(u) - +object tuple { // Duplicated here from shapeless.HList so that explicit imports of tuple._ don't // clobber the conversion to HListOps. implicit def hlistOps[L <: HList](l : L) : HListOps[L] = new HListOps(l) + implicit def productTupleOps[P: IsTuple](p: P): TupleOps[P] = new TupleOps(p) } final class TupleOps[T](t: T) extends Serializable { diff --git a/core/src/main/scala/shapeless/test/typechecking.scala b/core/src/main/scala/shapeless/test/typechecking.scala index 518dc9cc7..55b9e07b5 100644 --- a/core/src/main/scala/shapeless/test/typechecking.scala +++ b/core/src/main/scala/shapeless/test/typechecking.scala @@ -21,7 +21,7 @@ import scala.language.experimental.macros import java.util.regex.Pattern -import scala.reflect.macros.{ whitebox, ParseException, TypecheckException } +import scala.reflect.macros.{ blackbox, ParseException, TypecheckException } /** * A utility which ensures that a code fragment does not typecheck. @@ -34,7 +34,7 @@ object illTyped { } @macrocompat.bundle -class IllTypedMacros(val c: whitebox.Context) { +class IllTypedMacros(val c: blackbox.Context) { import c.universe._ def applyImplNoExp(code: Tree): Tree = applyImpl(code, null) @@ -55,7 +55,7 @@ class IllTypedMacros(val c: whitebox.Context) { } catch { case e: TypecheckException => val msg = e.getMessage - if((expected ne null) && !(expPat.matcher(msg)).matches) + if((expected ne null) && !expPat.matcher(msg).matches) c.error(c.enclosingPosition, "Type-checking failed in an unexpected way.\n"+expMsg+"\nActual error: "+msg) case e: ParseException => c.error(c.enclosingPosition, s"Parsing failed.\n${e.getMessage}") diff --git a/core/src/test/scala/shapeless/hlist.scala b/core/src/test/scala/shapeless/hlist.scala index 2b615cea8..07a119d20 100644 --- a/core/src/test/scala/shapeless/hlist.scala +++ b/core/src/test/scala/shapeless/hlist.scala @@ -3497,4 +3497,11 @@ class HListTests { assertEquals((), (HNil: HNil).toProduct) assertEquals(HNil, ().toHList) } + + @Test + def testAuxImplicits: Unit = { + the[SplitRight.Aux[String :: Int :: Boolean :: HNil, Int, String :: Int :: HNil, Boolean :: HNil]] + the[Grouper.Aux[Int :: String :: Boolean :: HNil, _2, _1, (Int, String) :: (String, Boolean) :: HNil]] + the[PaddedGrouper.Aux[Int :: String :: Boolean :: HNil, _2, _2, Long :: HNil, (Int, String) :: (Boolean, Long) :: HNil]] + } } diff --git a/core/src/test/scala/shapeless/singletons.scala b/core/src/test/scala/shapeless/singletons.scala index 048ccafab..b167eadd9 100644 --- a/core/src/test/scala/shapeless/singletons.scala +++ b/core/src/test/scala/shapeless/singletons.scala @@ -696,3 +696,19 @@ package UnrefineTest { new Bar(Foo.from( LabelledGeneric[FooBar] )).modify(Symbol("y").narrow, (_: Int) * 2).keys } } + +object VarArgsWitnessTest { + trait Base + class Dep[B <: Base with Singleton] + object instance extends Base + val dep = new Dep[instance.type] + + def varargs[B <: Base with Singleton](el: Dep[B]*)(implicit w: Witness.Aux[B]): Unit = () + def poly[B <: Base with Singleton](el1: Dep[B])(implicit w: Witness.Aux[B]): Unit = () + def poly[B <: Base with Singleton](el1: Dep[B], el2: Dep[B])(implicit w: Witness.Aux[B]): Unit = () + + varargs(dep) + varargs(dep, dep) + poly(dep) + poly(dep, dep) +} diff --git a/core/src/test/scala/shapeless/tuples.scala b/core/src/test/scala/shapeless/tuples.scala index f185d140f..0fdb01a0e 100644 --- a/core/src/test/scala/shapeless/tuples.scala +++ b/core/src/test/scala/shapeless/tuples.scala @@ -18,7 +18,7 @@ package shapeless import org.junit.Test import org.junit.Assert._ - +import shapeless.ops.tuple.IsComposite import shapeless.test._ import testutil._ @@ -49,6 +49,7 @@ class TupleTests { case class Pear() extends Fruit case class Banana() extends Fruit + case class Foo(i: Int, s: String) type PWS = Product with Serializable with Fruit type YYYY = (Any, Any, Any, Any) @@ -1462,8 +1463,7 @@ class TupleTests { @Test def testPropagation: Unit = { - def useHead[P <: Product](p: P)(implicit ic: ops.tuple.IsComposite[P]) = p.head - + def useHead[P: IsTuple: IsComposite](p: P) = p.head val h = useHead((23, "foo", true)) typed[Int](h) } @@ -1973,4 +1973,11 @@ class TupleTests { (23, "foo").align[(String, String)] """) } + + @Test + def testCompatibilityWithProductSyntax: Unit = { + import syntax.std.product._ + assertEquals(List(2, "a"), Foo(2, "a").to[List]) + assertEquals(List(2, "a"), (2, "a").to[List]) + } }