From 2662e6fc9bb5471d0b1c3460fd5e6a937f33cebf Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Tue, 18 Jun 2019 16:17:50 +0200 Subject: [PATCH 1/4] Prevent adaptations of extension method applications Prevent adaptations of extension method applications coming from implicit search. We already did the same for extension method applications coming from imports. --- compiler/src/dotty/tools/dotc/typer/Implicits.scala | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/compiler/src/dotty/tools/dotc/typer/Implicits.scala b/compiler/src/dotty/tools/dotc/typer/Implicits.scala index 99fb98fe6b61..daec20ff3a3b 100644 --- a/compiler/src/dotty/tools/dotc/typer/Implicits.scala +++ b/compiler/src/dotty/tools/dotc/typer/Implicits.scala @@ -1348,7 +1348,10 @@ trait Implicits { self: Typer => } else { val returned = - if (cand.isExtension) new Applications.ExtMethodApply(adapted).withType(adapted.tpe) + if (cand.isExtension) + new Applications.ExtMethodApply(adapted).withType(WildcardType) + // Use wildcard type in order not to prompt any further adaptations such as eta expansion + // before the method is fully applied. else adapted SearchSuccess(returned, ref, cand.level)(ctx.typerState, ctx.gadt) } From f28a83c5ae2511858d6818ef3cb4471efb909e26 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Tue, 18 Jun 2019 16:19:03 +0200 Subject: [PATCH 2/4] Dont use non-local returns in lubs These returns were non-local only when Config.tracingEnabled was set to true. But then they prevented recompilation under -Xfatal-errors. --- .../dotty/tools/dotc/core/TypeComparer.scala | 51 ++++++++++--------- 1 file changed, 28 insertions(+), 23 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/TypeComparer.scala b/compiler/src/dotty/tools/dotc/core/TypeComparer.scala index 0a76ff9f240b..7668498845c2 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeComparer.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeComparer.scala @@ -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 */ From 73c279036f1637bde9167aefdbcf5edba82e0b32 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Tue, 18 Jun 2019 16:27:58 +0200 Subject: [PATCH 3/4] Always use WildcardType for ExtMethodApply --- compiler/src/dotty/tools/dotc/typer/Applications.scala | 7 +++++-- compiler/src/dotty/tools/dotc/typer/Implicits.scala | 5 +---- compiler/src/dotty/tools/dotc/typer/Typer.scala | 4 +--- 3 files changed, 7 insertions(+), 9 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/typer/Applications.scala b/compiler/src/dotty/tools/dotc/typer/Applications.scala index b4398b171427..2d4d611843af 100644 --- a/compiler/src/dotty/tools/dotc/typer/Applications.scala +++ b/compiler/src/dotty/tools/dotc/typer/Applications.scala @@ -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 => diff --git a/compiler/src/dotty/tools/dotc/typer/Implicits.scala b/compiler/src/dotty/tools/dotc/typer/Implicits.scala index daec20ff3a3b..310c93befda9 100644 --- a/compiler/src/dotty/tools/dotc/typer/Implicits.scala +++ b/compiler/src/dotty/tools/dotc/typer/Implicits.scala @@ -1348,10 +1348,7 @@ trait Implicits { self: Typer => } else { val returned = - if (cand.isExtension) - new Applications.ExtMethodApply(adapted).withType(WildcardType) - // Use wildcard type in order not to prompt any further adaptations such as eta expansion - // before the method is fully applied. + if (cand.isExtension) Applications.ExtMethodApply(adapted) else adapted SearchSuccess(returned, ref, cand.level)(ctx.typerState, ctx.gadt) } diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index 29294dd68826..60c074f82955 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -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 _ => } From 5d8c1ea4fe1a9e2ebe32cffba9bf2a3707a6fee8 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Wed, 19 Jun 2019 11:48:01 +0200 Subject: [PATCH 4/4] Add test --- tests/pos/combine.scala | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 tests/pos/combine.scala diff --git a/tests/pos/combine.scala b/tests/pos/combine.scala new file mode 100644 index 000000000000..6c64da676f0f --- /dev/null +++ b/tests/pos/combine.scala @@ -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 +} \ No newline at end of file