Skip to content

Commit bc5e0d2

Browse files
authored
Merge pull request #5622 from dotty-staging/fix-#5578
Fix #5578: Refine matches condition
2 parents 5b7d5b6 + ab62df0 commit bc5e0d2

File tree

7 files changed

+89
-61
lines changed

7 files changed

+89
-61
lines changed

compiler/src/dotty/tools/dotc/core/Denotations.scala

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -437,8 +437,6 @@ object Denotations {
437437
* boundary of sym1. For protected access, we count the enclosing
438438
* package as access boundary.
439439
* 5. sym1 is a method but sym2 is not.
440-
* 6. sym1 is a non-polymorphic method but sym2 is a polymorphic method.
441-
* (to be consistent with infoMeet, see #4819)
442440
* The aim of these criteria is to give some disambiguation on access which
443441
* - does not depend on textual order or other arbitrary choices
444442
* - minimizes raising of doubleDef errors
@@ -453,7 +451,6 @@ object Denotations {
453451
accessBoundary(sym2).isProperlyContainedIn(accessBoundary(sym1)) ||
454452
sym2.is(Bridge) && !sym1.is(Bridge) ||
455453
sym1.is(Method) && !sym2.is(Method)) ||
456-
sym1.info.isInstanceOf[MethodType] && sym2.info.isInstanceOf[PolyType] ||
457454
sym1.info.isErroneous)
458455

459456
/** Sym preference provided types also override */
@@ -1097,8 +1094,12 @@ object Denotations {
10971094

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

11041105
def mapInherited(ownDenots: PreDenotation, prevDenots: PreDenotation, pre: Type)(implicit ctx: Context): SingleDenotation =

compiler/src/dotty/tools/dotc/core/Types.scala

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1914,8 +1914,6 @@ object Types {
19141914
setDenot(memberDenot(name, allowPrivate = !symbol.exists || symbol.is(Private)))
19151915

19161916
private def setDenot(denot: Denotation)(implicit ctx: Context): Unit = {
1917-
if (ctx.isAfterTyper)
1918-
assert(!denot.isOverloaded || ctx.mode.is(Mode.Printing), this)
19191917
if (Config.checkNoDoubleBindings)
19201918
if (ctx.settings.YnoDoubleBindings.value)
19211919
checkSymAssign(denot.symbol)

compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala

Lines changed: 56 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -381,65 +381,74 @@ trait TypeAssigner {
381381
}
382382

383383
def assignType(tree: untpd.TypeApply, fn: Tree, args: List[Tree])(implicit ctx: Context): TypeApply = {
384-
val ownType = fn.tpe.widen match {
384+
def fail = tree.withType(errorType(err.takesNoParamsStr(fn, "type "), tree.pos))
385+
fn.tpe.widen match {
385386
case pt: TypeLambda =>
386-
val paramNames = pt.paramNames
387-
if (hasNamedArg(args)) {
388-
val paramBoundsByName = paramNames.zip(pt.paramInfos).toMap
389-
390-
// Type arguments which are specified by name (immutable after this first loop)
391-
val namedArgMap = new mutable.HashMap[Name, Type]
392-
for (NamedArg(name, arg) <- args)
393-
if (namedArgMap.contains(name))
394-
ctx.error(DuplicateNamedTypeParameter(name), arg.pos)
395-
else if (!paramNames.contains(name))
396-
ctx.error(UndefinedNamedTypeParameter(name, paramNames), arg.pos)
397-
else
398-
namedArgMap(name) = arg.tpe
399-
400-
// Holds indexes of non-named typed arguments in paramNames
401-
val gapBuf = new mutable.ListBuffer[Int]
402-
def nextPoly(idx: Int) = {
403-
val newIndex = gapBuf.length
404-
gapBuf += idx
405-
// Re-index unassigned type arguments that remain after transformation
406-
pt.paramRefs(newIndex)
407-
}
387+
tree.withType {
388+
val paramNames = pt.paramNames
389+
if (hasNamedArg(args)) {
390+
val paramBoundsByName = paramNames.zip(pt.paramInfos).toMap
391+
392+
// Type arguments which are specified by name (immutable after this first loop)
393+
val namedArgMap = new mutable.HashMap[Name, Type]
394+
for (NamedArg(name, arg) <- args)
395+
if (namedArgMap.contains(name))
396+
ctx.error(DuplicateNamedTypeParameter(name), arg.pos)
397+
else if (!paramNames.contains(name))
398+
ctx.error(UndefinedNamedTypeParameter(name, paramNames), arg.pos)
399+
else
400+
namedArgMap(name) = arg.tpe
401+
402+
// Holds indexes of non-named typed arguments in paramNames
403+
val gapBuf = new mutable.ListBuffer[Int]
404+
def nextPoly(idx: Int) = {
405+
val newIndex = gapBuf.length
406+
gapBuf += idx
407+
// Re-index unassigned type arguments that remain after transformation
408+
pt.paramRefs(newIndex)
409+
}
408410

409-
// Type parameters after naming assignment, conserving paramNames order
410-
val normArgs: List[Type] = paramNames.zipWithIndex.map { case (pname, idx) =>
411-
namedArgMap.getOrElse(pname, nextPoly(idx))
412-
}
411+
// Type parameters after naming assignment, conserving paramNames order
412+
val normArgs: List[Type] = paramNames.zipWithIndex.map { case (pname, idx) =>
413+
namedArgMap.getOrElse(pname, nextPoly(idx))
414+
}
413415

414-
val transform = new TypeMap {
415-
def apply(t: Type) = t match {
416-
case TypeParamRef(`pt`, idx) => normArgs(idx)
417-
case _ => mapOver(t)
416+
val transform = new TypeMap {
417+
def apply(t: Type) = t match {
418+
case TypeParamRef(`pt`, idx) => normArgs(idx)
419+
case _ => mapOver(t)
420+
}
421+
}
422+
val resultType1 = transform(pt.resultType)
423+
if (gapBuf.isEmpty) resultType1
424+
else {
425+
val gaps = gapBuf.toList
426+
pt.derivedLambdaType(
427+
gaps.map(paramNames),
428+
gaps.map(idx => transform(pt.paramInfos(idx)).bounds),
429+
resultType1)
418430
}
419431
}
420-
val resultType1 = transform(pt.resultType)
421-
if (gapBuf.isEmpty) resultType1
422432
else {
423-
val gaps = gapBuf.toList
424-
pt.derivedLambdaType(
425-
gaps.map(paramNames),
426-
gaps.map(idx => transform(pt.paramInfos(idx)).bounds),
427-
resultType1)
433+
val argTypes = args.tpes
434+
if (sameLength(argTypes, paramNames)) pt.instantiate(argTypes)
435+
else wrongNumberOfTypeArgs(fn.tpe, pt.typeParams, args, tree.pos)
428436
}
429437
}
430-
else {
431-
val argTypes = args.tpes
432-
if (sameLength(argTypes, paramNames)) pt.instantiate(argTypes)
433-
else wrongNumberOfTypeArgs(fn.tpe, pt.typeParams, args, tree.pos)
434-
}
435438
case err: ErrorType =>
436-
err
439+
tree.withType(err)
440+
case ref: TermRef if ref.isOverloaded =>
441+
val disambiguated = ref.denot.suchThat(_.info.isInstanceOf[PolyType])
442+
if (disambiguated.exists) {
443+
val fn1 = fn.withType(ref.withDenot(disambiguated))
444+
val tree1 = untpd.cpy.TypeApply(tree)(fn1, args)
445+
assignType(tree1, fn1, args)
446+
}
447+
else fail
437448
case _ =>
438449
//println(i"bad type: $fn: ${fn.symbol} / ${fn.symbol.isType} / ${fn.symbol.info}") // DEBUG
439-
errorType(err.takesNoParamsStr(fn, "type "), tree.pos)
450+
fail
440451
}
441-
442-
tree.withType(ownType)
443452
}
444453

445454
def assignType(tree: untpd.Typed, tpt: Tree)(implicit ctx: Context): Typed =

tests/neg/i4819.scala

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,6 @@ trait Two[Y <: Foo] {
77
}
88

99
class Foo extends One[Foo] with Two[Foo] {
10-
concat(0) // OK
11-
12-
// TODO: This does not typecheck because the polymorphic overload is masked
13-
// (we merge the denotations for both overloads into one and always prefer
14-
// MethodType to PolyType, instead we should return a MultiDenotation). See #4819.
15-
concat[Int](0) // error (that should actually not be an error)
10+
concat(0) // error: ambiguous overload
11+
concat[Int](0) // OK
1612
}

tests/neg/i5445.scala

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
object Test {
2+
3+
trait A { def polymorphic[x]: Int }
4+
val a = new A { val polymorphic = Unit } // error: object creation impossible
5+
6+
}

tests/neg/i5578.scala

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
trait P[A]{
2+
def a[T]: A
3+
}
4+
class C extends P[Int]{ // error: class C needs to be abstract
5+
def a = 1
6+
}
7+
object O{
8+
def main(args: Array[String]) = {
9+
val p: P[Int] = new C
10+
println(p.a)
11+
}
12+
}

tests/pos/i4819.scala

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,12 @@ trait Two[Y <: Foo] {
77
}
88

99
class Foo extends One[Foo] with Two[Foo] {
10-
concat(0) // OK
10+
concat[Int](0) // OK
1111
// See also tests/neg/i4819.scala
1212
}
13+
14+
class Bar extends One[String] with Two[Foo] {
15+
val x: String = concat(0)
16+
val y = concat[Int](0)
17+
val z: Foo = concat(0)
18+
}

0 commit comments

Comments
 (0)