Skip to content

Commit a1b6bb6

Browse files
authored
Merge pull request #6706 from dotty-staging/fix-extmethod
Prevent adaptations of extension method applications
2 parents 53a3fe8 + 5d8c1ea commit a1b6bb6

File tree

5 files changed

+45
-29
lines changed

5 files changed

+45
-29
lines changed

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

Lines changed: 28 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1726,31 +1726,36 @@ class TypeComparer(initctx: Context) extends ConstraintHandling[AbsentContext] w
17261726
* @note We do not admit singleton types in or-types as lubs.
17271727
*/
17281728
def lub(tp1: Type, tp2: Type, canConstrain: Boolean = false): Type = /*>|>*/ trace(s"lub(${tp1.show}, ${tp2.show}, canConstrain=$canConstrain)", subtyping, show = true) /*<|<*/ {
1729-
if (tp1 eq tp2) return tp1
1730-
if (!tp1.exists) return tp1
1731-
if (!tp2.exists) return tp2
1732-
if ((tp1 isRef AnyClass) || (tp1 isRef AnyKindClass) || (tp2 isRef NothingClass)) return tp1
1733-
if ((tp2 isRef AnyClass) || (tp2 isRef AnyKindClass) || (tp1 isRef NothingClass)) return tp2
1734-
val atoms1 = tp1.atoms
1735-
if (atoms1.nonEmpty && !widenInUnions) {
1736-
val atoms2 = tp2.atoms
1737-
if (atoms2.nonEmpty) {
1738-
if (atoms1.subsetOf(atoms2)) return tp2
1739-
if (atoms2.subsetOf(atoms1)) return tp1
1740-
if ((atoms1 & atoms2).isEmpty) return orType(tp1, tp2)
1741-
}
1742-
}
1743-
val t1 = mergeIfSuper(tp1, tp2, canConstrain)
1744-
if (t1.exists) return t1
1729+
if (tp1 eq tp2) tp1
1730+
else if (!tp1.exists) tp1
1731+
else if (!tp2.exists) tp2
1732+
else if ((tp1 isRef AnyClass) || (tp1 isRef AnyKindClass) || (tp2 isRef NothingClass)) tp1
1733+
else if ((tp2 isRef AnyClass) || (tp2 isRef AnyKindClass) || (tp1 isRef NothingClass)) tp2
1734+
else {
1735+
def mergedLub: Type = {
1736+
val atoms1 = tp1.atoms
1737+
if (atoms1.nonEmpty && !widenInUnions) {
1738+
val atoms2 = tp2.atoms
1739+
if (atoms2.nonEmpty) {
1740+
if (atoms1.subsetOf(atoms2)) return tp2
1741+
if (atoms2.subsetOf(atoms1)) return tp1
1742+
if ((atoms1 & atoms2).isEmpty) return orType(tp1, tp2)
1743+
}
1744+
}
1745+
val t1 = mergeIfSuper(tp1, tp2, canConstrain)
1746+
if (t1.exists) return t1
17451747

1746-
val t2 = mergeIfSuper(tp2, tp1, canConstrain)
1747-
if (t2.exists) return t2
1748+
val t2 = mergeIfSuper(tp2, tp1, canConstrain)
1749+
if (t2.exists) return t2
17481750

1749-
def widen(tp: Type) = if (widenInUnions) tp.widen else tp.widenIfUnstable
1750-
val tp1w = widen(tp1)
1751-
val tp2w = widen(tp2)
1752-
if ((tp1 ne tp1w) || (tp2 ne tp2w)) lub(tp1w, tp2w)
1753-
else orType(tp1w, tp2w) // no need to check subtypes again
1751+
def widen(tp: Type) = if (widenInUnions) tp.widen else tp.widenIfUnstable
1752+
val tp1w = widen(tp1)
1753+
val tp2w = widen(tp2)
1754+
if ((tp1 ne tp1w) || (tp2 ne tp2w)) lub(tp1w, tp2w)
1755+
else orType(tp1w, tp2w) // no need to check subtypes again
1756+
}
1757+
mergedLub
1758+
}
17541759
}
17551760

17561761
/** The least upper bound of a list of types */

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

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -216,8 +216,11 @@ object Applications {
216216
/** A wrapper indicating that its argument is an application of an extension method.
217217
*/
218218
class ExtMethodApply(app: Tree)(implicit @constructorOnly src: SourceFile)
219-
extends IntegratedTypeArgs(app)
220-
219+
extends IntegratedTypeArgs(app) {
220+
overwriteType(WildcardType)
221+
// ExtMethodApply always has wildcard type in order not to prompt any further adaptations
222+
// such as eta expansion before the method is fully applied.
223+
}
221224
}
222225

223226
trait Applications extends Compatibility { self: Typer with Dynamic =>

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1348,7 +1348,7 @@ trait Implicits { self: Typer =>
13481348
}
13491349
else {
13501350
val returned =
1351-
if (cand.isExtension) new Applications.ExtMethodApply(adapted).withType(adapted.tpe)
1351+
if (cand.isExtension) Applications.ExtMethodApply(adapted)
13521352
else adapted
13531353
SearchSuccess(returned, ref, cand.level)(ctx.typerState, ctx.gadt)
13541354
}

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

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3021,9 +3021,7 @@ class Typer extends Namer
30213021
val app = tryExtension(nestedCtx)
30223022
if (!app.isEmpty && !nestedCtx.reporter.hasErrors) {
30233023
nestedCtx.typerState.commit()
3024-
return new ExtMethodApply(app).withType(WildcardType)
3025-
// Use wildcard type in order not to prompt any further adaptations such as eta expansion
3026-
// before the method is fully applied.
3024+
return ExtMethodApply(app)
30273025
}
30283026
case _ =>
30293027
}

tests/pos/combine.scala

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
trait Semigroup[A] {
2+
def (x: A) combine (y: A): A
3+
}
4+
delegate for Semigroup[Int] = ???
5+
delegate [A, B] for Semigroup[(A, B)] given Semigroup[A], Semigroup[B] = ???
6+
object Test extends App {
7+
((1, 1)) combine ((2, 2)) // doesn't compile
8+
((1, 1): (Int, Int)) combine (2, 2) // compiles
9+
//the error that compiler spat out was "value combine is not a member of ((Int, Int)) => (Int, Int)". what's
10+
}

0 commit comments

Comments
 (0)