Skip to content

Commit 2fbefb0

Browse files
authored
Merge pull request #9063 from dotty-staging/fix-#9050
Fix #9050: Allow multidenotations with same signature
2 parents cc8d6c3 + 19e658c commit 2fbefb0

File tree

20 files changed

+267
-395
lines changed

20 files changed

+267
-395
lines changed

community-build/test/scala/dotty/communitybuild/CommunityBuildTest.scala

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -384,7 +384,8 @@ class CommunityBuildTest:
384384
@Test def scodecBits = projects.scodecBits.run()
385385
@Test def scodec = projects.scodec.run()
386386
@Test def scalaParserCombinators = projects.scalaParserCombinators.run()
387-
@Test def dottyCpsAsync = projects.dottyCpsAsync.run()
387+
// blocked on #9074
388+
//@Test def dottyCpsAsync = projects.dottyCpsAsync.run()
388389
@Test def scalaz = projects.scalaz.run()
389390
@Test def endpoints = projects.endpoints.run()
390391
end CommunityBuildTest

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

Lines changed: 130 additions & 297 deletions
Large diffs are not rendered by default.

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -922,8 +922,8 @@ object SymDenotations {
922922
else true
923923
}
924924

925-
if (pre eq NoPrefix) true
926-
else if (isAbsent()) false
925+
if pre eq NoPrefix then true
926+
else if isAbsent() then false
927927
else {
928928
val boundary = accessBoundary(owner)
929929

@@ -2133,7 +2133,7 @@ object SymDenotations {
21332133
var names = Set[Name]()
21342134
def maybeAdd(name: Name) = if (keepOnly(thisType, name)) names += name
21352135
try {
2136-
for (p <- classParents)
2136+
for (p <- classParents if p.classSymbol.isClass)
21372137
for (name <- p.classSymbol.asClass.memberNames(keepOnly))
21382138
maybeAdd(name)
21392139
val ownSyms =

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

Lines changed: 4 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -2047,16 +2047,6 @@ class TypeComparer(initctx: Context) extends ConstraintHandling[AbsentContext] w
20472047
* instantiated TypeVars are dereferenced and annotations are stripped.
20482048
* Finally, refined types with the same refined name are
20492049
* opportunistically merged.
2050-
*
2051-
* Sometimes, the conjunction of two types cannot be formed because
2052-
* the types are in conflict of each other. In particular:
2053-
*
2054-
* 1. Two different class types are conflicting.
2055-
* 2. A class type conflicts with a type bounds that does not include the class reference.
2056-
* 3. Two method or poly types with different (type) parameters but the same
2057-
* signature are conflicting
2058-
*
2059-
* In these cases, a MergeError is thrown.
20602050
*/
20612051
final def andType(tp1: Type, tp2: Type, isErased: Boolean = ctx.erasedTypes): Type =
20622052
andTypeGen(tp1, tp2, AndType(_, _), isErased = isErased)
@@ -2070,10 +2060,6 @@ class TypeComparer(initctx: Context) extends ConstraintHandling[AbsentContext] w
20702060
* ExprType, LambdaType). Also, when forming an `|`,
20712061
* instantiated TypeVars are dereferenced and annotations are stripped.
20722062
*
2073-
* Sometimes, the disjunction of two types cannot be formed because
2074-
* the types are in conflict of each other. (@see `andType` for an enumeration
2075-
* of these cases). In cases of conflict a `MergeError` is raised.
2076-
*
20772063
* @param isErased Apply erasure semantics. If erased is true, instead of creating
20782064
* an OrType, the lub will be computed using TypeCreator#erasedLub.
20792065
*/
@@ -2139,13 +2125,11 @@ class TypeComparer(initctx: Context) extends ConstraintHandling[AbsentContext] w
21392125
// gives =:= types), but it keeps the type smaller.
21402126
tp2 match {
21412127
case tp2: RefinedType if tp1.refinedName == tp2.refinedName =>
2142-
try {
2143-
val jointInfo = Denotations.infoMeet(tp1.refinedInfo, tp2.refinedInfo, NoSymbol, NoSymbol, safeIntersection = false)
2128+
val jointInfo = Denotations.infoMeet(tp1.refinedInfo, tp2.refinedInfo, safeIntersection = false)
2129+
if jointInfo.exists then
21442130
tp1.derivedRefinedType(tp1.parent & tp2.parent, tp1.refinedName, jointInfo)
2145-
}
2146-
catch {
2147-
case ex: MergeError => NoType
2148-
}
2131+
else
2132+
NoType
21492133
case _ =>
21502134
NoType
21512135
}

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

Lines changed: 0 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -163,32 +163,3 @@ object CyclicReference {
163163
}
164164
}
165165

166-
class MergeError(val sym1: Symbol, val sym2: Symbol, val tp1: Type, val tp2: Type, prefix: Type) extends TypeError {
167-
168-
private def showSymbol(sym: Symbol)(implicit ctx: Context): String =
169-
if (sym.exists) sym.showLocated else "[unknown]"
170-
171-
private def showType(tp: Type)(implicit ctx: Context) = tp match {
172-
case ClassInfo(_, cls, _, _, _) => cls.showLocated
173-
case _ => tp.show
174-
}
175-
176-
protected def addendum(implicit ctx: Context): String =
177-
if (prefix `eq` NoPrefix) ""
178-
else {
179-
val owner = prefix match {
180-
case prefix: ThisType => prefix.cls.show
181-
case prefix: TermRef => prefix.symbol.show
182-
case _ => i"type $prefix"
183-
}
184-
s"\nas members of $owner"
185-
}
186-
187-
override def produceMessage(implicit ctx: Context): Message = {
188-
if (ctx.debug) printStackTrace()
189-
i"""cannot merge
190-
| ${showSymbol(sym1)} of type ${showType(tp1)} and
191-
| ${showSymbol(sym2)} of type ${showType(tp2)}$addendum
192-
"""
193-
}
194-
}

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -675,7 +675,7 @@ object Types {
675675
pdenot.asSingleDenotation.derivedSingleDenotation(pdenot.symbol, jointInfo)
676676
}
677677
else
678-
val joint = pdenot & (
678+
val joint = pdenot.meet(
679679
new JointRefDenotation(NoSymbol, rinfo, Period.allInRun(ctx.runId), pre),
680680
pre,
681681
safeIntersection = ctx.base.pendingMemberSearches.contains(name))
@@ -726,7 +726,7 @@ object Types {
726726
}
727727

728728
def goAnd(l: Type, r: Type) =
729-
go(l) & (go(r), pre, safeIntersection = ctx.base.pendingMemberSearches.contains(name))
729+
go(l).meet(go(r), pre, safeIntersection = ctx.base.pendingMemberSearches.contains(name))
730730

731731
def goOr(tp: OrType) = tp match {
732732
case OrUncheckedNull(tp1) =>

compiler/src/dotty/tools/dotc/core/tasty/TastyPrinter.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ class TastyPrinter(bytes: Array[Byte])(implicit ctx: Context) {
8282
printName(); printName()
8383
case VALDEF | DEFDEF | TYPEDEF | TYPEPARAM | PARAM | NAMEDARG | BIND =>
8484
printName(); printTrees()
85-
case REFINEDtype | TERMREFin | TYPEREFin =>
85+
case REFINEDtype | TERMREFin | TYPEREFin | SELECTin =>
8686
printName(); printTree(); printTrees()
8787
case RETURN | HOLE =>
8888
printNat(); printTrees()

compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala

Lines changed: 21 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import dotty.tools.tasty.TastyBuffer._
99
import ast.Trees._
1010
import ast.{untpd, tpd}
1111
import Contexts._, Symbols._, Types._, Names._, Constants._, Decorators._, Annotations._, Flags._
12+
import Denotations.MultiDenotation
1213
import typer.Inliner
1314
import NameKinds._
1415
import StdNames.nme
@@ -173,22 +174,20 @@ class TreePickler(pickler: TastyPickler) {
173174
case tpe: NamedType =>
174175
val sym = tpe.symbol
175176
def pickleExternalRef(sym: Symbol) = {
176-
def pickleCore() = {
177-
pickleNameAndSig(sym.name, tpe.signature)
178-
pickleType(tpe.prefix)
179-
}
180177
val isShadowedRef =
181178
sym.isClass && tpe.prefix.member(sym.name).symbol != sym
182179
if (sym.is(Flags.Private) || isShadowedRef) {
183180
writeByte(if (tpe.isType) TYPEREFin else TERMREFin)
184181
withLength {
185-
pickleCore()
182+
pickleNameAndSig(sym.name, tpe.symbol.signature)
183+
pickleType(tpe.prefix)
186184
pickleType(sym.owner.typeRef)
187185
}
188186
}
189187
else {
190188
writeByte(if (tpe.isType) TYPEREF else TERMREF)
191-
pickleCore()
189+
pickleNameAndSig(sym.name, tpe.signature)
190+
pickleType(tpe.prefix)
192191
}
193192
}
194193
if (sym.is(Flags.Package)) {
@@ -381,10 +380,23 @@ class TreePickler(pickler: TastyPickler) {
381380
pickleType(tp)
382381
}
383382
case _ =>
384-
writeByte(if (name.isTypeName) SELECTtpt else SELECT)
385383
val sig = tree.tpe.signature
386-
pickleNameAndSig(name, sig)
387-
pickleTree(qual)
384+
val isAmbiguous =
385+
sig != Signature.NotAMethod
386+
&& qual.tpe.nonPrivateMember(name).match
387+
case d: MultiDenotation => d.atSignature(sig).isInstanceOf[MultiDenotation]
388+
case _ => false
389+
if isAmbiguous then
390+
writeByte(SELECTin)
391+
withLength {
392+
pickleNameAndSig(name, tree.symbol.signature)
393+
pickleTree(qual)
394+
pickleType(tree.symbol.owner.typeRef)
395+
}
396+
else
397+
writeByte(if (name.isTypeName) SELECTtpt else SELECT)
398+
pickleNameAndSig(name, sig)
399+
pickleTree(qual)
388400
}
389401
case Apply(fun, args) =>
390402
if (fun.symbol eq defn.throwMethod) {

compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import Symbols._
99
import Types._
1010
import Scopes._
1111
import SymDenotations._
12+
import Denotations._
1213
import Names._
1314
import NameOps._
1415
import StdNames._
@@ -330,12 +331,12 @@ class TreeUnpickler(reader: TastyReader,
330331
case TERMREFin =>
331332
var sname = readName()
332333
val prefix = readType()
333-
val space = readType()
334+
val owner = readType()
334335
sname match {
335336
case SignedName(name, sig) =>
336-
TermRef(prefix, name, space.decl(name).asSeenFrom(prefix).atSignature(sig))
337+
TermRef(prefix, name, owner.decl(name).atSignature(sig).asSeenFrom(prefix))
337338
case name =>
338-
TermRef(prefix, name, space.decl(name).asSeenFrom(prefix))
339+
TermRef(prefix, name, owner.decl(name).asSeenFrom(prefix))
339340
}
340341
case TYPEREFin =>
341342
val name = readName().toTypeName
@@ -1040,10 +1041,8 @@ class TreeUnpickler(reader: TastyReader,
10401041
}
10411042
}
10421043

1043-
def completeSelect(name: Name, sig: Signature): Select = {
1044-
val qual = readTerm()(ctx)
1044+
def makeSelect(qual: Tree, name: Name, denot: Denotation): Select =
10451045
var qualType = qual.tpe.widenIfUnstable
1046-
val denot = accessibleDenot(qualType, name, sig)
10471046
val owner = denot.symbol.maybeOwner
10481047
if (owner.isPackageObject && qualType.termSymbol.is(Package))
10491048
qualType = qualType.select(owner.sourceModule)
@@ -1052,7 +1051,11 @@ class TreeUnpickler(reader: TastyReader,
10521051
case name: TermName => TermRef(qualType, name, denot)
10531052
}
10541053
ConstFold(untpd.Select(qual, name).withType(tpe))
1055-
}
1054+
1055+
def completeSelect(name: Name, sig: Signature): Select =
1056+
val qual = readTerm()(ctx)
1057+
val denot = accessibleDenot(qual.tpe.widenIfUnstable, name, sig)
1058+
makeSelect(qual, name, denot)
10561059

10571060
def readQualId(): (untpd.Ident, TypeRef) =
10581061
val qual = readTerm().asInstanceOf[untpd.Ident]
@@ -1165,6 +1168,18 @@ class TreeUnpickler(reader: TastyReader,
11651168
case SELECTouter =>
11661169
val levels = readNat()
11671170
readTerm().outerSelect(levels, SkolemType(readType()))
1171+
case SELECTin =>
1172+
var sname = readName()
1173+
val qual = readTerm()
1174+
val owner = readType()
1175+
def select(name: Name, denot: Denotation) =
1176+
val prefix = ctx.typeAssigner.maybeSkolemizePrefix(qual.tpe.widenIfUnstable, name)
1177+
makeSelect(qual, name, denot.asSeenFrom(prefix))
1178+
sname match
1179+
case SignedName(name, sig) =>
1180+
select(name, owner.decl(name).atSignature(sig))
1181+
case name =>
1182+
select(name, owner.decl(name))
11681183
case REPEATED =>
11691184
val elemtpt = readTpt()
11701185
SeqLiteral(until(end)(readTerm()), elemtpt)

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

Lines changed: 4 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -425,22 +425,10 @@ object RefChecks {
425425
}*/
426426
}
427427

428-
try {
429-
val opc = new OverridingPairs.Cursor(clazz)
430-
while (opc.hasNext) {
431-
checkOverride(opc.overriding, opc.overridden)
432-
opc.next()
433-
}
434-
}
435-
catch {
436-
case ex: MergeError =>
437-
val addendum = ex.tp1 match {
438-
case tp1: ClassInfo =>
439-
"\n(Note that having same-named member classes in types of a mixin composition is no longer allowed)"
440-
case _ => ""
441-
}
442-
ctx.error(ex.getMessage + addendum, clazz.sourcePos)
443-
}
428+
val opc = new OverridingPairs.Cursor(clazz)
429+
while opc.hasNext do
430+
checkOverride(opc.overriding, opc.overridden)
431+
opc.next()
444432
printMixinOverrideErrors()
445433

446434
// Verifying a concrete class has nothing unimplemented.

tasty/src/dotty/tools/tasty/TastyFormat.scala

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ Standard-Section: "ASTs" TopLevelStat*
7575
Term = Path -- Paths represent both types and terms
7676
IDENT NameRef Type -- Used when term ident’s type is not a TermRef
7777
SELECT possiblySigned_NameRef qual_Term -- qual.name
78+
SELECTin Length possiblySigned_NameRef qual_Term owner_Type -- qual.name, referring to a symbol declared in owner that has the given signature (see note below)
7879
QUALTHIS typeIdent_Tree -- id.this, different from THIS in that it contains a qualifier ident with position.
7980
NEW clsType_Term -- new cls
8081
THROW throwableExpr_Term -- throw throwableExpr
@@ -122,7 +123,7 @@ Standard-Section: "ASTs" TopLevelStat*
122123
TERMREFsymbol sym_ASTRef qual_Type -- A reference `qual.sym` to a local member with prefix `qual`
123124
TERMREFpkg fullyQualified_NameRef -- A reference to a package member with given fully qualified name
124125
TERMREF possiblySigned_NameRef qual_Type -- A reference `qual.name` to a non-local member
125-
TERMREFin Length possiblySigned_NameRef qual_Type namespace_Type -- A reference `qual.name` to a non-local member that's private in `namespace`
126+
TERMREFin Length possiblySigned_NameRef qual_Type owner_Type -- A reference `qual.name` referring to a non-local symbol declared in owner that has the given signature (see note below)
126127
THIS clsRef_Type -- cls.this
127128
RECthis recType_ASTRef -- The `this` in a recursive refined type `recType`.
128129
SHAREDtype path_ASTRef -- link to previously serialized path
@@ -212,6 +213,10 @@ Standard-Section: "ASTs" TopLevelStat*
212213
213214
Annotation = ANNOTATION Length tycon_Type fullAnnotation_Term -- An annotation, given (class) type of constructor, and full application tree
214215
216+
Note: The signature of a SELECTin or TERMREFin node is the signature of the selected symbol,
217+
not the signature of the reference. The latter undergoes an asSeenFrom but the former
218+
does not.
219+
215220
Note: Tree tags are grouped into 5 categories that determine what follows, and thus allow to compute the size of the tagged tree in a generic way.
216221
217222
Category 1 (tags 1-49) : tag
@@ -248,7 +253,7 @@ Standard Section: "Comments" Comment*
248253
object TastyFormat {
249254

250255
final val header: Array[Int] = Array(0x5C, 0xA1, 0xAB, 0x1F)
251-
val MajorVersion: Int = 22
256+
val MajorVersion: Int = 23
252257
val MinorVersion: Int = 0
253258

254259
/** Tags used to serialize names, should update [[nameTagToString]] if a new constant is added */
@@ -452,6 +457,7 @@ object TastyFormat {
452457
final val ANNOTATION = 173
453458
final val TERMREFin = 174
454459
final val TYPEREFin = 175
460+
final val SELECTin = 176
455461

456462
final val METHODtype = 180
457463

@@ -646,6 +652,7 @@ object TastyFormat {
646652
case SUPERtype => "SUPERtype"
647653
case TERMREFin => "TERMREFin"
648654
case TYPEREFin => "TYPEREFin"
655+
case SELECTin => "SELECTin"
649656

650657
case REFINEDtype => "REFINEDtype"
651658
case REFINEDtpt => "REFINEDtpt"
@@ -675,7 +682,7 @@ object TastyFormat {
675682
*/
676683
def numRefs(tag: Int): Int = tag match {
677684
case VALDEF | DEFDEF | TYPEDEF | TYPEPARAM | PARAM | NAMEDARG | RETURN | BIND |
678-
SELFDEF | REFINEDtype | TERMREFin | TYPEREFin | HOLE => 1
685+
SELFDEF | REFINEDtype | TERMREFin | TYPEREFin | SELECTin | HOLE => 1
679686
case RENAMED | PARAMtype => 2
680687
case POLYtype | TYPELAMBDAtype | METHODtype => -1
681688
case _ => 0

tests/neg-custom-args/allow-double-bindings/i1240.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ class C[T] {
88

99
object C {
1010
def main(args: Array[String]) =
11-
new C[D]().foo(new D()) // error: ambiguous
11+
new C[D]().foo(new D())
1212
}
1313

1414
class C1[T] {

tests/neg/i1240b.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,4 +9,4 @@ abstract class A[X] extends T[X] {
99
trait U[X] extends T[X] {
1010
abstract override def foo(x: X): X = super.foo(x)
1111
}
12-
object Test extends A[String] with U[String] // error: accidental override // error: merge error
12+
object Test extends A[String] with U[String] // error: accidental override

tests/neg/i4470a.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
object RepeatedEnum {
22

33
enum Maybe { // error
4-
case Foo // error
4+
case Foo
55
}
66

77
enum Maybe { // error

tests/neg/i4470b.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
object RepeatedExtendEnum {
22

3-
enum Maybe[T] derives Eql { // error // error
3+
enum Maybe[T] derives Eql { // error
44
case Foo extends Maybe[Int]
55
}
66

tests/neg/i4470c.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
object DuplicatedEnum {
22
enum Maybe[+T] { // error
3-
case Some(x: T) // error
3+
case Some(x: T)
44
}
55

66
enum Maybe[+T] { // error

0 commit comments

Comments
 (0)