Skip to content

Commit 8bad055

Browse files
committed
Make the illegal super accessor message a case class
1 parent ce97789 commit 8bad055

File tree

4 files changed

+68
-58
lines changed

4 files changed

+68
-58
lines changed

compiler/src/dotty/tools/dotc/reporting/diagnostic/ErrorMessageID.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,8 @@ public enum ErrorMessageID {
144144
OverloadInRefinementID,
145145
NoMatchingOverloadID,
146146
StableIdentPatternID,
147-
StaticFieldsShouldPrecedeNonStaticID
147+
StaticFieldsShouldPrecedeNonStaticID,
148+
IllegalSuperAccessorID
148149
;
149150

150151
public int errorNumber() {

compiler/src/dotty/tools/dotc/reporting/diagnostic/messages.scala

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2265,4 +2265,60 @@ object messages {
22652265
hl"""Stable identifier required, but ${tree.show} found"""
22662266
override def explanation: String = ""
22672267
}
2268+
2269+
case class IllegalSuperAccessor(base: Symbol, memberName: Name,
2270+
acc: Symbol, accTp: Type,
2271+
other: Symbol, otherTp: Type)(implicit val ctx: Context) extends Message(IllegalSuperAccessorID) {
2272+
val kind: String = "Reference"
2273+
val msg: String = {
2274+
// The mixin containing a super-call that requires a super-accessor
2275+
val accMixin = acc.owner
2276+
// The class or trait that the super-accessor should resolve too in `base`
2277+
val otherMixin = other.owner
2278+
// The super-call in `accMixin`
2279+
val superCall = i"super.$memberName"
2280+
// The super-call that the super-accesors in `base` forwards to
2281+
val resolvedSuperCall = i"super[${otherMixin.name}].$memberName"
2282+
// The super-call that we would have called if `super` in traits behaved like it
2283+
// does in classes, i.e. followed the linearization of the trait itself.
2284+
val staticSuperCall = {
2285+
val staticSuper = accMixin.asClass.info.parents.reverse
2286+
.find(_.nonPrivateMember(memberName).matchingDenotation(accMixin.thisType, acc.info).exists)
2287+
val staticSuperName = staticSuper match {
2288+
case Some(parent) =>
2289+
parent.classSymbol.name.show
2290+
case None => // Might be reachable under separate compilation
2291+
"SomeParent"
2292+
}
2293+
i"super[$staticSuperName].$memberName"
2294+
}
2295+
hl"""$base cannot be defined due to a conflict between its parents when
2296+
|implementing a super-accessor for $memberName in $accMixin:
2297+
|
2298+
|1. One of its parent (${accMixin.name}) contains a call $superCall in its body,
2299+
| and when a super-call in a trait is written without an explicit parent
2300+
| listed in brackets, it is implemented by a generated super-accessor in
2301+
| the class that extends this trait based on the linearization order of
2302+
| the class.
2303+
|2. Because ${otherMixin.name} comes before ${accMixin.name} in the linearization
2304+
| order of ${base.name}, and because ${otherMixin.name} overrides $memberName,
2305+
| the super-accessor in ${base.name} is implemented as a call to
2306+
| $resolvedSuperCall.
2307+
|3. However,
2308+
| ${otherTp.widenExpr} (the type of $resolvedSuperCall in ${base.name})
2309+
| is not a subtype of
2310+
| ${accTp.widenExpr} (the type of $memberName in $accMixin).
2311+
| Hence, the super-accessor that needs to be generated in ${base.name}
2312+
| is illegal.
2313+
|
2314+
|Here are two possible ways to resolve this:
2315+
|
2316+
|1. Change the linearization order of ${base.name} such that
2317+
| ${accMixin.name} comes before ${otherMixin.name}.
2318+
|2. Alternatively, replace $superCall in the body of $accMixin by a
2319+
| super-call to a specific parent, e.g. $staticSuperCall
2320+
|""".stripMargin
2321+
}
2322+
val explanation: String = ""
2323+
}
22682324
}

compiler/src/dotty/tools/dotc/transform/ResolveSuper.scala

Lines changed: 9 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import DenotTransformers._
1212
import NameOps._
1313
import NameKinds._
1414
import ResolveSuper._
15+
import reporting.diagnostic.messages.IllegalSuperAccessor
1516

1617
/** This phase adds super accessors and method overrides where
1718
* linearization differs from Java's rule for default methods in interfaces.
@@ -100,65 +101,17 @@ object ResolveSuper {
100101
val SuperAccessorName(memberName) = acc.name.unexpandedName
101102
ctx.debuglog(i"starting rebindsuper from $base of ${acc.showLocated}: ${acc.info} in $bcs, name = $memberName")
102103
while (bcs.nonEmpty && sym == NoSymbol) {
103-
val cur = bcs.head
104-
val other = cur.info.nonPrivateDecl(memberName)
105-
if (ctx.settings.Ydebug.value)
106-
ctx.log(i"rebindsuper ${bcs.head} $other deferred = ${other.symbol.is(Deferred)}")
107-
val otherMember = other.matchingDenotation(base.thisType, base.thisType.memberInfo(acc))
108-
if (otherMember.exists) {
109-
sym = otherMember.symbol
104+
val other = bcs.head.info.nonPrivateDecl(memberName)
105+
.matchingDenotation(base.thisType, base.thisType.memberInfo(acc))
106+
ctx.debuglog(i"rebindsuper ${bcs.head} $other deferred = ${other.symbol.is(Deferred)}")
107+
if (other.exists) {
108+
sym = other.symbol
110109
// Having a matching denotation is not enough: it should also be a subtype
111110
// of the superaccessor's type, see i5433.scala for an example where this matters
112-
val otherTp = otherMember.asSeenFrom(base.typeRef).info
111+
val otherTp = other.asSeenFrom(base.typeRef).info
113112
val accTp = acc.asSeenFrom(base.typeRef).info
114-
if (!(otherTp <:< accTp)) {
115-
// The mixin containing a super-call that requires a super-accessor
116-
val mixin = acc.owner
117-
// The super-call in `mixin`
118-
val superCall = i"super.$memberName"
119-
// The super-call that we end up trying to call
120-
val resolvedSuperCall = i"super[${cur.name}].$memberName"
121-
// The super-call that we would have called if `super` in traits behaved like it
122-
// does in classes, i.e. followed the linearization of the trait itself.
123-
val staticSuperCall = {
124-
val staticSuper = mixin.asClass.info.parents.reverse
125-
.find(_.nonPrivateMember(memberName).matchingDenotation(mixin.thisType, acc.info).exists)
126-
val staticSuperName = staticSuper match {
127-
case Some(parent) =>
128-
parent.classSymbol.name.show
129-
case None => // Might be reachable under separate compilation
130-
"SomeParent"
131-
}
132-
i"super[$staticSuperName].$memberName"
133-
}
134-
ctx.error(
135-
hl"""$base cannot be defined due to a conflict between its parents when
136-
|implementing a super-accessor for $memberName in $mixin:
137-
|
138-
|1. One of its parent ($mixin) contains a call $superCall in its body,
139-
| and when a super-call in a trait is written without an explicit parent
140-
| listed in brackets, it is implemented by a generated super-accessor in
141-
| the class that extends this trait based on the linearization order of
142-
| the class.
143-
|2. Because ${cur.name} comes before ${mixin.name} in the linearization
144-
| order of ${base.name}, and because ${cur.name} overrides $memberName,
145-
| the super-accessor in ${base.name} is implemented as a call to
146-
| $resolvedSuperCall.
147-
|3. However,
148-
| ${otherTp.widenExpr} (the type of $resolvedSuperCall in ${base.name})
149-
| is not a subtype of
150-
| ${accTp.widenExpr} (the type of $memberName in $mixin).
151-
| Hence, the super-accessor that needs to be generated in ${base.name}
152-
| is illegal.
153-
|
154-
|Here are two possible ways to resolve this:
155-
|
156-
|1. Change the linearization order of ${base.name} such that
157-
| ${mixin.name} comes before ${cur.name}.
158-
|2. Alternatively, replace $superCall in the body of $mixin by a
159-
| super-call to a specific parent, e.g. $staticSuperCall
160-
|""".stripMargin, base.sourcePos)
161-
}
113+
if (!(otherTp <:< accTp))
114+
ctx.error(IllegalSuperAccessor(base, memberName, acc, accTp, other.symbol, otherTp), base.sourcePos)
162115
}
163116

164117
bcs = bcs.tail

tests/neg/i5433.check

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
class Fail cannot be defined due to a conflict between its parents when
33
implementing a super-accessor for foo in trait C:
44

5-
1. One of its parent (trait C) contains a call super.foo in its body,
5+
1. One of its parent (C) contains a call super.foo in its body,
66
and when a super-call in a trait is written without an explicit parent
77
listed in brackets, it is implemented by a generated super-accessor in
88
the class that extends this trait based on the linearization order of

0 commit comments

Comments
 (0)