Skip to content

Commit 81eb004

Browse files
committed
Refactor from distribute{And,Or} to {meet,join}Info
The refactored logic only applies to infos of denotations, not general types. The reactoring avoids special cases down the road then PolyTypes and MethodTypes can be used for terms as well as type(bounds).
1 parent 75e5120 commit 81eb004

File tree

2 files changed

+195
-175
lines changed

2 files changed

+195
-175
lines changed

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

Lines changed: 193 additions & 90 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import SymDenotations.{ SymDenotation, ClassDenotation, NoDenotation, NotDefined
66
import Contexts.{Context, ContextBase}
77
import Names.{Name, PreName}
88
import Names.TypeName
9+
import StdNames._
910
import Symbols.NoSymbol
1011
import Symbols._
1112
import Types._
@@ -247,6 +248,25 @@ object Denotations {
247248
else asSingleDenotation
248249
}
249250

251+
/** Handle merge conflict by throwing a `MergeError` exception */
252+
private def mergeConflict(tp1: Type, tp2: Type)(implicit ctx: Context): Type = {
253+
def showType(tp: Type) = tp match {
254+
case ClassInfo(_, cls, _, _, _) => cls.showLocated
255+
case bounds: TypeBounds => i"type bounds $bounds"
256+
case _ => tp.show
257+
}
258+
if (true) throw new MergeError(s"cannot merge ${showType(tp1)} with ${showType(tp2)}", tp1, tp2)
259+
else throw new Error(s"cannot merge ${showType(tp1)} with ${showType(tp2)}") // flip condition for debugging
260+
}
261+
262+
/** Merge two lists of names. If names in corresponding positions match, keep them,
263+
* otherwise generate new synthetic names.
264+
*/
265+
def mergeNames[N <: Name](names1: List[N], names2: List[N], syntheticName: Int => N): List[N] = {
266+
for ((name1, name2, idx) <- (names1, names2, 0 until names1.length).zipped)
267+
yield if (name1 == name2) name1 else syntheticName(idx)
268+
}.toList
269+
250270
/** Form a denotation by conjoining with denotation `that`.
251271
*
252272
* NoDenotations are dropped. MultiDenotations are handled by merging
@@ -277,6 +297,50 @@ object Denotations {
277297
*/
278298
def & (that: Denotation, pre: Type, safeIntersection: Boolean = false)(implicit ctx: Context): Denotation = {
279299

300+
/** Normally, `tp1 & tp2`. Special cases for matching methods and classes, with
301+
* the possibility of raising a merge error.
302+
*/
303+
def infoMeet(tp1: Type, tp2: Type): Type = {
304+
if (tp1 eq tp2) tp1
305+
else tp1 match {
306+
case tp1: TypeBounds =>
307+
tp2 match {
308+
case tp2: TypeBounds => if (safeIntersection) tp1 safe_& tp2 else tp1 & tp2
309+
case tp2: ClassInfo if tp1 contains tp2 => tp2
310+
case _ => mergeConflict(tp1, tp2)
311+
}
312+
case tp1: ClassInfo =>
313+
tp2 match {
314+
case tp2: ClassInfo if tp1.cls eq tp2.cls => tp1.derivedClassInfo(tp1.prefix & tp2.prefix)
315+
case tp2: TypeBounds if tp2 contains tp1 => tp1
316+
case _ => mergeConflict(tp1, tp2)
317+
}
318+
case tp1 @ MethodType(names1, formals1) if isTerm =>
319+
tp2 match {
320+
case tp2 @ MethodType(names2, formals2) if ctx.typeComparer.matchingParams(formals1, formals2, tp1.isJava, tp2.isJava) &&
321+
tp1.isImplicit == tp2.isImplicit =>
322+
tp1.derivedMethodType(
323+
mergeNames(names1, names2, nme.syntheticParamName),
324+
formals1,
325+
infoMeet(tp1.resultType, tp2.resultType.subst(tp2, tp1)))
326+
case _ =>
327+
mergeConflict(tp1, tp2)
328+
}
329+
case tp1: PolyType if isTerm =>
330+
tp2 match {
331+
case tp2: PolyType if ctx.typeComparer.matchingTypeParams(tp1, tp2) =>
332+
tp1.derivedPolyType(
333+
mergeNames(tp1.paramNames, tp2.paramNames, tpnme.syntheticTypeParamName),
334+
tp1.paramBounds,
335+
infoMeet(tp1.resultType, tp2.resultType.subst(tp2, tp1)))
336+
case _: MethodicType =>
337+
mergeConflict(tp1, tp2)
338+
}
339+
case _ =>
340+
tp1 & tp2
341+
}
342+
}
343+
280344
/** Try to merge denot1 and denot2 without adding a new signature. */
281345
def mergeDenot(denot1: Denotation, denot2: SingleDenotation): Denotation = denot1 match {
282346
case denot1 @ MultiDenotation(denot11, denot12) =>
@@ -289,95 +353,94 @@ object Denotations {
289353
}
290354
case denot1: SingleDenotation =>
291355
if (denot1 eq denot2) denot1
292-
else if (denot1.matches(denot2)) {
293-
val info1 = denot1.info
294-
val info2 = denot2.info
295-
val sym1 = denot1.symbol
296-
val sym2 = denot2.symbol
297-
298-
val sym2Accessible = sym2.isAccessibleFrom(pre)
299-
300-
/** Does `sym1` come before `sym2` in the linearization of `pre`? */
301-
def precedes(sym1: Symbol, sym2: Symbol) = {
302-
def precedesIn(bcs: List[ClassSymbol]): Boolean = bcs match {
303-
case bc :: bcs1 => (sym1 eq bc) || !(sym2 eq bc) && precedesIn(bcs1)
304-
case Nil => true
305-
}
306-
(sym1 ne sym2) &&
307-
(sym1.derivesFrom(sym2) ||
308-
!sym2.derivesFrom(sym1) && precedesIn(pre.baseClasses))
309-
}
356+
else if (denot1.matches(denot2)) mergeSingleDenot(denot1, denot2)
357+
else NoDenotation
358+
}
310359

311-
/** Similar to SymDenotation#accessBoundary, but without the special cases. */
312-
def accessBoundary(sym: Symbol) =
313-
if (sym.is(Private)) sym.owner
314-
else sym.privateWithin.orElse(
315-
if (sym.is(Protected)) sym.owner.enclosingPackageClass
316-
else defn.RootClass
317-
)
318-
319-
/** Establish a partial order "preference" order between symbols.
320-
* Give preference to `sym1` over `sym2` if one of the following
321-
* conditions holds, in decreasing order of weight:
322-
* 1. sym1 is concrete and sym2 is abstract
323-
* 2. The owner of sym1 comes before the owner of sym2 in the linearization
324-
* of the type of the prefix `pre`.
325-
* 3. The access boundary of sym2 is properly contained in the access
326-
* boundary of sym1. For protected access, we count the enclosing
327-
* package as access boundary.
328-
* 4. sym1 a method but sym2 is not.
329-
* The aim of these criteria is to give some disambiguation on access which
330-
* - does not depend on textual order or other arbitrary choices
331-
* - minimizes raising of doubleDef errors
332-
*/
333-
def preferSym(sym1: Symbol, sym2: Symbol) =
334-
sym1.eq(sym2) ||
335-
sym1.isAsConcrete(sym2) &&
336-
(!sym2.isAsConcrete(sym1) ||
337-
precedes(sym1.owner, sym2.owner) ||
338-
accessBoundary(sym2).isProperlyContainedIn(accessBoundary(sym1)) ||
339-
sym1.is(Method) && !sym2.is(Method))
340-
341-
/** Sym preference provided types also override */
342-
def prefer(sym1: Symbol, sym2: Symbol, info1: Type, info2: Type) =
343-
preferSym(sym1, sym2) && info1.overrides(info2)
344-
345-
def handleDoubleDef =
346-
if (preferSym(sym1, sym2)) denot1
347-
else if (preferSym(sym2, sym1)) denot2
348-
else doubleDefError(denot1, denot2, pre)
349-
350-
if (sym2Accessible && prefer(sym2, sym1, info2, info1)) denot2
351-
else {
352-
val sym1Accessible = sym1.isAccessibleFrom(pre)
353-
if (sym1Accessible && prefer(sym1, sym2, info1, info2)) denot1
354-
else if (sym1Accessible && sym2.exists && !sym2Accessible) denot1
355-
else if (sym2Accessible && sym1.exists && !sym1Accessible) denot2
356-
else if (isDoubleDef(sym1, sym2)) handleDoubleDef
357-
else {
358-
val sym =
359-
if (!sym1.exists) sym2
360-
else if (!sym2.exists) sym1
361-
else if (preferSym(sym2, sym1)) sym2
362-
else sym1
363-
val jointInfo =
364-
try
365-
if (safeIntersection)
366-
info1 safe_& info2
367-
else
368-
info1 & info2
369-
catch {
370-
case ex: MergeError =>
371-
if (pre.widen.classSymbol.is(Scala2x) || ctx.scala2Mode)
372-
info1 // follow Scala2 linearization -
373-
// compare with way merge is performed in SymDenotation#computeMembersNamed
374-
else
375-
throw new MergeError(s"${ex.getMessage} as members of type ${pre.show}", ex.tp1, ex.tp2)
376-
}
377-
new JointRefDenotation(sym, jointInfo, denot1.validFor & denot2.validFor)
360+
/** Try to merge single-denotations. */
361+
def mergeSingleDenot(denot1: SingleDenotation, denot2: SingleDenotation): SingleDenotation = {
362+
val info1 = denot1.info
363+
val info2 = denot2.info
364+
val sym1 = denot1.symbol
365+
val sym2 = denot2.symbol
366+
367+
val sym2Accessible = sym2.isAccessibleFrom(pre)
368+
369+
/** Does `sym1` come before `sym2` in the linearization of `pre`? */
370+
def precedes(sym1: Symbol, sym2: Symbol) = {
371+
def precedesIn(bcs: List[ClassSymbol]): Boolean = bcs match {
372+
case bc :: bcs1 => (sym1 eq bc) || !(sym2 eq bc) && precedesIn(bcs1)
373+
case Nil => true
374+
}
375+
(sym1 ne sym2) &&
376+
(sym1.derivesFrom(sym2) ||
377+
!sym2.derivesFrom(sym1) && precedesIn(pre.baseClasses))
378+
}
379+
380+
/** Similar to SymDenotation#accessBoundary, but without the special cases. */
381+
def accessBoundary(sym: Symbol) =
382+
if (sym.is(Private)) sym.owner
383+
else sym.privateWithin.orElse(
384+
if (sym.is(Protected)) sym.owner.enclosingPackageClass
385+
else defn.RootClass)
386+
387+
/** Establish a partial order "preference" order between symbols.
388+
* Give preference to `sym1` over `sym2` if one of the following
389+
* conditions holds, in decreasing order of weight:
390+
* 1. sym1 is concrete and sym2 is abstract
391+
* 2. The owner of sym1 comes before the owner of sym2 in the linearization
392+
* of the type of the prefix `pre`.
393+
* 3. The access boundary of sym2 is properly contained in the access
394+
* boundary of sym1. For protected access, we count the enclosing
395+
* package as access boundary.
396+
* 4. sym1 a method but sym2 is not.
397+
* The aim of these criteria is to give some disambiguation on access which
398+
* - does not depend on textual order or other arbitrary choices
399+
* - minimizes raising of doubleDef errors
400+
*/
401+
def preferSym(sym1: Symbol, sym2: Symbol) =
402+
sym1.eq(sym2) ||
403+
sym1.isAsConcrete(sym2) &&
404+
(!sym2.isAsConcrete(sym1) ||
405+
precedes(sym1.owner, sym2.owner) ||
406+
accessBoundary(sym2).isProperlyContainedIn(accessBoundary(sym1)) ||
407+
sym1.is(Method) && !sym2.is(Method))
408+
409+
/** Sym preference provided types also override */
410+
def prefer(sym1: Symbol, sym2: Symbol, info1: Type, info2: Type) =
411+
preferSym(sym1, sym2) && info1.overrides(info2)
412+
413+
def handleDoubleDef =
414+
if (preferSym(sym1, sym2)) denot1
415+
else if (preferSym(sym2, sym1)) denot2
416+
else doubleDefError(denot1, denot2, pre)
417+
418+
if (sym2Accessible && prefer(sym2, sym1, info2, info1)) denot2
419+
else {
420+
val sym1Accessible = sym1.isAccessibleFrom(pre)
421+
if (sym1Accessible && prefer(sym1, sym2, info1, info2)) denot1
422+
else if (sym1Accessible && sym2.exists && !sym2Accessible) denot1
423+
else if (sym2Accessible && sym1.exists && !sym1Accessible) denot2
424+
else if (isDoubleDef(sym1, sym2)) handleDoubleDef
425+
else {
426+
val sym =
427+
if (!sym1.exists) sym2
428+
else if (!sym2.exists) sym1
429+
else if (preferSym(sym2, sym1)) sym2
430+
else sym1
431+
val jointInfo =
432+
try infoMeet(info1, info2)
433+
catch {
434+
case ex: MergeError =>
435+
if (pre.widen.classSymbol.is(Scala2x) || ctx.scala2Mode)
436+
info1 // follow Scala2 linearization -
437+
// compare with way merge is performed in SymDenotation#computeMembersNamed
438+
else
439+
throw new MergeError(s"${ex.getMessage} as members of type ${pre.show}", ex.tp1, ex.tp2)
378440
}
379-
}
380-
} else NoDenotation
441+
new JointRefDenotation(sym, jointInfo, denot1.validFor & denot2.validFor)
442+
}
443+
}
381444
}
382445

383446
if (this eq that) this
@@ -398,6 +461,46 @@ object Denotations {
398461
*/
399462
def | (that: Denotation, pre: Type)(implicit ctx: Context): Denotation = {
400463

464+
/** Normally, `tp1 | tp2`. Special cases for matching methods and classes, with
465+
* the possibility of raising a merge error.
466+
*/
467+
def infoJoin(tp1: Type, tp2: Type): Type = tp1 match {
468+
case tp1: TypeBounds =>
469+
tp2 match {
470+
case tp2: TypeBounds => tp1 | tp2
471+
case tp2: ClassInfo if tp1 contains tp2 => tp1
472+
case _ => mergeConflict(tp1, tp2)
473+
}
474+
case tp1: ClassInfo =>
475+
tp2 match {
476+
case tp2: ClassInfo if tp1.cls eq tp2.cls => tp1.derivedClassInfo(tp1.prefix | tp2.prefix)
477+
case tp2: TypeBounds if tp2 contains tp1 => tp2
478+
case _ => mergeConflict(tp1, tp2)
479+
}
480+
case tp1 @ MethodType(names1, formals1) =>
481+
tp2 match {
482+
case tp2 @ MethodType(names2, formals2)
483+
if ctx.typeComparer.matchingParams(formals1, formals2, tp1.isJava, tp2.isJava) &&
484+
tp1.isImplicit == tp2.isImplicit =>
485+
tp1.derivedMethodType(
486+
mergeNames(names1, names2, nme.syntheticParamName),
487+
formals1, tp1.resultType | tp2.resultType.subst(tp2, tp1))
488+
case _ =>
489+
mergeConflict(tp1, tp2)
490+
}
491+
case tp1: PolyType =>
492+
tp2 match {
493+
case tp2: PolyType if ctx.typeComparer.matchingTypeParams(tp1, tp2) =>
494+
tp1.derivedPolyType(
495+
mergeNames(tp1.paramNames, tp2.paramNames, tpnme.syntheticTypeParamName),
496+
tp1.paramBounds, tp1.resultType | tp2.resultType.subst(tp2, tp1))
497+
case _ =>
498+
mergeConflict(tp1, tp2)
499+
}
500+
case _ =>
501+
tp1 | tp2
502+
}
503+
401504
def unionDenot(denot1: SingleDenotation, denot2: SingleDenotation): Denotation =
402505
if (denot1.matches(denot2)) {
403506
val sym1 = denot1.symbol
@@ -427,7 +530,8 @@ object Denotations {
427530
}
428531
lubSym(sym1.allOverriddenSymbols, NoSymbol)
429532
}
430-
new JointRefDenotation(jointSym, info1 | info2, denot1.validFor & denot2.validFor)
533+
new JointRefDenotation(
534+
jointSym, infoJoin(info1, info2), denot1.validFor & denot2.validFor)
431535
}
432536
}
433537
else NoDenotation
@@ -1133,5 +1237,4 @@ object Denotations {
11331237
util.Stats.record("not defined here")
11341238
override def getMessage() = msg
11351239
}
1136-
}
1137-
1240+
}

0 commit comments

Comments
 (0)