Skip to content

Prevent adaptations of extension method applications #6706

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 4 commits into from
Jun 19, 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
51 changes: 28 additions & 23 deletions compiler/src/dotty/tools/dotc/core/TypeComparer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1726,31 +1726,36 @@ class TypeComparer(initctx: Context) extends ConstraintHandling[AbsentContext] w
* @note We do not admit singleton types in or-types as lubs.
*/
def lub(tp1: Type, tp2: Type, canConstrain: Boolean = false): Type = /*>|>*/ trace(s"lub(${tp1.show}, ${tp2.show}, canConstrain=$canConstrain)", subtyping, show = true) /*<|<*/ {
if (tp1 eq tp2) return tp1
if (!tp1.exists) return tp1
if (!tp2.exists) return tp2
if ((tp1 isRef AnyClass) || (tp1 isRef AnyKindClass) || (tp2 isRef NothingClass)) return tp1
if ((tp2 isRef AnyClass) || (tp2 isRef AnyKindClass) || (tp1 isRef NothingClass)) return tp2
val atoms1 = tp1.atoms
if (atoms1.nonEmpty && !widenInUnions) {
val atoms2 = tp2.atoms
if (atoms2.nonEmpty) {
if (atoms1.subsetOf(atoms2)) return tp2
if (atoms2.subsetOf(atoms1)) return tp1
if ((atoms1 & atoms2).isEmpty) return orType(tp1, tp2)
}
}
val t1 = mergeIfSuper(tp1, tp2, canConstrain)
if (t1.exists) return t1
if (tp1 eq tp2) tp1
else if (!tp1.exists) tp1
else if (!tp2.exists) tp2
else if ((tp1 isRef AnyClass) || (tp1 isRef AnyKindClass) || (tp2 isRef NothingClass)) tp1
else if ((tp2 isRef AnyClass) || (tp2 isRef AnyKindClass) || (tp1 isRef NothingClass)) tp2
else {
def mergedLub: Type = {
val atoms1 = tp1.atoms
if (atoms1.nonEmpty && !widenInUnions) {
val atoms2 = tp2.atoms
if (atoms2.nonEmpty) {
if (atoms1.subsetOf(atoms2)) return tp2
if (atoms2.subsetOf(atoms1)) return tp1
if ((atoms1 & atoms2).isEmpty) return orType(tp1, tp2)
}
}
val t1 = mergeIfSuper(tp1, tp2, canConstrain)
if (t1.exists) return t1

val t2 = mergeIfSuper(tp2, tp1, canConstrain)
if (t2.exists) return t2
val t2 = mergeIfSuper(tp2, tp1, canConstrain)
if (t2.exists) return t2

def widen(tp: Type) = if (widenInUnions) tp.widen else tp.widenIfUnstable
val tp1w = widen(tp1)
val tp2w = widen(tp2)
if ((tp1 ne tp1w) || (tp2 ne tp2w)) lub(tp1w, tp2w)
else orType(tp1w, tp2w) // no need to check subtypes again
def widen(tp: Type) = if (widenInUnions) tp.widen else tp.widenIfUnstable
val tp1w = widen(tp1)
val tp2w = widen(tp2)
if ((tp1 ne tp1w) || (tp2 ne tp2w)) lub(tp1w, tp2w)
else orType(tp1w, tp2w) // no need to check subtypes again
}
mergedLub
}
}

/** The least upper bound of a list of types */
Expand Down
7 changes: 5 additions & 2 deletions compiler/src/dotty/tools/dotc/typer/Applications.scala
Original file line number Diff line number Diff line change
Expand Up @@ -216,8 +216,11 @@ object Applications {
/** A wrapper indicating that its argument is an application of an extension method.
*/
class ExtMethodApply(app: Tree)(implicit @constructorOnly src: SourceFile)
extends IntegratedTypeArgs(app)

extends IntegratedTypeArgs(app) {
overwriteType(WildcardType)
// ExtMethodApply always has wildcard type in order not to prompt any further adaptations
// such as eta expansion before the method is fully applied.
}
}

trait Applications extends Compatibility { self: Typer with Dynamic =>
Expand Down
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/typer/Implicits.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1348,7 +1348,7 @@ trait Implicits { self: Typer =>
}
else {
val returned =
if (cand.isExtension) new Applications.ExtMethodApply(adapted).withType(adapted.tpe)
if (cand.isExtension) Applications.ExtMethodApply(adapted)
else adapted
SearchSuccess(returned, ref, cand.level)(ctx.typerState, ctx.gadt)
}
Expand Down
4 changes: 1 addition & 3 deletions compiler/src/dotty/tools/dotc/typer/Typer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3021,9 +3021,7 @@ class Typer extends Namer
val app = tryExtension(nestedCtx)
if (!app.isEmpty && !nestedCtx.reporter.hasErrors) {
nestedCtx.typerState.commit()
return new ExtMethodApply(app).withType(WildcardType)
// Use wildcard type in order not to prompt any further adaptations such as eta expansion
// before the method is fully applied.
return ExtMethodApply(app)
}
case _ =>
}
Expand Down
10 changes: 10 additions & 0 deletions tests/pos/combine.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
trait Semigroup[A] {
def (x: A) combine (y: A): A
}
delegate for Semigroup[Int] = ???
delegate [A, B] for Semigroup[(A, B)] given Semigroup[A], Semigroup[B] = ???
object Test extends App {
((1, 1)) combine ((2, 2)) // doesn't compile
((1, 1): (Int, Int)) combine (2, 2) // compiles
//the error that compiler spat out was "value combine is not a member of ((Int, Int)) => (Int, Int)". what's
}