aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMartin Odersky <odersky@gmail.com>2014-11-18 15:18:19 +0100
committerMartin Odersky <odersky@gmail.com>2014-11-18 15:18:19 +0100
commitea06d6618f63339fec0af8ca6835a3f34a100d0f (patch)
tree049d0deba496172146edd358da5aa5c5fa4e2458
parentb60f085e543e71577e0132b938facd0b6d544e81 (diff)
downloaddotty-ea06d6618f63339fec0af8ca6835a3f34a100d0f.tar.gz
dotty-ea06d6618f63339fec0af8ca6835a3f34a100d0f.tar.bz2
dotty-ea06d6618f63339fec0af8ca6835a3f34a100d0f.zip
Check that overriding members refine the types of overridden ones.
Somehow this was lost in porting (or was this done somewhere else in scalac?).
-rw-r--r--src/dotty/tools/dotc/core/Denotations.scala14
-rw-r--r--src/dotty/tools/dotc/core/Types.scala16
-rw-r--r--src/dotty/tools/dotc/typer/RefChecks.scala19
-rw-r--r--test/dotc/tests.scala2
-rw-r--r--tests/neg/overrides.scala11
5 files changed, 40 insertions, 22 deletions
diff --git a/src/dotty/tools/dotc/core/Denotations.scala b/src/dotty/tools/dotc/core/Denotations.scala
index 5ed0ebfb2..236bdb7f4 100644
--- a/src/dotty/tools/dotc/core/Denotations.scala
+++ b/src/dotty/tools/dotc/core/Denotations.scala
@@ -240,20 +240,8 @@ object Denotations {
val sym1 = denot1.symbol
val sym2 = denot2.symbol
val sym2Accessible = sym2.isAccessibleFrom(pre)
- def resultType(tp: Type) = tp match {
- case tp @ MethodType(Nil, _) => tp.resultType
- case ExprType(rt) => rt
- case _ => NoType
- }
- def isAsGood(tp1: Type, tp2: Type) =
- tp1 <:< tp2 || {
- val rtp1 = resultType(tp1)
- val rtp2 = resultType(tp2)
- rtp1 <:< rtp2
- }
def prefer(info1: Type, sym1: Symbol, info2: Type, sym2: Symbol) =
- isAsGood(info1, info2) &&
- (sym1.isAsConcrete(sym2) || !(info2 <:< info1))
+ info1.overrides(info2) && (sym1.isAsConcrete(sym2) || !info2.overrides(info1))
if (sym2Accessible && prefer(info2, sym2, info1, sym1)) denot2
else {
val sym1Accessible = sym1.isAccessibleFrom(pre)
diff --git a/src/dotty/tools/dotc/core/Types.scala b/src/dotty/tools/dotc/core/Types.scala
index 5228c077e..a09c0cd71 100644
--- a/src/dotty/tools/dotc/core/Types.scala
+++ b/src/dotty/tools/dotc/core/Types.scala
@@ -533,6 +533,22 @@ object Types {
ctx.typeComparer.isSameType(this, that)
}
+ /** Is this type a legal type for a member that overrides another
+ * member of type `that`? This is the same as `<:<`, except that
+ * the types ()T and => T are identified, and T is seen as overriding
+ * either type.
+ */
+ final def overrides(that: Type)(implicit ctx: Context) = {
+ def result(tp: Type): Type = tp match {
+ case ExprType(_) | MethodType(Nil, _) => tp.resultType
+ case _ => tp
+ }
+ this <:< that || {
+ val rthat = result(that)
+ (rthat ne that) && result(this) <:< rthat
+ }
+ }
+
/** Is this type close enough to that type so that members
* with the two type would override each other?d
* This means:
diff --git a/src/dotty/tools/dotc/typer/RefChecks.scala b/src/dotty/tools/dotc/typer/RefChecks.scala
index 29ac33498..6995076c5 100644
--- a/src/dotty/tools/dotc/typer/RefChecks.scala
+++ b/src/dotty/tools/dotc/typer/RefChecks.scala
@@ -134,14 +134,13 @@ object RefChecks {
def infoString0(sym: Symbol, showLocation: Boolean) = {
val sym1 = sym.underlyingSymbol
def info = self.memberInfo(sym1)
- if (showLocation) sym1.showLocated
- else
- i"$sym1${
- if (sym1.isAliasType) i", which equals $info"
- else if (sym1.isAbstractType) i" with bounds $info"
- else if (sym1.is(Module)) ""
- else if (sym1.isTerm) i" of type $info"
- else ""}"
+ i"${if (showLocation) sym1.showLocated else sym1}${
+ if (sym1.isAliasType) i", which equals $info"
+ else if (sym1.isAbstractType) i" with bounds $info"
+ else if (sym1.is(Module)) ""
+ else if (sym1.isTerm) i" of type $info"
+ else ""
+ }"
}
/* Check that all conditions for overriding `other` by `member`
@@ -287,6 +286,10 @@ object RefChecks {
overrideError("cannot be used here - term macros cannot override abstract methods")
} else if (other.is(Macro) && !member.is(Macro)) { // (1.10)
overrideError("cannot be used here - only term macros can override term macros")
+ } else if (member.isTerm && !isDefaultGetter(member.name) && !(memberTp overrides otherTp)) {
+ // types don't need to have their bounds in an overriding relationship
+ // since we automatically form their intersection when selecting.
+ overrideError("has incompatible type" + err.whyNoMatchStr(memberTp, otherTp))
} else {
checkOverrideDeprecated()
}
diff --git a/test/dotc/tests.scala b/test/dotc/tests.scala
index fb2b747a6..8a8c162c6 100644
--- a/test/dotc/tests.scala
+++ b/test/dotc/tests.scala
@@ -73,7 +73,7 @@ class tests extends CompilerTest {
@Test def neg_autoTupling2 = compileFile(negDir, "autoTuplingTest", xerrors = 4)
@Test def neg_companions = compileFile(negDir, "companions", xerrors = 1)
@Test def neg_over = compileFile(negDir, "over", xerrors = 1)
- @Test def neg_overrides = compileFile(negDir, "overrides", xerrors = 4)
+ @Test def neg_overrides = compileFile(negDir, "overrides", xerrors = 5)
@Test def neg_projections = compileFile(negDir, "projections", xerrors = 1)
@Test def neg_i39 = compileFile(negDir, "i39", xerrors = 1)
@Test def neg_i50_volatile = compileFile(negDir, "i50-volatile", xerrors = 4)
diff --git a/tests/neg/overrides.scala b/tests/neg/overrides.scala
index 4befe1623..d502af8dd 100644
--- a/tests/neg/overrides.scala
+++ b/tests/neg/overrides.scala
@@ -57,3 +57,14 @@ class X {
class Y extends X {
def f: A[Int] = ???
}
+
+
+class A1
+class B1
+
+class X1 {
+ def f(): A1 = ???
+}
+class Y1 extends X1 {
+ override def f(): B1 = ???
+}