Skip to content

Fix #5578: Refine matches condition #5622

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Jan 6, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 6 additions & 5 deletions compiler/src/dotty/tools/dotc/core/Denotations.scala
Original file line number Diff line number Diff line change
Expand Up @@ -494,8 +494,6 @@ object Denotations {
* boundary of sym1. For protected access, we count the enclosing
* package as access boundary.
* 5. sym1 is a method but sym2 is not.
* 6. sym1 is a non-polymorphic method but sym2 is a polymorphic method.
* (to be consistent with infoMeet, see #4819)
* The aim of these criteria is to give some disambiguation on access which
* - does not depend on textual order or other arbitrary choices
* - minimizes raising of doubleDef errors
Expand All @@ -510,7 +508,6 @@ object Denotations {
accessBoundary(sym2).isProperlyContainedIn(accessBoundary(sym1)) ||
sym2.is(Bridge) && !sym1.is(Bridge) ||
sym1.is(Method) && !sym2.is(Method)) ||
sym1.info.isInstanceOf[MethodType] && sym2.info.isInstanceOf[PolyType] ||
sym1.info.isErroneous)

/** Sym preference provided types also override */
Expand Down Expand Up @@ -1071,8 +1068,12 @@ object Denotations {

final def matches(other: SingleDenotation)(implicit ctx: Context): Boolean = {
val d = signature.matchDegree(other.signature)
d == Signature.FullMatch ||
d >= Signature.ParamMatch && info.matches(other.info)
(// fast path: signatures are the same and neither denotation is a PolyType
// For polytypes, signatures alone do not tell us enough to be sure about matching.
d == Signature.FullMatch &&
!infoOrCompleter.isInstanceOf[PolyType] && !other.infoOrCompleter.isInstanceOf[PolyType]
||
d >= Signature.ParamMatch && info.matches(other.info))
}

def mapInherited(ownDenots: PreDenotation, prevDenots: PreDenotation, pre: Type)(implicit ctx: Context): SingleDenotation =
Expand Down
2 changes: 0 additions & 2 deletions compiler/src/dotty/tools/dotc/core/Types.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1916,8 +1916,6 @@ object Types {
setDenot(memberDenot(name, allowPrivate = !symbol.exists || symbol.is(Private)))

private def setDenot(denot: Denotation)(implicit ctx: Context): Unit = {
if (ctx.isAfterTyper)
assert(!denot.isOverloaded || ctx.mode.is(Mode.Printing), this)
if (Config.checkNoDoubleBindings)
if (ctx.settings.YnoDoubleBindings.value)
checkSymAssign(denot.symbol)
Expand Down
103 changes: 56 additions & 47 deletions compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala
Original file line number Diff line number Diff line change
Expand Up @@ -381,65 +381,74 @@ trait TypeAssigner {
}

def assignType(tree: untpd.TypeApply, fn: Tree, args: List[Tree])(implicit ctx: Context): TypeApply = {
val ownType = fn.tpe.widen match {
def fail = tree.withType(errorType(err.takesNoParamsStr(fn, "type "), tree.pos))
fn.tpe.widen match {
case pt: TypeLambda =>
val paramNames = pt.paramNames
if (hasNamedArg(args)) {
val paramBoundsByName = paramNames.zip(pt.paramInfos).toMap

// Type arguments which are specified by name (immutable after this first loop)
val namedArgMap = new mutable.HashMap[Name, Type]
for (NamedArg(name, arg) <- args)
if (namedArgMap.contains(name))
ctx.error(DuplicateNamedTypeParameter(name), arg.pos)
else if (!paramNames.contains(name))
ctx.error(UndefinedNamedTypeParameter(name, paramNames), arg.pos)
else
namedArgMap(name) = arg.tpe

// Holds indexes of non-named typed arguments in paramNames
val gapBuf = new mutable.ListBuffer[Int]
def nextPoly(idx: Int) = {
val newIndex = gapBuf.length
gapBuf += idx
// Re-index unassigned type arguments that remain after transformation
pt.paramRefs(newIndex)
}
tree.withType {
val paramNames = pt.paramNames
if (hasNamedArg(args)) {
val paramBoundsByName = paramNames.zip(pt.paramInfos).toMap

// Type arguments which are specified by name (immutable after this first loop)
val namedArgMap = new mutable.HashMap[Name, Type]
for (NamedArg(name, arg) <- args)
if (namedArgMap.contains(name))
ctx.error(DuplicateNamedTypeParameter(name), arg.pos)
else if (!paramNames.contains(name))
ctx.error(UndefinedNamedTypeParameter(name, paramNames), arg.pos)
else
namedArgMap(name) = arg.tpe

// Holds indexes of non-named typed arguments in paramNames
val gapBuf = new mutable.ListBuffer[Int]
def nextPoly(idx: Int) = {
val newIndex = gapBuf.length
gapBuf += idx
// Re-index unassigned type arguments that remain after transformation
pt.paramRefs(newIndex)
}

// Type parameters after naming assignment, conserving paramNames order
val normArgs: List[Type] = paramNames.zipWithIndex.map { case (pname, idx) =>
namedArgMap.getOrElse(pname, nextPoly(idx))
}
// Type parameters after naming assignment, conserving paramNames order
val normArgs: List[Type] = paramNames.zipWithIndex.map { case (pname, idx) =>
namedArgMap.getOrElse(pname, nextPoly(idx))
}

val transform = new TypeMap {
def apply(t: Type) = t match {
case TypeParamRef(`pt`, idx) => normArgs(idx)
case _ => mapOver(t)
val transform = new TypeMap {
def apply(t: Type) = t match {
case TypeParamRef(`pt`, idx) => normArgs(idx)
case _ => mapOver(t)
}
}
val resultType1 = transform(pt.resultType)
if (gapBuf.isEmpty) resultType1
else {
val gaps = gapBuf.toList
pt.derivedLambdaType(
gaps.map(paramNames),
gaps.map(idx => transform(pt.paramInfos(idx)).bounds),
resultType1)
}
}
val resultType1 = transform(pt.resultType)
if (gapBuf.isEmpty) resultType1
else {
val gaps = gapBuf.toList
pt.derivedLambdaType(
gaps.map(paramNames),
gaps.map(idx => transform(pt.paramInfos(idx)).bounds),
resultType1)
val argTypes = args.tpes
if (sameLength(argTypes, paramNames)) pt.instantiate(argTypes)
else wrongNumberOfTypeArgs(fn.tpe, pt.typeParams, args, tree.pos)
}
}
else {
val argTypes = args.tpes
if (sameLength(argTypes, paramNames)) pt.instantiate(argTypes)
else wrongNumberOfTypeArgs(fn.tpe, pt.typeParams, args, tree.pos)
}
case err: ErrorType =>
err
tree.withType(err)
case ref: TermRef if ref.isOverloaded =>
val disambiguated = ref.denot.suchThat(_.info.isInstanceOf[PolyType])
if (disambiguated.exists) {
val fn1 = fn.withType(ref.withDenot(disambiguated))
val tree1 = untpd.cpy.TypeApply(tree)(fn1, args)
assignType(tree1, fn1, args)
}
else fail
case _ =>
//println(i"bad type: $fn: ${fn.symbol} / ${fn.symbol.isType} / ${fn.symbol.info}") // DEBUG
errorType(err.takesNoParamsStr(fn, "type "), tree.pos)
fail
}

tree.withType(ownType)
}

def assignType(tree: untpd.Typed, tpt: Tree)(implicit ctx: Context): Typed =
Expand Down
8 changes: 2 additions & 6 deletions tests/neg/i4819.scala
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,6 @@ trait Two[Y <: Foo] {
}

class Foo extends One[Foo] with Two[Foo] {
concat(0) // OK

// TODO: This does not typecheck because the polymorphic overload is masked
// (we merge the denotations for both overloads into one and always prefer
// MethodType to PolyType, instead we should return a MultiDenotation). See #4819.
concat[Int](0) // error (that should actually not be an error)
concat(0) // error: ambiguous overload
concat[Int](0) // OK
}
6 changes: 6 additions & 0 deletions tests/neg/i5445.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
object Test {

trait A { def polymorphic[x]: Int }
val a = new A { val polymorphic = Unit } // error: object creation impossible

}
12 changes: 12 additions & 0 deletions tests/neg/i5578.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
trait P[A]{
def a[T]: A
}
class C extends P[Int]{ // error: class C needs to be abstract
def a = 1
}
object O{
def main(args: Array[String]) = {
val p: P[Int] = new C
println(p.a)
}
}
8 changes: 7 additions & 1 deletion tests/pos/i4819.scala
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,12 @@ trait Two[Y <: Foo] {
}

class Foo extends One[Foo] with Two[Foo] {
concat(0) // OK
concat[Int](0) // OK
// See also tests/neg/i4819.scala
}

class Bar extends One[String] with Two[Foo] {
val x: String = concat(0)
val y = concat[Int](0)
val z: Foo = concat(0)
}