Skip to content

Commit 6807b44

Browse files
oderskysmarter
authored andcommitted
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 5456c8b commit 6807b44

File tree

2 files changed

+196
-176
lines changed

2 files changed

+196
-176
lines changed

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

Lines changed: 194 additions & 91 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,96 +353,95 @@ 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-
sym1.info.isErroneous
341-
342-
/** Sym preference provided types also override */
343-
def prefer(sym1: Symbol, sym2: Symbol, info1: Type, info2: Type) =
344-
preferSym(sym1, sym2) && info1.overrides(info2)
345-
346-
def handleDoubleDef =
347-
if (preferSym(sym1, sym2)) denot1
348-
else if (preferSym(sym2, sym1)) denot2
349-
else doubleDefError(denot1, denot2, pre)
350-
351-
if (sym2Accessible && prefer(sym2, sym1, info2, info1)) denot2
352-
else {
353-
val sym1Accessible = sym1.isAccessibleFrom(pre)
354-
if (sym1Accessible && prefer(sym1, sym2, info1, info2)) denot1
355-
else if (sym1Accessible && sym2.exists && !sym2Accessible) denot1
356-
else if (sym2Accessible && sym1.exists && !sym1Accessible) denot2
357-
else if (isDoubleDef(sym1, sym2)) handleDoubleDef
358-
else {
359-
val sym =
360-
if (!sym1.exists) sym2
361-
else if (!sym2.exists) sym1
362-
else if (preferSym(sym2, sym1)) sym2
363-
else sym1
364-
val jointInfo =
365-
try
366-
if (safeIntersection)
367-
info1 safe_& info2
368-
else
369-
info1 & info2
370-
catch {
371-
case ex: MergeError =>
372-
if (pre.widen.classSymbol.is(Scala2x) || ctx.scala2Mode)
373-
info1 // follow Scala2 linearization -
374-
// compare with way merge is performed in SymDenotation#computeMembersNamed
375-
else
376-
throw new MergeError(s"${ex.getMessage} as members of type ${pre.show}", ex.tp1, ex.tp2)
377-
}
378-
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+
sym1.info.isErroneous
409+
410+
/** Sym preference provided types also override */
411+
def prefer(sym1: Symbol, sym2: Symbol, info1: Type, info2: Type) =
412+
preferSym(sym1, sym2) && info1.overrides(info2)
413+
414+
def handleDoubleDef =
415+
if (preferSym(sym1, sym2)) denot1
416+
else if (preferSym(sym2, sym1)) denot2
417+
else doubleDefError(denot1, denot2, pre)
418+
419+
if (sym2Accessible && prefer(sym2, sym1, info2, info1)) denot2
420+
else {
421+
val sym1Accessible = sym1.isAccessibleFrom(pre)
422+
if (sym1Accessible && prefer(sym1, sym2, info1, info2)) denot1
423+
else if (sym1Accessible && sym2.exists && !sym2Accessible) denot1
424+
else if (sym2Accessible && sym1.exists && !sym1Accessible) denot2
425+
else if (isDoubleDef(sym1, sym2)) handleDoubleDef
426+
else {
427+
val sym =
428+
if (!sym1.exists) sym2
429+
else if (!sym2.exists) sym1
430+
else if (preferSym(sym2, sym1)) sym2
431+
else sym1
432+
val jointInfo =
433+
try infoMeet(info1, info2)
434+
catch {
435+
case ex: MergeError =>
436+
if (pre.widen.classSymbol.is(Scala2x) || ctx.scala2Mode)
437+
info1 // follow Scala2 linearization -
438+
// compare with way merge is performed in SymDenotation#computeMembersNamed
439+
else
440+
throw new MergeError(s"${ex.getMessage} as members of type ${pre.show}", ex.tp1, ex.tp2)
379441
}
380-
}
381-
} else NoDenotation
442+
new JointRefDenotation(sym, jointInfo, denot1.validFor & denot2.validFor)
443+
}
444+
}
382445
}
383446

384447
if (this eq that) this
@@ -399,6 +462,46 @@ object Denotations {
399462
*/
400463
def | (that: Denotation, pre: Type)(implicit ctx: Context): Denotation = {
401464

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

0 commit comments

Comments
 (0)