aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--compiler/src/dotty/tools/dotc/ast/TreeInfo.scala27
-rw-r--r--compiler/src/dotty/tools/dotc/ast/untpd.scala1
-rw-r--r--compiler/src/dotty/tools/dotc/config/Config.scala9
-rw-r--r--compiler/src/dotty/tools/dotc/core/Constraint.scala6
-rw-r--r--compiler/src/dotty/tools/dotc/core/ConstraintHandling.scala24
-rw-r--r--compiler/src/dotty/tools/dotc/core/Definitions.scala5
-rw-r--r--compiler/src/dotty/tools/dotc/core/Flags.scala9
-rw-r--r--compiler/src/dotty/tools/dotc/core/OrderingConstraint.scala8
-rw-r--r--compiler/src/dotty/tools/dotc/core/StdNames.scala1
-rw-r--r--compiler/src/dotty/tools/dotc/core/SymDenotations.scala2
-rw-r--r--compiler/src/dotty/tools/dotc/core/TypeComparer.scala33
-rw-r--r--compiler/src/dotty/tools/dotc/core/Types.scala23
-rw-r--r--compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala2
-rw-r--r--compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala4
-rw-r--r--compiler/src/dotty/tools/dotc/reporting/diagnostic/ErrorMessageID.java4
-rw-r--r--compiler/src/dotty/tools/dotc/reporting/diagnostic/messages.scala47
-rw-r--r--compiler/src/dotty/tools/dotc/typer/Applications.scala25
-rw-r--r--compiler/src/dotty/tools/dotc/typer/ErrorReporting.scala12
-rw-r--r--compiler/src/dotty/tools/dotc/typer/Implicits.scala28
-rw-r--r--compiler/src/dotty/tools/dotc/typer/Namer.scala130
-rw-r--r--compiler/src/dotty/tools/dotc/typer/ProtoTypes.scala47
-rw-r--r--compiler/src/dotty/tools/dotc/typer/Typer.scala1
-rw-r--r--compiler/test/dotty/tools/dotc/reporting/ErrorMessagesTests.scala95
-rw-r--r--library/src/dotty/DottyPredef.scala20
-rw-r--r--tests/disabled/pos/depmet_implicit_oopsla_session_simpler.scala (renamed from tests/pending/pos/depmet_implicit_oopsla_session_simpler.scala)3
-rw-r--r--tests/neg/falseView.scala7
-rw-r--r--tests/pending/pos/depmet_implicit_oopsla_session_2.scala5
-rw-r--r--tests/pickling/innerclass.scala3
-rw-r--r--tests/pos-scala2/typerep-stephane.scala (renamed from tests/pos/typerep-stephane.scala)0
-rw-r--r--tests/pos-scala2/viewtest1.scala (renamed from tests/pos/viewtest1.scala)0
-rw-r--r--tests/pos/depmet_implicit_norm_ret.scala (renamed from tests/pending/pos/depmet_implicit_norm_ret.scala)0
-rw-r--r--tests/pos/i2064.scala15
-rw-r--r--tests/pos/t0786.scala2
-rw-r--r--tests/pos/t2421_delitedsl.scala3
-rw-r--r--tests/pos/t5070.scala23
-rw-r--r--tests/pos/t5643.scala19
-rw-r--r--tests/run/HLists-nonvariant.check8
-rw-r--r--tests/run/HLists-nonvariant.scala78
-rw-r--r--tests/run/HLists.check8
-rw-r--r--tests/run/HLists.scala78
-rw-r--r--tests/run/hmap-covariant.scala97
-rw-r--r--tests/run/i2004.check1
-rw-r--r--tests/run/i2004.scala11
-rw-r--r--tests/run/iterator-from.scala6
-rw-r--r--tests/run/puzzler54.scala13
-rw-r--r--tests/run/t8280.check3
-rw-r--r--tests/run/t8280.scala9
47 files changed, 804 insertions, 151 deletions
diff --git a/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala b/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala
index dd890dc50..b00d7df71 100644
--- a/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala
+++ b/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala
@@ -26,23 +26,6 @@ trait TreeInfo[T >: Untyped <: Type] { self: Trees.Instance[T] =>
case _ => false
}
- /** The largest subset of {NoInits, PureInterface} that a
- * trait enclosing this statement can have as flags.
- * Does tree contain an initialization part when seen as a member of a class or trait?
- */
- def defKind(tree: Tree): FlagSet = unsplice(tree) match {
- case EmptyTree | _: Import =>
- NoInitsInterface
- case tree: TypeDef =>
- if (tree.isClassDef) NoInits else NoInitsInterface
- case tree: DefDef =>
- if (tree.unforcedRhs == EmptyTree && tree.vparamss.forall(_.forall(_.unforcedRhs == EmptyTree))) NoInitsInterface else NoInits
- case tree: ValDef =>
- if (tree.unforcedRhs == EmptyTree) NoInitsInterface else EmptyFlags
- case _ =>
- EmptyFlags
- }
-
def isOpAssign(tree: Tree) = unsplice(tree) match {
case Apply(fn, _ :: _) =>
unsplice(fn) match {
@@ -588,6 +571,16 @@ trait TypedTreeInfo extends TreeInfo[Type] { self: Trees.Instance[Type] =>
accum(Nil, root)
}
+ /** The largest subset of {NoInits, PureInterface} that a
+ * trait enclosing this statement can have as flags.
+ */
+ def defKind(tree: Tree): FlagSet = unsplice(tree) match {
+ case EmptyTree | _: Import => NoInitsInterface
+ case tree: TypeDef => if (tree.isClassDef) NoInits else NoInitsInterface
+ case tree: DefDef => if (tree.unforcedRhs == EmptyTree) NoInitsInterface else NoInits
+ case tree: ValDef => if (tree.unforcedRhs == EmptyTree) NoInitsInterface else EmptyFlags
+ case _ => EmptyFlags
+ }
/** The top level classes in this tree, including only those module classes that
* are not a linked class of some other class in the result.
diff --git a/compiler/src/dotty/tools/dotc/ast/untpd.scala b/compiler/src/dotty/tools/dotc/ast/untpd.scala
index 99bc3240f..8ca91590f 100644
--- a/compiler/src/dotty/tools/dotc/ast/untpd.scala
+++ b/compiler/src/dotty/tools/dotc/ast/untpd.scala
@@ -149,6 +149,7 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo {
def is(fs: FlagSet): Boolean = flags is fs
def is(fc: FlagConjunction): Boolean = flags is fc
+ def is(fc: FlagSet, butNot: FlagSet): Boolean = flags.is(fc, butNot = butNot)
def | (fs: FlagSet): Modifiers = withFlags(flags | fs)
def & (fs: FlagSet): Modifiers = withFlags(flags & fs)
diff --git a/compiler/src/dotty/tools/dotc/config/Config.scala b/compiler/src/dotty/tools/dotc/config/Config.scala
index 119af9483..dc56ad8b8 100644
--- a/compiler/src/dotty/tools/dotc/config/Config.scala
+++ b/compiler/src/dotty/tools/dotc/config/Config.scala
@@ -75,6 +75,15 @@ object Config {
/** If this flag is set, take the fast path when comparing same-named type-aliases and types */
final val fastPathForRefinedSubtype = true
+ /** If this flag is set, and we compute `T1 { X = S1 }` & `T2 { X = S2 }` as a new
+ * upper bound of a constrained parameter, try to align the refinements by computing
+ * `S1 =:= S2` (which might instantiate type parameters).
+ * This rule is contentious because it cuts the constraint set.
+ *
+ * For more info, see the comment in `TypeComparer#distributeAnd`.
+ */
+ final val alignArgsInAnd = true
+
/** If this flag is set, higher-kinded applications are checked for validity
*/
final val checkHKApplications = false
diff --git a/compiler/src/dotty/tools/dotc/core/Constraint.scala b/compiler/src/dotty/tools/dotc/core/Constraint.scala
index c99b748b7..50136a26c 100644
--- a/compiler/src/dotty/tools/dotc/core/Constraint.scala
+++ b/compiler/src/dotty/tools/dotc/core/Constraint.scala
@@ -111,12 +111,6 @@ abstract class Constraint extends Showable {
*/
def replace(param: PolyParam, tp: Type)(implicit ctx: Context): This
- /** Narrow one of the bounds of type parameter `param`
- * If `isUpper` is true, ensure that `param <: `bound`, otherwise ensure
- * that `param >: bound`.
- */
- def narrowBound(param: PolyParam, bound: Type, isUpper: Boolean)(implicit ctx: Context): This
-
/** Is entry associated with `pt` removable? This is the case if
* all type parameters of the entry are associated with type variables
* which have their `inst` fields set.
diff --git a/compiler/src/dotty/tools/dotc/core/ConstraintHandling.scala b/compiler/src/dotty/tools/dotc/core/ConstraintHandling.scala
index b3c50fb71..2a1f4ee6e 100644
--- a/compiler/src/dotty/tools/dotc/core/ConstraintHandling.scala
+++ b/compiler/src/dotty/tools/dotc/core/ConstraintHandling.scala
@@ -44,6 +44,13 @@ trait ConstraintHandling {
try op finally alwaysFluid = saved
}
+ /** If set, align arguments `S1`, `S2`when taking the glb
+ * `T1 { X = S1 } & T2 { X = S2 }` of a constraint upper bound for some type parameter.
+ * Aligning means computing `S1 =:= S2` which may change the current constraint.
+ * See note in TypeComparer#distributeAnd.
+ */
+ protected var homogenizeArgs = false
+
/** We are currently comparing polytypes. Used as a flag for
* optimization: when `false`, no need to do an expensive `pruneLambdaParams`
*/
@@ -64,7 +71,8 @@ trait ConstraintHandling {
}
if (Config.checkConstraintsSeparated)
assert(!occursIn(bound), s"$param occurs in $bound")
- val c1 = constraint.narrowBound(param, bound, isUpper)
+ val newBound = narrowedBound(param, bound, isUpper)
+ val c1 = constraint.updateEntry(param, newBound)
(c1 eq constraint) || {
constraint = c1
val TypeBounds(lo, hi) = constraint.entry(param)
@@ -72,6 +80,20 @@ trait ConstraintHandling {
}
}
+ /** Narrow one of the bounds of type parameter `param`
+ * If `isUpper` is true, ensure that `param <: `bound`, otherwise ensure
+ * that `param >: bound`.
+ */
+ def narrowedBound(param: PolyParam, bound: Type, isUpper: Boolean)(implicit ctx: Context): TypeBounds = {
+ val oldBounds @ TypeBounds(lo, hi) = constraint.nonParamBounds(param)
+ val saved = homogenizeArgs
+ homogenizeArgs = Config.alignArgsInAnd
+ try
+ if (isUpper) oldBounds.derivedTypeBounds(lo, hi & bound)
+ else oldBounds.derivedTypeBounds(lo | bound, hi)
+ finally homogenizeArgs = saved
+ }
+
protected def addUpperBound(param: PolyParam, bound: Type): Boolean = {
def description = i"constraint $param <: $bound to\n$constraint"
if (bound.isRef(defn.NothingClass) && ctx.typerState.isGlobalCommittable) {
diff --git a/compiler/src/dotty/tools/dotc/core/Definitions.scala b/compiler/src/dotty/tools/dotc/core/Definitions.scala
index 3cab75f93..4d4350f98 100644
--- a/compiler/src/dotty/tools/dotc/core/Definitions.scala
+++ b/compiler/src/dotty/tools/dotc/core/Definitions.scala
@@ -299,6 +299,8 @@ class Definitions {
lazy val ScalaPredefModuleRef = ctx.requiredModuleRef("scala.Predef")
def ScalaPredefModule(implicit ctx: Context) = ScalaPredefModuleRef.symbol
+ lazy val Predef_ConformsR = ScalaPredefModule.requiredClass("$less$colon$less").typeRef
+ def Predef_Conforms(implicit ctx: Context) = Predef_ConformsR.symbol
lazy val Predef_conformsR = ScalaPredefModule.requiredMethodRef("$conforms")
def Predef_conforms(implicit ctx: Context) = Predef_conformsR.symbol
lazy val Predef_classOfR = ScalaPredefModule.requiredMethodRef("classOf")
@@ -336,6 +338,8 @@ class Definitions {
def DottyPredefModule(implicit ctx: Context) = DottyPredefModuleRef.symbol
def Predef_eqAny(implicit ctx: Context) = DottyPredefModule.requiredMethod(nme.eqAny)
+ lazy val Predef_ImplicitConverterR = DottyPredefModule.requiredClass("ImplicitConverter").typeRef
+ def Predef_ImplicitConverter(implicit ctx: Context) = Predef_ImplicitConverterR.symbol
lazy val DottyArraysModuleRef = ctx.requiredModuleRef("dotty.runtime.Arrays")
def DottyArraysModule(implicit ctx: Context) = DottyArraysModuleRef.symbol
@@ -351,6 +355,7 @@ class Definitions {
enterCompleteClassSymbol(
ScalaPackageClass, tpnme.Singleton, PureInterfaceCreationFlags | Final,
List(AnyClass.typeRef), EmptyScope)
+ def SingletonType = SingletonClass.typeRef
lazy val SeqType: TypeRef = ctx.requiredClassRef("scala.collection.Seq")
def SeqClass(implicit ctx: Context) = SeqType.symbol.asClass
diff --git a/compiler/src/dotty/tools/dotc/core/Flags.scala b/compiler/src/dotty/tools/dotc/core/Flags.scala
index d2a1c58f5..c1267d8a2 100644
--- a/compiler/src/dotty/tools/dotc/core/Flags.scala
+++ b/compiler/src/dotty/tools/dotc/core/Flags.scala
@@ -287,9 +287,10 @@ object Flags {
/** A trait that has only abstract methods as members
- * (and therefore can be represented by a Java interface
+ * and therefore can be represented by a Java interface.
+ * Warning: flag is set during regular typer pass, should be tested only after typer.
*/
- final val PureInterface = typeFlag(22, "interface") // TODO when unpickling, reconstitute from context
+ final val PureInterface = typeFlag(22, "interface")
/** Labeled with of abstract & override */
final val AbsOverride = termFlag(22, "abstract override")
@@ -338,7 +339,9 @@ object Flags {
final val JavaStaticTerm = JavaStatic.toTermFlags
final val JavaStaticType = JavaStatic.toTypeFlags
- /** Trait does not have fields or initialization code */
+ /** Trait does not have fields or initialization code.
+ * Warning: flag is set during regular typer pass, should be tested only after typer.
+ */
final val NoInits = typeFlag(32, "<noInits>")
/** Variable is accessed from nested function. */
diff --git a/compiler/src/dotty/tools/dotc/core/OrderingConstraint.scala b/compiler/src/dotty/tools/dotc/core/OrderingConstraint.scala
index 72c7a8e51..61dd5a445 100644
--- a/compiler/src/dotty/tools/dotc/core/OrderingConstraint.scala
+++ b/compiler/src/dotty/tools/dotc/core/OrderingConstraint.scala
@@ -354,14 +354,6 @@ class OrderingConstraint(private val boundsMap: ParamBounds,
updateEntry(p1, p1Bounds).replace(p2, p1)
}
- def narrowBound(param: PolyParam, bound: Type, isUpper: Boolean)(implicit ctx: Context): This = {
- val oldBounds @ TypeBounds(lo, hi) = nonParamBounds(param)
- val newBounds =
- if (isUpper) oldBounds.derivedTypeBounds(lo, hi & bound)
- else oldBounds.derivedTypeBounds(lo | bound, hi)
- updateEntry(param, newBounds)
- }
-
// ---------- Removals ------------------------------------------------------------
/** A new constraint which is derived from this constraint by removing
diff --git a/compiler/src/dotty/tools/dotc/core/StdNames.scala b/compiler/src/dotty/tools/dotc/core/StdNames.scala
index 766e3733f..5b7dc3d1d 100644
--- a/compiler/src/dotty/tools/dotc/core/StdNames.scala
+++ b/compiler/src/dotty/tools/dotc/core/StdNames.scala
@@ -97,6 +97,7 @@ object StdNames {
val EMPTY: N = ""
val EMPTY_PACKAGE: N = Names.EMPTY_PACKAGE.toString
val EVIDENCE_PARAM_PREFIX: N = "evidence$"
+ val DEP_PARAM_PREFIX = "<param>"
val EXCEPTION_RESULT_PREFIX: N = "exceptionResult"
val EXPAND_SEPARATOR: N = "$$"
val IMPL_CLASS_SUFFIX: N = "$class"
diff --git a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala
index 9b9caf8e7..c98b444d9 100644
--- a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala
+++ b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala
@@ -158,7 +158,7 @@ object SymDenotations {
final def resetFlag(flags: FlagSet): Unit = { myFlags &~= flags }
/** Set applicable flags from `flags` which is a subset of {NoInits, PureInterface} */
- final def setApplicableFlags(flags: FlagSet): Unit = {
+ final def setNoInitsFlags(flags: FlagSet): Unit = {
val mask = if (myFlags.is(Trait)) NoInitsInterface else NoInits
setFlag(flags & mask)
}
diff --git a/compiler/src/dotty/tools/dotc/core/TypeComparer.scala b/compiler/src/dotty/tools/dotc/core/TypeComparer.scala
index b97dfe684..b61fccf31 100644
--- a/compiler/src/dotty/tools/dotc/core/TypeComparer.scala
+++ b/compiler/src/dotty/tools/dotc/core/TypeComparer.scala
@@ -1300,23 +1300,28 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
case tp1: RefinedType =>
tp2 match {
case tp2: RefinedType if tp1.refinedName == tp2.refinedName =>
- // Given two refinements `T1 { X = S1 }` and `T2 { X = S2 }`, if `S1 =:= S2`
- // (possibly by instantiating type parameters), rewrite to `T1 & T2 { X = S1 }`.
- // Otherwise rewrite to `T1 & T2 { X B }` where `B` is the conjunction of
- // the bounds of `X` in `T1` and `T2`.
- // The first rule above is contentious because it cuts the constraint set.
- // But without it we would replace the two aliases by
- // `T { X >: S1 | S2 <: S1 & S2 }`, which looks weird and is probably
- // not what's intended.
+ // Given two refinements `T1 { X = S1 }` and `T2 { X = S2 }` rewrite to
+ // `T1 & T2 { X B }` where `B` is the conjunction of the bounds of `X` in `T1` and `T2`.
+ //
+ // However, if `homogenizeArgs` is set, and both aliases `X = Si` are
+ // nonvariant, and `S1 =:= S2` (possibly by instantiating type parameters),
+ // rewrite instead to `T1 & T2 { X = S1 }`. This rule is contentious because
+ // it cuts the constraint set. On the other hand, without it we would replace
+ // the two aliases by `T { X >: S1 | S2 <: S1 & S2 }`, which looks weird
+ // and is probably not what's intended.
val rinfo1 = tp1.refinedInfo
val rinfo2 = tp2.refinedInfo
val parent = tp1.parent & tp2.parent
- val rinfo =
- if (rinfo1.isAlias && rinfo2.isAlias && isSameType(rinfo1, rinfo2))
- rinfo1
- else
- rinfo1 & rinfo2
- tp1.derivedRefinedType(parent, tp1.refinedName, rinfo)
+
+ def isNonvariantAlias(tp: Type) = tp match {
+ case tp: TypeAlias => tp.variance == 0
+ case _ => false
+ }
+ if (homogenizeArgs &&
+ isNonvariantAlias(rinfo1) && isNonvariantAlias(rinfo2))
+ isSameType(rinfo1, rinfo2) // establish new constraint
+
+ tp1.derivedRefinedType(parent, tp1.refinedName, rinfo1 & rinfo2)
case _ =>
NoType
}
diff --git a/compiler/src/dotty/tools/dotc/core/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala
index 9dd767a8e..b8f81f1bb 100644
--- a/compiler/src/dotty/tools/dotc/core/Types.scala
+++ b/compiler/src/dotty/tools/dotc/core/Types.scala
@@ -19,6 +19,7 @@ import util.Positions.{Position, NoPosition}
import util.Stats._
import util.{DotClass, SimpleMap}
import reporting.diagnostic.Message
+import reporting.diagnostic.messages.CyclicReferenceInvolving
import ast.tpd._
import ast.TreeTypeMap
import printing.Texts._
@@ -1273,10 +1274,14 @@ object Types {
def underlying(implicit ctx: Context): Type
/** The closest supertype of this type. This is the same as `underlying`,
- * except for TypeRefs where the upper bound is returned, and HKApplys,
- * where the upper bound of the constructor is re-applied to the arguments.
+ * except that
+ * - instead of a TyperBounds type it returns its upper bound, and
+ * - for HKApplys it returns the upper bound of the constructor re-applied to the arguments.
*/
- def superType(implicit ctx: Context): Type = underlying
+ def superType(implicit ctx: Context): Type = underlying match {
+ case TypeBounds(_, hi) => hi
+ case st => st
+ }
}
// Every type has to inherit one of the following four abstract type classes.,
@@ -1680,7 +1685,10 @@ object Types {
}
else newLikeThis(prefix)
}
- else newLikeThis(prefix)
+ else prefix match {
+ case _: WildcardType => WildcardType
+ case _ => newLikeThis(prefix)
+ }
/** Create a NamedType of the same kind as this type, but with a new prefix.
*/
@@ -1762,11 +1770,6 @@ object Types {
type ThisType = TypeRef
override def underlying(implicit ctx: Context): Type = info
-
- override def superType(implicit ctx: Context): Type = info match {
- case TypeBounds(_, hi) => hi
- case _ => info
- }
}
final class TermRefWithSignature(prefix: Type, name: TermName, override val sig: Signature) extends TermRef(prefix, name) {
@@ -3853,7 +3856,7 @@ object Types {
class CyclicReference private (val denot: SymDenotation)
extends TypeError(s"cyclic reference involving $denot") {
- def show(implicit ctx: Context) = s"cyclic reference involving ${denot.show}"
+ def toMessage(implicit ctx: Context) = CyclicReferenceInvolving(denot)
}
object CyclicReference {
diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala
index fcba957c0..fdb8a97ae 100644
--- a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala
+++ b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala
@@ -768,7 +768,7 @@ class TreeUnpickler(reader: TastyReader, tastyName: TastyName.Table, posUnpickle
}
else EmptyValDef
setClsInfo(parentRefs, if (self.isEmpty) NoType else self.tpt.tpe)
- cls.setApplicableFlags(fork.indexStats(end))
+ cls.setNoInitsFlags(fork.indexStats(end))
val constr = readIndexedDef().asInstanceOf[DefDef]
def mergeTypeParamsAndAliases(tparams: List[TypeDef], stats: List[Tree])(implicit ctx: Context): (List[Tree], List[Tree]) =
diff --git a/compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala b/compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala
index 0d1068b8c..6af902d1b 100644
--- a/compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala
+++ b/compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala
@@ -4,6 +4,7 @@ package printing
import core._
import Texts._, Types._, Flags._, Names._, Symbols._, NameOps._, Constants._, Denotations._
import Contexts.Context, Scopes.Scope, Denotations.Denotation, Annotations.Annotation
+import TypeApplications.AppliedType
import StdNames.{nme, tpnme}
import ast.Trees._, ast._
import typer.Implicits._
@@ -119,10 +120,11 @@ class PlainPrinter(_ctx: Context) extends Printer {
}
/** The longest sequence of refinement types, starting at given type
- * and following parents.
+ * and following parents, but stopping at applied types.
*/
private def refinementChain(tp: Type): List[Type] =
tp :: (tp match {
+ case AppliedType(_, _) => Nil
case tp: RefinedType => refinementChain(tp.parent.stripTypeVar)
case _ => Nil
})
diff --git a/compiler/src/dotty/tools/dotc/reporting/diagnostic/ErrorMessageID.java b/compiler/src/dotty/tools/dotc/reporting/diagnostic/ErrorMessageID.java
index a530a937f..7b56c8ed4 100644
--- a/compiler/src/dotty/tools/dotc/reporting/diagnostic/ErrorMessageID.java
+++ b/compiler/src/dotty/tools/dotc/reporting/diagnostic/ErrorMessageID.java
@@ -51,6 +51,10 @@ public enum ErrorMessageID {
MixedLeftAndRightAssociativeOpsID,
CantInstantiateAbstractClassOrTraitID,
AnnotatedPrimaryConstructorRequiresModifierOrThisID,
+ OverloadedOrRecursiveMethodNeedsResultTypeID,
+ RecursiveValueNeedsResultTypeID,
+ CyclicReferenceInvolvingID,
+ CyclicReferenceInvolvingImplicitID,
;
public int errorNumber() {
diff --git a/compiler/src/dotty/tools/dotc/reporting/diagnostic/messages.scala b/compiler/src/dotty/tools/dotc/reporting/diagnostic/messages.scala
index 54090074a..4c53fa496 100644
--- a/compiler/src/dotty/tools/dotc/reporting/diagnostic/messages.scala
+++ b/compiler/src/dotty/tools/dotc/reporting/diagnostic/messages.scala
@@ -18,6 +18,7 @@ import dotc.parsing.Tokens
import printing.Highlighting._
import printing.Formatting
import ErrorMessageID._
+import dotty.tools.dotc.core.SymDenotations.SymDenotation
object messages {
@@ -1134,7 +1135,7 @@ object messages {
}
case class AnnotatedPrimaryConstructorRequiresModifierOrThis(cls: Name)(implicit ctx: Context)
- extends Message(AnnotatedPrimaryConstructorRequiresModifierOrThisID) {
+ extends Message(AnnotatedPrimaryConstructorRequiresModifierOrThisID) {
val kind = "Syntax"
val msg = hl"""${"private"}, ${"protected"}, or ${"this"} expected for annotated primary constructor"""
val explanation =
@@ -1147,4 +1148,48 @@ object messages {
| ^^^^
|""".stripMargin
}
+
+ case class OverloadedOrRecursiveMethodNeedsResultType(tree: Names.TermName)(implicit ctx: Context)
+ extends Message(OverloadedOrRecursiveMethodNeedsResultTypeID) {
+ val kind = "Syntax"
+ val msg = hl"""overloaded or recursive method ${tree} needs return type"""
+ val explanation =
+ hl"""Case 1: ${tree} is overloaded
+ |If there are multiple methods named `${tree.name}` and at least one definition of
+ |it calls another, you need to specify the calling method's return type.
+ |
+ |Case 2: ${tree} is recursive
+ |If `${tree.name}` calls itself on any path, you need to specify its return type.
+ |""".stripMargin
+ }
+
+ case class RecursiveValueNeedsResultType(tree: Names.TermName)(implicit ctx: Context)
+ extends Message(RecursiveValueNeedsResultTypeID) {
+ val kind = "Syntax"
+ val msg = hl"""recursive value ${tree.name} needs type"""
+ val explanation =
+ hl"""The definition of `${tree.name}` is recursive and you need to specify its type.
+ |""".stripMargin
+ }
+
+ case class CyclicReferenceInvolving(denot: SymDenotation)(implicit ctx: Context)
+ extends Message(CyclicReferenceInvolvingID) {
+ val kind = "Syntax"
+ val msg = hl"""cyclic reference involving $denot"""
+ val explanation =
+ hl"""|$denot is declared as part of a cycle which makes it impossible for the
+ |compiler to decide upon ${denot.name}'s type.
+ |""".stripMargin
+ }
+
+ case class CyclicReferenceInvolvingImplicit(cycleSym: Symbol)(implicit ctx: Context)
+ extends Message(CyclicReferenceInvolvingImplicitID) {
+ val kind = "Syntax"
+ val msg = hl"""cyclic reference involving implicit $cycleSym"""
+ val explanation =
+ hl"""|This happens when the right hand-side of $cycleSym's definition involves an implicit search.
+ |To avoid this error, give `${cycleSym.name}` an explicit type.
+ |""".stripMargin
+ }
+
}
diff --git a/compiler/src/dotty/tools/dotc/typer/Applications.scala b/compiler/src/dotty/tools/dotc/typer/Applications.scala
index 222717e7e..a65ef00cc 100644
--- a/compiler/src/dotty/tools/dotc/typer/Applications.scala
+++ b/compiler/src/dotty/tools/dotc/typer/Applications.scala
@@ -213,16 +213,15 @@ trait Applications extends Compatibility { self: Typer with Dynamic =>
protected def init() = methType match {
case methType: MethodType =>
// apply the result type constraint, unless method type is dependent
- if (!methType.isDependent) {
- val savedConstraint = ctx.typerState.constraint
- if (!constrainResult(methType.resultType, resultType))
- if (ctx.typerState.isCommittable)
- // defer the problem until after the application;
- // it might be healed by an implicit conversion
- assert(ctx.typerState.constraint eq savedConstraint)
- else
- fail(err.typeMismatchMsg(methType.resultType, resultType))
- }
+ val resultApprox = resultTypeApprox(methType)
+ val savedConstraint = ctx.typerState.constraint
+ if (!constrainResult(resultApprox, resultType))
+ if (ctx.typerState.isCommittable)
+ // defer the problem until after the application;
+ // it might be healed by an implicit conversion
+ assert(ctx.typerState.constraint eq savedConstraint)
+ else
+ fail(err.typeMismatchMsg(methType.resultType, resultType))
// match all arguments with corresponding formal parameters
matchArgs(orderedArgs, methType.paramTypes, 0)
case _ =>
@@ -1100,10 +1099,8 @@ trait Applications extends Compatibility { self: Typer with Dynamic =>
/** Drop any implicit parameter section */
def stripImplicit(tp: Type): Type = tp match {
- case mt: ImplicitMethodType if !mt.isDependent =>
- mt.resultType
- // todo: make sure implicit method types are not dependent?
- // but check test case in /tests/pos/depmet_implicit_chaining_zw.scala
+ case mt: ImplicitMethodType =>
+ resultTypeApprox(mt)
case pt: PolyType =>
pt.derivedPolyType(pt.paramNames, pt.paramBounds, stripImplicit(pt.resultType))
case _ =>
diff --git a/compiler/src/dotty/tools/dotc/typer/ErrorReporting.scala b/compiler/src/dotty/tools/dotc/typer/ErrorReporting.scala
index 0978c2c1e..a1690955f 100644
--- a/compiler/src/dotty/tools/dotc/typer/ErrorReporting.scala
+++ b/compiler/src/dotty/tools/dotc/typer/ErrorReporting.scala
@@ -28,24 +28,22 @@ object ErrorReporting {
def cyclicErrorMsg(ex: CyclicReference)(implicit ctx: Context) = {
val cycleSym = ex.denot.symbol
- def errorMsg(msg: String, cx: Context): String =
+ def errorMsg(msg: Message, cx: Context): Message =
if (cx.mode is Mode.InferringReturnType) {
cx.tree match {
case tree: untpd.DefDef if !tree.tpt.typeOpt.exists =>
- em"overloaded or recursive method ${tree.name} needs result type"
+ OverloadedOrRecursiveMethodNeedsResultType(tree.name)
case tree: untpd.ValDef if !tree.tpt.typeOpt.exists =>
- em"recursive value ${tree.name} needs type"
+ RecursiveValueNeedsResultType(tree.name)
case _ =>
errorMsg(msg, cx.outer)
}
} else msg
if (cycleSym.is(Implicit, butNot = Method) && cycleSym.owner.isTerm)
- em"""cyclic reference involving implicit $cycleSym
- |This happens when the right hand-side of $cycleSym's definition involves an implicit search.
- |To avoid the error, give $cycleSym an explicit type."""
+ CyclicReferenceInvolvingImplicit(cycleSym)
else
- errorMsg(ex.show, ctx)
+ errorMsg(ex.toMessage, ctx)
}
def wrongNumberOfTypeArgs(fntpe: Type, expectedArgs: List[TypeParamInfo], actual: List[untpd.Tree], pos: Position)(implicit ctx: Context) =
diff --git a/compiler/src/dotty/tools/dotc/typer/Implicits.scala b/compiler/src/dotty/tools/dotc/typer/Implicits.scala
index 759cc62e9..ebbcbcc95 100644
--- a/compiler/src/dotty/tools/dotc/typer/Implicits.scala
+++ b/compiler/src/dotty/tools/dotc/typer/Implicits.scala
@@ -82,11 +82,29 @@ object Implicits {
case tpw: TermRef =>
false // can't discard overloaded refs
case tpw =>
- //if (ctx.typer.isApplicable(tp, argType :: Nil, resultType))
- // println(i"??? $tp is applicable to $this / typeSymbol = ${tpw.typeSymbol}")
- !tpw.derivesFrom(defn.FunctionClass(1)) ||
- ref.symbol == defn.Predef_conforms //
- // as an implicit conversion, Predef.$conforms is a no-op, so exclude it
+ // Only direct instances of Function1 and direct or indirect instances of <:< are eligible as views.
+ // However, Predef.$conforms is not eligible, because it is a no-op.
+ //
+ // In principle, it would be cleanest if only implicit methods qualified
+ // as implicit conversions. We could achieve that by having standard conversions like
+ // this in Predef:
+ //
+ // implicit def convertIfConforms[A, B](x: A)(implicit ev: A <:< B): B = ev(a)
+ // implicit def convertIfConverter[A, B](x: A)(implicit ev: ImplicitConverter[A, B]): B = ev(a)
+ //
+ // (Once `<:<` inherits from `ImplicitConverter` we only need the 2nd one.)
+ // But clauses like this currently slow down implicit search a lot, because
+ // they are eligible for all pairs of types, and therefore are tried too often.
+ // We emulate instead these conversions directly in the search.
+ // The reason for leaving out `Predef_conforms` is that we know it adds
+ // nothing since it only relates subtype with supertype.
+ //
+ // We keep the old behavior under -language:Scala2.
+ val isFunctionInS2 = ctx.scala2Mode && tpw.derivesFrom(defn.FunctionClass(1))
+ val isImplicitConverter = tpw.derivesFrom(defn.Predef_ImplicitConverter)
+ val isConforms =
+ tpw.derivesFrom(defn.Predef_Conforms) && ref.symbol != defn.Predef_conforms
+ !(isFunctionInS2 || isImplicitConverter || isConforms)
}
def discardForValueType(tpw: Type): Boolean = tpw match {
diff --git a/compiler/src/dotty/tools/dotc/typer/Namer.scala b/compiler/src/dotty/tools/dotc/typer/Namer.scala
index 96660f15c..f2ad1f7c9 100644
--- a/compiler/src/dotty/tools/dotc/typer/Namer.scala
+++ b/compiler/src/dotty/tools/dotc/typer/Namer.scala
@@ -484,46 +484,91 @@ class Namer { typer: Typer =>
/** Create top-level symbols for statements and enter them into symbol table */
def index(stats: List[Tree])(implicit ctx: Context): Context = {
- val classDef = mutable.Map[TypeName, TypeDef]()
- val moduleDef = mutable.Map[TypeName, TypeDef]()
+ // module name -> (stat, moduleCls | moduleVal)
+ val moduleClsDef = mutable.Map[TypeName, (Tree, TypeDef)]()
+ val moduleValDef = mutable.Map[TermName, (Tree, ValDef)]()
+
+ /** Remove the subtree `tree` from the expanded tree of `mdef` */
+ def removeInExpanded(mdef: Tree, tree: Tree): Unit = {
+ val Thicket(trees) = expanded(mdef)
+ mdef.putAttachment(ExpandedTree, Thicket(trees.filter(_ != tree)))
+ }
+
+ /** Merge the module class `modCls` in the expanded tree of `mdef` with the given stats */
+ def mergeModuleClass(mdef: Tree, modCls: TypeDef, stats: List[Tree]): TypeDef = {
+ var res: TypeDef = null
+ val Thicket(trees) = expanded(mdef)
+ val merged = trees.map { tree =>
+ if (tree == modCls) {
+ val impl = modCls.rhs.asInstanceOf[Template]
+ res = cpy.TypeDef(modCls)(rhs = cpy.Template(impl)(body = stats ++ impl.body))
+ res
+ }
+ else tree
+ }
+
+ mdef.putAttachment(ExpandedTree, Thicket(merged))
+
+ res
+ }
+
+ /** Merge `fromCls` of `fromStat` into `toCls` of `toStat`
+ * if the former is synthetic and the latter not.
+ *
+ * Note:
+ * 1. `fromStat` and `toStat` could be the same stat
+ * 2. `fromCls` and `toCls` are necessarily different
+ */
+ def mergeIfSynthetic(fromStat: Tree, fromCls: TypeDef, toStat: Tree, toCls: TypeDef): Unit =
+ if (fromCls.mods.is(Synthetic) && !toCls.mods.is(Synthetic)) {
+ removeInExpanded(fromStat, fromCls)
+ val mcls = mergeModuleClass(toStat, toCls, fromCls.rhs.asInstanceOf[Template].body)
+ moduleClsDef(fromCls.name) = (toStat, mcls)
+ }
/** Merge the definitions of a synthetic companion generated by a case class
* and the real companion, if both exist.
*/
def mergeCompanionDefs() = {
- for (cdef @ TypeDef(name, _) <- stats)
- if (cdef.isClassDef) {
- classDef(name) = cdef
- cdef.attachmentOrElse(ExpandedTree, cdef) match {
- case Thicket(cls :: mval :: (mcls @ TypeDef(mname, _: Template)) :: crest)
- if name.moduleClassName == mname =>
- moduleDef(name) = mcls
- case _ =>
- }
- }
- for (mdef @ ModuleDef(name, _) <- stats if !mdef.mods.is(Flags.Package)) {
- val typName = name.toTypeName
- // Expansion of object is a flattened thicket with the first two elements being:
- // module val :: module class :: rest
- val Thicket(vdef :: (mcls @ TypeDef(_, impl: Template)) :: rest) = expanded(mdef)
- moduleDef(typName) = mcls
- classDef get name.toTypeName match {
- case Some(cdef) =>
- cdef.attachmentOrElse(ExpandedTree, cdef) match {
- case Thicket(cls :: mval :: TypeDef(mname, compimpl: Template) :: crest)
- if name.moduleClassName == mname =>
- val mcls1 = cpy.TypeDef(mcls)(
- rhs = cpy.Template(impl)(body = compimpl.body ++ impl.body))
- mdef.putAttachment(ExpandedTree, Thicket(vdef :: mcls1 :: rest))
- moduleDef(typName) = mcls1
- cdef.putAttachment(ExpandedTree, Thicket(cls :: crest))
+ def valid(mdef: MemberDef): Boolean = mdef.mods.is(Module, butNot = Package)
+
+ for (stat <- stats)
+ expanded(stat) match {
+ case Thicket(trees) => // companion object always expands to thickets
+ trees.map {
+ case mcls @ TypeDef(name, impl: Template) if valid(mcls) =>
+ moduleClsDef.get(name) match {
+ case Some((stat1, mcls1@TypeDef(_, impl1: Template))) =>
+ mergeIfSynthetic(stat, mcls, stat1, mcls1)
+ mergeIfSynthetic(stat1, mcls1, stat, mcls)
+ case None =>
+ moduleClsDef(name) = (stat, mcls)
+ }
+
+ case vdef @ ValDef(name, _, _) if valid(vdef) =>
+ moduleValDef.get(name) match {
+ case Some((stat1, vdef1)) =>
+ if (vdef.mods.is(Synthetic) && !vdef1.mods.is(Synthetic))
+ removeInExpanded(stat, vdef)
+ else if (!vdef.mods.is(Synthetic) && vdef1.mods.is(Synthetic)) {
+ removeInExpanded(stat1, vdef1)
+ moduleValDef(name) = (stat, vdef)
+ }
+ else {
+ // double definition of objects or case classes, handled elsewhere
+ }
+ case None =>
+ moduleValDef(name) = (stat, vdef)
+ }
+
case _ =>
}
- case none =>
+ case _ =>
+
}
- }
}
+ /** Create links between companion object and companion class */
def createLinks(classTree: TypeDef, moduleTree: TypeDef)(implicit ctx: Context) = {
val claz = ctx.denotNamed(classTree.name.encode).symbol
val modl = ctx.denotNamed(moduleTree.name.encode).symbol
@@ -532,8 +577,29 @@ class Namer { typer: Typer =>
}
def createCompanionLinks(implicit ctx: Context): Unit = {
+ val classDef = mutable.Map[TypeName, TypeDef]()
+ val moduleDef = mutable.Map[TypeName, TypeDef]()
+
+ def updateCache(cdef: TypeDef): Unit = {
+ if (!cdef.isClassDef || cdef.mods.is(Package)) return
+
+ if (cdef.mods.is(ModuleClass)) moduleDef(cdef.name) = cdef
+ else classDef(cdef.name) = cdef
+ }
+
+ for (stat <- stats)
+ expanded(stat) match {
+ case cdef : TypeDef => updateCache(cdef)
+ case Thicket(trees) =>
+ trees.map {
+ case cdef: TypeDef => updateCache(cdef)
+ case _ =>
+ }
+ case _ =>
+ }
+
for (cdef @ TypeDef(name, _) <- classDef.values) {
- moduleDef.getOrElse(name, EmptyTree) match {
+ moduleDef.getOrElse(name.moduleClassName, EmptyTree) match {
case t: TypeDef =>
createLinks(cdef, t)
case EmptyTree =>
@@ -818,8 +884,6 @@ class Namer { typer: Typer =>
Checking.checkWellFormed(cls)
if (isDerivedValueClass(cls)) cls.setFlag(Final)
- cls.setApplicableFlags(
- (NoInitsInterface /: impl.body)((fs, stat) => fs & defKind(stat)))
cls.info = avoidPrivateLeaks(cls, cls.pos)
}
}
diff --git a/compiler/src/dotty/tools/dotc/typer/ProtoTypes.scala b/compiler/src/dotty/tools/dotc/typer/ProtoTypes.scala
index d666b563e..17f13d7c1 100644
--- a/compiler/src/dotty/tools/dotc/typer/ProtoTypes.scala
+++ b/compiler/src/dotty/tools/dotc/typer/ProtoTypes.scala
@@ -57,19 +57,14 @@ object ProtoTypes {
case pt: FunProto =>
mt match {
case mt: MethodType =>
- mt.isDependent || constrainResult(mt.resultType, pt.resultType)
+ constrainResult(resultTypeApprox(mt), pt.resultType)
case _ =>
true
}
case _: ValueTypeOrProto if !disregardProto(pt) =>
- mt match {
- case mt: MethodType =>
- mt.isDependent || isCompatible(normalize(mt, pt), pt)
- case _ =>
- isCompatible(mt, pt)
- }
- case _: WildcardType =>
- isCompatible(mt, pt)
+ isCompatible(normalize(mt, pt), pt)
+ case pt: WildcardType if pt.optBounds.exists =>
+ isCompatible(normalize(mt, pt), pt)
case _ =>
true
}
@@ -394,6 +389,26 @@ object ProtoTypes {
/** Same as `constrained(pt, EmptyTree)`, but returns just the created polytype */
def constrained(pt: PolyType)(implicit ctx: Context): PolyType = constrained(pt, EmptyTree)._1
+ /** Create a new polyparam that represents a dependent method parameter singleton */
+ def newDepPolyParam(tp: Type)(implicit ctx: Context): PolyParam = {
+ val poly = PolyType(ctx.freshName(nme.DEP_PARAM_PREFIX).toTypeName :: Nil, 0 :: Nil)(
+ pt => TypeBounds.upper(AndType(tp, defn.SingletonType)) :: Nil,
+ pt => defn.AnyType)
+ ctx.typeComparer.addToConstraint(poly, Nil)
+ PolyParam(poly, 0)
+ }
+
+ /** The result type of `mt`, where all references to parameters of `mt` are
+ * replaced by either wildcards (if typevarsMissContext) or polyparams.
+ */
+ def resultTypeApprox(mt: MethodType)(implicit ctx: Context): Type =
+ if (mt.isDependent) {
+ def replacement(tp: Type) =
+ if (ctx.mode.is(Mode.TypevarsMissContext)) WildcardType else newDepPolyParam(tp)
+ mt.resultType.substParams(mt, mt.paramTypes.map(replacement))
+ }
+ else mt.resultType
+
/** The normalized form of a type
* - unwraps polymorphic types, tracking their parameters in the current constraint
* - skips implicit parameters; if result type depends on implicit parameter,
@@ -413,22 +428,18 @@ object ProtoTypes {
tp.widenSingleton match {
case poly: PolyType => normalize(constrained(poly).resultType, pt)
case mt: MethodType =>
- if (mt.isImplicit)
- if (mt.isDependent)
- mt.resultType.substParams(mt, mt.paramTypes.map(Function.const(WildcardType)))
- else mt.resultType
- else
- if (mt.isDependent) tp
- else {
- val rt = normalize(mt.resultType, pt)
+ if (mt.isImplicit) resultTypeApprox(mt)
+ else if (mt.isDependent) tp
+ else {
+ val rt = normalize(mt.resultType, pt)
pt match {
case pt: IgnoredProto => mt
case pt: ApplyingProto => mt.derivedMethodType(mt.paramNames, mt.paramTypes, rt)
case _ =>
val ft = defn.FunctionOf(mt.paramTypes, rt)
if (mt.paramTypes.nonEmpty || ft <:< pt) ft else rt
- }
}
+ }
case et: ExprType => et.resultType
case _ => tp
}
diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala
index b2e9d639d..ba14b7498 100644
--- a/compiler/src/dotty/tools/dotc/typer/Typer.scala
+++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala
@@ -1314,6 +1314,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
val self1 = typed(self)(ctx.outer).asInstanceOf[ValDef] // outer context where class members are not visible
val dummy = localDummy(cls, impl)
val body1 = typedStats(impl.body, dummy)(inClassContext(self1.symbol))
+ cls.setNoInitsFlags((NoInitsInterface /: body1)((fs, stat) => fs & defKind(stat)))
// Expand comments and type usecases
cookComments(body1.map(_.symbol), self1.symbol)(localContext(cdef, cls).setNewScope)
diff --git a/compiler/test/dotty/tools/dotc/reporting/ErrorMessagesTests.scala b/compiler/test/dotty/tools/dotc/reporting/ErrorMessagesTests.scala
index d6e687a1e..f11c6dd96 100644
--- a/compiler/test/dotty/tools/dotc/reporting/ErrorMessagesTests.scala
+++ b/compiler/test/dotty/tools/dotc/reporting/ErrorMessagesTests.scala
@@ -6,7 +6,7 @@ import core.Contexts.Context
import diagnostic.messages._
import dotty.tools.dotc.parsing.Tokens
import org.junit.Assert._
-import org.junit.Test
+import org.junit.{Ignore, Test}
class ErrorMessagesTests extends ErrorMessagesTest {
// In the case where there are no errors, we can do "expectNoErrors" in the
@@ -212,4 +212,97 @@ class ErrorMessagesTests extends ErrorMessagesTest {
val AnnotatedPrimaryConstructorRequiresModifierOrThis(cls) :: Nil = messages
assertEquals("AnotherClass", cls.show)
}
+
+ @Test def overloadedMethodNeedsReturnType =
+ checkMessagesAfter("frontend") {
+ """
+ |class Scope() {
+ | def foo(i: Int) = foo(i.toString)
+ | def foo(s: String) = s
+ |}
+ """.stripMargin
+ }
+ .expect { (ictx, messages) =>
+ implicit val ctx: Context = ictx
+ val defn = ictx.definitions
+
+ assertMessageCount(1, messages)
+ val OverloadedOrRecursiveMethodNeedsResultType(tree) :: Nil = messages
+ assertEquals("foo", tree.show)
+ }
+
+ @Test def recursiveMethodNeedsReturnType =
+ checkMessagesAfter("frontend") {
+ """
+ |class Scope() {
+ | def i = i + 5
+ |}
+ """.stripMargin
+ }
+ .expect { (ictx, messages) =>
+ implicit val ctx: Context = ictx
+ val defn = ictx.definitions
+
+ assertMessageCount(1, messages)
+ val OverloadedOrRecursiveMethodNeedsResultType(tree) :: Nil = messages
+ assertEquals("i", tree.show)
+ }
+
+ @Test def recursiveValueNeedsReturnType =
+ checkMessagesAfter("frontend") {
+ """
+ |class Scope() {
+ | lazy val i = i + 5
+ |}
+ """.stripMargin
+ }
+ .expect { (ictx, messages) =>
+ implicit val ctx: Context = ictx
+ val defn = ictx.definitions
+
+ assertMessageCount(1, messages)
+ val RecursiveValueNeedsResultType(tree) :: Nil = messages
+ assertEquals("i", tree.show)
+ }
+
+ @Test def cyclicReferenceInvolving =
+ checkMessagesAfter("frontend") {
+ """
+ |class A {
+ | val x: T = ???
+ | type T <: x.type // error: cyclic reference involving value x
+ |}
+ """.stripMargin
+ }
+ .expect { (ictx, messages) =>
+ implicit val ctx: Context = ictx
+ val defn = ictx.definitions
+
+ assertMessageCount(1, messages)
+ val CyclicReferenceInvolving(denot) :: Nil = messages
+ assertEquals("value x", denot.show)
+ }
+
+ @Test def cyclicReferenceInvolvingImplicit =
+ checkMessagesAfter("frontend") {
+ """
+ |object implicitDefs {
+ | def foo(implicit x: String) = 1
+ | def bar() = {
+ | implicit val x = foo
+ | x
+ | }
+ |}
+ """.stripMargin
+ }
+ .expect { (ictx, messages) =>
+ implicit val ctx: Context = ictx
+ val defn = ictx.definitions
+
+ assertMessageCount(1, messages)
+ val CyclicReferenceInvolvingImplicit(tree) :: Nil = messages
+ assertEquals("x", tree.name.show)
+ }
+
+
}
diff --git a/library/src/dotty/DottyPredef.scala b/library/src/dotty/DottyPredef.scala
index 12040e0f3..e78fa9239 100644
--- a/library/src/dotty/DottyPredef.scala
+++ b/library/src/dotty/DottyPredef.scala
@@ -36,4 +36,24 @@ object DottyPredef {
implicit def eqNumFloat : Eq[Number, Float] = Eq
implicit def eqDoubleNum: Eq[Double, Number] = Eq
implicit def eqNumDouble: Eq[Number, Double] = Eq
+
+ /** A class for implicit values that can serve as implicit conversions
+ * The implicit resolution algorithm will act as if there existed
+ * the additional implicit definition:
+ *
+ * def $implicitConversion[T, U](x: T)(c: ImplicitConverter[T, U]): U = c(x)
+ *
+ * However, the presence of this definition would slow down implicit search since
+ * its outermost type matches any pair of types. Therefore, implicit search
+ * contains a special case in `Implicits#discardForView` which emulates the
+ * conversion in a more efficient way.
+ *
+ * Note that this is a SAM class - function literals are automatically converted
+ * to `ImplicitConverter` values.
+ *
+ * Also note that in bootstrapped dotty, `Predef.<:<` should inherit from
+ * `ImplicitConverter`. This would cut the number of special cases in
+ * `discardForView` from two to one.
+ */
+ abstract class ImplicitConverter[-T, +U] extends Function1[T, U]
}
diff --git a/tests/pending/pos/depmet_implicit_oopsla_session_simpler.scala b/tests/disabled/pos/depmet_implicit_oopsla_session_simpler.scala
index fad5eba40..8cc6fe754 100644
--- a/tests/pending/pos/depmet_implicit_oopsla_session_simpler.scala
+++ b/tests/disabled/pos/depmet_implicit_oopsla_session_simpler.scala
@@ -1,3 +1,4 @@
+// Disabled because we now get an unsafe instantiation error
object Sessions {
trait Session {
type Dual <: Session
@@ -40,5 +41,5 @@ object Sessions {
In{z: Int => System.out.println(z)
Stop()}}))
- def myRun = addServer run addClient
+ def myRun = addServer run addClient // error: unsafe instantiation
}
diff --git a/tests/neg/falseView.scala b/tests/neg/falseView.scala
new file mode 100644
index 000000000..613abe3f1
--- /dev/null
+++ b/tests/neg/falseView.scala
@@ -0,0 +1,7 @@
+object Test {
+
+ private implicit val xs: Map[String, Int] = ???
+
+ val x: Int = "abc" // error
+
+}
diff --git a/tests/pending/pos/depmet_implicit_oopsla_session_2.scala b/tests/pending/pos/depmet_implicit_oopsla_session_2.scala
index 29a76d5cf..26fa2a4cc 100644
--- a/tests/pending/pos/depmet_implicit_oopsla_session_2.scala
+++ b/tests/pending/pos/depmet_implicit_oopsla_session_2.scala
@@ -1,3 +1,8 @@
+// Fails on line 70 with: no implicit argument of type Sessions.Session[
+// | Sessions.In[Int, Sessions.In[Int, Sessions.Out[Int, Sessions.Stop]]]^
+// |]#HasDual[Sessions.Out[Int, Sessions.Out[Int, Sessions.In[Int, Sessions.Stop]]]^
+// | ] found for parameter evidence$1 of method runSession in object Sessions
+// This could be related to existential types (the # essentially boils down to one).
object Sessions {
def ?[T <: AnyRef](implicit w: T): w.type = w
diff --git a/tests/pickling/innerclass.scala b/tests/pickling/innerclass.scala
new file mode 100644
index 000000000..5881cf6ed
--- /dev/null
+++ b/tests/pickling/innerclass.scala
@@ -0,0 +1,3 @@
+trait Foo {
+ class Inner(x: Int = 42)
+}
diff --git a/tests/pos/typerep-stephane.scala b/tests/pos-scala2/typerep-stephane.scala
index 2cb899591..2cb899591 100644
--- a/tests/pos/typerep-stephane.scala
+++ b/tests/pos-scala2/typerep-stephane.scala
diff --git a/tests/pos/viewtest1.scala b/tests/pos-scala2/viewtest1.scala
index 38945ad2f..38945ad2f 100644
--- a/tests/pos/viewtest1.scala
+++ b/tests/pos-scala2/viewtest1.scala
diff --git a/tests/pending/pos/depmet_implicit_norm_ret.scala b/tests/pos/depmet_implicit_norm_ret.scala
index 42bfb9fe1..42bfb9fe1 100644
--- a/tests/pending/pos/depmet_implicit_norm_ret.scala
+++ b/tests/pos/depmet_implicit_norm_ret.scala
diff --git a/tests/pos/i2064.scala b/tests/pos/i2064.scala
new file mode 100644
index 000000000..909923372
--- /dev/null
+++ b/tests/pos/i2064.scala
@@ -0,0 +1,15 @@
+object p {
+
+class Foo[T] {
+ // Crashes:
+ def f(): Foo[T] = (if (true) this else this).bar
+
+ // Works:
+ // def f(): Foo[T] = new Bar(if (true) this else this).bar
+}
+
+implicit class Bar[A](val self: A) {
+ def bar(): A = self
+}
+
+}
diff --git a/tests/pos/t0786.scala b/tests/pos/t0786.scala
index b320de0ed..9346afdff 100644
--- a/tests/pos/t0786.scala
+++ b/tests/pos/t0786.scala
@@ -15,7 +15,7 @@ object ImplicitProblem {
def eval = f(nullval[T]).eval + 1
}
- def depth[T](n: T)(implicit ev: T => Rep[T]) = n.eval
+ def depth[T](n: T)(implicit ev: T => Rep[T]) = ev(n).eval
def main(args: Array[String]): Unit = {
println(depth(nullval[M[Int]])) // (1) this works
diff --git a/tests/pos/t2421_delitedsl.scala b/tests/pos/t2421_delitedsl.scala
index 22f1ecd85..bde3593c9 100644
--- a/tests/pos/t2421_delitedsl.scala
+++ b/tests/pos/t2421_delitedsl.scala
@@ -1,6 +1,9 @@
trait DeliteDSL {
abstract class <~<[-From, +To] extends (From => To)
+
implicit def trivial[A]: A <~< A = new (A <~< A) {def apply(x: A) = x}
+ implicit def convert_<-<[A, B](x: A)(implicit ev: A <~< B): B = ev(x)
+
trait Forcible[T]
object Forcible {
diff --git a/tests/pos/t5070.scala b/tests/pos/t5070.scala
index 410afba14..0e5c0ffc0 100644
--- a/tests/pos/t5070.scala
+++ b/tests/pos/t5070.scala
@@ -13,3 +13,26 @@ class Test {
implicitly[a.T](b(a)) // works
}
+
+
+class ImplicitVsTypeAliasTezt {
+
+ class Monad[m[_]] {
+ type For[a] = _For[m, a]
+ implicit def toFor[a](m: m[a]): For[a] = throw new Error("todo") // lookup fails
+// implicit def toFor[a](m: m[a]): _For[m, a] = throw new Error("todo") // fine.
+ }
+
+ trait _For[m[_], a] {
+ def map[b](p: a => b): m[b]
+ }
+
+ def useMonad[m[_], a](m: m[a])(implicit i: Monad[m]) = {
+ import i._
+
+ // value map is not a member of type parameter m[a]
+ for {
+ x <- m
+ } yield x.toString
+ }
+}
diff --git a/tests/pos/t5643.scala b/tests/pos/t5643.scala
new file mode 100644
index 000000000..1ce34ba36
--- /dev/null
+++ b/tests/pos/t5643.scala
@@ -0,0 +1,19 @@
+object TupledEvidenceTest {
+
+ abstract class TupledEvidence[M[_], T0] { type T = T0 }
+
+ implicit def witnessTuple2[M[_], T1, T2](implicit ev1: M[T1], ev2: M[T2]):
+ TupledEvidence[M, (T1, T2)] { type T = (T1, T2) } = sys.error("")
+
+ class GetResult[T]
+
+ implicit val getString: GetResult[String] = new GetResult[String]
+
+ implicit def getTuple[T](implicit w: TupledEvidence[GetResult, T]): GetResult[w.T] = sys.error("")
+
+ def f[T : GetResult] = ""
+
+ f[(String,String)](getTuple[(String, String)])
+
+ f[(String,String)]
+}
diff --git a/tests/run/HLists-nonvariant.check b/tests/run/HLists-nonvariant.check
new file mode 100644
index 000000000..53efe30d7
--- /dev/null
+++ b/tests/run/HLists-nonvariant.check
@@ -0,0 +1,8 @@
+1
+A
+true
+true
+HCons(1,HCons(A,HCons(true,HNil)))
+1
+A
+true
diff --git a/tests/run/HLists-nonvariant.scala b/tests/run/HLists-nonvariant.scala
new file mode 100644
index 000000000..a3a639bcd
--- /dev/null
+++ b/tests/run/HLists-nonvariant.scala
@@ -0,0 +1,78 @@
+sealed trait HList
+case class HCons[HD, TL](hd: HD, tl: TL) extends HList
+case object HNil extends HList
+
+sealed trait Num
+case object Zero extends Num
+case class Succ[N <: Num](pred: N) extends Num
+
+object Test {
+
+ type HNil = HNil.type
+ type Zero = Zero.type
+
+ trait Concat[Xs <: HList, Ys <: HList, Zs <: HList] {
+ def concat(xs: Xs, ys: Ys): Zs
+ }
+
+ implicit def concatNil[Xs <: HList]: Concat[HNil, Xs, Xs] =
+ new Concat[HNil, Xs, Xs] {
+ def concat(fst: HNil, snd: Xs) = snd
+ }
+
+ implicit def concatCons[X, Xs <: HList, Ys <: HList, Zs <: HList](
+ implicit ev: Concat[Xs, Ys, Zs]
+ ): Concat[HCons[X, Xs], Ys, HCons[X, Zs]] =
+ new Concat[HCons[X, Xs], Ys, HCons[X, Zs]] {
+ def concat(xs: HCons[X, Xs], ys: Ys): HCons[X, Zs] =
+ HCons(xs.hd, ev.concat(xs.tl, ys))
+ }
+
+ def concat[Xs <: HList, Ys <: HList, Zs <: HList](xs: Xs, ys: Ys)(implicit ev: Concat[Xs, Ys, Zs]): Zs =
+ ev.concat(xs, ys)
+
+ val xs = HCons(1, HCons("A", HNil))
+ val ys = HCons(true, HNil)
+ val zs = concat(xs, ys)
+ val zs1: HCons[Int, HCons[String, HCons[Boolean, HNil]]] = zs
+
+ trait At[Xs <: HList, N <: Num] {
+ type Out
+ def at(xs: Xs, n: N): Out
+ }
+
+ implicit def atZero[XZ, Xs <: HList]: At[HCons[XZ, Xs], Zero] { type Out = XZ } =
+ new At[HCons[XZ, Xs], Zero] {
+ type Out = XZ
+ def at(xs: HCons[XZ, Xs], n: Zero) = xs.hd
+ }
+
+ implicit def atSucc[XX, Xs <: HList, N <: Num](
+ implicit ev: At[Xs, N]
+ ): At[HCons[XX, Xs], Succ[N]] { type Out = ev.Out } = new At[HCons[XX, Xs], Succ[N]] {
+ type Out = ev.Out
+ def at(xs: HCons[XX, Xs], n: Succ[N]): Out = ev.at(xs.tl, n.pred)
+ }
+
+ def at[Xs <: HList, N <: Num](xs: Xs, n: N)(
+ implicit ev: At[Xs, N]
+ ): ev.Out = ev.at(xs, n)
+
+ def main(args: Array[String]) = {
+ val ys1 = HCons(1, HNil)
+ println(at(ys1, Zero))
+
+ val ys2 = HCons(1, HCons("A", HNil))
+ val y2 = at(ys2, Succ(Zero))
+ println(at(ys2, Succ(Zero)))
+ val ys3 = HCons(1, HCons("A", HCons(true, HNil)))
+ println(at(ys3, Succ(Succ(Zero))))
+ val ys4 = HCons(1, HCons("A", HCons(true, HCons(1.0, HNil))))
+ println(at(ys4, Succ(Succ(Zero))))
+
+ println(zs1)
+ println(at(zs1, Zero))
+ println(at(zs1, Succ(Zero)))
+ println(at(zs1, Succ(Succ(Zero))))
+ }
+}
diff --git a/tests/run/HLists.check b/tests/run/HLists.check
new file mode 100644
index 000000000..53efe30d7
--- /dev/null
+++ b/tests/run/HLists.check
@@ -0,0 +1,8 @@
+1
+A
+true
+true
+HCons(1,HCons(A,HCons(true,HNil)))
+1
+A
+true
diff --git a/tests/run/HLists.scala b/tests/run/HLists.scala
new file mode 100644
index 000000000..eebbcb4cd
--- /dev/null
+++ b/tests/run/HLists.scala
@@ -0,0 +1,78 @@
+sealed trait HList
+case class HCons[+HD, +TL](hd: HD, tl: TL) extends HList
+case object HNil extends HList
+
+sealed trait Num
+case object Zero extends Num
+case class Succ[N <: Num](pred: N) extends Num
+
+object Test {
+
+ type HNil = HNil.type
+ type Zero = Zero.type
+
+ trait Concat[Xs <: HList, Ys <: HList, Zs <: HList] {
+ def concat(xs: Xs, ys: Ys): Zs
+ }
+
+ implicit def concatNil[Xs <: HList]: Concat[HNil, Xs, Xs] =
+ new Concat[HNil, Xs, Xs] {
+ def concat(fst: HNil, snd: Xs) = snd
+ }
+
+ implicit def concatCons[X, Xs <: HList, Ys <: HList, Zs <: HList](
+ implicit ev: Concat[Xs, Ys, Zs]
+ ): Concat[HCons[X, Xs], Ys, HCons[X, Zs]] =
+ new Concat[HCons[X, Xs], Ys, HCons[X, Zs]] {
+ def concat(xs: HCons[X, Xs], ys: Ys): HCons[X, Zs] =
+ HCons(xs.hd, ev.concat(xs.tl, ys))
+ }
+
+ def concat[Xs <: HList, Ys <: HList, Zs <: HList](xs: Xs, ys: Ys)(implicit ev: Concat[Xs, Ys, Zs]): Zs =
+ ev.concat(xs, ys)
+
+ val xs = HCons(1, HCons("A", HNil))
+ val ys = HCons(true, HNil)
+ val zs = concat(xs, ys)
+ val zs1: HCons[Int, HCons[String, HCons[Boolean, HNil]]] = zs
+
+ trait At[Xs <: HList, N <: Num] {
+ type Out
+ def at(xs: Xs, n: N): Out
+ }
+
+ implicit def atZero[XZ, Xs <: HList]: At[HCons[XZ, Xs], Zero] { type Out = XZ } =
+ new At[HCons[XZ, Xs], Zero] {
+ type Out = XZ
+ def at(xs: HCons[XZ, Xs], n: Zero) = xs.hd
+ }
+
+ implicit def atSucc[XX, Xs <: HList, N <: Num](
+ implicit ev: At[Xs, N]
+ ): At[HCons[XX, Xs], Succ[N]] { type Out = ev.Out } = new At[HCons[XX, Xs], Succ[N]] {
+ type Out = ev.Out
+ def at(xs: HCons[XX, Xs], n: Succ[N]): Out = ev.at(xs.tl, n.pred)
+ }
+
+ def at[Xs <: HList, N <: Num](xs: Xs, n: N)(
+ implicit ev: At[Xs, N]
+ ): ev.Out = ev.at(xs, n)
+
+ def main(args: Array[String]) = {
+ val ys1 = HCons(1, HNil)
+ println(at(ys1, Zero))
+
+ val ys2 = HCons(1, HCons("A", HNil))
+ val y2 = at(ys2, Succ(Zero))
+ println(at(ys2, Succ(Zero)))
+ val ys3 = HCons(1, HCons("A", HCons(true, HNil)))
+ println(at(ys3, Succ(Succ(Zero))))
+ val ys4 = HCons(1, HCons("A", HCons(true, HCons(1.0, HNil))))
+ println(at(ys4, Succ(Succ(Zero))))
+
+ println(zs1)
+ println(at(zs1, Zero))
+ println(at(zs1, Succ(Zero)))
+ println(at(zs1, Succ(Succ(Zero))))
+ }
+}
diff --git a/tests/run/hmap-covariant.scala b/tests/run/hmap-covariant.scala
new file mode 100644
index 000000000..475cc6ee6
--- /dev/null
+++ b/tests/run/hmap-covariant.scala
@@ -0,0 +1,97 @@
+trait Tuple
+case class TCons[+H, +T <: Tuple](h: H, t: T) extends Tuple
+final case object TNil extends Tuple
+
+// Type level natural numbers -------------------------------------------------
+
+sealed trait Nat
+sealed trait Succ[P <: Nat] extends Nat
+sealed trait Zero extends Nat
+
+// Accessor type class to compute the N'th element of an Tuple L --------------
+
+trait At[L <: Tuple, N <: Nat, Out] {
+ def apply(l: L): Out
+}
+
+object At {
+ implicit def caseZero[H, T <: Tuple]: At[H TCons T, Zero, H] =
+ new At[H TCons T, Zero, H] {
+ def apply(l: H TCons T): H = {
+ val (h TCons _) = l
+ h
+ }
+ }
+
+ implicit def caseN[H, T <: Tuple, N <: Nat, O]
+ (implicit a: At[T, N, O]): At[H TCons T, Succ[N], O] =
+ new At[H TCons T, Succ[N], O] {
+ def apply(l: H TCons T): O = {
+ val (_ TCons t) = l
+ a(t)
+ }
+ }
+}
+
+// An HMap is an Tuple with HEntry elements. We are reusing Tuple for it's nice syntax
+
+final case class HEntry[K, V](value: V)
+
+// Accessor type class to compute the element of type K in a HMap L -----------
+
+trait PhantomGet[K, M <: Tuple, I <: Nat] // extends PhantomAny
+
+object PhantomGet {
+ implicit def getHead[K, V, T <: Tuple]
+ : PhantomGet[K, HEntry[K, V] TCons T, Zero] = null
+
+ implicit def getTail[K, H, T <: Tuple, I <: Nat]
+ (implicit t: PhantomGet[K, T, I])
+ : PhantomGet[K, H TCons T, Succ[I]] = null
+}
+
+// Syntax ---------------------------------------------------------------------
+
+object syntax {
+ object hmap {
+ implicit class HmapGet[M <: Tuple](m: M) {
+ def get[K, V, I <: Nat](k: K)
+ (implicit
+ g: PhantomGet[k.type, M, I],
+ a: At[M, I, HEntry[k.type, V]]
+ ): V = a(m).value
+ }
+
+ def --[K, V](key: K, value: V) = HEntry[key.type, V](value)
+ }
+}
+
+object Test {
+ def main(args: Array[String]): Unit = {
+ import syntax.hmap._
+
+ val map1 =
+ TCons(HEntry[K = "name"]("foo"),
+ TCons(HEntry[K = "genre"](true),
+ TCons(HEntry[K = "moneyz"](123),
+ TCons(HEntry[K = "cat"]("bar"),
+ (TNil: TNil.type)))))
+
+ assert(map1.get("name") == "foo")
+ assert(map1.get("genre") == true)
+ assert(map1.get("moneyz") == 123)
+ assert(map1.get("cat") == "bar")
+
+ val map2 =
+ TCons(--("name" , "foo"),
+ TCons(--("genre" , true),
+ TCons(--("moneyz", 123),
+ TCons(--("cat" , "bar"),
+ TNil))))
+
+ assert(map2.get("name") == "foo")
+ assert(map2.get("genre") == true)
+ assert(map2.get("moneyz") == 123)
+ assert(map2.get("cat") == "bar")
+ }
+}
diff --git a/tests/run/i2004.check b/tests/run/i2004.check
new file mode 100644
index 000000000..d00491fd7
--- /dev/null
+++ b/tests/run/i2004.check
@@ -0,0 +1 @@
+1
diff --git a/tests/run/i2004.scala b/tests/run/i2004.scala
new file mode 100644
index 000000000..ff21a6cc9
--- /dev/null
+++ b/tests/run/i2004.scala
@@ -0,0 +1,11 @@
+object Test {
+ def main(args: Array[String]) = {
+ val f: (Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int,Int, Int, Int, Int, Int, Int, Int, Int, Int, Int) => Int =
+ {
+ (x1: Int, x2: Int, x3: Int, x4: Int, x5: Int, x6: Int, x7: Int, x8: Int, x9: Int, x10: Int, x11: Int, x12: Int, x13: Int, x14: Int, x15: Int, x16: Int, x17: Int, x18: Int, x19: Int, x20: Int, x21: Int, x22: Int, x23: Int, x24: Int, x25: Int, x26: Int) =>
+ x1
+ }
+ println(f.apply(1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1))
+
+ }
+}
diff --git a/tests/run/iterator-from.scala b/tests/run/iterator-from.scala
index 4f403680c..c7c0f9809 100644
--- a/tests/run/iterator-from.scala
+++ b/tests/run/iterator-from.scala
@@ -11,7 +11,9 @@ object Test extends dotty.runtime.LegacyApp {
val maxKey = 50
val maxValue = 50
- def testSet[A <% Ordered[A]](set: SortedSet[A], list: List[A]): Unit = {
+ implicit def convertIfView[A](x: A)(implicit view: A => Ordered[A]): Ordered[A] = view(x)
+
+ def testSet[A: Ordering](set: SortedSet[A], list: List[A]): Unit = {
val distinctSorted = list.distinct.sorted
assertEquals("Set size wasn't the same as list sze", set.size, distinctSorted.size)
@@ -24,7 +26,7 @@ object Test extends dotty.runtime.LegacyApp {
}
}
- def testMap[A <% Ordered[A], B](map: SortedMap[A, B], list: List[(A, B)]): Unit = {
+ def testMap[A: Ordering, B](map: SortedMap[A, B], list: List[(A, B)]): Unit = {
val distinctSorted = distinctByKey(list).sortBy(_._1)
assertEquals("Map size wasn't the same as list sze", map.size, distinctSorted.size)
diff --git a/tests/run/puzzler54.scala b/tests/run/puzzler54.scala
new file mode 100644
index 000000000..9dd4cbb47
--- /dev/null
+++ b/tests/run/puzzler54.scala
@@ -0,0 +1,13 @@
+// Scala Puzzler 54
+object Test {
+ case class Card(number: Int, suit: String = "clubs") {
+ val value = (number % 13) + 1 // ace = 1, king = 13
+ def isInDeck(implicit deck: List[Card]) = deck contains this
+ }
+
+ def main(args: Array[String]) = {
+ implicit val deck = List(Card(1, "clubs"))
+ implicit def intToCard(n: Int): Card = Card(n)
+ assert(1.isInDeck)
+ }
+}
diff --git a/tests/run/t8280.check b/tests/run/t8280.check
index 44c51f5aa..b5885df48 100644
--- a/tests/run/t8280.check
+++ b/tests/run/t8280.check
@@ -1,7 +1,6 @@
-Int
-Int
Long
Int
Int
Int
Int
+Int
diff --git a/tests/run/t8280.scala b/tests/run/t8280.scala
index f433dcc32..5fcbad0a3 100644
--- a/tests/run/t8280.scala
+++ b/tests/run/t8280.scala
@@ -37,7 +37,8 @@ object Moop1 {
implicit object f1 extends (Int => String) { def apply(x: Int): String = "Int" }
implicit val f2: Long => String = _ => "Long"
- println(5: String)
+ //println(5: String)
+ // This picked f1 before, but is now disallowed since subtypes of functions are no longer implicit conversions
}
}
@@ -73,14 +74,14 @@ object Moop3 {
// Dotty deviation. This fails for Dotty with ambiguity error for similar reasons as ob1.
}
object ob2 {
- implicit val f1: Int => String = _ => "Int"
+ implicit val f1: ImplicitConverter[Int, String] = _ => "Int"
implicit def f2(x: Long): String = "Long"
println(5: String)
}
object ob3 {
- implicit val f1: Int => String = _ => "Int"
- implicit val f2: Long => String = _ => "Long"
+ implicit val f1: ImplicitConverter[Int, String] = _ => "Int"
+ implicit val f2: ImplicitConverter[Long, String] = _ => "Long"
println(5: String)
}